diff --git a/MobileGL/MG_Pipe/FieldOwnership.def b/MobileGL/MG_Pipe/FieldOwnership.def index 673dd15b..beeca7c4 100644 --- a/MobileGL/MG_Pipe/FieldOwnership.def +++ b/MobileGL/MG_Pipe/FieldOwnership.def @@ -236,17 +236,20 @@ // row below with a reason. "Verb-shaped" is the union of two mechanical tests, both derived: // (a) PipeCalls.def gives the call kind kCtxVerb - the catalogue's own word for it; // (b) the call's name is also an MGPipeVerb name in FillPoints.def. -// Twelve rows and three exemptions cover all fifteen. Only four of the twelve can arrive in P5 -// (CONTRACT §7 class B minus Present); the other eight are Fatal{UnmigratedVerb} today and are -// mapped ANYWAY, because the failure mode of an absent row is silent: the applier would run the -// record under the PREVIOUS verb's serial, mask and name, and a field inside that mask would -// read FRESH while holding the previous verb's value. +// Eighteen rows and three exemptions cover all twenty-one (P5: twelve and three; P5b appended +// the five kCtxVerb rows 72..76 and settled ResourceCopyRegion). Only four of the rows could +// arrive in P5 (CONTRACT §7 class B minus Present); the rest are Fatal{UnmigratedVerb} on the +// client until a P5b migration package flips them and are mapped ANYWAY, because the failure +// mode of an absent row is silent: the applier would run the record under the PREVIOUS verb's +// serial, mask and name, and a field inside that mask would read FRESH while holding the +// previous verb's value. // // NOT MECHANICALLY DETECTABLE, and so not claimed: a call that is a verb boundary, is not -// kCtxVerb, and whose name differs from its verb's. `ResourceCopyRegion` is the one in the tree -// (its verbs are CopyImageSubData / CopyTexSubImage2D / CopyTexImage2D - three of them, which is -// why it cannot be a row without a rule for choosing). The phase that emits it adds its row and -// decides which verb it is. +// kCtxVerb, and whose name differs from its verb's. `ResourceCopyRegion` WAS the one in the +// tree (its verbs could have been CopyImageSubData / CopyTexSubImage2D / CopyTexImage2D). P5b +// decided: resource_copy_region is glCopyImageSubData only, and the two framebuffer-sourced +// copies have their own row (copy_framebuffer_to_texture), so the choice is a row here rather +// than a rule in a reader's head. #define MGP_VERB_OP_LIST(X) \ /* CONTRACT §7 class B - the four that can actually arrive in P5. */ \ X(Clear, Clear) \ @@ -263,7 +266,20 @@ X(PauseStreamOutput, PauseTransformFeedback) \ X(ResumeStreamOutput, ResumeTransformFeedback) \ X(GenerateMipmap, GenerateMipmap) \ - X(GetTextureImage, GetTextureImage) + X(GetTextureImage, GetTextureImage) \ + /* P5b (MG_Remote/CONTRACT-P5B.md). The ONE renamed, non-kCtxVerb boundary the file above */ \ + /* said "the phase that emits it adds its row and decides which verb it is": P5b rules */ \ + /* resource_copy_region = glCopyImageSubData ONLY; the two framebuffer-sourced copies */ \ + /* (CopyTexImage2D / CopyTexSubImage2D) are copy_framebuffer_to_texture below, stamped as */ \ + /* CopyTexImage2D - both are kBlitOrCopy, one mask, and a row can name one verb. */ \ + X(ResourceCopyRegion, CopyImageSubData) \ + /* The five P5b-appended verbs, opcodes 72..76, each stamped as the GLFunctionsTable verb */ \ + /* it reproduces. */ \ + X(BindShaderImage, BindImageTexture) \ + X(PatchParameter, PatchParameteri) \ + X(BindStreamOutput, BindTransformFeedback) \ + X(SetStorageBlockBinding, ShaderStorageBlockBinding) \ + X(CopyFramebufferToTexture, CopyTexImage2D) // X(Op, Why) - verb-shaped calls that are deliberately NOT stamp points. // diff --git a/MobileGL/MG_Pipe/MGPipeTypes.h b/MobileGL/MG_Pipe/MGPipeTypes.h index b97fc229..0c3ec042 100644 --- a/MobileGL/MG_Pipe/MGPipeTypes.h +++ b/MobileGL/MG_Pipe/MGPipeTypes.h @@ -1247,15 +1247,39 @@ namespace MobileGL::MG_Pipe { }; MGP_ASSERT_POD(MGPReadback, 24); + // resource_copy_region = glCopyImageSubData (P5b, CONTRACT-P5B.md i1). The record had no + // producer and no consumer before P5b, so its shape is settled here for the first time: + // + // Src / Dst the two endpoints' handles - kind Texture, or kind Renderbuffer when the + // matching *Target is GL_RENDERBUFFER. The handle is the identity. + // SrcTarget / the GL target enum the APPLICATION named, verbatim (GL_TEXTURE_2D, + // DstTarget GL_TEXTURE_CUBE_MAP, GL_RENDERBUFFER, ...); every one fits a Uint16. + // Espryt's own MakeGLESCopyImageEndpoint does the ES lowering + // (DirectGLES.cpp) and must keep seeing the enum the app passed. + // SrcBox {SrcX, SrcY, SrcZ, Width, Height, Depth} of the copied region. + // SrcGlName / THE P5b DEBT, stated rather than hidden. GLFunctionsTable:: + // DstGlName CopyImageSubData takes two CopyImageEndpoints, each a frontend SharedPtr, + // and the server has none. Under inproc and the verb barrier the server + // rebuilds a TEXTURE endpoint from the GL name through the barrier-pulled + // sticky forward MGB_CTX->GetTextureObject(name) (counted in `rsp`, + // retired by P7 / P3b-P4b when the backend takes handles). A GL name is + // NEVER an identity (section 4.2.1); it is the lookup key of a pull the + // handle beside it will replace. No forward hands out a renderbuffer, + // so a RENDERBUFFER endpoint is refused by name in P5b + // (Fatal{UnmigratedVerb, "CopyImageSubData+RENDERBUFFER"}, ID-57's shape). + // + // 64 -> 72: the two names did not fit the four pad bytes, and the record had no wire history + // to preserve. struct MGPCopyRegion { MGPipeHandle Src, Dst; MGPBox SrcBox; Int32 DstX, DstY, DstZ; Uint16 SrcTarget, DstTarget; Uint16 SrcLevel, DstLevel; + Uint32 SrcGlName, DstGlName; Uint32 Pad0; }; - MGP_ASSERT_POD(MGPCopyRegion, 64); + MGP_ASSERT_POD(MGPCopyRegion, 72); struct MGPBlit { MGPipeHandle ReadFbo, DrawFbo; @@ -1270,16 +1294,61 @@ namespace MobileGL::MG_Pipe { // glClearNamedFramebuffer* entry points (section 4.4.4). struct MGPClear { MGPipeHandle Fbo; - Uint32 Kind; // Whole | Color | Depth | Stencil | DepthStencil + Uint32 Kind; // kMGPipeClearKind*, below Int32 DrawBufferIndex; Uint32 BufferMask; // GL_COLOR_BUFFER_BIT etc. for the whole-framebuffer form - Uint32 ValueClass; // Float | Int | Uint + Uint32 ValueClass; // kMGPipeClearValueClass*, below Uint32 ColorValue[4]; Float DepthValue; Int32 StencilValue; }; MGP_ASSERT_POD(MGPClear, 48); + // MGPClear's two discriminants, and THE ONLY SPELLING OF THEM (P5b, CONTRACT-P5B.md f1). + // Through P5 the list lived in this struct's comment and both the client emitter + // (MG_Remote/Client/EmitTables.h) and the server sink (MG_Remote/Server/PipeApplier.h) + // minted their own copy of the numbers, each flagged "this belongs in MGPipeTypes.h". A + // disagreement between the two copies is a clear of the wrong attachment with the wrong + // value type, which renders plausibly - table 0's "a decoder that open-codes it is the + // class-1 defect". Both files now alias these. + // + // Kind is the comment's order, left to right. ValueClass reuses the numbering + // MG_State/GLState/Core.h gives the identical three-way split on MGPAttribValue::ValueClass. + // + // WHICH GL ENTRY POINT PRODUCES WHICH KIND, so the emitter and the sink cannot disagree: + // glClear(mask) Whole, BufferMask = mask, DrawBufferIndex = -1 + // glClearBuffer{f,i,ui}v(GL_COLOR, i, v) Color, DrawBufferIndex = i, ValueClass by suffix + // glClearBufferfv(GL_DEPTH, 0, &d) Depth, DepthValue = d + // glClearBufferiv(GL_STENCIL, 0, &s) Stencil, StencilValue = s + // glClearBufferfi(GL_DEPTH_STENCIL, 0, d, s) DepthStencil, DepthValue = d, StencilValue = s + // The four glClearNamedFramebuffer* forms are the same rows with Fbo = the named + // framebuffer's handle (preceded by its MGPipeFramebufferTarget::Named record, ID-19); + // the bound forms carry Fbo = kMGPipeNullHandle, "the bound draw framebuffer". + inline constexpr Uint32 kMGPipeClearKindWhole = 0; + inline constexpr Uint32 kMGPipeClearKindColor = 1; + inline constexpr Uint32 kMGPipeClearKindDepth = 2; + inline constexpr Uint32 kMGPipeClearKindStencil = 3; + inline constexpr Uint32 kMGPipeClearKindDepthStencil = 4; + inline constexpr Uint32 kMGPipeClearKindCount = 5; + inline constexpr Uint32 kMGPipeClearValueClassFloat = 0; + inline constexpr Uint32 kMGPipeClearValueClassInt = 1; + inline constexpr Uint32 kMGPipeClearValueClassUint = 2; + inline constexpr Uint32 kMGPipeClearValueClassCount = 3; + + // generate_mipmap = glGenerateMipmap (P5b, CONTRACT-P5B.md f1). First producer and first + // consumer in P5b, so the fields are settled here: + // Res the texture bound to the active unit at `Target`, resolved on the client. The + // server's backend slot takes only the GL target and resolves the bound texture + // itself through the barrier-pulled unit state (kTextureOp's may-read mask), so + // Res is the P8 form's identity and is not what the P5b sink dispatches on. + // Target the GL texture target enum VERBATIM (GL_TEXTURE_2D 0x0DE1, GL_TEXTURE_3D + // 0x806F, GL_TEXTURE_CUBE_MAP 0x8513, GL_TEXTURE_2D_ARRAY 0x8C1A ...), which + // is what GLFunctionsTable::GenerateMipmap takes; every GL texture target fits + // a Uint16 and the sink passes it straight through. NOT MGPipeResourceTarget: + // there is no resource-target -> GL-enum inverse in the tree to spend on it. + // BaseLevel / the plan the client computed from GL_TEXTURE_BASE_LEVEL and the level count: + // LevelCount informational in P5b (the backend derives its own), load-bearing in P8's + // "generate_mipmap plan + CPU fallback texels" row. struct MGPMipPlan { MGPipeHandle Res; Uint16 Target, BaseLevel, LevelCount, Pad0; @@ -1307,6 +1376,14 @@ namespace MobileGL::MG_Pipe { kDrawIndicesAreClient = 1u << 2, kDrawHasIndexRange = 1u << 3, kDrawHasXfbCount = 1u << 4, + // P5b (CONTRACT-P5B.md d1): the draw's ranges come from a GL_DRAW_INDIRECT_BUFFER and + // the record's SECOND TAIL is one MGPDrawIndirect rather than an MGHostSpan. The two + // flags are EXCLUSIVE - an indirect draw takes its indices from the bound element + // buffer by GL rule, so it can never carry user indices - and a record that sets both + // is Fatal{ProtocolCorruption} at the layout. NumDraws is 0 for an indirect draw: the + // server never reads the indirect buffer to learn a count and the client never sends + // ranges it does not have. + kDrawIsIndirect = 1u << 5, }; // = pipe_draw_info. Today's twenty draw entry points collapse onto this one call, with @@ -1341,8 +1418,22 @@ namespace MobileGL::MG_Pipe { }; MGP_ASSERT_POD(MGPDrawRange, 12); - // Present when the draw is indirect. The client resolves the COUNT itself, so the - // server never reads an indirect command block to learn how many draws there are. + // Present when the draw is indirect: draw_vbo's second tail under kDrawIsIndirect (P5b, + // CONTRACT-P5B.md d1), 8-aligned behind the (empty) range tail exactly where the user-index + // span would sit. The client resolves the COUNT itself, so the server never reads an + // indirect command block to learn how many draws there are: + // glMultiDraw*Indirect DrawCount = drawcount, ParameterBuffer = null + // glMultiDraw*IndirectCount DrawCount = maxdrawcount, ParameterBuffer = the bound + // GL_PARAMETER_BUFFER, ParameterOffset = drawcount (the byte + // offset the GL call spells as GLintptr drawcount). The server's + // backend slot takes exactly that pair and reads the real count + // where it does today; nothing about the parameter buffer's + // CONTENT crosses. (ROADMAP P8's HostResolve.cpp is where a + // client-side count resolve would live if a backend ever needed + // the number rather than the buffer; no backend does.) + // Buffer / Offset / Stride the bound GL_DRAW_INDIRECT_BUFFER handle, the command byte + // offset the GL call passed as `indirect`, and the stride (0 = + // tightly packed, as GL spells it - the backend normalises). struct MGPDrawIndirect { MGPipeHandle Buffer; MGPipeHandle ParameterBuffer; @@ -1407,6 +1498,127 @@ namespace MobileGL::MG_Pipe { }; MGP_ASSERT_POD(MGPSwapInterval, 8); + // --------------------------------------------------------------------------------- + // P5b: the five appended rows (MG_Remote/CONTRACT-P5B.md). APPENDED, never inserted: + // the wire opcode is a call's position in PipeCalls.def, so these sit after + // FenceWaitServer at opcodes 72..76 and no earlier opcode moved. + // + // Every one of them is a GLFunctionsTable VERB the class-C census measured as a first + // blocker and for which no existing row could carry the call. Four of the five exist + // because the backend slot does REAL WORK AT THE CALL rather than at the next validate, + // so a validate-time set record (set_shader_images, set_patch_state) cannot stand in for + // it: Espryt's BindImageTexture syncs the unit's binding to the driver on the spot, + // PatchParameteri pushes glPatchParameteri, BindTransformFeedback rebinds the driver + // object and invalidates the capture-binding shadows, and ShaderStorageBlockBinding + // pushes a rebinding onto an already-built driver program. The fifth, + // copy_framebuffer_to_texture, is a copy WITH the read framebuffer as its source, which + // resource_copy_region (resource -> resource) cannot express. + // + // NONE OF THEM HAS AN MGPipeApply* ENTRY POINT AND NONE GAINS ONE. They reach + // MG_Remote::Wire::WireVerbSink like the five P5 class-B verbs (the census's correction: + // a verb reaches the sink, not an applier), and under monolith they have no producer at + // all - the backend's own slot is called directly, byte for byte as today (G2/G14). + // --------------------------------------------------------------------------------- + + // bind_shader_image = glBindImageTexture (P5b i1). The arguments VERBATIM plus the handle: + // Res the texture's handle (kind Texture); kMGPipeNullHandle for texture 0. + // Unit the image unit. + // GlName the GL texture name the app passed - what the ES backend slot is handed as + // `texture` and what it currently ignores ((void)texture); never an identity. + // Level / Layer / Layered / Access / Format the GL arguments verbatim. Access is the + // GL token (GL_READ_ONLY 0x88B8, GL_WRITE_ONLY 0x88B9, GL_READ_WRITE 0x88BA), + // NOT MGPImageView's three-value encoding: this record reproduces a call, and + // the encoding belongs to the validate-time set record, which still travels + // beside it. Format is the GL internal-format enum. + // The server's slot reads the unit's binding from the barrier-pulled GetImageTextureBinding + // (kTextureOp's mask) exactly as it does in monolith; the record is the verb boundary and + // the stamp, and the fields are what P7 / P3b-P4b will dispatch on instead. + struct MGPImageBind { + MGPipeHandle Res; + Uint32 Unit; + Uint32 GlName; + Int32 Level; + Int32 Layer; + Uint32 Access; + Uint32 Format; + Uint8 Layered; + Uint8 Pad0[7]; + }; + MGP_ASSERT_POD(MGPImageBind, 40); + + // patch_parameter = glPatchParameteri (P5b t2). Pname is GL_PATCH_VERTICES - the only + // pname that reaches the backend slot; the frontend answers GL_PATCH_DEFAULT_*_LEVEL itself + // (GL_Drawing.cpp's PatchParameterfv) and bakes them into set_patch_state. It is carried + // verbatim so the sink reproduces the call and not a reading of it. set_patch_state (op 43) + // still travels at the next validate: it is the applier's working-block copy, this is the + // driver push Espryt does at the call, and the two are two pushes today too. + struct MGPPatchParameter { + Uint32 Pname; + Int32 Value; + }; + MGP_ASSERT_POD(MGPPatchParameter, 8); + + // bind_stream_output = glBindTransformFeedback (P5b t2). The XFB object namespace is the + // frontend's and has no wire lifetime (no create/delete row); Espryt keys its driver + // objects by the GL NAME (XfbImpl::g_xfbObjects[name]) and the D21 rekey by the lifetime + // id, so both cross. Name 0 is the default object. A GL name is never an identity; here it + // is the key the backend has always used, carried so the backend need not change. + struct MGPStreamOutputBind { + Uint32 GlName; + Uint32 Pad0; + Uint64 LifetimeId; + }; + MGP_ASSERT_POD(MGPStreamOutputBind, 16); + + // set_storage_block_binding = glShaderStorageBlockBinding (P5b i1). The block is named, + // not indexed: BackendObject.h says why - the application's index is the frontend + // interface-query enumeration's and no backend shares that index space, the NAME is the + // one coordinate all three agree on. The name is bytes of unbounded length, so it rides + // the ONE client -> server byte carrier, SEG_STAGE, as a kHasBlob blob: Name.Size is + // strlen + 1 (the NUL travels), never 0. + // ShaderCso the program's CSO handle - the identity, and what P7 dispatches on. + // GlName the GL program name the backend slot takes; under inproc the slot resolves + // it through the barrier-pulled sticky forward GetProgramObject(name) + // (`rsp`, retired by P9). The P5b debt, stated. + // Binding storageBlockBinding. + struct MGPStorageBlockBinding { + MGPipeHandle ShaderCso; + Uint32 GlName; + Uint32 Binding; + MGPBlobRef Name; + }; + MGP_ASSERT_POD(MGPStorageBlockBinding, 40); + + // copy_framebuffer_to_texture = glCopyTexImage2D / glCopyTexSubImage2D (P5b f1). The + // source is the READ framebuffer the server already has bound (set_framebuffer_state), so + // no source handle crosses; the destination is the texture bound to the active unit at + // Target, which the backend slot resolves through the barrier-pulled unit state exactly as + // generate_mipmap does. glCopyTexImage2D (SubImage = 0) also REDEFINES the level, and the + // frontend's own TexImage2D_State has already emitted that level's resource_respecify + // (GL_Texture.cpp's CopyTexImage2D validator) before the backend slot is reached, so the + // record carries the copy and nothing about storage. + // Dst the destination texture's handle (P8 form; the P5b sink does not + // dispatch on it). + // Target the GL texture target enum verbatim (a 2D target or a cube face). + // Level the destination level. + // InternalFormat glCopyTexImage2D's internalformat (GL enum); 0 for the sub-image form. + // X, Y, Width, Height the read-framebuffer rectangle. + // XOffset, YOffset the sub-image form's destination origin; 0 for the image form. + // SubImage 0 = glCopyTexImage2D, 1 = glCopyTexSubImage2D (unmeasured; shares + // the row because the two calls differ in exactly these two fields). + struct MGPCopyFromFramebuffer { + MGPipeHandle Dst; + Uint32 Target; + Int32 Level; + Uint32 InternalFormat; + Int32 X, Y; + Int32 Width, Height; + Int32 XOffset, YOffset; + Uint8 SubImage; + Uint8 Pad0[3]; + }; + MGP_ASSERT_POD(MGPCopyFromFramebuffer, 48); + // --------------------------------------------------------------------------------- // Reverse channel payloads (section 7.1) // --------------------------------------------------------------------------------- diff --git a/MobileGL/MG_Pipe/PipeCalls.def b/MobileGL/MG_Pipe/PipeCalls.def index b4bfd8d7..bcfeb0e9 100644 --- a/MobileGL/MG_Pipe/PipeCalls.def +++ b/MobileGL/MG_Pipe/PipeCalls.def @@ -47,8 +47,11 @@ // kCtxCso 13 CSO create/bind/delete // kCtxState 17 16 of the 17 set_* calls + the temporary set_residual_value_state // kCtxObject 9 set_texture_params (the 17th set_*) + 8 object-scoped transfers -// kCtxVerb 13 3 context-reading transfer calls + the 10 commands -// total 71 +// kCtxVerb 18 3 context-reading transfer calls + the 10 commands, plus the five +// P5b-appended verbs (bind_shader_image, patch_parameter, +// bind_stream_output, set_storage_block_binding, +// copy_framebuffer_to_texture - MG_Remote/CONTRACT-P5B.md) +// total 76 // // Reconciliation with the plan's headline numbers (section 4.4 / appendix A), because they // do not add up to a set of UNIQUE records and this file has to hold unique records: @@ -78,9 +81,16 @@ // QueryTimestamp, QueryCounter and FenceWaitServer, APPENDED at the end of the list - // not slotted into their groups - because the wire opcode is the position, so a record // that arrives late goes last. 71 unique records. +// - P5b appended FIVE more after them (opcodes 72..76), for the same reason and by the same +// rule: five GLFunctionsTable verbs the class-C census measured as first blockers - the +// image bind, the patch parameter, the XFB object bind, the storage-block rebinding and +// the framebuffer-to-texture copy - for which no existing row could carry the call. +// 76 unique records. The catalogue footer's "ShaderStorageBlockBinding is folded into the +// reflection archive" is CORRECTED by the row: the archive carries the bindings the +// program LINKED with, and glShaderStorageBlockBinding moves one AFTER link. // --------------------------------------------------------------------------------------- -#define MGP_CALL_LIST_DOCUMENTED_COUNT 71 +#define MGP_CALL_LIST_DOCUMENTED_COUNT 76 // clang-format off #define MGP_CALL_LIST(X) \ @@ -201,7 +211,26 @@ X(QueryCounter, MGPQueryDesc, kCtxQuery, kNone) \ /* glWaitSync - WaitSync, the GPU-side wait, distinct from FenceWait's client-side one. */ \ /* TimeoutNs is GL_TIMEOUT_IGNORED by contract. */ \ - X(FenceWaitServer, MGPFenceWait, kScreen, kNone) + X(FenceWaitServer, MGPFenceWait, kScreen, kNone) \ + /* ---- APPENDED BY P5b (MG_Remote/CONTRACT-P5B.md): five GLFunctionsTable verbs the ---- */ \ + /* ---- class-C census measured as first blockers and that no existing row could carry. ---- */ \ + /* ---- Opcodes 72..76. Every one reaches WireVerbSink, none has an MGPipeApply* entry ---- */ \ + /* ---- point, and under monolith none has a producer - the backend slot is called ---- */ \ + /* ---- directly, as today. The payload comments in MGPipeTypes.h say why each exists. ---- */ \ + /* glBindImageTexture - Espryt syncs the unit's binding to the driver AT THE CALL */ \ + /* (SyncImageTextureBinding), so the validate-time set_shader_images cannot stand in. */ \ + X(BindShaderImage, MGPImageBind, kCtxVerb, kNone) \ + /* glPatchParameteri(GL_PATCH_VERTICES) - Espryt pushes glPatchParameteri at the call. */ \ + X(PatchParameter, MGPPatchParameter, kCtxVerb, kNone) \ + /* glBindTransformFeedback - the driver object rebind and the capture-binding shadow */ \ + /* invalidation happen at the call; the XFB namespace has no wire lifetime. */ \ + X(BindStreamOutput, MGPStreamOutputBind, kCtxVerb, kNone) \ + /* glShaderStorageBlockBinding - the block NAME is the coordinate all three sides share */ \ + /* (BackendObject.h), and a name is bytes: kHasBlob, staged whole in SEG_STAGE, NUL included. */ \ + X(SetStorageBlockBinding, MGPStorageBlockBinding, kCtxVerb, kHasBlob) \ + /* glCopyTexImage2D / glCopyTexSubImage2D - a copy whose SOURCE is the read framebuffer, */ \ + /* which resource_copy_region (resource -> resource) cannot express. */ \ + X(CopyFramebufferToTexture, MGPCopyFromFramebuffer, kCtxVerb, kNone) // clang-format on // Explicitly NOT migrated (plan 4.4.6 / appendix A "explicit deletions"): @@ -215,7 +244,10 @@ // - GetProgramiv. GL_COMPUTE_WORK_GROUP_SIZE is a FRONTEND link artifact // (ProgramObject::GetComputeLocalSize, what GL_Program.cpp has always answered from), not // a backend answer at all; nothing a backend knows about a program crosses this way. -// - ShaderStorageBlockBinding (folded into MGPProgramDesc's reflection archive), +// - ShaderStorageBlockBinding WAS listed here as "folded into MGPProgramDesc's reflection +// archive". P5b's set_storage_block_binding row (opcode 75) replaces that: the archive +// carries the bindings as LINKED, and the call moves one after link, which the backend +// applies to an already-built driver program (DirectGLES.cpp ShaderStorageBlockBinding). // set_pixel_unpack_state (no such state crosses the line - plan 4.6 D5), a // compressed-format concept, pipe_transfer, and the stage dimension of set_sampler_views // (MobileGL's texture unit space is merged, not per stage - plan 4.4.3). diff --git a/MobileGL/MG_Pipe/PipeFields.def b/MobileGL/MG_Pipe/PipeFields.def index 486c993b..f24ea712 100644 --- a/MobileGL/MG_Pipe/PipeFields.def +++ b/MobileGL/MG_Pipe/PipeFields.def @@ -185,8 +185,11 @@ #define MGP_FIELDS_MGPReadback(F) \ F(Res) F(Offset) F(Size) +// P5b (CONTRACT-P5B.md i1): the two GL names are the barrier-pulled lookup keys the sink +// rebuilds the CopyImageEndpoints from; listed like any member, so a verify build sees them. #define MGP_FIELDS_MGPCopyRegion(F) \ - F(Src) F(Dst) F(SrcBox) F(DstX) F(DstY) F(DstZ) F(SrcTarget) F(DstTarget) F(SrcLevel) F(DstLevel) + F(Src) F(Dst) F(SrcBox) F(DstX) F(DstY) F(DstZ) F(SrcTarget) F(DstTarget) F(SrcLevel) F(DstLevel) \ + F(SrcGlName) F(DstGlName) #define MGP_FIELDS_MGPBlit(F) \ F(ReadFbo) F(DrawFbo) F(SrcX0) F(SrcY0) F(SrcX1) F(SrcY1) F(DstX0) F(DstY0) F(DstX1) F(DstY1) \ @@ -240,6 +243,25 @@ #define MGP_FIELDS_MGPSurfaceInfo(F) \ F(Width) F(Height) F(InternalFormat) F(Samples) F(Layers) F(IsDefault) +// ---- P5b's five appended rows (MG_Remote/CONTRACT-P5B.md). Pad-named members excluded, as +// everywhere; MGPStorageBlockBinding's Name is an MGPBlobRef and recurses into that list. + +#define MGP_FIELDS_MGPImageBind(F) \ + F(Res) F(Unit) F(GlName) F(Level) F(Layer) F(Access) F(Format) F(Layered) + +#define MGP_FIELDS_MGPPatchParameter(F) \ + F(Pname) F(Value) + +#define MGP_FIELDS_MGPStreamOutputBind(F) \ + F(GlName) F(LifetimeId) + +#define MGP_FIELDS_MGPStorageBlockBinding(F) \ + F(ShaderCso) F(GlName) F(Binding) F(Name) + +#define MGP_FIELDS_MGPCopyFromFramebuffer(F) \ + F(Dst) F(Target) F(Level) F(InternalFormat) F(X) F(Y) F(Width) F(Height) F(XOffset) F(YOffset) \ + F(SubImage) + // ---- the value structs and the host span (P1 brief D8). Not call payloads themselves, but // members of ones (ResidualValueBlock, MGPPixelPackState, MGPCaps) and of PipeInputs, so the // comparator has to see INTO them: with these lists the memcmp fallback of MGPipeFieldEqual is @@ -355,6 +377,8 @@ P(MGPDrawRange) P(MGPDrawIndirect) P(MGPGridInfo) P(MGPMemoryBarrier) P(MGPStreamOutputBegin) \ P(MGPXfbAccounting) P(MGPStreamOutputControl) P(MGPFlush) P(MGPPresent) P(MGPSwapInterval) \ P(MGPSurfaceInfo) \ + P(MGPImageBind) P(MGPPatchParameter) P(MGPStreamOutputBind) P(MGPStorageBlockBinding) \ + P(MGPCopyFromFramebuffer) \ P(RenderStateParameters) P(PixelStoreParameters) P(SamplerParameters) P(PerBufferBlendState) \ P(StencilFaceState) \ P(DynamicBackendParameters) P(MGHostSpan) \ diff --git a/MobileGL/MG_Pipe/generated/PipeFieldOwnership.inc b/MobileGL/MG_Pipe/generated/PipeFieldOwnership.inc index becbccc2..8e2e6faa 100644 --- a/MobileGL/MG_Pipe/generated/PipeFieldOwnership.inc +++ b/MobileGL/MG_Pipe/generated/PipeFieldOwnership.inc @@ -281,12 +281,18 @@ constexpr MGPipeVerb MGPipeVerbForWireOp(MGPWireOp op) { case MGPWireOp::ResumeStreamOutput: return MGPipeVerb::ResumeTransformFeedback; case MGPWireOp::GenerateMipmap: return MGPipeVerb::GenerateMipmap; case MGPWireOp::GetTextureImage: return MGPipeVerb::GetTextureImage; + case MGPWireOp::ResourceCopyRegion: return MGPipeVerb::CopyImageSubData; + case MGPWireOp::BindShaderImage: return MGPipeVerb::BindImageTexture; + case MGPWireOp::PatchParameter: return MGPipeVerb::PatchParameteri; + case MGPWireOp::BindStreamOutput: return MGPipeVerb::BindTransformFeedback; + case MGPWireOp::SetStorageBlockBinding: return MGPipeVerb::ShaderStorageBlockBinding; + case MGPWireOp::CopyFramebufferToTexture: return MGPipeVerb::CopyTexImage2D; default: return MGPipeVerb::kVerbCount; } } -inline constexpr SizeT kMGPipeVerbBoundaryOpCount = 12; +inline constexpr SizeT kMGPipeVerbBoundaryOpCount = 18; inline constexpr SizeT kMGPipeVerbBoundaryExemptCount = 3; // The class sizes, as constants a test can pin without recounting the table. diff --git a/MobileGL/MG_Pipe/generated/PipeTables.inc b/MobileGL/MG_Pipe/generated/PipeTables.inc index f62d915a..5786a8de 100644 --- a/MobileGL/MG_Pipe/generated/PipeTables.inc +++ b/MobileGL/MG_Pipe/generated/PipeTables.inc @@ -28,7 +28,7 @@ struct MGPipeScreen { void (*FenceWaitServer)(const MGPFenceWait* payload); }; -// context: 60 calls. A null entry means the backend does not implement this +// context: 65 calls. A null entry means the backend does not implement this // call and the frontend keeps its own path (plan B section 4.1). struct MGPipeContext { void (*QueryCreate)(const MGPQueryDesc* payload); @@ -91,11 +91,16 @@ struct MGPipeContext { void (*SetSwapInterval)(const MGPSwapInterval* payload); void (*QueryTimestamp)(const MGPTimestampRequest* payload, MGPReplySlot* reply); void (*QueryCounter)(const MGPQueryDesc* payload); + void (*BindShaderImage)(const MGPImageBind* payload); + void (*PatchParameter)(const MGPPatchParameter* payload); + void (*BindStreamOutput)(const MGPStreamOutputBind* payload); + void (*SetStorageBlockBinding)(const MGPStorageBlockBinding* payload, const void* blobBytes, Uint64 blobByteCount); + void (*CopyFramebufferToTexture)(const MGPCopyFromFramebuffer* payload); }; inline constexpr SizeT kMGPipeScreenCallCount = 11; -inline constexpr SizeT kMGPipeContextCallCount = 60; -inline constexpr SizeT kMGPipeCallCount = 71; +inline constexpr SizeT kMGPipeContextCallCount = 65; +inline constexpr SizeT kMGPipeCallCount = 76; // A table that is not exactly its call count of function pointers has grown a // member that no generator knows about. diff --git a/MobileGL/MG_Pipe/generated/PipeThunks.inc b/MobileGL/MG_Pipe/generated/PipeThunks.inc index 585e2469..f5930223 100644 --- a/MobileGL/MG_Pipe/generated/PipeThunks.inc +++ b/MobileGL/MG_Pipe/generated/PipeThunks.inc @@ -300,3 +300,23 @@ inline void MGP_QueryCounter(const MGPQueryDesc* payload) { inline void MGP_FenceWaitServer(const MGPFenceWait* payload) { gMGPipeScreen.FenceWaitServer(payload); } + +inline void MGP_BindShaderImage(const MGPImageBind* payload) { + gMGPipeContext.BindShaderImage(payload); +} + +inline void MGP_PatchParameter(const MGPPatchParameter* payload) { + gMGPipeContext.PatchParameter(payload); +} + +inline void MGP_BindStreamOutput(const MGPStreamOutputBind* payload) { + gMGPipeContext.BindStreamOutput(payload); +} + +inline void MGP_SetStorageBlockBinding(const MGPStorageBlockBinding* payload, const void* blobBytes, Uint64 blobByteCount) { + gMGPipeContext.SetStorageBlockBinding(payload, blobBytes, blobByteCount); +} + +inline void MGP_CopyFramebufferToTexture(const MGPCopyFromFramebuffer* payload) { + gMGPipeContext.CopyFramebufferToTexture(payload); +} diff --git a/MobileGL/MG_Pipe/generated/PipeVerify.inc b/MobileGL/MG_Pipe/generated/PipeVerify.inc index d8d8b091..d94b9c1f 100644 --- a/MobileGL/MG_Pipe/generated/PipeVerify.inc +++ b/MobileGL/MG_Pipe/generated/PipeVerify.inc @@ -107,6 +107,11 @@ inline Bool MGPipeVerify(const MGPFlush& a, const MGPFlush& b, const char** outF inline Bool MGPipeVerify(const MGPPresent& a, const MGPPresent& b, const char** outField); inline Bool MGPipeVerify(const MGPSwapInterval& a, const MGPSwapInterval& b, const char** outField); inline Bool MGPipeVerify(const MGPSurfaceInfo& a, const MGPSurfaceInfo& b, const char** outField); +inline Bool MGPipeVerify(const MGPImageBind& a, const MGPImageBind& b, const char** outField); +inline Bool MGPipeVerify(const MGPPatchParameter& a, const MGPPatchParameter& b, const char** outField); +inline Bool MGPipeVerify(const MGPStreamOutputBind& a, const MGPStreamOutputBind& b, const char** outField); +inline Bool MGPipeVerify(const MGPStorageBlockBinding& a, const MGPStorageBlockBinding& b, const char** outField); +inline Bool MGPipeVerify(const MGPCopyFromFramebuffer& a, const MGPCopyFromFramebuffer& b, const char** outField); inline Bool MGPipeVerify(const RenderStateParameters& a, const RenderStateParameters& b, const char** outField); inline Bool MGPipeVerify(const PixelStoreParameters& a, const PixelStoreParameters& b, const char** outField); inline Bool MGPipeVerify(const SamplerParameters& a, const SamplerParameters& b, const char** outField); @@ -244,6 +249,16 @@ struct MGPipeHasFieldVerifier : std::true_type {}; template <> struct MGPipeHasFieldVerifier : std::true_type {}; template <> +struct MGPipeHasFieldVerifier : std::true_type {}; +template <> +struct MGPipeHasFieldVerifier : std::true_type {}; +template <> +struct MGPipeHasFieldVerifier : std::true_type {}; +template <> +struct MGPipeHasFieldVerifier : std::true_type {}; +template <> +struct MGPipeHasFieldVerifier : std::true_type {}; +template <> struct MGPipeHasFieldVerifier : std::true_type {}; template <> struct MGPipeHasFieldVerifier : std::true_type {}; @@ -622,6 +637,31 @@ inline Bool MGPipeVerify(const MGPSurfaceInfo& a, const MGPSurfaceInfo& b, const return true; } +inline Bool MGPipeVerify(const MGPImageBind& a, const MGPImageBind& b, const char** outField) { + MGP_FIELDS_MGPImageBind(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPPatchParameter& a, const MGPPatchParameter& b, const char** outField) { + MGP_FIELDS_MGPPatchParameter(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPStreamOutputBind& a, const MGPStreamOutputBind& b, const char** outField) { + MGP_FIELDS_MGPStreamOutputBind(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPStorageBlockBinding& a, const MGPStorageBlockBinding& b, const char** outField) { + MGP_FIELDS_MGPStorageBlockBinding(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPCopyFromFramebuffer& a, const MGPCopyFromFramebuffer& b, const char** outField) { + MGP_FIELDS_MGPCopyFromFramebuffer(MGP_VERIFY_FIELD) + return true; +} + inline Bool MGPipeVerify(const RenderStateParameters& a, const RenderStateParameters& b, const char** outField) { MGP_FIELDS_RenderStateParameters(MGP_VERIFY_FIELD) return true; @@ -669,4 +709,4 @@ inline Bool MGPipeVerify(const MGPVertexBindingPointWire& a, const MGPVertexBind #undef MGP_VERIFY_FIELD -inline constexpr SizeT kMGPipeVerifiedPayloadCount = 72; +inline constexpr SizeT kMGPipeVerifiedPayloadCount = 77; diff --git a/MobileGL/MG_Pipe/generated/PipeWire.inc b/MobileGL/MG_Pipe/generated/PipeWire.inc index 63466643..80e66755 100644 --- a/MobileGL/MG_Pipe/generated/PipeWire.inc +++ b/MobileGL/MG_Pipe/generated/PipeWire.inc @@ -141,7 +141,12 @@ enum class MGPWireOp : Uint16 { QueryTimestamp = 69, QueryCounter = 70, FenceWaitServer = 71, - kOpCount = 72, + BindShaderImage = 72, + PatchParameter = 73, + BindStreamOutput = 74, + SetStorageBlockBinding = 75, + CopyFramebufferToTexture = 76, + kOpCount = 77, }; // THE FLAGS, EXPORTED ONCE, INDEXED BY OPCODE (P5 R-13.4). MGPWireRecHeader::Flags is @@ -234,6 +239,11 @@ inline constexpr Uint32 kMGPipeCallFlags[static_cast(MGPWireOp::kOpCount) /* 69 QueryTimestamp */ static_cast(kReplySlot), /* 70 QueryCounter */ static_cast(kNone), /* 71 FenceWaitServer */ static_cast(kNone), + /* 72 BindShaderImage */ static_cast(kNone), + /* 73 PatchParameter */ static_cast(kNone), + /* 74 BindStreamOutput */ static_cast(kNone), + /* 75 SetStorageBlockBinding */ static_cast(kHasBlob), + /* 76 CopyFramebufferToTexture*/ static_cast(kNone), }; static_assert(sizeof(kMGPipeCallFlags) / sizeof(kMGPipeCallFlags[0]) == static_cast(MGPWireOp::kOpCount), @@ -855,6 +865,46 @@ static_assert(sizeof(MGPWireRec_FenceWaitServer) == ((sizeof(MGPWireRecHeader) + sizeof(MGPFenceWait) + 7u) & ~SizeT(7u)), "MGPWireRec_FenceWaitServer gained padding; the wire format moved"); +struct alignas(8) MGPWireRec_BindShaderImage { + MGPWireRecHeader Header; + MGPImageBind Payload; +}; +static_assert(sizeof(MGPWireRec_BindShaderImage) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPImageBind) + 7u) & ~SizeT(7u)), + "MGPWireRec_BindShaderImage gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_PatchParameter { + MGPWireRecHeader Header; + MGPPatchParameter Payload; +}; +static_assert(sizeof(MGPWireRec_PatchParameter) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPPatchParameter) + 7u) & ~SizeT(7u)), + "MGPWireRec_PatchParameter gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_BindStreamOutput { + MGPWireRecHeader Header; + MGPStreamOutputBind Payload; +}; +static_assert(sizeof(MGPWireRec_BindStreamOutput) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPStreamOutputBind) + 7u) & ~SizeT(7u)), + "MGPWireRec_BindStreamOutput gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetStorageBlockBinding { + MGPWireRecHeader Header; + MGPStorageBlockBinding Payload; +}; +static_assert(sizeof(MGPWireRec_SetStorageBlockBinding) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPStorageBlockBinding) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetStorageBlockBinding gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_CopyFramebufferToTexture { + MGPWireRecHeader Header; + MGPCopyFromFramebuffer Payload; +}; +static_assert(sizeof(MGPWireRec_CopyFramebufferToTexture) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPCopyFromFramebuffer) + 7u) & ~SizeT(7u)), + "MGPWireRec_CopyFramebufferToTexture gained padding; the wire format moved"); + [[noreturn]] inline void MGPipeWireProtocolFatal(const char* call, Uint64 size, Uint64 remaining) { MGLOG_F("MGPipe: protocol corruption applying %s: size=%llu remaining=%llu", call, static_cast(size), static_cast(remaining)); @@ -1117,6 +1167,21 @@ inline Bool MGPipeApplyWireRecord(MGPWireOp op, const void* record, Uint64 size, case MGPWireOp::FenceWaitServer: MGP_WIRE_CHECK_BOUNDS(MGPWireRec_FenceWaitServer, "FenceWaitServer"); break; + case MGPWireOp::BindShaderImage: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_BindShaderImage, "BindShaderImage"); + break; + case MGPWireOp::PatchParameter: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_PatchParameter, "PatchParameter"); + break; + case MGPWireOp::BindStreamOutput: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_BindStreamOutput, "BindStreamOutput"); + break; + case MGPWireOp::SetStorageBlockBinding: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetStorageBlockBinding, "SetStorageBlockBinding"); + break; + case MGPWireOp::CopyFramebufferToTexture: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_CopyFramebufferToTexture, "CopyFramebufferToTexture"); + break; case MGPWireOp::kInvalid: case MGPWireOp::kOpCount: default: