Files
MobileGL/MobileGL/MG_Pipe/PipeVerbSink.h
T

238 lines
12 KiB
C++

// MobileGL - MobileGL/MG_Pipe/PipeVerbSink.h
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
// P13 W5: THE VERB SINK AND ITS REPLIES, WITHOUT THE WIRE. These were the wire codec's
// (MG_Remote/Wire/PipeWireCodec.h, which keeps their old names as aliases): the decoder hands each
// validated verb to a sink, and the sink answers through a reply sink. The record arm's in-process
// port does exactly the same in a library that has no wire, so the interfaces live here.
#pragma once
#include <Includes.h>
#include <MG_Pipe/MGPipe.h>
namespace MobileGL::MG_Pipe {
// One tail array. Two of the rows carry two (SetShaderBuffers, SetStreamOutputTargets),
// DrawVbo carries a conditional second one and P5e's SetProgramBindings carries three,
// which is why EncodeRecord's one-tail form could not stay the only one.
struct MGPipeVerbTail {
const void* Bytes = nullptr;
Uint64 Size = 0;
};
// Where a kReplySlot answer goes. Declared HERE and not in Server/ so the codec does not
// depend on the server session: the decoder's job ends at "produce the answer bytes".
//
// The slot is addressed seq % slots and the server writes the seq back into the slot
// header for self-check (table 0's slot header row). Status: 0 = OK, 1 = DECLINED,
// 2 = ERROR. DECLINED IS A REAL ANSWER, not a failure - it is how MapPersistent says
// nullptr (R-6) and how the four Bool acceptance entry points say false (R-5).
class MGPipeReplySink {
public:
virtual ~MGPipeReplySink() = default;
static constexpr Int32 kStatusOk = 0;
static constexpr Int32 kStatusDeclined = 1;
static constexpr Int32 kStatusError = 2;
virtual void PostReply(Uint64 seq, Int32 status, const void* bytes, Uint64 size) = 0;
};
// ---- the verb sink: the rows with no MGPipeApply* entry point to delegate to ------------
//
// The decoder owns no semantics. A row with an applier entry point ends in its MGPipeApply*
// function; the GLFunctionsTable verbs, fences, queries and control records are bounds-checked,
// tail-checked and segment-resolved, then handed to the sink below as a validated argument
// list. With no sink installed those arms return false ("this build does not implement it"),
// as does every row with neither an entry point nor a sink method, once validated
// (SetStreamOutputTargets checks both its tails first).
struct MGPipeQueryResultReply {
Uint64 Value = 0;
Uint32 Produced = 0;
Uint32 Reserved = 0;
};
static_assert(sizeof(MGPipeQueryResultReply) == 16);
class MGPipeVerbSink {
public:
virtual ~MGPipeVerbSink() = default;
// P11 B2 (CONTRACT-P11.md B2): map_persistent in a session that runs T0. True = the sink
// handled it and `status` is the answer (OK: the client's AHardwareBuffer is now the
// store; DECLINED: the store runs T2); false = not a T0 session, and the codec gives
// today's constant decline. `seq` is the record's, which is what the store's Offer names.
// `inProcessBase`: a client in this process gets the donated store's address in the OK
// answer (kCapAdoptInProcess); 0 = the answer carries no bytes (the AHardwareBuffer arm).
virtual Bool OnMapPersistent(const MGPHandleOnly&, Uint64 /*seq*/, Int32& /*status*/,
Uint64& /*inProcessBase*/) {
return false;
}
virtual Bool OnFenceCreate(const MGPHandleOnly&) { return false; }
virtual Bool OnFenceDestroy(const MGPHandleOnly&) { return false; }
virtual Bool OnFenceStatus(const MGPHandleOnly&, Uint32&) { return false; }
virtual Bool OnFenceWait(const MGPFenceWait&, Uint32&) { return false; }
virtual Bool OnFenceWaitServer(const MGPFenceWait&) { return false; }
virtual Bool OnQueryCreate(const MGPQueryDesc&) { return false; }
virtual Bool OnQueryBegin(const MGPQueryDesc&) { return false; }
virtual Bool OnQueryEnd(const MGPQueryDesc&) { return false; }
virtual Bool OnQueryCounter(const MGPQueryDesc&) { return false; }
virtual Bool OnQueryAvailable(const MGPHandleOnly&, Uint32&) { return false; }
virtual Bool OnQueryResult(const MGPQueryResultRequest&, MGPipeQueryResultReply&) { return false; }
virtual Bool OnQueryDestroy(const MGPHandleOnly&) { return false; }
virtual Bool OnQueryTimestamp(const MGPTimestampRequest&, Int64&) { return false; }
virtual Bool OnDeleteStreamOutput(const MGPStreamOutputBind&) { return false; }
virtual Bool OnClear(const MGPClear& clear) {
(void)clear;
return false;
}
virtual Bool OnBlit(const MGPBlit& blit) {
(void)blit;
return false;
}
virtual Bool OnPresent(const MGPPresent& present) {
(void)present;
return false;
}
// The pixels go back in the reply slot (contract table 1 row 23: the destination is
// ALWAYS SEG_REPLY in P5, which is why MGPReadbackInfo gains no Seg field), so the
// sink is handed the seq and the sink it must answer into.
virtual Bool OnReadPixels(const MGPReadbackInfo& info, Uint64 seq, MGPipeReplySink* replies) {
(void)info;
(void)seq;
(void)replies;
return false;
}
virtual Bool OnGetTextureImage(const MGPReadbackInfo&, Uint64, MGPipeReplySink*) {
return false;
}
// P9 (CONTRACT-P9.md §1): the same two reads with a pack buffer as the destination. No
// seq and no sink - nothing is answered; the rows land in `Dst` on this side.
virtual Bool OnReadPixelsToBuffer(const MGPReadbackToBuffer&) { return false; }
virtual Bool OnGetTextureImageToBuffer(const MGPReadbackToBuffer&) { return false; }
// `ranges` is info.NumDraws entries. `userIndices` is null unless the record set
// kDrawHasUserIndices; `indirect` is null unless it set kDrawIsIndirect (P5b d1,
// CONTRACT-P5B.md). The layout refuses a record that sets both, so at most one of the
// two is non-null. The span is VALIDATED (all four R-2 arms, the segment-range one
// included) and names a SEG_STAGE run the client staged; the sink resolves it through
// MGPipeHostBytes and never holds the pointer past its return (rule C).
virtual Bool OnDrawVbo(const MGPDrawInfo& info, const MGPDrawRange* ranges,
const MGHostSpan* userIndices,
const MGPDrawIndirect* indirect) {
(void)info;
(void)ranges;
(void)userIndices;
(void)indirect;
return false;
}
// ---- the remaining verbs with no MGPipeApply* entry point: dispatch, barriers, copies,
// image and storage-block binds, transform feedback, patch parameter and mipmaps.
//
// Same hand-over as the rows above: the codec validates the record, and the backend call is
// made by MG_Record::RecordVerbSink, the base of the server's ServerVerbSink.
virtual Bool OnLaunchGrid(const MGPGridInfo& grid) {
(void)grid;
return false;
}
virtual Bool OnMemoryBarrier(const MGPMemoryBarrier& barrier) {
(void)barrier;
return false;
}
virtual Bool OnResourceCopyRegion(const MGPCopyRegion& copy) {
(void)copy;
return false;
}
virtual Bool OnBindShaderImage(const MGPImageBind& bind) {
(void)bind;
return false;
}
// `name` is the NUL-terminated block name the decoder copied out of the record's
// SEG_STAGE blob; valid for the call only.
virtual Bool OnSetStorageBlockBinding(const MGPStorageBlockBinding& binding,
const char* name) {
(void)binding;
(void)name;
return false;
}
virtual Bool OnBeginStreamOutput(const MGPStreamOutputBegin& begin) {
(void)begin;
return false;
}
virtual Bool OnEndStreamOutput(const MGPXfbAccounting& accounting) {
(void)accounting;
return false;
}
virtual Bool OnPauseStreamOutput(const MGPStreamOutputControl& control) {
(void)control;
return false;
}
virtual Bool OnResumeStreamOutput(const MGPStreamOutputControl& control) {
(void)control;
return false;
}
virtual Bool OnBindStreamOutput(const MGPStreamOutputBind& bind) {
(void)bind;
return false;
}
virtual Bool OnPatchParameter(const MGPPatchParameter& patch) {
(void)patch;
return false;
}
virtual Bool OnGenerateMipmap(const MGPMipPlan& plan) {
(void)plan;
return false;
}
virtual Bool OnCopyFramebufferToTexture(const MGPCopyFromFramebuffer& copy) {
(void)copy;
return false;
}
// ---- P5c (docs/archive/contracts/CONTRACT-P5C.md §5): the two control records, opcodes 77..78.
//
// Same hand-over shape as the P5b rows: the codec validates the record (a fixed-size
// POD, no blob, no tail) and the SERVER'S sink does the work - OnApplierReset runs the
// server's own MGPipeApplierReset() after asserting ContextSerial against the
// session's (§5.1: ASSERTED, never dispatched on, P5c has one context per session),
// and OnObjectDeath releases the kind's twin table by the handle the record carried
// (§5.2). The default bodies return false ("this build does not implement it"), so a
// unit decoder without a server declines by the same answer every other unimplemented
// row gives.
virtual Bool OnApplierReset(const MGPApplierReset& reset) {
(void)reset;
return false;
}
virtual Bool OnObjectDeath(const MGPHandleOnly& death) {
(void)death;
return false;
}
// ---- P14 S1 (docs/Disaggregated/design/11-state-ownership.md): bind_context, opcode 84.
//
// The applier_reset hand-over shape, one plane over: a fixed-size POD the bounds gate has
// already proved, no blob, no tail, no reply, so the codec validates nothing further and
// the SERVER'S sink does the work - the session's current context token becomes the
// record's. It is not an assertion like OnApplierReset's serial: the token is minted by
// the client's EGL layer and the server has no independent count of it, so an unknown
// token is the sink's own refusal to make and not a shape the codec can check.
virtual Bool OnBindContext(const MGPBindContext& bind) {
(void)bind;
return false;
}
// ---- shared images, opcode 85 (docs/archive/notes/anland/plan-ahb-dmabuf.md).
// `seq` is the record's reply-slot id, which an Import's descriptor names on the aux
// socket. True fills `reply`; false is a DECLINED answer.
virtual Bool OnSharedImage(const MGPSharedImageOp& op, Uint64 seq,
MGPSharedImageReply& reply) {
(void)op;
(void)seq;
(void)reply;
return false;
}
};
} // namespace MobileGL::MG_Pipe