diff --git a/CMakeLists.txt b/CMakeLists.txt index eb574dc6..f89dc387 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -472,6 +472,10 @@ message(STATUS "MOBILEGL_COMPILE_DEF=${MOBILEGL_COMPILE_DEF}") set(MOBILEGL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/MobileGL + # The MGPipe boundary headers. They are reachable as through the + # line above too; this entry lets the client, the backends and MG_Remote spell them + # as once MG_Pipe stops being a leaf of the frontend tree. + ${CMAKE_SOURCE_DIR}/MobileGL/MG_Pipe ${spirv-tools_SOURCE_DIR} ${spirv-tools_SOURCE_DIR}/include ${spirv-tools_BINARY_DIR} diff --git a/MobileGL/MG_Pipe/Coverage.def b/MobileGL/MG_Pipe/Coverage.def new file mode 100644 index 00000000..f5ee58e1 --- /dev/null +++ b/MobileGL/MG_Pipe/Coverage.def @@ -0,0 +1,106 @@ +// MobileGL - MobileGL/MG_Pipe/Coverage.def +// 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 + +// The hand-maintained half of G6 (plan B section 4.7, gate 10.3-5): which MGPipe call +// answers each backend read point in scripts/data/backend_read_inventory.md (477 rows, 57 +// files, generated from the backends by MobileGL-CS's extract_backend_read_inventory.py). +// +// gen_pipe.py joins the inventory's `member` column against MGP_COVERAGE_ACCESSOR_LIST and +// its `delta` column against MGP_COVERAGE_DELTA_LIST, then writes generated/PipeCoverage.inc +// with the per-accessor table and prints the coverage summary. Rows matching neither are +// UNMAPPED: allowed in P0 and merely counted, ZERO from P5 onward, when the gate becomes +// "regenerate and git diff --exit-code with 0 UNMAPPED". +// +// Three pseudo-calls stand for read points that do NOT become a forward call: +// kClientResolved - the frontend answers it itself; the server is never asked +// (section 4.4.6: "the server answers nothing the client can answer"). +// kReverseChannel - it becomes one of the ten MGPipeCallbacks (section 7.1). +// kStructuralHandle - the row is a SIGNATURE carrying SharedPtr, which +// becomes an MGPipeHandle parameter; there is no single call to name. +// +// clang-format off + +// X(Accessor, PipeCall) +#define MGP_COVERAGE_ACCESSOR_LIST(X) \ + X(GetActiveTextureUnit, SetSamplerViews) \ + X(GetBlendColor, SetDynamicState) \ + X(GetBlendEquationIndexed, CreateRenderState) \ + X(GetBlendFuncIndexed, CreateRenderState) \ + X(GetBoundTransformFeedbackName, SetStreamOutputTargets) \ + X(GetBoundVertexArray, BindVertexElements) \ + /* Polymorphic over BufferTarget: its rows split across set_vertex_buffers, */ \ + /* set_index_buffer, set_indirect_buffers and set_shader_buffers once the */ \ + /* inventory carries the target argument (P1). Named for the plan's explicit */ \ + /* replacement of the DrawIndirect/Parameter pair. */ \ + X(GetBufferBindingSlot, SetIndirectBuffers) \ + X(GetBufferBindingPoint, SetShaderBuffers) \ + X(GetBufferBindingPointCount, SetShaderBuffers) \ + X(GetTouchedBufferBindingPointCount, SetShaderBuffers) \ + X(GetClampReadColor, SetDynamicState) \ + X(GetClearColor, SetDynamicState) \ + X(GetClearDepth, SetDynamicState) \ + X(GetClearStencil, SetDynamicState) \ + X(GetColorMaskIndexed, CreateRenderState) \ + X(GetCullFaceMode, CreateRenderState) \ + X(GetCurrentVertexAttribute, SetVertexAttribDefaults) \ + X(GetDepthFunc, CreateRenderState) \ + X(GetDepthMask, CreateRenderState) \ + X(GetDepthRangeIndexed, SetDynamicState) \ + X(GetFramebufferBindingSlot, SetFramebufferState) \ + X(GetImageTextureBinding, SetShaderImages) \ + X(GetLineWidth, SetDynamicState) \ + X(GetLogicOp, CreateRenderState) \ + X(GetMaxTouchedTextureUnit, SetSamplerViews) \ + X(GetMinSampleShadingValue, CreateRenderState) \ + X(GetPatchDefaultInnerLevel, SetPatchState) \ + X(GetPatchDefaultOuterLevel, SetPatchState) \ + X(GetPatchVertices, SetPatchState) \ + X(GetPipelineStateVersion, BindRenderState) \ + X(GetPixelStoreParameters, SetPixelPackState) \ + X(GetPolygonModeFront, CreateRenderState) \ + X(GetPolygonOffsetFactor, SetDynamicState) \ + X(GetPolygonOffsetUnits, SetDynamicState) \ + X(GetPrimitiveRestartIndex, DrawVbo) \ + X(GetProgramForDispatch, SetDispatchProgram) \ + X(GetProgramForDraw, SetDrawProgram) \ + X(GetProgramObject, CreateShaderState) \ + /* Not in ComputePipelineStateHash today even though Vulkan makes it pipeline */ \ + /* state; recorded here so the G7 chunk table has to answer for it before it */ \ + /* freezes (section 10.3-5). */ \ + X(GetProvokingVertexMode, CreateRenderState) \ + X(GetRenderStateParameters, CreateRenderState) \ + X(GetRenderStateParametersVersion, BindRenderState) \ + X(GetSamplingResolutionGeneration, SetSamplerViews) \ + X(GetScissorBox, SetDynamicState) \ + X(GetStencilState, CreateRenderState) \ + X(GetTextureBindGeneration, SetSamplerViews) \ + X(GetTextureContextId, SetSamplerViews) \ + X(GetTextureObject, SetSamplerViews) \ + X(GetTextureUnitObject, SetSamplerViews) \ + X(GetTransformFeedbackCapturedVertices, DrawVbo) \ + X(GetTransformFeedbackGeneration, SetStreamOutputTargets) \ + X(GetTransformFeedbackPausedPrimitiveCounter, EndStreamOutput) \ + X(GetTransformFeedbackProgram, SetStreamOutputTargets) \ + X(GetViewport, SetDynamicState) \ + X(GetViewportIndexed, SetDynamicState) \ + X(IsCapabilityEnabled, CreateRenderState) \ + X(IsCapabilityEnabledIndexed, CreateRenderState) \ + X(IsTransformFeedbackActive, BeginStreamOutput) \ + X(IsTransformFeedbackPaused, PauseStreamOutput) \ + X(InvalidateCompileEnv, kClientResolved) \ + X(ValidateProgramName, kClientResolved) \ + X(RecordError, kReverseChannel) + +// X(DeltaKind, PipeCall) - for inventory rows with no accessor in the member column. +// Read by gen_pipe.py ONLY, never by the C++ preprocessor: the delta kinds are the +// inventory's own free-text labels, not C tokens. +#define MGP_COVERAGE_DELTA_LIST(X) \ + X(handle-ify (wire handle), kStructuralHandle) \ + X(Buffer ops delta, ResourceRespecify) + +// clang-format on diff --git a/MobileGL/MG_Pipe/MGPipe.h b/MobileGL/MG_Pipe/MGPipe.h new file mode 100644 index 00000000..85e26d81 --- /dev/null +++ b/MobileGL/MG_Pipe/MGPipe.h @@ -0,0 +1,93 @@ +// MobileGL - MobileGL/MG_Pipe/MGPipe.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 + +#pragma once +#include + +#include "MGPipeCallbacks.h" +#include "MGPipeHandles.h" +#include "MGPipeHostSpan.h" +#include "MGPipeTypes.h" + +// The MGPipe boundary (plan B section 4). +// +// The two interface tables are FUNCTION-POINTER STRUCTS, not virtual bases. Three reasons +// out of this repository rather than out of gallium: the boundary already is a +// function-pointer struct sitting on one hook point in MG_Backend/Init.cpp; a nullptr entry +// already means "not implemented, frontend falls back", which is exactly what a +// not-yet-migrated subsystem needs to say while it keeps pulling; and MG_Test already +// substitutes this table to mock a backend. The rare EGL and caps surface stays on +// pActiveBackendObject's virtual functions. +namespace MobileGL::MG_Pipe { + // Unscoped on purpose: PipeCalls.def spells these as bare tokens so the same file can + // be read by the C++ preprocessor and by scripts/gen_pipe.py. + enum MGPipeCallClass : Uint8 { + kScreen, + kCtxCso, + kCtxState, + kCtxObject, + kCtxVerb, + kCtxQuery, + kCallClassCount, + }; + + enum MGPipeCallFlags : Uint32 { + kNone = 0, + // The caller must not proceed until the server has acknowledged. Rare by design. + kNeedsAck = 1u << 0, + // Carries an MGPBlobRef. + kHasBlob = 1u << 1, + // Carries a variable-length array after the fixed payload. + kVarTail = 1u << 2, + // Carries an MGHostSpan - the one shape that changes with the transport. + kHostSpan = 1u << 3, + // Answers into an MGPReplySlot; never blocks. + kReplySlot = 1u << 4, + // May be null in a backend's table. A null entry is a real answer ("this backend + // does not implement it"), not an error: DirectVulkan deliberately leaves + // buffer_subdata_resident unregistered, and SetSwapInterval likewise. + kOptional = 1u << 5, + }; + + // The pipeline/dynamic split of RenderStateParameters, defined exactly once (section + // 4.5.2). Generated by G7 from the field list ComputePipelineStateHash already hashes; + // MGPipeRenderStateSpans.cpp and the setter-consistency test land with P2, which is + // when the chunk table can be filled with real offsets. + struct MGPipeRenderStateSpans; + + // The catalogue itself. Only macros, so it is safe to expand inside the namespace, and + // consumers (the unit test, later the transport) get MGP_CALL_LIST from this header. +#include "PipeCalls.def" + + // G1: the two interface tables. A null entry means "not implemented" (section 4.1). +#include "generated/PipeTables.inc" + + // The installed tables. Zero-initialized, so an un-installed MGPipe is every entry + // null - which is precisely the pre-migration state. + inline MGPipeScreen gMGPipeScreen{}; + inline MGPipeContext gMGPipeContext{}; + + // G2: monolith thunks. These are what MG_Impl call sites move onto, replacing + // gBackendFunctionsTable.GL.* one name at a time. +#include "generated/PipeThunks.inc" + + // G3: wire records, their size assertions, and the applier's bounds precondition. +#include "generated/PipeWire.inc" + + // G4: the MOBILEGL_PIPE_VERIFY field-wise comparators. +#include "generated/PipeVerify.inc" + + // G5: PipeInputs field ids and the per-verb poison generations. +#include "generated/PipeFilled.inc" + + // G6: the backend read inventory's coverage table. +#include "generated/PipeCoverage.inc" + + // G7: the render-state pipeline subset, by member name. +#include "generated/PipeSpanTable.inc" +} // namespace MobileGL::MG_Pipe diff --git a/MobileGL/MG_Pipe/MGPipeCallbacks.h b/MobileGL/MG_Pipe/MGPipeCallbacks.h new file mode 100644 index 00000000..f379acea --- /dev/null +++ b/MobileGL/MG_Pipe/MGPipeCallbacks.h @@ -0,0 +1,61 @@ +// MobileGL - MobileGL/MG_Pipe/MGPipeCallbacks.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 + +#pragma once +#include + +#include "MGPipeHandles.h" +#include "MGPipeTypes.h" + +// The backend -> frontend reverse channel, named (plan B section 7.1). +// +// Today this traffic is 95 call sites across 17 methods poked directly into frontend +// objects. gallium has no vocabulary for shadow writeback, GPU-write notification, texture +// re-send requests or default-framebuffer geometry, because in Mesa the state tracker and +// the driver share an address space. Naming them as ten callbacks plus one forward +// terminator (MGPipeContext::ResourceSubDataComplete) is the deliberate deviation (D8). +// +// Installed at context creation. In a monolith these are direct calls; under split they are +// records on the reverse channel, and their ORDER is a correctness requirement rather than +// an optimization (section 7.4). +namespace MobileGL::MG_Pipe { + struct MGPipeCallbacks { + // A driver-detected GL error that only the server could have seen. + void (*OnGlError)(Uint32 code); + // Ranges of a resource the GPU wrote; retires MarkGpuWritten. + void (*OnGpuWritten)(MGPipeHandle res, Uint rangeCount, const MGPRange* ranges); + void (*OnBufferWriteback)(MGPipeHandle res, Uint64 offset, MGPBlobRef bytes); + void (*OnTextureWriteback)(MGPipeHandle res, const MGPBox* box, MGPBlobRef bytes); + // The one new stall class in this design (D-B6): the server recast a texture and + // needs its texels back. The client answers with zero or more ResourceSubData + // records terminated by ResourceSubDataComplete carrying the same pullSerial. + void (*OnTexturePullRequest)(MGPipeHandle res, Uint16 target, Uint16 firstLevel, Uint16 levelCount, + Uint64 pullSerial); + // SHAPE ONLY, never bytes: the client owns the CPU shadow and allocates the levels + // itself. + void (*OnMipLevelsGenerated)(MGPipeHandle res, Uint16 base, Uint16 count); + // Retires the layering inversion where the swapchain writes into MG_Impl's + // pDefaultFramebufferInfo. + void (*OnSurfaceChanged)(const MGPSurfaceInfo* info); + void (*OnCapsInvalidated)(); + // <= WARN is lossy, >= ERROR is lossless and rate limited. + void (*OnLog)(Uint8 level, const char* text); + // The XFB scatter is a read-modify-write of the CLIENT's shadow, so the server + // hands back the packed scratch and the client scatters (section 7.2.1). + void (*OnXfbScatterReady)(MGPipeHandle scratch, Uint64 packedStride, Uint64 vertices); + }; + + // Ten, and the count is asserted so an eleventh cannot be added without touching the + // transport's reverse-channel record table. + inline constexpr SizeT kMGPipeCallbackCount = 10; + static_assert(sizeof(MGPipeCallbacks) == kMGPipeCallbackCount * sizeof(void (*)()), + "MGPipeCallbacks gained or lost a callback"); + + // Null-initialized: a backend that installs nothing sends nothing. + inline MGPipeCallbacks gMGPipeCallbacks{}; +} // namespace MobileGL::MG_Pipe diff --git a/MobileGL/MG_Pipe/MGPipeHandles.h b/MobileGL/MG_Pipe/MGPipeHandles.h new file mode 100644 index 00000000..586880db --- /dev/null +++ b/MobileGL/MG_Pipe/MGPipeHandles.h @@ -0,0 +1,98 @@ +// MobileGL - MobileGL/MG_Pipe/MGPipeHandles.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 + +#pragma once +#include + +// MGPipe object identity (plan B section 4.2). +// +// A handle is a {slot, gen} pair minted by the CLIENT and never by the server: no create_* +// call in the catalogue returns a server-cast handle, which is the deliberate deviation +// from gallium (D1) that lets the whole catalogue be remoted with ZERO creation round +// trips. +// +// Slots are dense and allocated PER KIND, so the server's object table is an array rather +// than a hash map. The allocator is a free list plus a high-water mark and has nothing to +// do with MG_State's IndexGenerator - that container's LIFO name reuse is the very problem +// {slot, gen} exists to close. +namespace MobileGL::MG_Pipe { + enum class MGPipeKind : Uint8 { + None = 0, + Buffer = 1, + Texture, + Renderbuffer, + Framebuffer, + Xfb, + RenderStateCso, + VertexElementsCso, + SamplerCso, + SamplerViewCso, + ShaderCso, + Fence, + Query, + Context, + KindCount, + }; + + // 8 bytes, POD, passed by value in a register pair. + // + // Gen increments only when a SLOT IS REUSED - never on a respecify - so {slot, gen} is + // unique until the same slot has been recycled 2^32 times. That bound is documented + // rather than defended at runtime in release builds: at one recycle per frame at + // 1000 fps a single slot would take ~50 days of continuous churn to wrap, and the + // debug allocator asserts on the wrap. + // + // Two generations exist in this design and they are strictly separate (section 4.2.2): + // this one is the CLIENT's answer to "is this still the same GL object", while MGGen is + // the SERVER's own epoch for "did I recast my driver object". Interface rule: no MGPipe + // call may require the client to supply or know MGGen. + struct MGPipeHandle { + Uint32 Slot; + Uint32 Gen; + + friend constexpr Bool operator==(const MGPipeHandle& a, const MGPipeHandle& b) { + return a.Slot == b.Slot && a.Gen == b.Gen; + } + }; + + static_assert(sizeof(MGPipeHandle) == 8, "MGPipeHandle is the 8-byte {slot, gen} pair"); + static_assert(alignof(MGPipeHandle) == 4, "MGPipeHandle must not gain padding on the wire"); + static_assert(std::is_trivially_copyable_v); + + // Reserved handles (section 4.2.1). + // {0, 0} is null for every kind. + // {0, 1} of kind Framebuffer is the DEFAULT framebuffer. It exists so the four + // pDefaultFramebufferInfo->defaultFBO identity comparisons in DirectGLES retire into + // an ordinary handle compare. + inline constexpr MGPipeHandle kMGPipeNullHandle{0, 0}; + inline constexpr MGPipeHandle kMGPipeDefaultFramebuffer{0, 1}; + + inline constexpr Bool MGPipeHandleIsNull(const MGPipeHandle& handle) { + return handle.Slot == 0 && handle.Gen == 0; + } + + // Slot 0 of every kind is reserved (null, and the default framebuffer for kind + // Framebuffer), so a real allocation starts at 1. + inline constexpr Uint32 kMGPipeFirstAllocatableSlot = 1; + + // ShaderCso slot space. The top 1/16 of it is reserved for PROGRAM PIPELINE COMPOSITES + // (section 5.6.3): a composite is minted client-side out of the stage programs bound to + // a pipeline object, and the server never learns it is a composite - it is just another + // ShaderCso. Reserving a band rather than a flag keeps the composite resolver's + // lifetime bookkeeping out of the ordinary program slot allocator. + inline constexpr Uint32 kMGPipeShaderCsoSlotLimit = 1u << 20; + inline constexpr Uint32 kMGPipeShaderCsoCompositeSlotBase = + kMGPipeShaderCsoSlotLimit - (kMGPipeShaderCsoSlotLimit >> 4); + + inline constexpr Bool MGPipeIsCompositeShaderSlot(Uint32 slot) { + return slot >= kMGPipeShaderCsoCompositeSlotBase && slot < kMGPipeShaderCsoSlotLimit; + } + + static_assert(kMGPipeShaderCsoCompositeSlotBase > kMGPipeFirstAllocatableSlot, + "the composite band must not swallow the ordinary program slots"); +} // namespace MobileGL::MG_Pipe diff --git a/MobileGL/MG_Pipe/MGPipeHostSpan.h b/MobileGL/MG_Pipe/MGPipeHostSpan.h new file mode 100644 index 00000000..edec2fa4 --- /dev/null +++ b/MobileGL/MG_Pipe/MGPipeHostSpan.h @@ -0,0 +1,57 @@ +// MobileGL - MobileGL/MG_Pipe/MGPipeHostSpan.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 + +#pragma once +#include + +// The ONE thing in MGPipe whose shape changes with the transport (plan B section 4.5.7). +// +// Monolith: Ptr addresses the frontend shadow or the application's own memory and the +// accessor is one predictable branch. Split: Ptr is null and the bytes live in a staging +// segment named by Seg/Offset, or - for the index bytes a server-side primitive-restart +// rewrite or multi-draw flattening consumes - in the server's own index host mirror, which +// costs no wire traffic at all (D-B7). +namespace MobileGL::MG_Pipe { + // Seg sentinels. Anything else is a real SEG_STAGE id assigned by the transport. + inline constexpr Uint32 kMGHostSpanSegNone = 0; + // "The bytes are already on your side": the server reads them out of the index host + // mirror it maintains for every resource created with the ELEMENT_ARRAY bind bit while + // kCapNeedsHostIndexBytes is set. When the mirror is over budget the tracker degrades + // to per-draw staging and counts the bytes in index-bytes-shipped. + inline constexpr Uint32 kMGHostSpanSegFromServerIndexMirror = 0xFFFFFFFFu; + + struct MGHostSpan { + // Field order is chosen so the struct is 32 bytes with natural alignment on both a + // 64-bit and a 32-bit host: the pointer and the two 32-bit words fill the first + // 16-byte block either way. + const void* Ptr; + Uint32 Seg; + Uint32 Pad0; + Uint64 Size; + Uint64 Offset; + }; + + static_assert(sizeof(MGHostSpan) == 32, "MGHostSpan is the 32-byte host-bytes descriptor"); + static_assert(std::is_trivially_copyable_v); + + // Split-mode resolution needs the transport's segment table, which does not exist in a + // monolith build; the hook is a weak-ish indirection installed by MG_Remote when it is + // compiled in. In P0 there is no transport, so a span that names a segment resolves to + // null and every caller is still on the monolith branch. + using MGPipeSegmentResolver = const void* (*)(Uint32 seg, Uint64 offset, Uint64 size); + inline MGPipeSegmentResolver gMGPipeSegmentResolver = nullptr; + + // One predictable branch on the hot path. + inline const void* MGPipeHostBytes(const MGHostSpan& span) { + if (span.Ptr != nullptr) { + return static_cast(span.Ptr) + span.Offset; + } + if (gMGPipeSegmentResolver == nullptr) return nullptr; + return gMGPipeSegmentResolver(span.Seg, span.Offset, span.Size); + } +} // namespace MobileGL::MG_Pipe diff --git a/MobileGL/MG_Pipe/MGPipeTypes.h b/MobileGL/MG_Pipe/MGPipeTypes.h new file mode 100644 index 00000000..227338d3 --- /dev/null +++ b/MobileGL/MG_Pipe/MGPipeTypes.h @@ -0,0 +1,754 @@ +// MobileGL - MobileGL/MG_Pipe/MGPipeTypes.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 + +#pragma once +#include + +#include "MGPipeHandles.h" +#include "MGPipeHostSpan.h" + +// Every MGPipe payload (plan B section 4.5). Each one is a flat POD with explicit padding, +// carries a static_assert on trivial copyability and one on its exact size, and never +// contains a pointer other than the single MGHostSpan the design isolates on purpose. +// +// Sizes are asserted rather than merely documented because the wire records generated from +// these structs (generated/PipeWire.inc) are memcpy'd; a field silently changing width is a +// protocol break that no test would otherwise see. +// +// P0.5 DEBT, recorded here so it is impossible to miss: two payloads reach into headers +// this directory is eventually forbidden to see - MGPCaps embeds MG_Backend's +// DynamicBackendParameters, and ResidualValueBlock embeds MG_State's RenderStateParameters +// and PixelStoreParameters. Both are deliberate: the caps block IS that struct (section +// 4.4.1) and the residual block is the migration carrier for Track V (section 6.3). P0.5 +// extracts MGPipeValueTypes.h and both includes below go away; until then purity gate A +// (section 10.3) cannot be armed for this header. +#include +#include + +namespace MobileGL::MG_Pipe { + using MG_Backend::DynamicBackendParameters; + // Both live directly in namespace MobileGL today; P0.5 moves them into + // MG_Pipe/MGPipeValueTypes.h. + using MobileGL::PixelStoreParameters; + using MobileGL::RenderStateParameters; + + // A payload must be memcpy-able and its size must be an exact, stated number. +#define MGP_ASSERT_POD(T, Size) \ + static_assert(std::is_trivially_copyable_v, #T " must be trivially copyable"); \ + static_assert(sizeof(T) == (Size), #T " changed size; update the wire format and this assertion") + + // --------------------------------------------------------------------------------- + // Shared primitives + // --------------------------------------------------------------------------------- + + // A run of bytes in the command stream's blob area. Monolith: Seg is + // kMGHostSpanSegNone and Offset is an address into the caller's staging arena. Split: + // Seg names a transport segment. + struct MGPBlobRef { + Uint64 Offset; + Uint64 Size; + Uint32 Seg; + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPBlobRef, 24); + + struct MGPRange { + Uint64 Offset; + Uint64 Size; + }; + MGP_ASSERT_POD(MGPRange, 16); + + // Destination box in the level's own coordinate system (section 4.5.6). + struct MGPBox { + Int32 X, Y, Z; + Uint32 W, H, D; + }; + MGP_ASSERT_POD(MGPBox, 24); + + // Where an asynchronous answer lands. Every server query in this catalogue is + // async-with-handle; none of them blocks (section 4.4.6, "the total rule"). + struct MGPReplySlot { + Uint64 Id; + }; + MGP_ASSERT_POD(MGPReplySlot, 8); + + // One contiguous run of RenderStateParameters bytes. The pipeline/dynamic split is + // defined exactly once, in MGPipeRenderStateSpans, and generated by G7 from the field + // list VulkanRenderer::ComputePipelineStateHash already hashes (section 4.5.2). + struct MGPStateChunk { + Uint16 Offset; + Uint16 Length; + }; + MGP_ASSERT_POD(MGPStateChunk, 4); + + // The payload of every call that carries nothing but an object identity. + struct MGPHandleOnly { + MGPipeHandle Handle; + Uint32 Kind; // MGPipeKind, widened for a stable wire size + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPHandleOnly, 16); + + // --------------------------------------------------------------------------------- + // Screen: caps, resources, fences + // --------------------------------------------------------------------------------- + + // Capability bits that replace "is this table slot null" as an implicit feature probe + // (section 4.4.1). The five ownership-switch bits of v1 are deliberately absent: what + // they tried to express - who performs primitive-restart rewriting and multi-draw + // flattening - is not expressible as a capability (D-B7). + enum MGPCapBit : Uint64 { + kCapNone = 0, + kCapViewportArray = 1ull << 0, + kCapFloat64VertexAttrib = 1ull << 1, + kCapResidentSubData = 1ull << 2, + kCapCpuXfbPrimitiveAccounting = 1ull << 3, + kCapTimerQuery = 1ull << 4, + kCapOcclusionQuery = 1ull << 5, + kCapXfbPrimitivesQuery = 1ull << 6, + // The server rewrites restart indices / flattens multi-draws itself and therefore + // needs the index bytes on its side: under split this arms the index host mirror + // (D-B7). + kCapNeedsHostIndexBytes = 1ull << 7, + // The server packs named uniform blocks into its own ring and therefore needs the + // host bytes of a set_shader_buffers(Uniform) range (D-B8). + kCapNeedsHostUboBytes = 1ull << 8, + }; + + struct MGPCaps { + // The ~90 flat scalars the backends already publish, by inclusion rather than by + // restatement: a caps field added there must not need a second edit here. + DynamicBackendParameters Dynamic; + Uint64 CallMask; // MGPCapBit + // The two halves that are not flat PODs travel as blobs: the format capability + // cache holds Vector sample-count lists, and the renderer strings are + // Strings. Their serializers land with the transport (P5). + MGPBlobRef FormatCapabilities; + MGPBlobRef RendererInfo; + }; + static_assert(std::is_trivially_copyable_v, "MGPCaps must be trivially copyable"); + // Stated as a COMPOSITION rather than a literal: DynamicBackendParameters still carries + // SizeT fields, so its literal size is ABI-dependent until P0.5 moves the caps block + // into MGPipeValueTypes.h with fixed-width members. The assertion still fires on any + // padding introduced between the members below. + static_assert(sizeof(MGPCaps) == sizeof(DynamicBackendParameters) + 8 + 24 + 24, + "MGPCaps gained padding or a member; update the wire format"); + + // Discriminated resource descriptor: buffers, every texture target and renderbuffers + // share one create/respecify shape (section 4.5.1). + struct MGPResourceDesc { + MGPipeHandle Resource; + Uint8 Target; // Buffer | Tex1D..TexCubeArray | Tex2DMS.. | Renderbuffer | TexBuffer + Uint8 StorageKind; // == TextureStorageType (Mipmap | Buffer) + // VERTEX|INDEX|CONSTANT|SHADER_BUFFER|INDIRECT|SAMPLER|SHADER_IMAGE|RENDER_TARGET| + // DEPTH_STENCIL|STREAM_OUTPUT|ATOMIC|ELEMENT_ARRAY. The ELEMENT_ARRAY bit is the + // D-B7 switch: with kCapNeedsHostIndexBytes set the server mirrors this resource. + Uint16 BindMask; + Uint32 InternalFormat; // already resolved to an uncompressed fallback by the client + Uint32 Width, Height, Depth; + Uint16 ArrayLayers, Levels, Samples; + Uint8 FixedSampleLocations, Immutable; + Uint32 Usage; // BufferUsage + Uint32 StorageFlags; // glBufferStorage flags + Uint8 HasDefinedContent; // false after a NULL-data respecify + Uint8 ImageBindableHint; // client-side everImageBound; pre-emptive allocation + Uint16 Pad0; + // Diagnostics only. A GL name is NEVER an identity, never a memo key and never part + // of a content hash (section 4.2.1). Widened from the plan's two bytes, which + // cannot hold one. + Uint32 GlNameForDiag; + Uint32 Pad1; + MGPipeHandle ViewOf; // storage owner for a texture view + MGPipeHandle BufferForTexBuffer; // texture-buffer backing store + Uint64 BufOffset, BufSize; // kWholeBuffer == ~0, resolved live + }; + MGP_ASSERT_POD(MGPResourceDesc, 88); + inline constexpr Uint64 kMGPipeWholeBuffer = ~0ull; + + struct MGPFenceWait { + MGPipeHandle Fence; + Uint64 TimeoutNs; + }; + MGP_ASSERT_POD(MGPFenceWait, 16); + + struct MGPQueryDesc { + MGPipeHandle Query; + Uint32 Kind; // GL query target + Uint32 Stream; // indexed query stream, 0 otherwise + }; + MGP_ASSERT_POD(MGPQueryDesc, 16); + + struct MGPQueryResultRequest { + MGPipeHandle Query; + Uint8 Wait; // the two-value contract of GetSyncStatus is preserved verbatim + Uint8 Pad0[3]; + Uint32 Pad1; + }; + MGP_ASSERT_POD(MGPQueryResultRequest, 16); + + // --------------------------------------------------------------------------------- + // CSOs + // --------------------------------------------------------------------------------- + + // create_render_state carries ONLY the pipeline subset's chunk bytes. chunkMask lets an + // incremental create send just the chunks that moved, against baseCso (section 4.5.2). + struct MGPRenderStateDesc { + MGPipeHandle Cso; + MGPipeHandle BaseCso; + Uint32 ChunkMask; // all ones for a brand new CSO + Uint32 Pad0; + MGPBlobRef Blob; + }; + MGP_ASSERT_POD(MGPRenderStateDesc, 48); + + // Steady state: 12 bytes on the wire, no hashing, no blob. + struct MGPBindRenderState { + MGPipeHandle Cso; + Uint16 Version; + Uint16 PipelineVersion; + }; + MGP_ASSERT_POD(MGPBindRenderState, 12); + + // The half of the render state that must NOT mint a CSO: viewport, scissor, depth + // range, blend colour, line width, polygon offset, stencil ref/write mask, clear + // values, sample coverage, hints and the point-size family. This is what keeps + // glViewport from evicting Magma's pipeline memo (D-B1). + struct MGPDynamicState { + Uint32 ChunkMask; + Uint16 Version; + Uint16 Pad0; + MGPBlobRef Blob; + }; + MGP_ASSERT_POD(MGPDynamicState, 32); + + // Both views travel, and neither is derivable from the other: the resolved + // VertexAttribute[32] AND the binding points, because a pointer-call stride of 0 means + // "element size" while a binding-model stride of 0 means "every vertex reads the same + // element" (section 4.5.3). IsLong and Type == Float64 are carried separately. + struct MGPVertexElements { + MGPipeHandle Cso; + Uint32 AttributeCount; + Uint32 BindingPointCount; + MGPBlobRef Blob; // VertexAttribute[] followed by VertexBufferBindingPoint[] + }; + MGP_ASSERT_POD(MGPVertexElements, 40); + + // SamplerParameters crosses byte for byte INCLUDING borderColorForm: without it the + // backend cannot choose between glSamplerParameterIiv and fv, or between the + // VkBorderColor families, because all three representations are always numerically + // populated (section 4.5.4). Carried as a blob until P0.5 gives it a value header. + struct MGPSamplerDesc { + MGPipeHandle Cso; + MGPBlobRef Parameters; + }; + MGP_ASSERT_POD(MGPSamplerDesc, 32); + + // = pipe_sampler_view, and ONLY the view restrictions. Everything a glTexParameter + // writes lives on set_texture_params instead, because a texture that is only an FBO + // attachment, only an image binding or only a glCopyImageSubData endpoint has no + // sampler view to hang it on (section 4.4.3). + struct MGPSamplerView { + MGPipeHandle Cso; + MGPipeHandle Texture; + Uint32 InternalFormat; // aliasing format for glTextureView + Uint8 Target; + Uint8 Pad0[3]; + Uint16 MinLevel, NumLevels, MinLayer, NumLayers; + Uint16 Samples; + Uint8 FixedSampleLocations; + Uint8 Pad1; + }; + MGP_ASSERT_POD(MGPSamplerView, 36); + + // Per texture OBJECT, independent of any view. + struct MGPTextureParams { + MGPipeHandle Res; + Uint16 BaseLevel, MaxLevel; + Uint8 Swizzle[4]; + Uint8 DepthStencilMode; + // Mirrors m_forceTextureParamsResync: the widened-channel carrier needs a swizzle + // override that the frontend params version does not move for. + Uint8 ForceResync; + Uint8 Pad0[2]; + Float MinLod, MaxLod, LodBias; + }; + MGP_ASSERT_POD(MGPTextureParams, 32); + + // create_shader_state. The reflection blob is the whole LinkArtifacts + SpirvArtifacts + // archive; P0.5 extracts those types out of ProgramObject.h so a server can + // deserialize into them without dragging in glslang (section 4.5.5). + struct MGPProgramDesc { + MGPipeHandle Cso; + Uint32 StageMask; // == GetLinkedShaderStages() + Uint32 GlobalUboSize; + Uint32 ReservedNumSamplesOffset; + Uint8 SpirvStatus; + Uint8 NativeFloat64; + Uint8 PointSizeDemoted; + Uint8 EnableSpirvValidation; + MGPBlobRef Spirv[6]; // per stage + MGPBlobRef Reflection; + }; + MGP_ASSERT_POD(MGPProgramDesc, 192); + + // --------------------------------------------------------------------------------- + // set_* + // --------------------------------------------------------------------------------- + + // = pipe_surface. internalFormat is INLINE so the four cross-object masks fall out at + // push time with no lookup (section 4.5.6). + struct MGPSurface { + MGPipeHandle Res; + Uint32 InternalFormat; + Uint8 Kind; // Texture | Renderbuffer | None + Uint8 Layered; + Uint16 Level; + Uint32 Layer; + Uint16 UploadTarget; + Uint16 Pad0; + }; + MGP_ASSERT_POD(MGPSurface, 24); + + struct MGPFramebufferState { + MGPipeHandle Fbo; // kMGPipeDefaultFramebuffer for the default framebuffer + MGPSurface Color[8]; + MGPSurface Depth, Stencil; + // The RESOLVED read surface, not an index. This is what structurally closes the + // read-buffer-shared-FBO defect class. + MGPSurface ReadSurface; + Int8 DrawBuffers[8]; // attachment index, -1 = NONE + Uint16 Width, Height, Layers, Samples; + Uint8 FixedSampleLocations, IsDefault, Complete, Pad0; + Uint32 Pad1; + // Two jobs (section 4.5.6): the server's render-pass memo key, and the CLIENT's + // emission suppressor - an unchanged hash means this record is not sent at all. + // The same pattern is mandatory for every kVarTail set_* below, or 26.2's + // redundant glBindSampler traffic reappears as a variable-length record per batch. + Uint64 ContentHash; + }; + MGP_ASSERT_POD(MGPFramebufferState, 304); + + struct MGPVertexBuffer { + MGPipeHandle Res; + Uint64 Offset; + Uint32 Stride; + Uint32 Divisor; + Uint32 BindingIndex; + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPVertexBuffer, 32); + + // Var-tail header: MGPVertexBuffer[Count] follows. + struct MGPVertexBuffers { + Uint32 Start, Count; + Uint64 ContentHash; + }; + MGP_ASSERT_POD(MGPVertexBuffers, 16); + + // An independent call, NOT a subset of the VAO configuration version (D5). + struct MGPIndexBuffer { + MGPipeHandle Res; + Uint64 Offset; + Uint32 IndexSize; + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPIndexBuffer, 24); + + struct MGPIndirectBuffers { + MGPipeHandle DrawIndirect; + MGPipeHandle Parameter; + }; + MGP_ASSERT_POD(MGPIndirectBuffers, 16); + + // One entry of set_sampler_views. No stage dimension: MobileGL's texture unit space is + // MERGED (TextureState::m_textureUnits is one Array of MAX_TEXTURE_IMAGE_UNITS = 192), + // and the same unit may be sampled from two stages (section 4.4.3). + struct MGPBoundView { + MGPipeHandle View; + MGPipeHandle Texture; + Uint32 Unit; + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPBoundView, 24); + + struct MGPSamplerViews { + Uint32 Start, Count; + Uint64 ContentHash; + }; + MGP_ASSERT_POD(MGPSamplerViews, 16); + + // Var-tail header: MGPipeHandle[Count] of sampler CSOs follows. + struct MGPSamplerStates { + Uint32 Start, Count; + Uint64 ContentHash; + }; + MGP_ASSERT_POD(MGPSamplerStates, 16); + + struct MGPImageView { + MGPipeHandle Res; + Uint32 Unit; + Uint32 InternalFormat; + Uint32 Layer; + Uint16 Level; + Uint8 Layered; + Uint8 Access; + }; + MGP_ASSERT_POD(MGPImageView, 24); + + struct MGPShaderImages { + Uint32 Start, Count; + Uint64 ContentHash; + }; + MGP_ASSERT_POD(MGPShaderImages, 16); + + // One bound buffer range. Payload is populated for the Uniform class only, and only + // while kCapNeedsHostUboBytes is set (D-B8). + struct MGPBufferRange { + MGPipeHandle Res; + Uint64 Offset; + Uint64 Size; + MGHostSpan Payload; + }; + MGP_ASSERT_POD(MGPBufferRange, 56); + + // Var-tail header: MGPBufferRange[Count] follows. + struct MGPShaderBuffers { + Uint32 Class; // Uniform | ShaderStorage | AtomicCounter + Uint32 Start; + Uint32 Count; + Uint32 WritableMask; + Uint64 ContentHash; + }; + MGP_ASSERT_POD(MGPShaderBuffers, 24); + + // Var-tail header: MGPBufferRange[Count] then Uint32 offsets[Count]. + struct MGPStreamOutputTargets { + Uint32 Count; + Uint32 Pad0; + Uint64 Generation; + Uint64 ContentHash; + }; + MGP_ASSERT_POD(MGPStreamOutputTargets, 24); + + // Covers the DEFAULT UNIFORM BLOCK only (D6). + struct MGPGlobalConstants { + MGPipeHandle ShaderCso; + Uint32 Version; + Uint32 Pad0; + MGPBlobRef Blob; + }; + MGP_ASSERT_POD(MGPGlobalConstants, 40); + + // The float/int/uint view is resolved on the CLIENT by ClassifyVertexAttribType. + struct MGPAttribValue { + Uint32 Location; + Uint8 ValueClass; // Float | Int | Uint | Double + Uint8 Pad0[3]; + Uint32 Data[4]; + }; + MGP_ASSERT_POD(MGPAttribValue, 24); + + // Var-tail header: MGPAttribValue[popcount(Mask)] follows. + struct MGPVertexAttribDefaults { + Uint32 Mask; + Uint32 Count; + }; + MGP_ASSERT_POD(MGPVertexAttribDefaults, 8); + + // PACK only. There is deliberately no unpack counterpart: nothing on the far side of + // the boundary reads unpack state (section 4.6 D5), and the staged-repack upload path + // does not even issue glPixelStorei. + struct MGPPixelPackState { + PixelStoreParameters Pack; + }; + static_assert(std::is_trivially_copyable_v); + static_assert(sizeof(MGPPixelPackState) == sizeof(PixelStoreParameters)); + + // Also a shader-variant input: both backends bake these into the synthesized + // pass-through control stage. + struct MGPPatchState { + Uint32 Vertices; + Uint32 Pad0; + Float Outer[4]; + Float Inner[2]; + Uint32 Pad1[2]; + }; + MGP_ASSERT_POD(MGPPatchState, 40); + + // Migration-only (section 6.3). Every stage removes fields and lowers + // MGL_RESIDUAL_BLOCK_SIZE; P13 asserts it is zero, which is the retirement trip wire. + // + // Layout must be asserted MEMBER BY MEMBER, not only by sizeof: a heterogeneous POD + // union is where padding differs across ABIs, and the monolith verify harness is blind + // to it because both sides are the same translation unit. G3 emits the offsetof + // assertions; under split the block is serialized field-wise rather than memcpy'd. + struct ResidualValueBlock { + RenderStateParameters RenderState; // until create/bind_render_state + set_dynamic_state land + PixelStoreParameters Pack; // until set_pixel_pack_state lands + Uint64 CapabilityBits; + Uint32 PatchVertices; + Uint32 Pad0; + Float PatchOuter[4]; + Float PatchInner[2]; + Uint32 Pad1[2]; + }; + static_assert(std::is_trivially_copyable_v); +// The retirement ratchet. This number only ever goes DOWN: every stage that lands a real +// set_* call deletes fields here and lowers it, and P13 replaces it with +// static_assert(sizeof(ResidualValueBlock) == 0), which stays red until the last field is +// gone. Shrinking the block without lowering the number, or growing it at all, is a build +// break - which is the point. +// +// Stable across the ABIs MobileGL ships on: every member of RenderStateParameters and +// PixelStoreParameters is a fixed-width scalar or an array of one, with no pointer and no +// SizeT. +#define MGL_RESIDUAL_BLOCK_SIZE 1248 + static_assert(sizeof(ResidualValueBlock) == MGL_RESIDUAL_BLOCK_SIZE, + "the residual value block changed size; lower MGL_RESIDUAL_BLOCK_SIZE if a field " + "retired, and do not raise it"); + + struct MGPResidualValueState { + Uint32 Version; + Uint32 Pad0; + MGPBlobRef Blob; + }; + MGP_ASSERT_POD(MGPResidualValueState, 32); + + // --------------------------------------------------------------------------------- + // Transfer + // --------------------------------------------------------------------------------- + + // Shape copied from the unpack ring's existing UnpackStagingBlock. The source strides + // are CARRIED, not inferred from a pointer comparison: the old + // `uploadData == mipData` test cannot survive a split, where the client neither ships + // the whole level nor keeps a server-side mirror of it (section 4.5.6). + struct MGPSubRegion { + Int32 X, Y, Z; + Uint32 W, H, D; + Uint64 SrcOffset; // into the blob + Uint32 SrcRowStride; // bytes; 0 = tightly packed (w * bpp) + Uint32 SrcSliceStride; // bytes; 0 = tightly packed + }; + MGP_ASSERT_POD(MGPSubRegion, 40); + + // Carries the union box AND the region list so the SERVER picks the upload shape - the + // decision belongs on the side that pays the GPU cost. Mali prices texture upload by + // JOB COUNT: ~100 sprite rects against one union box measured +6 ms/frame. + struct MGPSubData { + MGPipeHandle Res; + Uint16 Target, Level; + // Replaces the backend's `uploadData == mipData` pointer comparison: are these + // bytes an untransformed level shadow? + Uint8 SourceIsVerbatimLevelShadow; + Uint8 Pad0[3]; + MGPBox UnionBox; + Uint32 RegionCount; // MGPSubRegion[] in the variable tail + Uint32 Pad1; + MGPBlobRef Blob; + }; + MGP_ASSERT_POD(MGPSubData, 72); + + // The forward terminator for a server-initiated texture pull (section 7.1). May carry + // zero regions - that is how a pull that needs nothing is answered. + struct MGPSubDataComplete { + MGPipeHandle Res; + Uint16 Target, FirstLevel, LevelCount, Pad0; + Uint64 PullSerial; + }; + MGP_ASSERT_POD(MGPSubDataComplete, 24); + + // Carries the application's REAL access flags, not a normalized subset. + struct MGPFlushRange { + MGPipeHandle Res; + Uint64 Offset, Size; + Uint32 AccessFlags; // Flags + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPFlushRange, 32); + + struct MGPReadback { + MGPipeHandle Res; + Uint64 Offset, Size; + }; + MGP_ASSERT_POD(MGPReadback, 24); + + struct MGPCopyRegion { + MGPipeHandle Src, Dst; + MGPBox SrcBox; + Int32 DstX, DstY, DstZ; + Uint16 SrcTarget, DstTarget; + Uint16 SrcLevel, DstLevel; + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPCopyRegion, 64); + + struct MGPBlit { + MGPipeHandle ReadFbo, DrawFbo; + Int32 SrcX0, SrcY0, SrcX1, SrcY1; + Int32 DstX0, DstY0, DstX1, DstY1; + Uint32 Mask; + Uint32 Filter; + }; + MGP_ASSERT_POD(MGPBlit, 56); + + // One discriminated record replacing glClear, the four glClearBuffer* and the four + // glClearNamedFramebuffer* entry points (section 4.4.4). + struct MGPClear { + MGPipeHandle Fbo; + Uint32 Kind; // Whole | Color | Depth | Stencil | DepthStencil + Int32 DrawBufferIndex; + Uint32 BufferMask; // GL_COLOR_BUFFER_BIT etc. for the whole-framebuffer form + Uint32 ValueClass; // Float | Int | Uint + Uint32 ColorValue[4]; + Float DepthValue; + Int32 StencilValue; + }; + MGP_ASSERT_POD(MGPClear, 48); + + struct MGPMipPlan { + MGPipeHandle Res; + Uint16 Target, BaseLevel, LevelCount, Pad0; + }; + MGP_ASSERT_POD(MGPMipPlan, 16); + + // read_pixels and get_texture_image share one shape; both answer into a reply slot. + struct MGPReadbackInfo { + MGPipeHandle Res; // null for read_pixels: the bound read surface answers + MGPBox Box; + Uint32 Format, Type; + Uint16 Target, Level; + Uint32 Pad0; + Uint64 DstOffset, DstSize; + }; + MGP_ASSERT_POD(MGPReadbackInfo, 64); + + // --------------------------------------------------------------------------------- + // Commands + // --------------------------------------------------------------------------------- + + enum MGPDrawFlagBit : Uint8 { + kDrawHasUserIndices = 1u << 0, + kDrawPrimitiveRestart = 1u << 1, + kDrawIndicesAreClient = 1u << 2, + kDrawHasIndexRange = 1u << 3, + kDrawHasXfbCount = 1u << 4, + }; + + // = pipe_draw_info. Today's twenty draw entry points collapse onto this one call, with + // MGPDrawRange[] holding exactly the shape the glMultiDraw* family already has. + // + // minIndex/maxIndex are computed only on the client-memory array path today, and + // xfbCpuCapturedVertices only on the XFB scatter path, so Flags gates the WORK. They + // stay in the fixed head; moving them into the variable tail is a wire-format decision + // that belongs with the transport (P5), where per-draw byte histograms exist to size + // it. userIndices is in the variable tail already, so the VBO path - every Minecraft + // and Sodium draw - never pays the 32 bytes of an MGHostSpan. + struct MGPDrawInfo { + Uint32 Mode; + Uint8 IndexSize; // 0 = arrays, else 1 / 2 / 4 + Uint8 Flags; // MGPDrawFlagBit + Uint16 Pad0; + Uint32 InstanceCount, StartInstance; + Uint32 RestartIndex; + Uint32 DrawIdOffset; + MGPipeHandle IndexResource; + Uint32 MinIndex, MaxIndex; // ~0 = unknown + Uint64 XfbCpuCapturedVertices; + Uint32 NumDraws; // MGPDrawRange[] in the variable tail + Uint32 Pad1; + }; + MGP_ASSERT_POD(MGPDrawInfo, 56); + + // = pipe_draw_start_count_bias. + struct MGPDrawRange { + Uint32 Start, Count; + Int32 IndexBias; + }; + 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. + struct MGPDrawIndirect { + MGPipeHandle Buffer; + MGPipeHandle ParameterBuffer; + Uint64 Offset, ParameterOffset; + Uint32 Stride, DrawCount; + }; + MGP_ASSERT_POD(MGPDrawIndirect, 40); + + struct MGPGridInfo { + Uint32 GridX, GridY, GridZ; + Uint32 BlockX, BlockY, BlockZ; + MGPipeHandle IndirectBuffer; + Uint64 IndirectOffset; + Uint8 IsIndirect; + Uint8 Pad0[7]; + }; + MGP_ASSERT_POD(MGPGridInfo, 48); + + struct MGPMemoryBarrier { + Uint32 Bits; // GLbitfield + Uint8 ByRegion; + Uint8 Pad0[3]; + }; + MGP_ASSERT_POD(MGPMemoryBarrier, 8); + + struct MGPStreamOutputBegin { + Uint32 PrimitiveMode; + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPStreamOutputBegin, 8); + + // end_stream_output carries the accounting the client owns; the scatter itself is a + // read-modify-write of the client's shadow and lives there (section 7.2.1). + struct MGPXfbAccounting { + Uint64 CapturedVertices; + Uint64 PrimitivesWritten; + Uint32 PrimitiveMode; + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPXfbAccounting, 24); + + struct MGPStreamOutputControl { + Uint32 Reserved; + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPStreamOutputControl, 8); + + struct MGPFlush { + Uint32 Flags; + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPFlush, 8); + + struct MGPPresent { + Uint64 FrameSerial; + }; + MGP_ASSERT_POD(MGPPresent, 8); + + struct MGPSwapInterval { + Int32 Interval; + Uint32 Pad0; + }; + MGP_ASSERT_POD(MGPSwapInterval, 8); + + // --------------------------------------------------------------------------------- + // Reverse channel payloads (section 7.1) + // --------------------------------------------------------------------------------- + + struct MGPSurfaceInfo { + Uint32 Width, Height; + Uint32 InternalFormat; + Uint16 Samples, Layers; + Uint8 IsDefault; + Uint8 Pad0[7]; + }; + MGP_ASSERT_POD(MGPSurfaceInfo, 24); + +#undef MGP_ASSERT_POD +} // namespace MobileGL::MG_Pipe diff --git a/MobileGL/MG_Pipe/PipeCalls.def b/MobileGL/MG_Pipe/PipeCalls.def new file mode 100644 index 00000000..799fdea6 --- /dev/null +++ b/MobileGL/MG_Pipe/PipeCalls.def @@ -0,0 +1,148 @@ +// MobileGL - MobileGL/MG_Pipe/PipeCalls.def +// 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 + +// The single source of truth for the MGPipe call catalogue (plan B section 4.1 / 4.4 / +// appendix A). One line per call; seven generators consume this file +// (scripts/gen_pipe.py -> MG_Pipe/generated/*.inc) and one unit test +// (MG_Test/Pipe/PipeCatalogueTest.cpp) pins the arithmetic. +// +// X(Name, PayloadStruct, Class, Flags) +// Class : kScreen | kCtxCso | kCtxState | kCtxObject | kCtxVerb | kCtxQuery +// kScreen lands in struct MGPipeScreen, every other class in struct +// MGPipeContext (plan section 4.3). +// Flags : kNone | kNeedsAck | kHasBlob | kVarTail | kHostSpan | kReplySlot | kOptional +// +// RECORD NUMBERING NEVER CHURNS. Entries that are not implemented yet still occupy their +// line (plan section 11, P0: "the complete call catalogue, placeholders included"). A new +// call is APPENDED to its group; a retired call keeps its slot with a comment. The wire +// opcode is the 1-based position in this list, so reordering is a protocol break. +// +// --------------------------------------------------------------------------------------- +// COUNTS. MGP_CALL_LIST_DOCUMENTED_COUNT below is the authority; PipeCatalogueTest asserts +// that the expansion, the two generated tables and this number agree. +// +// class entries group (as the plan tabulates it) +// kScreen 10 screen: caps 1 + resource 3 + persistent map 2 + fence 4 +// kCtxQuery 6 query object namespace +// 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 68 +// +// 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: +// - "screen 14" tabulates the fence and query families together with the screen block. +// Section 4.3 assigns the query NAMESPACE to the context ("VAO / FBO / XFB object / +// query namespaces, the command stream, present"), so the six query calls carry +// kCtxQuery and live in MGPipeContext. Screen keeps 10. The eight EGL lifecycle entry +// points stay virtual functions on pActiveBackendObject and are deliberately NOT calls +// here (section 4.4.1, last row). +// - "CSO 15" is create/bind/delete x 5 kinds. Two of those binds are ALSO named in the +// set_* catalogue as their array forms - bind_sampler_states and set_sampler_views +// (section 4.4.3) - and a call may only exist once, so they are emitted under +// kCtxState and the CSO group holds 13: create/delete x 5 plus the three remaining +// binds (render state, vertex elements, shader). +// - "transfer 12" enumerates 11 calls in section 4.4.4 plus appendix A +// (resource_subdata, buffer_subdata_resident, resource_flush_range, resource_readback, +// resource_copy_region, blit, clear, generate_mipmap, read_pixels, get_texture_image, +// resource_subdata_complete). Eleven is what is emitted; the twelfth is not named +// anywhere in the plan. +// - "about 74 items" in section 4.1 is the sum of those headline numbers, so it inherits +// the same double counting. 68 unique records is the honest total. +// --------------------------------------------------------------------------------------- + +#define MGP_CALL_LIST_DOCUMENTED_COUNT 68 + +// clang-format off +#define MGP_CALL_LIST(X) \ + /* ---- screen: caps, resources, persistent map, fences (plan 4.4.1) ---- */ \ + X(GetCaps, MGPCaps, kScreen, kReplySlot) \ + X(ResourceCreate, MGPResourceDesc, kScreen, kNone) \ + X(ResourceRespecify, MGPResourceDesc, kScreen, kNone) \ + X(ResourceDestroy, MGPHandleOnly, kScreen, kNone) \ + X(MapPersistent, MGPHandleOnly, kScreen, kReplySlot|kOptional) \ + X(UnmapPersistent, MGPHandleOnly, kScreen, kOptional) \ + X(FenceCreate, MGPHandleOnly, kScreen, kNone) \ + X(FenceStatus, MGPHandleOnly, kScreen, kReplySlot) \ + X(FenceWait, MGPFenceWait, kScreen, kReplySlot) \ + X(FenceDestroy, MGPHandleOnly, kScreen, kNone) \ + /* ---- context: query objects (plan 4.3 gives the namespace to the context) ---- */ \ + X(QueryCreate, MGPQueryDesc, kCtxQuery, kNone) \ + X(QueryBegin, MGPQueryDesc, kCtxQuery, kNone) \ + X(QueryEnd, MGPQueryDesc, kCtxQuery, kNone) \ + X(QueryAvailable, MGPHandleOnly, kCtxQuery, kReplySlot) \ + X(QueryResult, MGPQueryResultRequest, kCtxQuery, kReplySlot) \ + X(QueryDestroy, MGPHandleOnly, kCtxQuery, kNone) \ + /* ---- context: CSO create/bind/delete (plan 4.4.2, 4.5.2-4.5.5) ---- */ \ + X(CreateRenderState, MGPRenderStateDesc, kCtxCso, kHasBlob) \ + X(BindRenderState, MGPBindRenderState, kCtxCso, kNone) \ + X(DeleteRenderState, MGPHandleOnly, kCtxCso, kNone) \ + X(CreateVertexElements, MGPVertexElements, kCtxCso, kHasBlob) \ + X(BindVertexElements, MGPHandleOnly, kCtxCso, kNone) \ + X(DeleteVertexElements, MGPHandleOnly, kCtxCso, kNone) \ + X(CreateSamplerState, MGPSamplerDesc, kCtxCso, kNone) \ + X(DeleteSamplerState, MGPHandleOnly, kCtxCso, kNone) \ + X(CreateSamplerView, MGPSamplerView, kCtxCso, kNone) \ + X(DeleteSamplerView, MGPHandleOnly, kCtxCso, kNone) \ + X(CreateShaderState, MGPProgramDesc, kCtxCso, kHasBlob) \ + X(BindShaderState, MGPHandleOnly, kCtxCso, kNone) \ + X(DeleteShaderState, MGPHandleOnly, kCtxCso, kNone) \ + /* ---- context: set_* (plan 4.4.3) ---- */ \ + X(SetDynamicState, MGPDynamicState, kCtxState, kHasBlob) \ + X(SetFramebufferState, MGPFramebufferState, kCtxState, kNone) \ + X(SetVertexBuffers, MGPVertexBuffers, kCtxState, kVarTail) \ + X(SetIndexBuffer, MGPIndexBuffer, kCtxState, kNone) \ + X(SetIndirectBuffers, MGPIndirectBuffers, kCtxState, kNone) \ + X(SetSamplerViews, MGPSamplerViews, kCtxState, kVarTail) \ + X(BindSamplerStates, MGPSamplerStates, kCtxState, kVarTail) \ + X(SetShaderImages, MGPShaderImages, kCtxState, kVarTail) \ + X(SetShaderBuffers, MGPShaderBuffers, kCtxState, kVarTail|kHostSpan) \ + X(SetStreamOutputTargets, MGPStreamOutputTargets, kCtxState, kVarTail) \ + X(SetGlobalConstants, MGPGlobalConstants, kCtxState, kHasBlob) \ + X(SetVertexAttribDefaults, MGPVertexAttribDefaults, kCtxState, kVarTail) \ + X(SetPixelPackState, MGPPixelPackState, kCtxState, kNone) \ + X(SetPatchState, MGPPatchState, kCtxState, kNone) \ + X(SetDrawProgram, MGPHandleOnly, kCtxState, kNone) \ + X(SetDispatchProgram, MGPHandleOnly, kCtxState, kNone) \ + /* Migration-only carrier for Track V, retired field by field across P2..P13. Its */ \ + /* retirement is a compile error: MGL_RESIDUAL_BLOCK_SIZE only ever goes DOWN and the */ \ + /* final step asserts sizeof(ResidualValueBlock) == 0 (plan 6.3). */ \ + X(SetResidualValueState, MGPResidualValueState, kCtxState, kHasBlob) \ + /* ---- context: per-object state and transfer (plan 4.4.3 set_texture_params, 4.4.4) ---- */ \ + X(SetTextureParams, MGPTextureParams, kCtxObject, kNone) \ + X(ResourceSubData, MGPSubData, kCtxObject, kHasBlob|kVarTail) \ + X(BufferSubDataResident, MGPSubData, kCtxObject, kHasBlob|kOptional) \ + X(ResourceSubDataComplete, MGPSubDataComplete, kCtxObject, kNone) \ + X(ResourceFlushRange, MGPFlushRange, kCtxObject, kNone) \ + X(ResourceReadback, MGPReadback, kCtxObject, kReplySlot) \ + X(ResourceCopyRegion, MGPCopyRegion, kCtxObject, kNone) \ + X(GenerateMipmap, MGPMipPlan, kCtxObject, kNone) \ + X(GetTextureImage, MGPReadbackInfo, kCtxObject, kReplySlot) \ + /* ---- context: transfer calls that read whole-context state, and the commands ---- */ \ + X(Blit, MGPBlit, kCtxVerb, kNone) \ + X(Clear, MGPClear, kCtxVerb, kNone) \ + X(ReadPixels, MGPReadbackInfo, kCtxVerb, kReplySlot) \ + X(DrawVbo, MGPDrawInfo, kCtxVerb, kHostSpan|kVarTail) \ + X(LaunchGrid, MGPGridInfo, kCtxVerb, kNone) \ + X(MemoryBarrier, MGPMemoryBarrier, kCtxVerb, kNone) \ + X(BeginStreamOutput, MGPStreamOutputBegin, kCtxVerb, kNone) \ + X(EndStreamOutput, MGPXfbAccounting, kCtxVerb, kNone) \ + X(PauseStreamOutput, MGPStreamOutputControl, kCtxVerb, kNone) \ + X(ResumeStreamOutput, MGPStreamOutputControl, kCtxVerb, kNone) \ + X(Flush, MGPFlush, kCtxVerb, kNone) \ + X(Present, MGPPresent, kCtxVerb, kNone) \ + X(SetSwapInterval, MGPSwapInterval, kCtxVerb, kOptional) +// clang-format on + +// Explicitly NOT migrated (plan 4.4.6 / appendix A "explicit deletions"): GetIntegeri_v, +// GetInteger64i_v, GetProgramiv (only GL_COMPUTE_WORK_GROUP_SIZE is a real backend answer +// and it lives in MGPCaps), ShaderStorageBlockBinding (folded into MGPProgramDesc's +// reflection archive), 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 new file mode 100644 index 00000000..0aaddd1d --- /dev/null +++ b/MobileGL/MG_Pipe/PipeFields.def @@ -0,0 +1,235 @@ +// MobileGL - MobileGL/MG_Pipe/PipeFields.def +// 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 + +// Field lists for the G4 shadow comparator (plan B section 10.3-2). One macro per payload +// in MGPipeTypes.h, listing the fields that carry MEANING - padding is deliberately absent, +// because MOBILEGL_PIPE_VERIFY has to have ZERO false positives and a padding byte is +// exactly what makes a memcmp of RenderStateParameters false-DIFFER +// (DirectGLES.cpp documents that behaviour where it does the same comparison itself). +// +// Hand maintained alongside MGPipeTypes.h. Adding a field to a payload without adding it +// here makes the comparator blind to it; that gap closes in P1, when the verify harness +// goes live and the comparator's coverage is itself asserted. +// +// clang-format off + +#define MGP_FIELDS_MGPBlobRef(F) \ + F(Offset) F(Size) F(Seg) + +#define MGP_FIELDS_MGPRange(F) \ + F(Offset) F(Size) + +#define MGP_FIELDS_MGPBox(F) \ + F(X) F(Y) F(Z) F(W) F(H) F(D) + +#define MGP_FIELDS_MGPReplySlot(F) \ + F(Id) + +#define MGP_FIELDS_MGPStateChunk(F) \ + F(Offset) F(Length) + +#define MGP_FIELDS_MGPHandleOnly(F) \ + F(Handle) F(Kind) + +#define MGP_FIELDS_MGPCaps(F) \ + F(Dynamic) F(CallMask) F(FormatCapabilities) F(RendererInfo) + +#define MGP_FIELDS_MGPResourceDesc(F) \ + F(Resource) F(Target) F(StorageKind) F(BindMask) F(InternalFormat) F(Width) F(Height) F(Depth) \ + F(ArrayLayers) F(Levels) F(Samples) F(FixedSampleLocations) F(Immutable) F(Usage) F(StorageFlags) \ + F(HasDefinedContent) F(ImageBindableHint) F(GlNameForDiag) F(ViewOf) F(BufferForTexBuffer) \ + F(BufOffset) F(BufSize) + +#define MGP_FIELDS_MGPFenceWait(F) \ + F(Fence) F(TimeoutNs) + +#define MGP_FIELDS_MGPQueryDesc(F) \ + F(Query) F(Kind) F(Stream) + +#define MGP_FIELDS_MGPQueryResultRequest(F) \ + F(Query) F(Wait) + +#define MGP_FIELDS_MGPRenderStateDesc(F) \ + F(Cso) F(BaseCso) F(ChunkMask) F(Blob) + +#define MGP_FIELDS_MGPBindRenderState(F) \ + F(Cso) F(Version) F(PipelineVersion) + +#define MGP_FIELDS_MGPDynamicState(F) \ + F(ChunkMask) F(Version) F(Blob) + +#define MGP_FIELDS_MGPVertexElements(F) \ + F(Cso) F(AttributeCount) F(BindingPointCount) F(Blob) + +#define MGP_FIELDS_MGPSamplerDesc(F) \ + F(Cso) F(Parameters) + +#define MGP_FIELDS_MGPSamplerView(F) \ + F(Cso) F(Texture) F(InternalFormat) F(Target) F(MinLevel) F(NumLevels) F(MinLayer) F(NumLayers) \ + F(Samples) F(FixedSampleLocations) + +#define MGP_FIELDS_MGPTextureParams(F) \ + F(Res) F(BaseLevel) F(MaxLevel) F(Swizzle) F(DepthStencilMode) F(ForceResync) F(MinLod) F(MaxLod) \ + F(LodBias) + +#define MGP_FIELDS_MGPProgramDesc(F) \ + F(Cso) F(StageMask) F(GlobalUboSize) F(ReservedNumSamplesOffset) F(SpirvStatus) F(NativeFloat64) \ + F(PointSizeDemoted) F(EnableSpirvValidation) F(Spirv) F(Reflection) + +#define MGP_FIELDS_MGPSurface(F) \ + F(Res) F(InternalFormat) F(Kind) F(Layered) F(Level) F(Layer) F(UploadTarget) + +#define MGP_FIELDS_MGPFramebufferState(F) \ + F(Fbo) F(Color) F(Depth) F(Stencil) F(ReadSurface) F(DrawBuffers) F(Width) F(Height) F(Layers) \ + F(Samples) F(FixedSampleLocations) F(IsDefault) F(Complete) F(ContentHash) + +#define MGP_FIELDS_MGPVertexBuffer(F) \ + F(Res) F(Offset) F(Stride) F(Divisor) F(BindingIndex) + +#define MGP_FIELDS_MGPVertexBuffers(F) \ + F(Start) F(Count) F(ContentHash) + +#define MGP_FIELDS_MGPIndexBuffer(F) \ + F(Res) F(Offset) F(IndexSize) + +#define MGP_FIELDS_MGPIndirectBuffers(F) \ + F(DrawIndirect) F(Parameter) + +#define MGP_FIELDS_MGPBoundView(F) \ + F(View) F(Texture) F(Unit) + +#define MGP_FIELDS_MGPSamplerViews(F) \ + F(Start) F(Count) F(ContentHash) + +#define MGP_FIELDS_MGPSamplerStates(F) \ + F(Start) F(Count) F(ContentHash) + +#define MGP_FIELDS_MGPImageView(F) \ + F(Res) F(Unit) F(InternalFormat) F(Layer) F(Level) F(Layered) F(Access) + +#define MGP_FIELDS_MGPShaderImages(F) \ + F(Start) F(Count) F(ContentHash) + +#define MGP_FIELDS_MGPBufferRange(F) \ + F(Res) F(Offset) F(Size) F(Payload) + +#define MGP_FIELDS_MGPShaderBuffers(F) \ + F(Class) F(Start) F(Count) F(WritableMask) F(ContentHash) + +#define MGP_FIELDS_MGPStreamOutputTargets(F) \ + F(Count) F(Generation) F(ContentHash) + +#define MGP_FIELDS_MGPGlobalConstants(F) \ + F(ShaderCso) F(Version) F(Blob) + +#define MGP_FIELDS_MGPAttribValue(F) \ + F(Location) F(ValueClass) F(Data) + +#define MGP_FIELDS_MGPVertexAttribDefaults(F) \ + F(Mask) F(Count) + +#define MGP_FIELDS_MGPPixelPackState(F) \ + F(Pack) + +#define MGP_FIELDS_MGPPatchState(F) \ + F(Vertices) F(Outer) F(Inner) + +#define MGP_FIELDS_ResidualValueBlock(F) \ + F(RenderState) F(Pack) F(CapabilityBits) F(PatchVertices) F(PatchOuter) F(PatchInner) + +#define MGP_FIELDS_MGPResidualValueState(F) \ + F(Version) F(Blob) + +#define MGP_FIELDS_MGPSubRegion(F) \ + F(X) F(Y) F(Z) F(W) F(H) F(D) F(SrcOffset) F(SrcRowStride) F(SrcSliceStride) + +#define MGP_FIELDS_MGPSubData(F) \ + F(Res) F(Target) F(Level) F(SourceIsVerbatimLevelShadow) F(UnionBox) F(RegionCount) F(Blob) + +#define MGP_FIELDS_MGPSubDataComplete(F) \ + F(Res) F(Target) F(FirstLevel) F(LevelCount) F(PullSerial) + +#define MGP_FIELDS_MGPFlushRange(F) \ + F(Res) F(Offset) F(Size) F(AccessFlags) + +#define MGP_FIELDS_MGPReadback(F) \ + F(Res) F(Offset) F(Size) + +#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) + +#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) \ + F(Mask) F(Filter) + +#define MGP_FIELDS_MGPClear(F) \ + F(Fbo) F(Kind) F(DrawBufferIndex) F(BufferMask) F(ValueClass) F(ColorValue) F(DepthValue) \ + F(StencilValue) + +#define MGP_FIELDS_MGPMipPlan(F) \ + F(Res) F(Target) F(BaseLevel) F(LevelCount) + +#define MGP_FIELDS_MGPReadbackInfo(F) \ + F(Res) F(Box) F(Format) F(Type) F(Target) F(Level) F(DstOffset) F(DstSize) + +#define MGP_FIELDS_MGPDrawInfo(F) \ + F(Mode) F(IndexSize) F(Flags) F(InstanceCount) F(StartInstance) F(RestartIndex) F(DrawIdOffset) \ + F(IndexResource) F(MinIndex) F(MaxIndex) F(XfbCpuCapturedVertices) F(NumDraws) + +#define MGP_FIELDS_MGPDrawRange(F) \ + F(Start) F(Count) F(IndexBias) + +#define MGP_FIELDS_MGPDrawIndirect(F) \ + F(Buffer) F(ParameterBuffer) F(Offset) F(ParameterOffset) F(Stride) F(DrawCount) + +#define MGP_FIELDS_MGPGridInfo(F) \ + F(GridX) F(GridY) F(GridZ) F(BlockX) F(BlockY) F(BlockZ) F(IndirectBuffer) F(IndirectOffset) \ + F(IsIndirect) + +#define MGP_FIELDS_MGPMemoryBarrier(F) \ + F(Bits) F(ByRegion) + +#define MGP_FIELDS_MGPStreamOutputBegin(F) \ + F(PrimitiveMode) + +#define MGP_FIELDS_MGPXfbAccounting(F) \ + F(CapturedVertices) F(PrimitivesWritten) F(PrimitiveMode) + +#define MGP_FIELDS_MGPStreamOutputControl(F) \ + F(Reserved) + +#define MGP_FIELDS_MGPFlush(F) \ + F(Flags) + +#define MGP_FIELDS_MGPPresent(F) \ + F(FrameSerial) + +#define MGP_FIELDS_MGPSwapInterval(F) \ + F(Interval) + +#define MGP_FIELDS_MGPSurfaceInfo(F) \ + F(Width) F(Height) F(InternalFormat) F(Samples) F(Layers) F(IsDefault) + +// Every payload above, in the order the comparator is generated. Keep in sync with the +// macros; gen_pipe.py reads THIS list to know what to emit. +#define MGP_VERIFY_PAYLOAD_LIST(P) \ + P(MGPBlobRef) P(MGPRange) P(MGPBox) P(MGPReplySlot) P(MGPStateChunk) P(MGPHandleOnly) P(MGPCaps) \ + P(MGPResourceDesc) P(MGPFenceWait) P(MGPQueryDesc) P(MGPQueryResultRequest) P(MGPRenderStateDesc) \ + P(MGPBindRenderState) P(MGPDynamicState) P(MGPVertexElements) P(MGPSamplerDesc) P(MGPSamplerView) \ + P(MGPTextureParams) P(MGPProgramDesc) P(MGPSurface) P(MGPFramebufferState) P(MGPVertexBuffer) \ + P(MGPVertexBuffers) P(MGPIndexBuffer) P(MGPIndirectBuffers) P(MGPBoundView) P(MGPSamplerViews) \ + P(MGPSamplerStates) P(MGPImageView) P(MGPShaderImages) P(MGPBufferRange) P(MGPShaderBuffers) \ + P(MGPStreamOutputTargets) P(MGPGlobalConstants) P(MGPAttribValue) P(MGPVertexAttribDefaults) \ + P(MGPPixelPackState) P(MGPPatchState) P(ResidualValueBlock) P(MGPResidualValueState) \ + P(MGPSubRegion) P(MGPSubData) P(MGPSubDataComplete) P(MGPFlushRange) P(MGPReadback) \ + P(MGPCopyRegion) P(MGPBlit) P(MGPClear) P(MGPMipPlan) P(MGPReadbackInfo) P(MGPDrawInfo) \ + P(MGPDrawRange) P(MGPDrawIndirect) P(MGPGridInfo) P(MGPMemoryBarrier) P(MGPStreamOutputBegin) \ + P(MGPXfbAccounting) P(MGPStreamOutputControl) P(MGPFlush) P(MGPPresent) P(MGPSwapInterval) \ + P(MGPSurfaceInfo) + +// clang-format on diff --git a/MobileGL/MG_Pipe/generated/PipeCoverage.inc b/MobileGL/MG_Pipe/generated/PipeCoverage.inc new file mode 100644 index 00000000..fa451ec7 --- /dev/null +++ b/MobileGL/MG_Pipe/generated/PipeCoverage.inc @@ -0,0 +1,108 @@ +// MobileGL - MobileGL/MG_Pipe/generated/PipeCoverage.inc +// 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 + +// G6: backend read inventory -> MGPipe call coverage. +// +// GENERATED by scripts/gen_pipe.py from Coverage.def and scripts/data/backend_read_inventory.md - DO NOT EDIT. +// Regenerate with `python3 scripts/gen_pipe.py`; CI runs it and diffs the result. +// This file is included from MG_Pipe/MGPipe.h inside namespace MobileGL::MG_Pipe. + +// The acceptance rule (plan B section 10.3-5): regenerate, `git diff --exit-code`, and +// ZERO unmapped rows. P0 permits unmapped rows and only counts them; the count below is +// the number the later gate has to drive to zero. +// +// Three pseudo-calls stand for read points that never become a forward record: +// kClientResolved (the frontend answers it), kReverseChannel (it becomes one of the ten +// MGPipeCallbacks) and kStructuralHandle (the row is a signature carrying a +// SharedPtr that becomes an MGPipeHandle parameter). + +struct MGPipeCoverageEntry { + const char* Accessor; + const char* Call; + Uint32 ReadPoints; +}; + +inline constexpr MGPipeCoverageEntry kMGPipeCoverage[] = { + {"Buffer ops delta", "ResourceRespecify", 17}, + {"GetActiveTextureUnit", "SetSamplerViews", 8}, + {"GetBlendColor", "SetDynamicState", 1}, + {"GetBlendEquationIndexed", "CreateRenderState", 1}, + {"GetBlendFuncIndexed", "CreateRenderState", 1}, + {"GetBoundTransformFeedbackName", "SetStreamOutputTargets", 1}, + {"GetBoundVertexArray", "BindVertexElements", 12}, + {"GetBufferBindingPoint", "SetShaderBuffers", 19}, + {"GetBufferBindingPointCount", "SetShaderBuffers", 3}, + {"GetBufferBindingSlot", "SetIndirectBuffers", 29}, + {"GetClampReadColor", "SetDynamicState", 1}, + {"GetClearColor", "SetDynamicState", 1}, + {"GetClearDepth", "SetDynamicState", 1}, + {"GetClearStencil", "SetDynamicState", 1}, + {"GetColorMaskIndexed", "CreateRenderState", 6}, + {"GetCullFaceMode", "CreateRenderState", 1}, + {"GetCurrentVertexAttribute", "SetVertexAttribDefaults", 2}, + {"GetDepthFunc", "CreateRenderState", 1}, + {"GetDepthMask", "CreateRenderState", 5}, + {"GetDepthRangeIndexed", "SetDynamicState", 1}, + {"GetFramebufferBindingSlot", "SetFramebufferState", 19}, + {"GetImageTextureBinding", "SetShaderImages", 14}, + {"GetLineWidth", "SetDynamicState", 1}, + {"GetLogicOp", "CreateRenderState", 1}, + {"GetMaxTouchedTextureUnit", "SetSamplerViews", 1}, + {"GetMinSampleShadingValue", "CreateRenderState", 1}, + {"GetPatchDefaultInnerLevel", "SetPatchState", 3}, + {"GetPatchDefaultOuterLevel", "SetPatchState", 3}, + {"GetPatchVertices", "SetPatchState", 3}, + {"GetPipelineStateVersion", "BindRenderState", 3}, + {"GetPixelStoreParameters", "SetPixelPackState", 6}, + {"GetPolygonModeFront", "CreateRenderState", 1}, + {"GetPolygonOffsetFactor", "SetDynamicState", 1}, + {"GetPolygonOffsetUnits", "SetDynamicState", 1}, + {"GetPrimitiveRestartIndex", "DrawVbo", 3}, + {"GetProgramForDispatch", "SetDispatchProgram", 3}, + {"GetProgramForDraw", "SetDrawProgram", 7}, + {"GetProgramObject", "CreateShaderState", 3}, + {"GetProvokingVertexMode", "CreateRenderState", 1}, + {"GetRenderStateParameters", "CreateRenderState", 11}, + {"GetRenderStateParametersVersion", "BindRenderState", 2}, + {"GetSamplingResolutionGeneration", "SetSamplerViews", 9}, + {"GetScissorBox", "SetDynamicState", 3}, + {"GetStencilState", "CreateRenderState", 8}, + {"GetTextureBindGeneration", "SetSamplerViews", 5}, + {"GetTextureContextId", "SetSamplerViews", 6}, + {"GetTextureObject", "SetSamplerViews", 1}, + {"GetTextureUnitObject", "SetSamplerViews", 19}, + {"GetTouchedBufferBindingPointCount", "SetShaderBuffers", 2}, + {"GetTransformFeedbackCapturedVertices", "DrawVbo", 1}, + {"GetTransformFeedbackGeneration", "SetStreamOutputTargets", 1}, + {"GetTransformFeedbackPausedPrimitiveCounter", "EndStreamOutput", 2}, + {"GetTransformFeedbackProgram", "SetStreamOutputTargets", 3}, + {"GetViewport", "SetDynamicState", 1}, + {"GetViewportIndexed", "SetDynamicState", 1}, + {"InvalidateCompileEnv", "kClientResolved", 2}, + {"IsCapabilityEnabled", "CreateRenderState", 29}, + {"IsCapabilityEnabledIndexed", "CreateRenderState", 1}, + {"IsTransformFeedbackActive", "BeginStreamOutput", 5}, + {"IsTransformFeedbackPaused", "PauseStreamOutput", 2}, + {"RecordError", "kReverseChannel", 6}, + {"ValidateProgramName", "kClientResolved", 3}, + {"handle-ify (wire handle)", "kStructuralHandle", 167}, +}; + +inline constexpr SizeT kMGPipeCoverageEntryCount = 63; +inline constexpr Uint32 kMGPipeInventoryReadPoints = 477; +inline constexpr Uint32 kMGPipeInventoryMappedToCall = 299; +inline constexpr Uint32 kMGPipeInventoryClientResolved = 5; +inline constexpr Uint32 kMGPipeInventoryReverseChannel = 6; +inline constexpr Uint32 kMGPipeInventoryStructuralHandle = 167; +inline constexpr Uint32 kMGPipeInventoryUnmapped = 0; +static_assert(kMGPipeCoverageEntryCount == sizeof(kMGPipeCoverage) / sizeof(kMGPipeCoverage[0])); +static_assert(kMGPipeInventoryMappedToCall + kMGPipeInventoryClientResolved + + kMGPipeInventoryReverseChannel + kMGPipeInventoryStructuralHandle + + kMGPipeInventoryUnmapped == + kMGPipeInventoryReadPoints, + "every inventory row must land in exactly one bucket"); diff --git a/MobileGL/MG_Pipe/generated/PipeFilled.inc b/MobileGL/MG_Pipe/generated/PipeFilled.inc new file mode 100644 index 00000000..7dec6e86 --- /dev/null +++ b/MobileGL/MG_Pipe/generated/PipeFilled.inc @@ -0,0 +1,308 @@ +// MobileGL - MobileGL/MG_Pipe/generated/PipeFilled.inc +// 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 + +// G5: PipeInputs field ids and the per-verb poison generations. +// +// GENERATED by scripts/gen_pipe.py from Coverage.def and PipeCalls.def - DO NOT EDIT. +// Regenerate with `python3 scripts/gen_pipe.py`; CI runs it and diffs the result. +// This file is included from MG_Pipe/MGPipe.h inside namespace MobileGL::MG_Pipe. + +// One field id per GLContext accessor the backends actually read (plan B section 6.2: +// PipeInputs is organized by MEMO KEY, not by read point, which is why the field set is +// small and stable across the whole migration). +// +// The poison is a per-verb GENERATION, not a bit. A bitmap cannot see the dangerous case: +// a field filled by the previous DRAW and then read by the glTexSubImage that follows is +// stale, and its bit is already set. So every verb bumps CurrentVerbSerial, filling a +// field stamps it with that serial, and reading a non-sticky field whose stamp is older is +// Fatal{UnmigratedPipeInput} (section 6.2.2). +// +// P0 is the skeleton: the enum, the tables and the assertion helper exist, PipeInputs +// itself lands in P1. + +enum class MGPipeInputField : Uint16 { + GetActiveTextureUnit, + GetBlendColor, + GetBlendEquationIndexed, + GetBlendFuncIndexed, + GetBoundTransformFeedbackName, + GetBoundVertexArray, + GetBufferBindingSlot, + GetBufferBindingPoint, + GetBufferBindingPointCount, + GetTouchedBufferBindingPointCount, + GetClampReadColor, + GetClearColor, + GetClearDepth, + GetClearStencil, + GetColorMaskIndexed, + GetCullFaceMode, + GetCurrentVertexAttribute, + GetDepthFunc, + GetDepthMask, + GetDepthRangeIndexed, + GetFramebufferBindingSlot, + GetImageTextureBinding, + GetLineWidth, + GetLogicOp, + GetMaxTouchedTextureUnit, + GetMinSampleShadingValue, + GetPatchDefaultInnerLevel, + GetPatchDefaultOuterLevel, + GetPatchVertices, + GetPipelineStateVersion, + GetPixelStoreParameters, + GetPolygonModeFront, + GetPolygonOffsetFactor, + GetPolygonOffsetUnits, + GetPrimitiveRestartIndex, + GetProgramForDispatch, + GetProgramForDraw, + GetProgramObject, + GetProvokingVertexMode, + GetRenderStateParameters, + GetRenderStateParametersVersion, + GetSamplingResolutionGeneration, + GetScissorBox, + GetStencilState, + GetTextureBindGeneration, + GetTextureContextId, + GetTextureObject, + GetTextureUnitObject, + GetTransformFeedbackCapturedVertices, + GetTransformFeedbackGeneration, + GetTransformFeedbackPausedPrimitiveCounter, + GetTransformFeedbackProgram, + GetViewport, + GetViewportIndexed, + IsCapabilityEnabled, + IsCapabilityEnabledIndexed, + IsTransformFeedbackActive, + IsTransformFeedbackPaused, + InvalidateCompileEnv, + ValidateProgramName, + RecordError, + kFieldCount, +}; + +inline constexpr SizeT kMGPipeInputFieldCount = static_cast(MGPipeInputField::kFieldCount); +static_assert(kMGPipeInputFieldCount == 61, "the PipeInputs field set moved"); + +inline constexpr const char* kMGPipeInputFieldNames[kMGPipeInputFieldCount] = { + "GetActiveTextureUnit", + "GetBlendColor", + "GetBlendEquationIndexed", + "GetBlendFuncIndexed", + "GetBoundTransformFeedbackName", + "GetBoundVertexArray", + "GetBufferBindingSlot", + "GetBufferBindingPoint", + "GetBufferBindingPointCount", + "GetTouchedBufferBindingPointCount", + "GetClampReadColor", + "GetClearColor", + "GetClearDepth", + "GetClearStencil", + "GetColorMaskIndexed", + "GetCullFaceMode", + "GetCurrentVertexAttribute", + "GetDepthFunc", + "GetDepthMask", + "GetDepthRangeIndexed", + "GetFramebufferBindingSlot", + "GetImageTextureBinding", + "GetLineWidth", + "GetLogicOp", + "GetMaxTouchedTextureUnit", + "GetMinSampleShadingValue", + "GetPatchDefaultInnerLevel", + "GetPatchDefaultOuterLevel", + "GetPatchVertices", + "GetPipelineStateVersion", + "GetPixelStoreParameters", + "GetPolygonModeFront", + "GetPolygonOffsetFactor", + "GetPolygonOffsetUnits", + "GetPrimitiveRestartIndex", + "GetProgramForDispatch", + "GetProgramForDraw", + "GetProgramObject", + "GetProvokingVertexMode", + "GetRenderStateParameters", + "GetRenderStateParametersVersion", + "GetSamplingResolutionGeneration", + "GetScissorBox", + "GetStencilState", + "GetTextureBindGeneration", + "GetTextureContextId", + "GetTextureObject", + "GetTextureUnitObject", + "GetTransformFeedbackCapturedVertices", + "GetTransformFeedbackGeneration", + "GetTransformFeedbackPausedPrimitiveCounter", + "GetTransformFeedbackProgram", + "GetViewport", + "GetViewportIndexed", + "IsCapabilityEnabled", + "IsCapabilityEnabledIndexed", + "IsTransformFeedbackActive", + "IsTransformFeedbackPaused", + "InvalidateCompileEnv", + "ValidateProgramName", + "RecordError", +}; + +// Fields whose value is valid ACROSS verbs. Every entry is false in P0 and each +// true has to be argued for in P1 when the fillers land: a sticky field is a field +// the poison cannot protect. +inline constexpr Bool kMGPipeInputFieldSticky[kMGPipeInputFieldCount] = { + false, // GetActiveTextureUnit + false, // GetBlendColor + false, // GetBlendEquationIndexed + false, // GetBlendFuncIndexed + false, // GetBoundTransformFeedbackName + false, // GetBoundVertexArray + false, // GetBufferBindingSlot + false, // GetBufferBindingPoint + false, // GetBufferBindingPointCount + false, // GetTouchedBufferBindingPointCount + false, // GetClampReadColor + false, // GetClearColor + false, // GetClearDepth + false, // GetClearStencil + false, // GetColorMaskIndexed + false, // GetCullFaceMode + false, // GetCurrentVertexAttribute + false, // GetDepthFunc + false, // GetDepthMask + false, // GetDepthRangeIndexed + false, // GetFramebufferBindingSlot + false, // GetImageTextureBinding + false, // GetLineWidth + false, // GetLogicOp + false, // GetMaxTouchedTextureUnit + false, // GetMinSampleShadingValue + false, // GetPatchDefaultInnerLevel + false, // GetPatchDefaultOuterLevel + false, // GetPatchVertices + false, // GetPipelineStateVersion + false, // GetPixelStoreParameters + false, // GetPolygonModeFront + false, // GetPolygonOffsetFactor + false, // GetPolygonOffsetUnits + false, // GetPrimitiveRestartIndex + false, // GetProgramForDispatch + false, // GetProgramForDraw + false, // GetProgramObject + false, // GetProvokingVertexMode + false, // GetRenderStateParameters + false, // GetRenderStateParametersVersion + false, // GetSamplingResolutionGeneration + false, // GetScissorBox + false, // GetStencilState + false, // GetTextureBindGeneration + false, // GetTextureContextId + false, // GetTextureObject + false, // GetTextureUnitObject + false, // GetTransformFeedbackCapturedVertices + false, // GetTransformFeedbackGeneration + false, // GetTransformFeedbackPausedPrimitiveCounter + false, // GetTransformFeedbackProgram + false, // GetViewport + false, // GetViewportIndexed + false, // IsCapabilityEnabled + false, // IsCapabilityEnabledIndexed + false, // IsTransformFeedbackActive + false, // IsTransformFeedbackPaused + false, // InvalidateCompileEnv + false, // ValidateProgramName + false, // RecordError +}; + +// Which call is expected to have filled a field by the time a verb reads it. Names +// come from Coverage.def, so this table and the coverage table cannot disagree. +inline constexpr const char* kMGPipeInputFieldFilledBy[kMGPipeInputFieldCount] = { + "SetSamplerViews", + "SetDynamicState", + "CreateRenderState", + "CreateRenderState", + "SetStreamOutputTargets", + "BindVertexElements", + "SetIndirectBuffers", + "SetShaderBuffers", + "SetShaderBuffers", + "SetShaderBuffers", + "SetDynamicState", + "SetDynamicState", + "SetDynamicState", + "SetDynamicState", + "CreateRenderState", + "CreateRenderState", + "SetVertexAttribDefaults", + "CreateRenderState", + "CreateRenderState", + "SetDynamicState", + "SetFramebufferState", + "SetShaderImages", + "SetDynamicState", + "CreateRenderState", + "SetSamplerViews", + "CreateRenderState", + "SetPatchState", + "SetPatchState", + "SetPatchState", + "BindRenderState", + "SetPixelPackState", + "CreateRenderState", + "SetDynamicState", + "SetDynamicState", + "DrawVbo", + "SetDispatchProgram", + "SetDrawProgram", + "CreateShaderState", + "CreateRenderState", + "CreateRenderState", + "BindRenderState", + "SetSamplerViews", + "SetDynamicState", + "CreateRenderState", + "SetSamplerViews", + "SetSamplerViews", + "SetSamplerViews", + "SetSamplerViews", + "DrawVbo", + "SetStreamOutputTargets", + "EndStreamOutput", + "SetStreamOutputTargets", + "SetDynamicState", + "SetDynamicState", + "CreateRenderState", + "CreateRenderState", + "BeginStreamOutput", + "PauseStreamOutput", + "kClientResolved", // pseudo-call: not filled by a forward record + "kClientResolved", // pseudo-call: not filled by a forward record + "kReverseChannel", // pseudo-call: not filled by a forward record +}; + +struct MGPipeFilledState { + Uint64 CurrentVerbSerial; + Uint64 FilledGen[kMGPipeInputFieldCount]; +}; + +[[noreturn]] inline void MGPipeInputPoisonFatal(MGPipeInputField field, const char* verb) { + MGLOG_F("MGPipe: Fatal{UnmigratedPipeInput, \"%s@%s\"}", + kMGPipeInputFieldNames[static_cast(field)], verb); + std::abort(); +} + +inline Bool MGPipeInputFieldIsFresh(const MGPipeFilledState& state, MGPipeInputField field) { + const SizeT index = static_cast(field); + return kMGPipeInputFieldSticky[index] ? state.FilledGen[index] != 0 + : state.FilledGen[index] == state.CurrentVerbSerial; +} diff --git a/MobileGL/MG_Pipe/generated/PipeSpanTable.inc b/MobileGL/MG_Pipe/generated/PipeSpanTable.inc new file mode 100644 index 00000000..eb94a9b6 --- /dev/null +++ b/MobileGL/MG_Pipe/generated/PipeSpanTable.inc @@ -0,0 +1,67 @@ +// MobileGL - MobileGL/MG_Pipe/generated/PipeSpanTable.inc +// 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 + +// G7: the render-state pipeline subset, by member name. +// +// GENERATED by scripts/gen_pipe.py from the field list in scripts/gen_pipe.py - DO NOT EDIT. +// Regenerate with `python3 scripts/gen_pipe.py`; CI runs it and diffs the result. +// This file is included from MG_Pipe/MGPipe.h inside namespace MobileGL::MG_Pipe. + +// D-B1 rejected three CSOs and demanded this table instead, so the table needs its own +// completeness trip wire: MG_Test walks every public RenderState setter and asserts that +// the pipeline-subset hash moves IF AND ONLY IF m_pipelineStateVersion moves. That test +// and MGPipeRenderStateSpans.cpp land with P2; what P0 pins is the MEMBER LIST, taken from +// what VulkanRenderer::ComputePipelineStateHash hashes today, so the later offsets are +// derived from a list that was reviewed rather than invented. +// +// Deliberately absent, and each absence is a question P2 has to answer before the chunk +// table freezes: +// - FramebufferSrgb and DepthClamp have NO STORAGE at all (RenderState.cpp's SetCapability +// falls to "not supported currently" and IsCapabilityEnabled returns false), so six +// backend read points are constant false today. Pipeline state or dead capability? +// - ProvokingVertexModeSetting is Vulkan pipeline state but is not hashed today. +// - FrontFaceModeSetting, ClipOrigin and ClipDepthMode are pipeline state on Vulkan and +// are handled elsewhere in the payload path rather than in the memo word. +// +// The complement of this list is the DYNAMIC subset - the half whose whole purpose is that +// glViewport must not mint a new CSO. + +inline constexpr const char* const kMGPipePipelineStateMembers[] = { + "CullFaceEnabled", + "DepthTestEnabled", + "PolygonOffsetFillEnabled", + "RasterizerDiscardEnabled", + "ColorLogicOpEnabled", + "StencilTestEnabled", + "PrimitiveRestartEnabled", + "PrimitiveRestartFixedIndexEnabled", + "DepthMask", + "SampleShadingEnabled", + "MultisampleEnabled", + "SampleMaskEnabled", + "SampleMaskValue", + "MinSampleShadingValue", + "PatchVertices", + "PatchDefaultOuterLevel", + "PatchDefaultInnerLevel", + "PolygonModeFront", + "CullFaceModeSetting", + "DepthFunc", + "LogicOp", + "StencilStates", + "BlendStates", + "ColorMasks", +}; +inline constexpr SizeT kMGPipePipelineStateMemberCount = 24; +static_assert(kMGPipePipelineStateMemberCount == + sizeof(kMGPipePipelineStateMembers) / sizeof(kMGPipePipelineStateMembers[0])); + +// Filled in by MG_Pipe/MGPipeRenderStateSpans.cpp (P2), which computes the offsets +// in C++ with offsetof rather than guessing them in python. +extern const MGPStateChunk kMGPipePipelineChunks[]; +extern const MGPStateChunk kMGPipeDynamicChunks[]; diff --git a/MobileGL/MG_Pipe/generated/PipeTables.inc b/MobileGL/MG_Pipe/generated/PipeTables.inc new file mode 100644 index 00000000..cb8a602d --- /dev/null +++ b/MobileGL/MG_Pipe/generated/PipeTables.inc @@ -0,0 +1,105 @@ +// MobileGL - MobileGL/MG_Pipe/generated/PipeTables.inc +// 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 + +// G1: the two MGPipe interface tables. +// +// GENERATED by scripts/gen_pipe.py from PipeCalls.def - DO NOT EDIT. +// Regenerate with `python3 scripts/gen_pipe.py`; CI runs it and diffs the result. +// This file is included from MG_Pipe/MGPipe.h inside namespace MobileGL::MG_Pipe. + +// share group: 10 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 MGPipeScreen { + void (*GetCaps)(const MGPCaps* payload, MGPReplySlot* reply); + void (*ResourceCreate)(const MGPResourceDesc* payload); + void (*ResourceRespecify)(const MGPResourceDesc* payload); + void (*ResourceDestroy)(const MGPHandleOnly* payload); + void (*MapPersistent)(const MGPHandleOnly* payload, MGPReplySlot* reply); + void (*UnmapPersistent)(const MGPHandleOnly* payload); + void (*FenceCreate)(const MGPHandleOnly* payload); + void (*FenceStatus)(const MGPHandleOnly* payload, MGPReplySlot* reply); + void (*FenceWait)(const MGPFenceWait* payload, MGPReplySlot* reply); + void (*FenceDestroy)(const MGPHandleOnly* payload); +}; + +// context: 58 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); + void (*QueryBegin)(const MGPQueryDesc* payload); + void (*QueryEnd)(const MGPQueryDesc* payload); + void (*QueryAvailable)(const MGPHandleOnly* payload, MGPReplySlot* reply); + void (*QueryResult)(const MGPQueryResultRequest* payload, MGPReplySlot* reply); + void (*QueryDestroy)(const MGPHandleOnly* payload); + void (*CreateRenderState)(const MGPRenderStateDesc* payload); + void (*BindRenderState)(const MGPBindRenderState* payload); + void (*DeleteRenderState)(const MGPHandleOnly* payload); + void (*CreateVertexElements)(const MGPVertexElements* payload); + void (*BindVertexElements)(const MGPHandleOnly* payload); + void (*DeleteVertexElements)(const MGPHandleOnly* payload); + void (*CreateSamplerState)(const MGPSamplerDesc* payload); + void (*DeleteSamplerState)(const MGPHandleOnly* payload); + void (*CreateSamplerView)(const MGPSamplerView* payload); + void (*DeleteSamplerView)(const MGPHandleOnly* payload); + void (*CreateShaderState)(const MGPProgramDesc* payload); + void (*BindShaderState)(const MGPHandleOnly* payload); + void (*DeleteShaderState)(const MGPHandleOnly* payload); + void (*SetDynamicState)(const MGPDynamicState* payload); + void (*SetFramebufferState)(const MGPFramebufferState* payload); + void (*SetVertexBuffers)(const MGPVertexBuffers* payload, const void* varTail, Uint32 varTailCount); + void (*SetIndexBuffer)(const MGPIndexBuffer* payload); + void (*SetIndirectBuffers)(const MGPIndirectBuffers* payload); + void (*SetSamplerViews)(const MGPSamplerViews* payload, const void* varTail, Uint32 varTailCount); + void (*BindSamplerStates)(const MGPSamplerStates* payload, const void* varTail, Uint32 varTailCount); + void (*SetShaderImages)(const MGPShaderImages* payload, const void* varTail, Uint32 varTailCount); + void (*SetShaderBuffers)(const MGPShaderBuffers* payload, const void* varTail, Uint32 varTailCount); + void (*SetStreamOutputTargets)(const MGPStreamOutputTargets* payload, const void* varTail, Uint32 varTailCount); + void (*SetGlobalConstants)(const MGPGlobalConstants* payload); + void (*SetVertexAttribDefaults)(const MGPVertexAttribDefaults* payload, const void* varTail, Uint32 varTailCount); + void (*SetPixelPackState)(const MGPPixelPackState* payload); + void (*SetPatchState)(const MGPPatchState* payload); + void (*SetDrawProgram)(const MGPHandleOnly* payload); + void (*SetDispatchProgram)(const MGPHandleOnly* payload); + void (*SetResidualValueState)(const MGPResidualValueState* payload); + void (*SetTextureParams)(const MGPTextureParams* payload); + void (*ResourceSubData)(const MGPSubData* payload, const void* varTail, Uint32 varTailCount); + void (*BufferSubDataResident)(const MGPSubData* payload); + void (*ResourceSubDataComplete)(const MGPSubDataComplete* payload); + void (*ResourceFlushRange)(const MGPFlushRange* payload); + void (*ResourceReadback)(const MGPReadback* payload, MGPReplySlot* reply); + void (*ResourceCopyRegion)(const MGPCopyRegion* payload); + void (*GenerateMipmap)(const MGPMipPlan* payload); + void (*GetTextureImage)(const MGPReadbackInfo* payload, MGPReplySlot* reply); + void (*Blit)(const MGPBlit* payload); + void (*Clear)(const MGPClear* payload); + void (*ReadPixels)(const MGPReadbackInfo* payload, MGPReplySlot* reply); + void (*DrawVbo)(const MGPDrawInfo* payload, const void* varTail, Uint32 varTailCount); + void (*LaunchGrid)(const MGPGridInfo* payload); + void (*MemoryBarrier)(const MGPMemoryBarrier* payload); + void (*BeginStreamOutput)(const MGPStreamOutputBegin* payload); + void (*EndStreamOutput)(const MGPXfbAccounting* payload); + void (*PauseStreamOutput)(const MGPStreamOutputControl* payload); + void (*ResumeStreamOutput)(const MGPStreamOutputControl* payload); + void (*Flush)(const MGPFlush* payload); + void (*Present)(const MGPPresent* payload); + void (*SetSwapInterval)(const MGPSwapInterval* payload); +}; + +inline constexpr SizeT kMGPipeScreenCallCount = 10; +inline constexpr SizeT kMGPipeContextCallCount = 58; +inline constexpr SizeT kMGPipeCallCount = 68; + +// A table that is not exactly its call count of function pointers has grown a +// member that no generator knows about. +static_assert(sizeof(MGPipeScreen) == kMGPipeScreenCallCount * sizeof(void (*)()), + "MGPipeScreen is not exactly its catalogue's function pointers"); +static_assert(sizeof(MGPipeContext) == kMGPipeContextCallCount * sizeof(void (*)()), + "MGPipeContext is not exactly its catalogue's function pointers"); +static_assert(kMGPipeScreenCallCount + kMGPipeContextCallCount == kMGPipeCallCount); +static_assert(kMGPipeCallCount == MGP_CALL_LIST_DOCUMENTED_COUNT, + "the catalogue and its documented count disagree"); diff --git a/MobileGL/MG_Pipe/generated/PipeThunks.inc b/MobileGL/MG_Pipe/generated/PipeThunks.inc new file mode 100644 index 00000000..70e99931 --- /dev/null +++ b/MobileGL/MG_Pipe/generated/PipeThunks.inc @@ -0,0 +1,290 @@ +// MobileGL - MobileGL/MG_Pipe/generated/PipeThunks.inc +// 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 + +// G2: monolith thunks over the two tables. +// +// GENERATED by scripts/gen_pipe.py from PipeCalls.def - DO NOT EDIT. +// Regenerate with `python3 scripts/gen_pipe.py`; CI runs it and diffs the result. +// This file is included from MG_Pipe/MGPipe.h inside namespace MobileGL::MG_Pipe. + +// One inline call through the installed table. These are the names MG_Impl call +// sites move onto, replacing gBackendFunctionsTable.GL.* one at a time. An +// unimplemented (null) entry is the caller's business to check, exactly as it is +// with the table this replaces. + +inline void MGP_GetCaps(const MGPCaps* payload, MGPReplySlot* reply) { + gMGPipeScreen.GetCaps(payload, reply); +} + +inline void MGP_ResourceCreate(const MGPResourceDesc* payload) { + gMGPipeScreen.ResourceCreate(payload); +} + +inline void MGP_ResourceRespecify(const MGPResourceDesc* payload) { + gMGPipeScreen.ResourceRespecify(payload); +} + +inline void MGP_ResourceDestroy(const MGPHandleOnly* payload) { + gMGPipeScreen.ResourceDestroy(payload); +} + +inline void MGP_MapPersistent(const MGPHandleOnly* payload, MGPReplySlot* reply) { + gMGPipeScreen.MapPersistent(payload, reply); +} + +inline void MGP_UnmapPersistent(const MGPHandleOnly* payload) { + gMGPipeScreen.UnmapPersistent(payload); +} + +inline void MGP_FenceCreate(const MGPHandleOnly* payload) { + gMGPipeScreen.FenceCreate(payload); +} + +inline void MGP_FenceStatus(const MGPHandleOnly* payload, MGPReplySlot* reply) { + gMGPipeScreen.FenceStatus(payload, reply); +} + +inline void MGP_FenceWait(const MGPFenceWait* payload, MGPReplySlot* reply) { + gMGPipeScreen.FenceWait(payload, reply); +} + +inline void MGP_FenceDestroy(const MGPHandleOnly* payload) { + gMGPipeScreen.FenceDestroy(payload); +} + +inline void MGP_QueryCreate(const MGPQueryDesc* payload) { + gMGPipeContext.QueryCreate(payload); +} + +inline void MGP_QueryBegin(const MGPQueryDesc* payload) { + gMGPipeContext.QueryBegin(payload); +} + +inline void MGP_QueryEnd(const MGPQueryDesc* payload) { + gMGPipeContext.QueryEnd(payload); +} + +inline void MGP_QueryAvailable(const MGPHandleOnly* payload, MGPReplySlot* reply) { + gMGPipeContext.QueryAvailable(payload, reply); +} + +inline void MGP_QueryResult(const MGPQueryResultRequest* payload, MGPReplySlot* reply) { + gMGPipeContext.QueryResult(payload, reply); +} + +inline void MGP_QueryDestroy(const MGPHandleOnly* payload) { + gMGPipeContext.QueryDestroy(payload); +} + +inline void MGP_CreateRenderState(const MGPRenderStateDesc* payload) { + gMGPipeContext.CreateRenderState(payload); +} + +inline void MGP_BindRenderState(const MGPBindRenderState* payload) { + gMGPipeContext.BindRenderState(payload); +} + +inline void MGP_DeleteRenderState(const MGPHandleOnly* payload) { + gMGPipeContext.DeleteRenderState(payload); +} + +inline void MGP_CreateVertexElements(const MGPVertexElements* payload) { + gMGPipeContext.CreateVertexElements(payload); +} + +inline void MGP_BindVertexElements(const MGPHandleOnly* payload) { + gMGPipeContext.BindVertexElements(payload); +} + +inline void MGP_DeleteVertexElements(const MGPHandleOnly* payload) { + gMGPipeContext.DeleteVertexElements(payload); +} + +inline void MGP_CreateSamplerState(const MGPSamplerDesc* payload) { + gMGPipeContext.CreateSamplerState(payload); +} + +inline void MGP_DeleteSamplerState(const MGPHandleOnly* payload) { + gMGPipeContext.DeleteSamplerState(payload); +} + +inline void MGP_CreateSamplerView(const MGPSamplerView* payload) { + gMGPipeContext.CreateSamplerView(payload); +} + +inline void MGP_DeleteSamplerView(const MGPHandleOnly* payload) { + gMGPipeContext.DeleteSamplerView(payload); +} + +inline void MGP_CreateShaderState(const MGPProgramDesc* payload) { + gMGPipeContext.CreateShaderState(payload); +} + +inline void MGP_BindShaderState(const MGPHandleOnly* payload) { + gMGPipeContext.BindShaderState(payload); +} + +inline void MGP_DeleteShaderState(const MGPHandleOnly* payload) { + gMGPipeContext.DeleteShaderState(payload); +} + +inline void MGP_SetDynamicState(const MGPDynamicState* payload) { + gMGPipeContext.SetDynamicState(payload); +} + +inline void MGP_SetFramebufferState(const MGPFramebufferState* payload) { + gMGPipeContext.SetFramebufferState(payload); +} + +inline void MGP_SetVertexBuffers(const MGPVertexBuffers* payload, const void* varTail, Uint32 varTailCount) { + gMGPipeContext.SetVertexBuffers(payload, varTail, varTailCount); +} + +inline void MGP_SetIndexBuffer(const MGPIndexBuffer* payload) { + gMGPipeContext.SetIndexBuffer(payload); +} + +inline void MGP_SetIndirectBuffers(const MGPIndirectBuffers* payload) { + gMGPipeContext.SetIndirectBuffers(payload); +} + +inline void MGP_SetSamplerViews(const MGPSamplerViews* payload, const void* varTail, Uint32 varTailCount) { + gMGPipeContext.SetSamplerViews(payload, varTail, varTailCount); +} + +inline void MGP_BindSamplerStates(const MGPSamplerStates* payload, const void* varTail, Uint32 varTailCount) { + gMGPipeContext.BindSamplerStates(payload, varTail, varTailCount); +} + +inline void MGP_SetShaderImages(const MGPShaderImages* payload, const void* varTail, Uint32 varTailCount) { + gMGPipeContext.SetShaderImages(payload, varTail, varTailCount); +} + +inline void MGP_SetShaderBuffers(const MGPShaderBuffers* payload, const void* varTail, Uint32 varTailCount) { + gMGPipeContext.SetShaderBuffers(payload, varTail, varTailCount); +} + +inline void MGP_SetStreamOutputTargets(const MGPStreamOutputTargets* payload, const void* varTail, Uint32 varTailCount) { + gMGPipeContext.SetStreamOutputTargets(payload, varTail, varTailCount); +} + +inline void MGP_SetGlobalConstants(const MGPGlobalConstants* payload) { + gMGPipeContext.SetGlobalConstants(payload); +} + +inline void MGP_SetVertexAttribDefaults(const MGPVertexAttribDefaults* payload, const void* varTail, Uint32 varTailCount) { + gMGPipeContext.SetVertexAttribDefaults(payload, varTail, varTailCount); +} + +inline void MGP_SetPixelPackState(const MGPPixelPackState* payload) { + gMGPipeContext.SetPixelPackState(payload); +} + +inline void MGP_SetPatchState(const MGPPatchState* payload) { + gMGPipeContext.SetPatchState(payload); +} + +inline void MGP_SetDrawProgram(const MGPHandleOnly* payload) { + gMGPipeContext.SetDrawProgram(payload); +} + +inline void MGP_SetDispatchProgram(const MGPHandleOnly* payload) { + gMGPipeContext.SetDispatchProgram(payload); +} + +inline void MGP_SetResidualValueState(const MGPResidualValueState* payload) { + gMGPipeContext.SetResidualValueState(payload); +} + +inline void MGP_SetTextureParams(const MGPTextureParams* payload) { + gMGPipeContext.SetTextureParams(payload); +} + +inline void MGP_ResourceSubData(const MGPSubData* payload, const void* varTail, Uint32 varTailCount) { + gMGPipeContext.ResourceSubData(payload, varTail, varTailCount); +} + +inline void MGP_BufferSubDataResident(const MGPSubData* payload) { + gMGPipeContext.BufferSubDataResident(payload); +} + +inline void MGP_ResourceSubDataComplete(const MGPSubDataComplete* payload) { + gMGPipeContext.ResourceSubDataComplete(payload); +} + +inline void MGP_ResourceFlushRange(const MGPFlushRange* payload) { + gMGPipeContext.ResourceFlushRange(payload); +} + +inline void MGP_ResourceReadback(const MGPReadback* payload, MGPReplySlot* reply) { + gMGPipeContext.ResourceReadback(payload, reply); +} + +inline void MGP_ResourceCopyRegion(const MGPCopyRegion* payload) { + gMGPipeContext.ResourceCopyRegion(payload); +} + +inline void MGP_GenerateMipmap(const MGPMipPlan* payload) { + gMGPipeContext.GenerateMipmap(payload); +} + +inline void MGP_GetTextureImage(const MGPReadbackInfo* payload, MGPReplySlot* reply) { + gMGPipeContext.GetTextureImage(payload, reply); +} + +inline void MGP_Blit(const MGPBlit* payload) { + gMGPipeContext.Blit(payload); +} + +inline void MGP_Clear(const MGPClear* payload) { + gMGPipeContext.Clear(payload); +} + +inline void MGP_ReadPixels(const MGPReadbackInfo* payload, MGPReplySlot* reply) { + gMGPipeContext.ReadPixels(payload, reply); +} + +inline void MGP_DrawVbo(const MGPDrawInfo* payload, const void* varTail, Uint32 varTailCount) { + gMGPipeContext.DrawVbo(payload, varTail, varTailCount); +} + +inline void MGP_LaunchGrid(const MGPGridInfo* payload) { + gMGPipeContext.LaunchGrid(payload); +} + +inline void MGP_MemoryBarrier(const MGPMemoryBarrier* payload) { + gMGPipeContext.MemoryBarrier(payload); +} + +inline void MGP_BeginStreamOutput(const MGPStreamOutputBegin* payload) { + gMGPipeContext.BeginStreamOutput(payload); +} + +inline void MGP_EndStreamOutput(const MGPXfbAccounting* payload) { + gMGPipeContext.EndStreamOutput(payload); +} + +inline void MGP_PauseStreamOutput(const MGPStreamOutputControl* payload) { + gMGPipeContext.PauseStreamOutput(payload); +} + +inline void MGP_ResumeStreamOutput(const MGPStreamOutputControl* payload) { + gMGPipeContext.ResumeStreamOutput(payload); +} + +inline void MGP_Flush(const MGPFlush* payload) { + gMGPipeContext.Flush(payload); +} + +inline void MGP_Present(const MGPPresent* payload) { + gMGPipeContext.Present(payload); +} + +inline void MGP_SetSwapInterval(const MGPSwapInterval* payload) { + gMGPipeContext.SetSwapInterval(payload); +} diff --git a/MobileGL/MG_Pipe/generated/PipeVerify.inc b/MobileGL/MG_Pipe/generated/PipeVerify.inc new file mode 100644 index 00000000..4b14a829 --- /dev/null +++ b/MobileGL/MG_Pipe/generated/PipeVerify.inc @@ -0,0 +1,565 @@ +// MobileGL - MobileGL/MG_Pipe/generated/PipeVerify.inc +// 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 + +// G4: the MOBILEGL_PIPE_VERIFY field-wise comparators. +// +// GENERATED by scripts/gen_pipe.py from PipeFields.def - DO NOT EDIT. +// Regenerate with `python3 scripts/gen_pipe.py`; CI runs it and diffs the result. +// This file is included from MG_Pipe/MGPipe.h inside namespace MobileGL::MG_Pipe. + +// Field by field, never memcmp over a whole payload: RenderStateParameters is documented +// in DirectGLES.cpp to false-DIFFER on padding under memcmp (harmlessly there, fatally +// here - a comparator with false positives is a comparator nobody reads). Each function +// reports the FIRST differing field by name, which with the draw serial is what the verify +// harness prints. +// +// Floating-point fields are compared by BITS, so a NaN patch level - which +// glPatchParameterfv accepts and ComputePipelineStateHash already hashes bitwise - equals +// itself instead of tripping every draw. + +#include "../PipeFields.def" + +template +struct MGPipeHasFieldVerifier : std::false_type {}; + +inline Bool MGPipeVerify(const MGPBlobRef& a, const MGPBlobRef& b, const char** outField); +inline Bool MGPipeVerify(const MGPRange& a, const MGPRange& b, const char** outField); +inline Bool MGPipeVerify(const MGPBox& a, const MGPBox& b, const char** outField); +inline Bool MGPipeVerify(const MGPReplySlot& a, const MGPReplySlot& b, const char** outField); +inline Bool MGPipeVerify(const MGPStateChunk& a, const MGPStateChunk& b, const char** outField); +inline Bool MGPipeVerify(const MGPHandleOnly& a, const MGPHandleOnly& b, const char** outField); +inline Bool MGPipeVerify(const MGPCaps& a, const MGPCaps& b, const char** outField); +inline Bool MGPipeVerify(const MGPResourceDesc& a, const MGPResourceDesc& b, const char** outField); +inline Bool MGPipeVerify(const MGPFenceWait& a, const MGPFenceWait& b, const char** outField); +inline Bool MGPipeVerify(const MGPQueryDesc& a, const MGPQueryDesc& b, const char** outField); +inline Bool MGPipeVerify(const MGPQueryResultRequest& a, const MGPQueryResultRequest& b, const char** outField); +inline Bool MGPipeVerify(const MGPRenderStateDesc& a, const MGPRenderStateDesc& b, const char** outField); +inline Bool MGPipeVerify(const MGPBindRenderState& a, const MGPBindRenderState& b, const char** outField); +inline Bool MGPipeVerify(const MGPDynamicState& a, const MGPDynamicState& b, const char** outField); +inline Bool MGPipeVerify(const MGPVertexElements& a, const MGPVertexElements& b, const char** outField); +inline Bool MGPipeVerify(const MGPSamplerDesc& a, const MGPSamplerDesc& b, const char** outField); +inline Bool MGPipeVerify(const MGPSamplerView& a, const MGPSamplerView& b, const char** outField); +inline Bool MGPipeVerify(const MGPTextureParams& a, const MGPTextureParams& b, const char** outField); +inline Bool MGPipeVerify(const MGPProgramDesc& a, const MGPProgramDesc& b, const char** outField); +inline Bool MGPipeVerify(const MGPSurface& a, const MGPSurface& b, const char** outField); +inline Bool MGPipeVerify(const MGPFramebufferState& a, const MGPFramebufferState& b, const char** outField); +inline Bool MGPipeVerify(const MGPVertexBuffer& a, const MGPVertexBuffer& b, const char** outField); +inline Bool MGPipeVerify(const MGPVertexBuffers& a, const MGPVertexBuffers& b, const char** outField); +inline Bool MGPipeVerify(const MGPIndexBuffer& a, const MGPIndexBuffer& b, const char** outField); +inline Bool MGPipeVerify(const MGPIndirectBuffers& a, const MGPIndirectBuffers& b, const char** outField); +inline Bool MGPipeVerify(const MGPBoundView& a, const MGPBoundView& b, const char** outField); +inline Bool MGPipeVerify(const MGPSamplerViews& a, const MGPSamplerViews& b, const char** outField); +inline Bool MGPipeVerify(const MGPSamplerStates& a, const MGPSamplerStates& b, const char** outField); +inline Bool MGPipeVerify(const MGPImageView& a, const MGPImageView& b, const char** outField); +inline Bool MGPipeVerify(const MGPShaderImages& a, const MGPShaderImages& b, const char** outField); +inline Bool MGPipeVerify(const MGPBufferRange& a, const MGPBufferRange& b, const char** outField); +inline Bool MGPipeVerify(const MGPShaderBuffers& a, const MGPShaderBuffers& b, const char** outField); +inline Bool MGPipeVerify(const MGPStreamOutputTargets& a, const MGPStreamOutputTargets& b, const char** outField); +inline Bool MGPipeVerify(const MGPGlobalConstants& a, const MGPGlobalConstants& b, const char** outField); +inline Bool MGPipeVerify(const MGPAttribValue& a, const MGPAttribValue& b, const char** outField); +inline Bool MGPipeVerify(const MGPVertexAttribDefaults& a, const MGPVertexAttribDefaults& b, const char** outField); +inline Bool MGPipeVerify(const MGPPixelPackState& a, const MGPPixelPackState& b, const char** outField); +inline Bool MGPipeVerify(const MGPPatchState& a, const MGPPatchState& b, const char** outField); +inline Bool MGPipeVerify(const ResidualValueBlock& a, const ResidualValueBlock& b, const char** outField); +inline Bool MGPipeVerify(const MGPResidualValueState& a, const MGPResidualValueState& b, const char** outField); +inline Bool MGPipeVerify(const MGPSubRegion& a, const MGPSubRegion& b, const char** outField); +inline Bool MGPipeVerify(const MGPSubData& a, const MGPSubData& b, const char** outField); +inline Bool MGPipeVerify(const MGPSubDataComplete& a, const MGPSubDataComplete& b, const char** outField); +inline Bool MGPipeVerify(const MGPFlushRange& a, const MGPFlushRange& b, const char** outField); +inline Bool MGPipeVerify(const MGPReadback& a, const MGPReadback& b, const char** outField); +inline Bool MGPipeVerify(const MGPCopyRegion& a, const MGPCopyRegion& b, const char** outField); +inline Bool MGPipeVerify(const MGPBlit& a, const MGPBlit& b, const char** outField); +inline Bool MGPipeVerify(const MGPClear& a, const MGPClear& b, const char** outField); +inline Bool MGPipeVerify(const MGPMipPlan& a, const MGPMipPlan& b, const char** outField); +inline Bool MGPipeVerify(const MGPReadbackInfo& a, const MGPReadbackInfo& b, const char** outField); +inline Bool MGPipeVerify(const MGPDrawInfo& a, const MGPDrawInfo& b, const char** outField); +inline Bool MGPipeVerify(const MGPDrawRange& a, const MGPDrawRange& b, const char** outField); +inline Bool MGPipeVerify(const MGPDrawIndirect& a, const MGPDrawIndirect& b, const char** outField); +inline Bool MGPipeVerify(const MGPGridInfo& a, const MGPGridInfo& b, const char** outField); +inline Bool MGPipeVerify(const MGPMemoryBarrier& a, const MGPMemoryBarrier& b, const char** outField); +inline Bool MGPipeVerify(const MGPStreamOutputBegin& a, const MGPStreamOutputBegin& b, const char** outField); +inline Bool MGPipeVerify(const MGPXfbAccounting& a, const MGPXfbAccounting& b, const char** outField); +inline Bool MGPipeVerify(const MGPStreamOutputControl& a, const MGPStreamOutputControl& b, const char** outField); +inline Bool MGPipeVerify(const MGPFlush& a, const MGPFlush& b, const char** outField); +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); + +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 {}; +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 {}; +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 {}; +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 {}; +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 {}; +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 {}; +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 {}; +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 +inline Bool MGPipeFieldEqual(const T& a, const T& b) { + if constexpr (MGPipeHasFieldVerifier::value) { + const char* unusedField = nullptr; + return MGPipeVerify(a, b, &unusedField); + } else if constexpr (std::is_floating_point_v) { + return std::memcmp(&a, &b, sizeof(T)) == 0; + } else if constexpr (std::is_scalar_v || std::is_enum_v) { + return a == b; + } else if constexpr (requires(const T& x, const T& y) { x == y; }) { + return a == b; + } else { + // MEMCMP FALLBACK. Only reached by the payload members that are still MG_State / + // MG_Backend value structs (RenderStateParameters, PixelStoreParameters, + // DynamicBackendParameters) and by MGHostSpan. Those are exactly the types P0.5 + // moves into MGPipeValueTypes.h, at which point they get field lists of their own + // and this branch stops being reachable from any payload. + return std::memcmp(&a, &b, sizeof(T)) == 0; + } +} + +template +inline Bool MGPipeFieldEqual(const T (&a)[N], const T (&b)[N]) { + for (SizeT i = 0; i < N; ++i) { + if (!MGPipeFieldEqual(a[i], b[i])) return false; + } + return true; +} + +#define MGP_VERIFY_FIELD(FieldName) \ + if (!MGPipeFieldEqual(a.FieldName, b.FieldName)) { \ + if (outField != nullptr) *outField = #FieldName; \ + return false; \ + } + +inline Bool MGPipeVerify(const MGPBlobRef& a, const MGPBlobRef& b, const char** outField) { + MGP_FIELDS_MGPBlobRef(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPRange& a, const MGPRange& b, const char** outField) { + MGP_FIELDS_MGPRange(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPBox& a, const MGPBox& b, const char** outField) { + MGP_FIELDS_MGPBox(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPReplySlot& a, const MGPReplySlot& b, const char** outField) { + MGP_FIELDS_MGPReplySlot(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPStateChunk& a, const MGPStateChunk& b, const char** outField) { + MGP_FIELDS_MGPStateChunk(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPHandleOnly& a, const MGPHandleOnly& b, const char** outField) { + MGP_FIELDS_MGPHandleOnly(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPCaps& a, const MGPCaps& b, const char** outField) { + MGP_FIELDS_MGPCaps(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPResourceDesc& a, const MGPResourceDesc& b, const char** outField) { + MGP_FIELDS_MGPResourceDesc(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPFenceWait& a, const MGPFenceWait& b, const char** outField) { + MGP_FIELDS_MGPFenceWait(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPQueryDesc& a, const MGPQueryDesc& b, const char** outField) { + MGP_FIELDS_MGPQueryDesc(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPQueryResultRequest& a, const MGPQueryResultRequest& b, const char** outField) { + MGP_FIELDS_MGPQueryResultRequest(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPRenderStateDesc& a, const MGPRenderStateDesc& b, const char** outField) { + MGP_FIELDS_MGPRenderStateDesc(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPBindRenderState& a, const MGPBindRenderState& b, const char** outField) { + MGP_FIELDS_MGPBindRenderState(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPDynamicState& a, const MGPDynamicState& b, const char** outField) { + MGP_FIELDS_MGPDynamicState(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPVertexElements& a, const MGPVertexElements& b, const char** outField) { + MGP_FIELDS_MGPVertexElements(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPSamplerDesc& a, const MGPSamplerDesc& b, const char** outField) { + MGP_FIELDS_MGPSamplerDesc(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPSamplerView& a, const MGPSamplerView& b, const char** outField) { + MGP_FIELDS_MGPSamplerView(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPTextureParams& a, const MGPTextureParams& b, const char** outField) { + MGP_FIELDS_MGPTextureParams(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPProgramDesc& a, const MGPProgramDesc& b, const char** outField) { + MGP_FIELDS_MGPProgramDesc(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPSurface& a, const MGPSurface& b, const char** outField) { + MGP_FIELDS_MGPSurface(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPFramebufferState& a, const MGPFramebufferState& b, const char** outField) { + MGP_FIELDS_MGPFramebufferState(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPVertexBuffer& a, const MGPVertexBuffer& b, const char** outField) { + MGP_FIELDS_MGPVertexBuffer(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPVertexBuffers& a, const MGPVertexBuffers& b, const char** outField) { + MGP_FIELDS_MGPVertexBuffers(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPIndexBuffer& a, const MGPIndexBuffer& b, const char** outField) { + MGP_FIELDS_MGPIndexBuffer(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPIndirectBuffers& a, const MGPIndirectBuffers& b, const char** outField) { + MGP_FIELDS_MGPIndirectBuffers(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPBoundView& a, const MGPBoundView& b, const char** outField) { + MGP_FIELDS_MGPBoundView(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPSamplerViews& a, const MGPSamplerViews& b, const char** outField) { + MGP_FIELDS_MGPSamplerViews(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPSamplerStates& a, const MGPSamplerStates& b, const char** outField) { + MGP_FIELDS_MGPSamplerStates(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPImageView& a, const MGPImageView& b, const char** outField) { + MGP_FIELDS_MGPImageView(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPShaderImages& a, const MGPShaderImages& b, const char** outField) { + MGP_FIELDS_MGPShaderImages(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPBufferRange& a, const MGPBufferRange& b, const char** outField) { + MGP_FIELDS_MGPBufferRange(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPShaderBuffers& a, const MGPShaderBuffers& b, const char** outField) { + MGP_FIELDS_MGPShaderBuffers(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPStreamOutputTargets& a, const MGPStreamOutputTargets& b, const char** outField) { + MGP_FIELDS_MGPStreamOutputTargets(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPGlobalConstants& a, const MGPGlobalConstants& b, const char** outField) { + MGP_FIELDS_MGPGlobalConstants(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPAttribValue& a, const MGPAttribValue& b, const char** outField) { + MGP_FIELDS_MGPAttribValue(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPVertexAttribDefaults& a, const MGPVertexAttribDefaults& b, const char** outField) { + MGP_FIELDS_MGPVertexAttribDefaults(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPPixelPackState& a, const MGPPixelPackState& b, const char** outField) { + MGP_FIELDS_MGPPixelPackState(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPPatchState& a, const MGPPatchState& b, const char** outField) { + MGP_FIELDS_MGPPatchState(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const ResidualValueBlock& a, const ResidualValueBlock& b, const char** outField) { + MGP_FIELDS_ResidualValueBlock(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPResidualValueState& a, const MGPResidualValueState& b, const char** outField) { + MGP_FIELDS_MGPResidualValueState(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPSubRegion& a, const MGPSubRegion& b, const char** outField) { + MGP_FIELDS_MGPSubRegion(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPSubData& a, const MGPSubData& b, const char** outField) { + MGP_FIELDS_MGPSubData(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPSubDataComplete& a, const MGPSubDataComplete& b, const char** outField) { + MGP_FIELDS_MGPSubDataComplete(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPFlushRange& a, const MGPFlushRange& b, const char** outField) { + MGP_FIELDS_MGPFlushRange(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPReadback& a, const MGPReadback& b, const char** outField) { + MGP_FIELDS_MGPReadback(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPCopyRegion& a, const MGPCopyRegion& b, const char** outField) { + MGP_FIELDS_MGPCopyRegion(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPBlit& a, const MGPBlit& b, const char** outField) { + MGP_FIELDS_MGPBlit(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPClear& a, const MGPClear& b, const char** outField) { + MGP_FIELDS_MGPClear(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPMipPlan& a, const MGPMipPlan& b, const char** outField) { + MGP_FIELDS_MGPMipPlan(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPReadbackInfo& a, const MGPReadbackInfo& b, const char** outField) { + MGP_FIELDS_MGPReadbackInfo(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPDrawInfo& a, const MGPDrawInfo& b, const char** outField) { + MGP_FIELDS_MGPDrawInfo(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPDrawRange& a, const MGPDrawRange& b, const char** outField) { + MGP_FIELDS_MGPDrawRange(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPDrawIndirect& a, const MGPDrawIndirect& b, const char** outField) { + MGP_FIELDS_MGPDrawIndirect(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPGridInfo& a, const MGPGridInfo& b, const char** outField) { + MGP_FIELDS_MGPGridInfo(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPMemoryBarrier& a, const MGPMemoryBarrier& b, const char** outField) { + MGP_FIELDS_MGPMemoryBarrier(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPStreamOutputBegin& a, const MGPStreamOutputBegin& b, const char** outField) { + MGP_FIELDS_MGPStreamOutputBegin(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPXfbAccounting& a, const MGPXfbAccounting& b, const char** outField) { + MGP_FIELDS_MGPXfbAccounting(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPStreamOutputControl& a, const MGPStreamOutputControl& b, const char** outField) { + MGP_FIELDS_MGPStreamOutputControl(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPFlush& a, const MGPFlush& b, const char** outField) { + MGP_FIELDS_MGPFlush(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPPresent& a, const MGPPresent& b, const char** outField) { + MGP_FIELDS_MGPPresent(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPSwapInterval& a, const MGPSwapInterval& b, const char** outField) { + MGP_FIELDS_MGPSwapInterval(MGP_VERIFY_FIELD) + return true; +} + +inline Bool MGPipeVerify(const MGPSurfaceInfo& a, const MGPSurfaceInfo& b, const char** outField) { + MGP_FIELDS_MGPSurfaceInfo(MGP_VERIFY_FIELD) + return true; +} + +#undef MGP_VERIFY_FIELD + +inline constexpr SizeT kMGPipeVerifiedPayloadCount = 62; diff --git a/MobileGL/MG_Pipe/generated/PipeWire.inc b/MobileGL/MG_Pipe/generated/PipeWire.inc new file mode 100644 index 00000000..426d3e14 --- /dev/null +++ b/MobileGL/MG_Pipe/generated/PipeWire.inc @@ -0,0 +1,885 @@ +// MobileGL - MobileGL/MG_Pipe/generated/PipeWire.inc +// 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 + +// G3: wire records, size assertions and the applier's bounds gate. +// +// GENERATED by scripts/gen_pipe.py from PipeCalls.def - DO NOT EDIT. +// Regenerate with `python3 scripts/gen_pipe.py`; CI runs it and diffs the result. +// This file is included from MG_Pipe/MGPipe.h inside namespace MobileGL::MG_Pipe. + +// Every record is a fixed header plus its payload, padded to the stream's 8-byte +// granularity. The size assertion is stated as a COMPOSITION so it fires on any padding +// the compiler inserts between the header and the payload while staying honest about the +// tail padding the alignment requires. +// +// The applier's precondition is checked BEFORE dispatch, on every record, in every build: +// a record that is shorter than its own type, longer than what is left in the buffer, or +// not a multiple of 8 is protocol corruption and is fatal. There is no recovery path - +// silently applying a truncated record is how a corrupt stream becomes a wrong picture. + +struct MGPWireRecHeader { + Uint16 Op; // MGPWireOp + Uint16 Flags; // MGPipeCallFlags of the call, for asserts and tracing + Uint32 Size; // bytes of this record including the header and the variable tail +}; +static_assert(sizeof(MGPWireRecHeader) == 8, "the wire header is 8 bytes"); +static_assert(std::is_trivially_copyable_v); + +// The opcode is the call's position in PipeCalls.def. Reordering that file is a protocol +// break; appending to it is not. +enum class MGPWireOp : Uint16 { + kInvalid = 0, + GetCaps = 1, + ResourceCreate = 2, + ResourceRespecify = 3, + ResourceDestroy = 4, + MapPersistent = 5, + UnmapPersistent = 6, + FenceCreate = 7, + FenceStatus = 8, + FenceWait = 9, + FenceDestroy = 10, + QueryCreate = 11, + QueryBegin = 12, + QueryEnd = 13, + QueryAvailable = 14, + QueryResult = 15, + QueryDestroy = 16, + CreateRenderState = 17, + BindRenderState = 18, + DeleteRenderState = 19, + CreateVertexElements = 20, + BindVertexElements = 21, + DeleteVertexElements = 22, + CreateSamplerState = 23, + DeleteSamplerState = 24, + CreateSamplerView = 25, + DeleteSamplerView = 26, + CreateShaderState = 27, + BindShaderState = 28, + DeleteShaderState = 29, + SetDynamicState = 30, + SetFramebufferState = 31, + SetVertexBuffers = 32, + SetIndexBuffer = 33, + SetIndirectBuffers = 34, + SetSamplerViews = 35, + BindSamplerStates = 36, + SetShaderImages = 37, + SetShaderBuffers = 38, + SetStreamOutputTargets = 39, + SetGlobalConstants = 40, + SetVertexAttribDefaults = 41, + SetPixelPackState = 42, + SetPatchState = 43, + SetDrawProgram = 44, + SetDispatchProgram = 45, + SetResidualValueState = 46, + SetTextureParams = 47, + ResourceSubData = 48, + BufferSubDataResident = 49, + ResourceSubDataComplete = 50, + ResourceFlushRange = 51, + ResourceReadback = 52, + ResourceCopyRegion = 53, + GenerateMipmap = 54, + GetTextureImage = 55, + Blit = 56, + Clear = 57, + ReadPixels = 58, + DrawVbo = 59, + LaunchGrid = 60, + MemoryBarrier = 61, + BeginStreamOutput = 62, + EndStreamOutput = 63, + PauseStreamOutput = 64, + ResumeStreamOutput = 65, + Flush = 66, + Present = 67, + SetSwapInterval = 68, + kOpCount = 69, +}; + +struct alignas(8) MGPWireRec_GetCaps { + MGPWireRecHeader Header; + MGPCaps Payload; +}; +static_assert(sizeof(MGPWireRec_GetCaps) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPCaps) + 7u) & ~SizeT(7u)), + "MGPWireRec_GetCaps gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_ResourceCreate { + MGPWireRecHeader Header; + MGPResourceDesc Payload; +}; +static_assert(sizeof(MGPWireRec_ResourceCreate) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPResourceDesc) + 7u) & ~SizeT(7u)), + "MGPWireRec_ResourceCreate gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_ResourceRespecify { + MGPWireRecHeader Header; + MGPResourceDesc Payload; +}; +static_assert(sizeof(MGPWireRec_ResourceRespecify) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPResourceDesc) + 7u) & ~SizeT(7u)), + "MGPWireRec_ResourceRespecify gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_ResourceDestroy { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_ResourceDestroy) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_ResourceDestroy gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_MapPersistent { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_MapPersistent) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_MapPersistent gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_UnmapPersistent { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_UnmapPersistent) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_UnmapPersistent gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_FenceCreate { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_FenceCreate) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_FenceCreate gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_FenceStatus { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_FenceStatus) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_FenceStatus gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_FenceWait { + MGPWireRecHeader Header; + MGPFenceWait Payload; +}; +static_assert(sizeof(MGPWireRec_FenceWait) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPFenceWait) + 7u) & ~SizeT(7u)), + "MGPWireRec_FenceWait gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_FenceDestroy { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_FenceDestroy) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_FenceDestroy gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_QueryCreate { + MGPWireRecHeader Header; + MGPQueryDesc Payload; +}; +static_assert(sizeof(MGPWireRec_QueryCreate) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPQueryDesc) + 7u) & ~SizeT(7u)), + "MGPWireRec_QueryCreate gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_QueryBegin { + MGPWireRecHeader Header; + MGPQueryDesc Payload; +}; +static_assert(sizeof(MGPWireRec_QueryBegin) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPQueryDesc) + 7u) & ~SizeT(7u)), + "MGPWireRec_QueryBegin gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_QueryEnd { + MGPWireRecHeader Header; + MGPQueryDesc Payload; +}; +static_assert(sizeof(MGPWireRec_QueryEnd) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPQueryDesc) + 7u) & ~SizeT(7u)), + "MGPWireRec_QueryEnd gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_QueryAvailable { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_QueryAvailable) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_QueryAvailable gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_QueryResult { + MGPWireRecHeader Header; + MGPQueryResultRequest Payload; +}; +static_assert(sizeof(MGPWireRec_QueryResult) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPQueryResultRequest) + 7u) & ~SizeT(7u)), + "MGPWireRec_QueryResult gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_QueryDestroy { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_QueryDestroy) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_QueryDestroy gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_CreateRenderState { + MGPWireRecHeader Header; + MGPRenderStateDesc Payload; +}; +static_assert(sizeof(MGPWireRec_CreateRenderState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPRenderStateDesc) + 7u) & ~SizeT(7u)), + "MGPWireRec_CreateRenderState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_BindRenderState { + MGPWireRecHeader Header; + MGPBindRenderState Payload; +}; +static_assert(sizeof(MGPWireRec_BindRenderState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPBindRenderState) + 7u) & ~SizeT(7u)), + "MGPWireRec_BindRenderState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_DeleteRenderState { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_DeleteRenderState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_DeleteRenderState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_CreateVertexElements { + MGPWireRecHeader Header; + MGPVertexElements Payload; +}; +static_assert(sizeof(MGPWireRec_CreateVertexElements) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPVertexElements) + 7u) & ~SizeT(7u)), + "MGPWireRec_CreateVertexElements gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_BindVertexElements { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_BindVertexElements) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_BindVertexElements gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_DeleteVertexElements { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_DeleteVertexElements) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_DeleteVertexElements gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_CreateSamplerState { + MGPWireRecHeader Header; + MGPSamplerDesc Payload; +}; +static_assert(sizeof(MGPWireRec_CreateSamplerState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPSamplerDesc) + 7u) & ~SizeT(7u)), + "MGPWireRec_CreateSamplerState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_DeleteSamplerState { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_DeleteSamplerState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_DeleteSamplerState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_CreateSamplerView { + MGPWireRecHeader Header; + MGPSamplerView Payload; +}; +static_assert(sizeof(MGPWireRec_CreateSamplerView) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPSamplerView) + 7u) & ~SizeT(7u)), + "MGPWireRec_CreateSamplerView gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_DeleteSamplerView { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_DeleteSamplerView) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_DeleteSamplerView gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_CreateShaderState { + MGPWireRecHeader Header; + MGPProgramDesc Payload; +}; +static_assert(sizeof(MGPWireRec_CreateShaderState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPProgramDesc) + 7u) & ~SizeT(7u)), + "MGPWireRec_CreateShaderState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_BindShaderState { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_BindShaderState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_BindShaderState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_DeleteShaderState { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_DeleteShaderState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_DeleteShaderState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetDynamicState { + MGPWireRecHeader Header; + MGPDynamicState Payload; +}; +static_assert(sizeof(MGPWireRec_SetDynamicState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPDynamicState) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetDynamicState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetFramebufferState { + MGPWireRecHeader Header; + MGPFramebufferState Payload; +}; +static_assert(sizeof(MGPWireRec_SetFramebufferState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPFramebufferState) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetFramebufferState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetVertexBuffers { + MGPWireRecHeader Header; + MGPVertexBuffers Payload; +}; +static_assert(sizeof(MGPWireRec_SetVertexBuffers) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPVertexBuffers) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetVertexBuffers gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetIndexBuffer { + MGPWireRecHeader Header; + MGPIndexBuffer Payload; +}; +static_assert(sizeof(MGPWireRec_SetIndexBuffer) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPIndexBuffer) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetIndexBuffer gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetIndirectBuffers { + MGPWireRecHeader Header; + MGPIndirectBuffers Payload; +}; +static_assert(sizeof(MGPWireRec_SetIndirectBuffers) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPIndirectBuffers) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetIndirectBuffers gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetSamplerViews { + MGPWireRecHeader Header; + MGPSamplerViews Payload; +}; +static_assert(sizeof(MGPWireRec_SetSamplerViews) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPSamplerViews) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetSamplerViews gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_BindSamplerStates { + MGPWireRecHeader Header; + MGPSamplerStates Payload; +}; +static_assert(sizeof(MGPWireRec_BindSamplerStates) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPSamplerStates) + 7u) & ~SizeT(7u)), + "MGPWireRec_BindSamplerStates gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetShaderImages { + MGPWireRecHeader Header; + MGPShaderImages Payload; +}; +static_assert(sizeof(MGPWireRec_SetShaderImages) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPShaderImages) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetShaderImages gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetShaderBuffers { + MGPWireRecHeader Header; + MGPShaderBuffers Payload; +}; +static_assert(sizeof(MGPWireRec_SetShaderBuffers) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPShaderBuffers) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetShaderBuffers gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetStreamOutputTargets { + MGPWireRecHeader Header; + MGPStreamOutputTargets Payload; +}; +static_assert(sizeof(MGPWireRec_SetStreamOutputTargets) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPStreamOutputTargets) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetStreamOutputTargets gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetGlobalConstants { + MGPWireRecHeader Header; + MGPGlobalConstants Payload; +}; +static_assert(sizeof(MGPWireRec_SetGlobalConstants) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPGlobalConstants) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetGlobalConstants gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetVertexAttribDefaults { + MGPWireRecHeader Header; + MGPVertexAttribDefaults Payload; +}; +static_assert(sizeof(MGPWireRec_SetVertexAttribDefaults) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPVertexAttribDefaults) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetVertexAttribDefaults gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetPixelPackState { + MGPWireRecHeader Header; + MGPPixelPackState Payload; +}; +static_assert(sizeof(MGPWireRec_SetPixelPackState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPPixelPackState) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetPixelPackState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetPatchState { + MGPWireRecHeader Header; + MGPPatchState Payload; +}; +static_assert(sizeof(MGPWireRec_SetPatchState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPPatchState) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetPatchState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetDrawProgram { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_SetDrawProgram) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetDrawProgram gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetDispatchProgram { + MGPWireRecHeader Header; + MGPHandleOnly Payload; +}; +static_assert(sizeof(MGPWireRec_SetDispatchProgram) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPHandleOnly) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetDispatchProgram gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetResidualValueState { + MGPWireRecHeader Header; + MGPResidualValueState Payload; +}; +static_assert(sizeof(MGPWireRec_SetResidualValueState) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPResidualValueState) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetResidualValueState gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetTextureParams { + MGPWireRecHeader Header; + MGPTextureParams Payload; +}; +static_assert(sizeof(MGPWireRec_SetTextureParams) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPTextureParams) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetTextureParams gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_ResourceSubData { + MGPWireRecHeader Header; + MGPSubData Payload; +}; +static_assert(sizeof(MGPWireRec_ResourceSubData) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPSubData) + 7u) & ~SizeT(7u)), + "MGPWireRec_ResourceSubData gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_BufferSubDataResident { + MGPWireRecHeader Header; + MGPSubData Payload; +}; +static_assert(sizeof(MGPWireRec_BufferSubDataResident) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPSubData) + 7u) & ~SizeT(7u)), + "MGPWireRec_BufferSubDataResident gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_ResourceSubDataComplete { + MGPWireRecHeader Header; + MGPSubDataComplete Payload; +}; +static_assert(sizeof(MGPWireRec_ResourceSubDataComplete) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPSubDataComplete) + 7u) & ~SizeT(7u)), + "MGPWireRec_ResourceSubDataComplete gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_ResourceFlushRange { + MGPWireRecHeader Header; + MGPFlushRange Payload; +}; +static_assert(sizeof(MGPWireRec_ResourceFlushRange) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPFlushRange) + 7u) & ~SizeT(7u)), + "MGPWireRec_ResourceFlushRange gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_ResourceReadback { + MGPWireRecHeader Header; + MGPReadback Payload; +}; +static_assert(sizeof(MGPWireRec_ResourceReadback) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPReadback) + 7u) & ~SizeT(7u)), + "MGPWireRec_ResourceReadback gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_ResourceCopyRegion { + MGPWireRecHeader Header; + MGPCopyRegion Payload; +}; +static_assert(sizeof(MGPWireRec_ResourceCopyRegion) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPCopyRegion) + 7u) & ~SizeT(7u)), + "MGPWireRec_ResourceCopyRegion gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_GenerateMipmap { + MGPWireRecHeader Header; + MGPMipPlan Payload; +}; +static_assert(sizeof(MGPWireRec_GenerateMipmap) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPMipPlan) + 7u) & ~SizeT(7u)), + "MGPWireRec_GenerateMipmap gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_GetTextureImage { + MGPWireRecHeader Header; + MGPReadbackInfo Payload; +}; +static_assert(sizeof(MGPWireRec_GetTextureImage) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPReadbackInfo) + 7u) & ~SizeT(7u)), + "MGPWireRec_GetTextureImage gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_Blit { + MGPWireRecHeader Header; + MGPBlit Payload; +}; +static_assert(sizeof(MGPWireRec_Blit) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPBlit) + 7u) & ~SizeT(7u)), + "MGPWireRec_Blit gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_Clear { + MGPWireRecHeader Header; + MGPClear Payload; +}; +static_assert(sizeof(MGPWireRec_Clear) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPClear) + 7u) & ~SizeT(7u)), + "MGPWireRec_Clear gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_ReadPixels { + MGPWireRecHeader Header; + MGPReadbackInfo Payload; +}; +static_assert(sizeof(MGPWireRec_ReadPixels) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPReadbackInfo) + 7u) & ~SizeT(7u)), + "MGPWireRec_ReadPixels gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_DrawVbo { + MGPWireRecHeader Header; + MGPDrawInfo Payload; +}; +static_assert(sizeof(MGPWireRec_DrawVbo) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPDrawInfo) + 7u) & ~SizeT(7u)), + "MGPWireRec_DrawVbo gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_LaunchGrid { + MGPWireRecHeader Header; + MGPGridInfo Payload; +}; +static_assert(sizeof(MGPWireRec_LaunchGrid) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPGridInfo) + 7u) & ~SizeT(7u)), + "MGPWireRec_LaunchGrid gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_MemoryBarrier { + MGPWireRecHeader Header; + MGPMemoryBarrier Payload; +}; +static_assert(sizeof(MGPWireRec_MemoryBarrier) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPMemoryBarrier) + 7u) & ~SizeT(7u)), + "MGPWireRec_MemoryBarrier gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_BeginStreamOutput { + MGPWireRecHeader Header; + MGPStreamOutputBegin Payload; +}; +static_assert(sizeof(MGPWireRec_BeginStreamOutput) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPStreamOutputBegin) + 7u) & ~SizeT(7u)), + "MGPWireRec_BeginStreamOutput gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_EndStreamOutput { + MGPWireRecHeader Header; + MGPXfbAccounting Payload; +}; +static_assert(sizeof(MGPWireRec_EndStreamOutput) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPXfbAccounting) + 7u) & ~SizeT(7u)), + "MGPWireRec_EndStreamOutput gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_PauseStreamOutput { + MGPWireRecHeader Header; + MGPStreamOutputControl Payload; +}; +static_assert(sizeof(MGPWireRec_PauseStreamOutput) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPStreamOutputControl) + 7u) & ~SizeT(7u)), + "MGPWireRec_PauseStreamOutput gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_ResumeStreamOutput { + MGPWireRecHeader Header; + MGPStreamOutputControl Payload; +}; +static_assert(sizeof(MGPWireRec_ResumeStreamOutput) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPStreamOutputControl) + 7u) & ~SizeT(7u)), + "MGPWireRec_ResumeStreamOutput gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_Flush { + MGPWireRecHeader Header; + MGPFlush Payload; +}; +static_assert(sizeof(MGPWireRec_Flush) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPFlush) + 7u) & ~SizeT(7u)), + "MGPWireRec_Flush gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_Present { + MGPWireRecHeader Header; + MGPPresent Payload; +}; +static_assert(sizeof(MGPWireRec_Present) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPPresent) + 7u) & ~SizeT(7u)), + "MGPWireRec_Present gained padding; the wire format moved"); + +struct alignas(8) MGPWireRec_SetSwapInterval { + MGPWireRecHeader Header; + MGPSwapInterval Payload; +}; +static_assert(sizeof(MGPWireRec_SetSwapInterval) == + ((sizeof(MGPWireRecHeader) + sizeof(MGPSwapInterval) + 7u) & ~SizeT(7u)), + "MGPWireRec_SetSwapInterval 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)); + std::abort(); +} + +#define MGP_WIRE_CHECK_BOUNDS(RecType, CallName) \ + do { \ + if (!(size >= sizeof(RecType) && size <= remaining && (size % 8) == 0)) { \ + MGPipeWireProtocolFatal(CallName, size, remaining); \ + } \ + } while (0) + +// Returns whether the record was applied. P0 is a SKELETON: every case validates its +// bounds and then reports "not applied", because no applier exists until P5 wires +// MG_Remote/Server/PipeApplier.cpp to the real backend tables. The switch and the opcode +// enum come from the same list, so a call added to the catalogue cannot be forgotten here; +// the default arm is for the opcode that never came from this catalogue at all - a byte +// off a corrupt stream - and it is fatal for the same reason the bounds check is. +inline Bool MGPipeApplyWireRecord(MGPWireOp op, const void* record, Uint64 size, Uint64 remaining) { + (void)record; + switch (op) { + case MGPWireOp::GetCaps: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_GetCaps, "GetCaps"); + return false; + case MGPWireOp::ResourceCreate: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ResourceCreate, "ResourceCreate"); + return false; + case MGPWireOp::ResourceRespecify: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ResourceRespecify, "ResourceRespecify"); + return false; + case MGPWireOp::ResourceDestroy: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ResourceDestroy, "ResourceDestroy"); + return false; + case MGPWireOp::MapPersistent: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_MapPersistent, "MapPersistent"); + return false; + case MGPWireOp::UnmapPersistent: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_UnmapPersistent, "UnmapPersistent"); + return false; + case MGPWireOp::FenceCreate: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_FenceCreate, "FenceCreate"); + return false; + case MGPWireOp::FenceStatus: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_FenceStatus, "FenceStatus"); + return false; + case MGPWireOp::FenceWait: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_FenceWait, "FenceWait"); + return false; + case MGPWireOp::FenceDestroy: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_FenceDestroy, "FenceDestroy"); + return false; + case MGPWireOp::QueryCreate: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_QueryCreate, "QueryCreate"); + return false; + case MGPWireOp::QueryBegin: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_QueryBegin, "QueryBegin"); + return false; + case MGPWireOp::QueryEnd: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_QueryEnd, "QueryEnd"); + return false; + case MGPWireOp::QueryAvailable: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_QueryAvailable, "QueryAvailable"); + return false; + case MGPWireOp::QueryResult: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_QueryResult, "QueryResult"); + return false; + case MGPWireOp::QueryDestroy: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_QueryDestroy, "QueryDestroy"); + return false; + case MGPWireOp::CreateRenderState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_CreateRenderState, "CreateRenderState"); + return false; + case MGPWireOp::BindRenderState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_BindRenderState, "BindRenderState"); + return false; + case MGPWireOp::DeleteRenderState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_DeleteRenderState, "DeleteRenderState"); + return false; + case MGPWireOp::CreateVertexElements: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_CreateVertexElements, "CreateVertexElements"); + return false; + case MGPWireOp::BindVertexElements: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_BindVertexElements, "BindVertexElements"); + return false; + case MGPWireOp::DeleteVertexElements: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_DeleteVertexElements, "DeleteVertexElements"); + return false; + case MGPWireOp::CreateSamplerState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_CreateSamplerState, "CreateSamplerState"); + return false; + case MGPWireOp::DeleteSamplerState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_DeleteSamplerState, "DeleteSamplerState"); + return false; + case MGPWireOp::CreateSamplerView: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_CreateSamplerView, "CreateSamplerView"); + return false; + case MGPWireOp::DeleteSamplerView: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_DeleteSamplerView, "DeleteSamplerView"); + return false; + case MGPWireOp::CreateShaderState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_CreateShaderState, "CreateShaderState"); + return false; + case MGPWireOp::BindShaderState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_BindShaderState, "BindShaderState"); + return false; + case MGPWireOp::DeleteShaderState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_DeleteShaderState, "DeleteShaderState"); + return false; + case MGPWireOp::SetDynamicState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetDynamicState, "SetDynamicState"); + return false; + case MGPWireOp::SetFramebufferState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetFramebufferState, "SetFramebufferState"); + return false; + case MGPWireOp::SetVertexBuffers: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetVertexBuffers, "SetVertexBuffers"); + return false; + case MGPWireOp::SetIndexBuffer: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetIndexBuffer, "SetIndexBuffer"); + return false; + case MGPWireOp::SetIndirectBuffers: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetIndirectBuffers, "SetIndirectBuffers"); + return false; + case MGPWireOp::SetSamplerViews: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetSamplerViews, "SetSamplerViews"); + return false; + case MGPWireOp::BindSamplerStates: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_BindSamplerStates, "BindSamplerStates"); + return false; + case MGPWireOp::SetShaderImages: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetShaderImages, "SetShaderImages"); + return false; + case MGPWireOp::SetShaderBuffers: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetShaderBuffers, "SetShaderBuffers"); + return false; + case MGPWireOp::SetStreamOutputTargets: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetStreamOutputTargets, "SetStreamOutputTargets"); + return false; + case MGPWireOp::SetGlobalConstants: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetGlobalConstants, "SetGlobalConstants"); + return false; + case MGPWireOp::SetVertexAttribDefaults: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetVertexAttribDefaults, "SetVertexAttribDefaults"); + return false; + case MGPWireOp::SetPixelPackState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetPixelPackState, "SetPixelPackState"); + return false; + case MGPWireOp::SetPatchState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetPatchState, "SetPatchState"); + return false; + case MGPWireOp::SetDrawProgram: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetDrawProgram, "SetDrawProgram"); + return false; + case MGPWireOp::SetDispatchProgram: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetDispatchProgram, "SetDispatchProgram"); + return false; + case MGPWireOp::SetResidualValueState: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetResidualValueState, "SetResidualValueState"); + return false; + case MGPWireOp::SetTextureParams: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetTextureParams, "SetTextureParams"); + return false; + case MGPWireOp::ResourceSubData: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ResourceSubData, "ResourceSubData"); + return false; + case MGPWireOp::BufferSubDataResident: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_BufferSubDataResident, "BufferSubDataResident"); + return false; + case MGPWireOp::ResourceSubDataComplete: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ResourceSubDataComplete, "ResourceSubDataComplete"); + return false; + case MGPWireOp::ResourceFlushRange: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ResourceFlushRange, "ResourceFlushRange"); + return false; + case MGPWireOp::ResourceReadback: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ResourceReadback, "ResourceReadback"); + return false; + case MGPWireOp::ResourceCopyRegion: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ResourceCopyRegion, "ResourceCopyRegion"); + return false; + case MGPWireOp::GenerateMipmap: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_GenerateMipmap, "GenerateMipmap"); + return false; + case MGPWireOp::GetTextureImage: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_GetTextureImage, "GetTextureImage"); + return false; + case MGPWireOp::Blit: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_Blit, "Blit"); + return false; + case MGPWireOp::Clear: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_Clear, "Clear"); + return false; + case MGPWireOp::ReadPixels: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ReadPixels, "ReadPixels"); + return false; + case MGPWireOp::DrawVbo: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_DrawVbo, "DrawVbo"); + return false; + case MGPWireOp::LaunchGrid: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_LaunchGrid, "LaunchGrid"); + return false; + case MGPWireOp::MemoryBarrier: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_MemoryBarrier, "MemoryBarrier"); + return false; + case MGPWireOp::BeginStreamOutput: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_BeginStreamOutput, "BeginStreamOutput"); + return false; + case MGPWireOp::EndStreamOutput: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_EndStreamOutput, "EndStreamOutput"); + return false; + case MGPWireOp::PauseStreamOutput: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_PauseStreamOutput, "PauseStreamOutput"); + return false; + case MGPWireOp::ResumeStreamOutput: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_ResumeStreamOutput, "ResumeStreamOutput"); + return false; + case MGPWireOp::Flush: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_Flush, "Flush"); + return false; + case MGPWireOp::Present: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_Present, "Present"); + return false; + case MGPWireOp::SetSwapInterval: + MGP_WIRE_CHECK_BOUNDS(MGPWireRec_SetSwapInterval, "SetSwapInterval"); + return false; + case MGPWireOp::kInvalid: + case MGPWireOp::kOpCount: + default: + MGPipeWireProtocolFatal("", size, remaining); + } +} + +#undef MGP_WIRE_CHECK_BOUNDS diff --git a/scripts/data/backend_read_inventory.md b/scripts/data/backend_read_inventory.md new file mode 100644 index 00000000..140d8fe0 --- /dev/null +++ b/scripts/data/backend_read_inventory.md @@ -0,0 +1,626 @@ +# Backend read inventory -> delta catalog matrix + +> GENERATED by `scripts/extract_backend_read_inventory.py` - do not edit by hand. +> Acceptance rule: zero UNMAPPED rows before the delta path goes live (P5). + +## Summary + +- scanned files: 57 (MG_Backend/**.cpp|.h) +- total read points: 477 +- pGLContext accesses: 293 +- SharedPtr Texture;` | +| 35 | SharedPtr Renderbuffer;` | +| 158 | SharedPtr& framebuffer,` | +| 160 | SharedPtr& framebuffer,` | +| 162 | SharedPtr& framebuffer,` | +| 164 | SharedPtr& framebuffer,` | +| 168 | SharedPtr& readFramebuffer,` | +| 169 | SharedPtr& drawFramebuffer,` | +| 186 | SharedPtr& texture,` | + +### `MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp` (1 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 849 | BufferBackendOps | - | Buffer ops delta | `BufferImpl::RegisterBufferBackendOps();` | + +### `MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp` (142 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 55 | SharedPtr g_rawDepthFetchSamplerState;` | +| 142 | pGLContext | GetFramebufferBindingSlot | FboAttach | `std::remove_reference_tGetFramebufferBindingSlot(FramebufferTarget::Draw))>;` | +| 182 | SharedPtr& samplerObject,` | +| 260 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 356 | pGLContext | GetTouchedBufferBindingPointCount | ObjectBind(BufferRange) | `auto bindingPointCnt = MG_State::pGLContext->GetTouchedBufferBindingPointCount(target);` | +| 369 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(target, i);` | +| 428 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::TransformFeedback, i);` | +| 461 | pGLContext | GetTouchedBufferBindingPointCount | ObjectBind(BufferRange) | `MG_State::pGLContext->GetTouchedBufferBindingPointCount(BufferTarget::ShaderStorage);` | +| 464 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, i).GetBoundObject();` | +| 473 | pGLContext | GetBufferBindingPointCount | ObjectBind(BufferRange) | `const SizeT pointCount = MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::AtomicCounter);` | +| 480 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::AtomicCounter,` | +| 517 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto& bufferObject = MG_State::pGLContext->GetBufferBindingSlot(target).GetBoundObject();` | +| 537 | SharedPtr& currentVAOObject,` | +| 661 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 712 | SharedPtr buffer;` | +| 739 | SharedPtr scatterProgram;` | +| 891 | pGLContext | GetTransformFeedbackCapturedVertices | XfbOp | `static_cast(MG_State::pGLContext->GetTransformFeedbackCapturedVertices());` | +| 986 | pGLContext | GetTransformFeedbackProgram | XfbOp | `const auto& program = MG_State::pGLContext->GetTransformFeedbackProgram();` | +| 1006 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::TransformFeedback,` | +| 1199 | SharedPtr& vao) {` | +| 1215 | SharedPtr& currentVAOObject,` | +| 1237 | SharedPtr& program) {` | +| 1243 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& vao = MG_State::pGLContext->GetBoundVertexArray();` | +| 1274 | pGLContext | GetCurrentVertexAttribute | CurrentAttrib | `const auto& currentValue = MG_State::pGLContext->GetCurrentVertexAttribute(location);` | +| 1298 | SharedPtr& textureObject,` | +| 1372 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);` | +| 1385 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);` | +| 1413 | pGLContext | GetTextureContextId | Texture state | `const Uint64 contextId = MG_State::pGLContext->GetTextureContextId();` | +| 1414 | pGLContext | GetTextureBindGeneration | ObjectBind(Texture) | `const Uint64 bindGeneration = MG_State::pGLContext->GetTextureBindGeneration();` | +| 1457 | SharedPtr* slot = nullptr;` | +| 1503 | pGLContext | GetTextureContextId | Texture state | `keys.contextId = MG_State::pGLContext->GetTextureContextId();` | +| 1505 | pGLContext | GetMaxTouchedTextureUnit | ObjectBind(Texture) | `keys.maxTouchedUnit = MG_State::pGLContext->GetMaxTouchedTextureUnit();` | +| 1506 | pGLContext | GetSamplingResolutionGeneration | TexParam | `keys.samplingGeneration = MG_State::pGLContext->GetSamplingResolutionGeneration();` | +| 1552 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& unit = MG_State::pGLContext->GetTextureUnitObject(index);` | +| 1700 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(static_cast(unit));` | +| 1792 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `const auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(static_cast(unit));` | +| 1998 | pGLContext | GetRenderStateParametersVersion | RenderStateBlob | `Uint16 currentRenderStateVersion = MG_State::pGLContext->GetRenderStateParametersVersion();` | +| 2012 | pGLContext | GetRenderStateParameters | RenderStateBlob | `const auto& parameters = MG_State::pGLContext->GetRenderStateParameters();` | +| 2041 | pGLContext | GetViewport | RenderStateBlob | `IntVec4 backendViewport = MG_State::pGLContext->GetViewport();` | +| 2124 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `const Bool srgbWrites = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::FramebufferSrgb);` | +| 2680 | SharedPtr& currentProgram) {` | +| 2800 | pGLContext | GetPatchVertices | RenderStateBlob | `static_cast(MG_State::pGLContext->GetPatchVertices()) \|\|` | +| 2802 | pGLContext | GetPatchDefaultOuterLevel | RenderStateBlob | `MG_State::pGLContext->GetPatchDefaultOuterLevel()) \|\|` | +| 2804 | pGLContext | GetPatchDefaultInnerLevel | RenderStateBlob | `MG_State::pGLContext->GetPatchDefaultInnerLevel())))) {` | +| 2853 | SharedPtr& framebuffer,` | +| 2902 | SharedPtr& currentProgram,` | +| 2905 | SharedPtr& currentProgram);` | +| 2915 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& currentVAO = MG_State::pGLContext->GetBoundVertexArray();` | +| 2926 | pGLContext | GetProgramForDraw | ObjectBind(Program) | `const auto& currentProgram = MG_State::pGLContext->GetProgramForDraw();` | +| 2973 | SharedPtr& currentProgram,` | +| 2998 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);` | +| 3113 | SharedPtr& samplerObject) {` | +| 3162 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `const auto& samplerObject = MG_State::pGLContext->GetTextureUnitObject(unit).GetSamplerObject();` | +| 3250 | SharedPtr& currentProgram) {` | +| 3312 | pGLContext | GetProgramForDraw | ObjectBind(Program) | `BindCurrentTextures(TextureImpl::CaptureDrawTextureSyncKeys(), MG_State::pGLContext->GetProgramForDraw());` | +| 3322 | SharedPtr& currentProgram,` | +| 3423 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, binding);` | +| 3521 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);` | +| 3547 | SharedPtr* rawDepthSamplerObject =` | +| 3603 | pGLContext | GetProgramForDraw | ObjectBind(Program) | `const auto& currentProgram = MG_State::pGLContext->GetProgramForDraw();` | +| 3653 | pGLContext | GetProgramForDraw | ObjectBind(Program) | `const auto& currentProgram = MG_State::pGLContext->GetProgramForDraw();` | +| 3762 | pGLContext | IsTransformFeedbackActive | XfbOp | `if (MG_State::pGLContext->IsTransformFeedbackActive() \|\|` | +| 3763 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) {` | +| 3767 | pGLContext | GetRenderStateParameters | RenderStateBlob | `const auto& parameters = MG_State::pGLContext->GetRenderStateParameters();` | +| 3862 | SharedPtr& drawIndirectBuffer,` | +| 3941 | SharedPtr& drawIndirectBuffer,` | +| 4003 | pGLContext | GetProgramForDispatch | ObjectBind(Program) | `const auto& currentProgram = MG_State::pGLContext->GetProgramForDispatch();` | +| 4029 | pGLContext | ValidateProgramName | client-resolved (validation) | `if (!MG_State::pGLContext->ValidateProgramName(program)) {` | +| 4034 | pGLContext | GetProgramObject | ProgramPublish | `auto& programObject = MG_State::pGLContext->GetProgramObject(program);` | +| 4097 | pGLContext | GetRenderStateParameters | RenderStateBlob | `const FloatVec4& cc = MG_State::pGLContext->GetRenderStateParameters().ClearColor;` | +| 4156 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();` | +| 4313 | SharedPtr& BoundElementArrayBuffer() {` | +| 4314 | SharedPtr none;` | +| 4315 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& vao = MG_State::pGLContext->GetBoundVertexArray();` | +| 4331 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `if (!MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PrimitiveRestart) \|\|` | +| 4332 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PrimitiveRestartFixedIndex)) {` | +| 4337 | pGLContext | GetPrimitiveRestartIndex | RenderStateBlob | `const Uint32 restartIndex = MG_State::pGLContext->GetPrimitiveRestartIndex();` | +| 4377 | pGLContext | GetPrimitiveRestartIndex | RenderStateBlob | `const Uint32 applicationRestartIndex = MG_State::pGLContext->GetPrimitiveRestartIndex();` | +| 4505 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& currentVAO = MG_State::pGLContext->GetBoundVertexArray();` | +| 4544 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& currentVAO = MG_State::pGLContext->GetBoundVertexArray();` | +| 4615 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 4646 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 4647 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();` | +| 4717 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 4748 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 4749 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();` | +| 4908 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 4949 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 5194 | pGLContext | IsTransformFeedbackActive | XfbOp | `if (MG_State::pGLContext->IsTransformFeedbackActive() &&` | +| 5195 | pGLContext | IsTransformFeedbackPaused | XfbOp | `!MG_State::pGLContext->IsTransformFeedbackPaused() && g_GLESFuncs.glPauseTransformFeedback) {` | +| 5811 | SharedPtr& readFramebuffer,` | +| 5812 | SharedPtr& drawFramebuffer, GLint srcX0, GLint srcY0,` | +| 5979 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(),` | +| 5980 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(), srcX0, srcY0,` | +| 5990 | SharedPtr& readFramebuffer,` | +| 5991 | SharedPtr& drawFramebuffer,` | +| 6042 | pGLContext | GetActiveTextureUnit | ObjectBind(Texture) | `auto unit = MG_State::pGLContext->GetActiveTextureUnit();` | +| 6043 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);` | +| 6120 | pGLContext | GetPixelStoreParameters | PixelStoreBlob | `const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false);` | +| 6267 | SharedPtr& texture) {` | +| 6310 | pGLContext | RecordError | client-resolved (error queue) | `MG_State::pGLContext->RecordError(` | +| 6335 | SharedPtr& texture) {` | +| 6561 | SharedPtr& texture,` | +| 6586 | SharedPtr& texture,` | +| 6634 | pGLContext | GetActiveTextureUnit | ObjectBind(Texture) | `Uint activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit();` | +| 6635 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject((Int)activeTextureUnit)` | +| 6729 | pGLContext | GetActiveTextureUnit | ObjectBind(Texture) | `auto activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit();` | +| 6730 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject(activeTextureUnit)` | +| 6794 | SharedPtr& texture) {` | +| 6867 | pGLContext | GetActiveTextureUnit | ObjectBind(Texture) | `auto unitIndex = MG_State::pGLContext->GetActiveTextureUnit();` | +| 6868 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& unit = MG_State::pGLContext->GetTextureUnitObject(unitIndex);` | +| 6994 | SharedPtr& renderbufferObject) {` | +| 7059 | SharedPtr& texture) {` | +| 7260 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, index);` | +| 7266 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, index);` | +| 7271 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, index);` | +| 7288 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(static_cast(index));` | +| 7297 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(static_cast(index));` | +| 7306 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(static_cast(index));` | +| 7315 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(static_cast(index));` | +| 7324 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(static_cast(index));` | +| 7333 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(static_cast(index));` | +| 7352 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, index);` | +| 7357 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, index);` | +| 7408 | pGLContext | ValidateProgramName | client-resolved (validation) | `if (!MG_State::pGLContext->ValidateProgramName(program)) return;` | +| 7409 | pGLContext | GetProgramObject | ProgramPublish | `auto& programObject = MG_State::pGLContext->GetProgramObject(program);` | +| 7456 | SharedPtr& framebuffer,` | +| 7514 | SharedPtr& framebuffer,` | +| 7534 | SharedPtr& framebuffer,` | +| 7551 | SharedPtr& framebuffer,` | +| 7571 | SharedPtr& framebuffer,` | +| 7605 | pGLContext | GetPixelStoreParameters | PixelStoreBlob | `const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false);` | +| 7613 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();` | +| 8595 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();` | +| 8825 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();` | +| 9092 | pGLContext | GetPixelStoreParameters | PixelStoreBlob | `const Bool packSwapBytes = MG_State::pGLContext->GetPixelStoreParameters(false).SwapBytes;` | +| 9135 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();` | +| 9245 | pGLContext | GetActiveTextureUnit | ObjectBind(Texture) | `auto activeTextureUnit = MG_State::pGLContext->GetActiveTextureUnit();` | +| 9248 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `const auto& textureObject = MG_State::pGLContext->GetTextureUnitObject(activeTextureUnit)` | +| 9471 | pGLContext | GetPixelStoreParameters | PixelStoreBlob | `const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false);` | +| 9561 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();` | +| 10114 | BufferBackendOps | - | Buffer ops delta | `BufferImpl::RegisterBufferBackendOps();` | + +### `MobileGL/MG_Backend/DirectGLES/DirectGLES.h` (6 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 60 | SharedPtr& framebuffer,` | +| 62 | SharedPtr& framebuffer,` | +| 64 | SharedPtr& framebuffer,` | +| 66 | SharedPtr& framebuffer,` | +| 70 | SharedPtr& readFramebuffer,` | +| 71 | SharedPtr& drawFramebuffer,` | + +### `MobileGL/MG_Backend/DirectGLES/Managers.cpp` (39 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 443 | SharedPtr& stateProgramObject) {` | +| 495 | BufferBackendOps | - | Buffer ops delta | `using MG_State::GLState::BufferBackendOps;` | +| 1333 | BufferBackendOps | - | Buffer ops delta | `const BufferBackendOps g_glesBufferBackendOps = {` | +| 1356 | BufferBackendOps | - | Buffer ops delta | `void RegisterBufferBackendOps() {` | +| 1357 | BufferBackendOps | - | Buffer ops delta | `MG_State::GLState::SetBufferBackendOps(&g_glesBufferBackendOps);` | +| 1363 | BufferBackendOps | - | Buffer ops delta | `void UnregisterBufferBackendOps() {` | +| 1364 | BufferBackendOps | - | Buffer ops delta | `if (MG_State::GLState::GetBufferBackendOps() == &g_glesBufferBackendOps) {` | +| 1365 | BufferBackendOps | - | Buffer ops delta | `MG_State::GLState::SetBufferBackendOps(nullptr);` | +| 1379 | BufferBackendOps | - | Buffer ops delta | `UnregisterBufferBackendOps(); // also bumps the buffer-mutation epoch` | +| 1453 | SharedPtr& bufferObject) {` | +| 2264 | SharedPtr& stateVAOObject) {` | +| 2496 | SharedPtr& stateVAOObject, GLint first, GLsizei count) {` | +| 2787 | SharedPtr& stateTextureObject) {` | +| 3604 | SharedPtr& stateTextureObject) {` | +| 3606 | pGLContext | GetTextureContextId | Texture state | `m_syncedShapeContextId = MG_State::pGLContext->GetTextureContextId();` | +| 3607 | pGLContext | GetSamplingResolutionGeneration | TexParam | `m_syncedShapeGeneration = MG_State::pGLContext->GetSamplingResolutionGeneration();` | +| 3614 | SharedPtr& stateTextureObject) {` | +| 3707 | SharedPtr& stateTextureObject) {` | +| 3735 | pGLContext | GetTextureContextId | Texture state | `m_syncedShapeContextId == MG_State::pGLContext->GetTextureContextId() &&` | +| 3736 | pGLContext | GetSamplingResolutionGeneration | TexParam | `m_syncedShapeGeneration == MG_State::pGLContext->GetSamplingResolutionGeneration() &&` | +| 3806 | pGLContext | GetTextureContextId | Texture state | `m_syncedShapeContextId = MG_State::pGLContext->GetTextureContextId();` | +| 3807 | pGLContext | GetSamplingResolutionGeneration | TexParam | `m_syncedShapeGeneration = MG_State::pGLContext->GetSamplingResolutionGeneration();` | +| 4661 | pGLContext | GetTextureContextId | Texture state | `m_syncedShapeContextId = MG_State::pGLContext->GetTextureContextId();` | +| 4662 | pGLContext | GetSamplingResolutionGeneration | TexParam | `m_syncedShapeGeneration = MG_State::pGLContext->GetSamplingResolutionGeneration();` | +| 4670 | SharedPtr& stateTextureObject) {` | +| 4781 | SharedPtr& stateTextureObject) {` | +| 5280 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();` | +| 5385 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();` | +| 5409 | SharedPtr& stateFBOObject) {` | +| 5520 | SharedPtr& stateFBOObject, FramebufferTarget asTarget) {` | +| 6155 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `return static_cast(MG_State::pGLContext->GetImageTextureBinding(unit).Format);` | +| 7118 | pGLContext | GetPatchVertices | RenderStateBlob | `? MG_State::pGLContext->GetPatchVertices()` | +| 7126 | pGLContext | GetPatchDefaultOuterLevel | RenderStateBlob | `? MG_State::pGLContext->GetPatchDefaultOuterLevel()` | +| 7129 | pGLContext | GetPatchDefaultInnerLevel | RenderStateBlob | `? MG_State::pGLContext->GetPatchDefaultInnerLevel()` | +| 7229 | SharedPtr& stateProgramObject) {` | +| 8262 | SharedPtr& stateProgramObject) {` | +| 8450 | SharedPtr& stateSamplerObject) {` | +| 8620 | SharedPtr& stateRBOObject) {` | +| 8676 | pGLContext | RecordError | client-resolved (error queue) | `MG_State::pGLContext->RecordError(` | + +### `MobileGL/MG_Backend/DirectGLES/Managers.h` (18 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 501 | BufferBackendOps | - | Buffer ops delta | `void RegisterBufferBackendOps();` | +| 502 | BufferBackendOps | - | Buffer ops delta | `void UnregisterBufferBackendOps();` | +| 511 | SharedPtr& bufferObject);` | +| 679 | SharedPtr& stateVAOObject);` | +| 681 | SharedPtr& stateVAOObject, GLint first, GLsizei count);` | +| 953 | SharedPtr& stateTextureObject);` | +| 959 | SharedPtr& stateTextureObject);` | +| 960 | SharedPtr& stateTextureObject);` | +| 966 | SharedPtr& stateTextureObject);` | +| 967 | SharedPtr& stateTextureObject);` | +| 973 | SharedPtr& stateTextureObject);` | +| 1127 | SharedPtr& textureObject,` | +| 1151 | SharedPtr& stateFBOObject,` | +| 1156 | SharedPtr& stateFBOObject);` | +| 1531 | SharedPtr& stateProgramObject);` | +| 1639 | SharedPtr& stateProgramObject);` | +| 1817 | SharedPtr& stateSamplerObject);` | +| 1846 | SharedPtr& stateRBOObject);` | + +### `MobileGL/MG_Backend/DirectGLES/MultiDraw.cpp` (8 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 44 | pGLContext | GetPrimitiveRestartIndex | RenderStateBlob | `return MG_State::pGLContext->GetPrimitiveRestartIndex();` | +| 50 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `return MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PrimitiveRestart) \|\|` | +| 51 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PrimitiveRestartFixedIndex);` | +| 86 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 92 | SharedPtr& BoundIndexBuffer() {` | +| 93 | SharedPtr none;` | +| 94 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& vao = MG_State::pGLContext->GetBoundVertexArray();` | +| 355 | SharedPtr& indexBuffer,` | + +### `MobileGL/MG_Backend/DirectGLES/Utils.cpp` (2 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 2297 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();` | +| 2301 | pGLContext | GetPixelStoreParameters | PixelStoreBlob | `const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false);` | + +### `MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp` (2 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 389 | pGLContext | InvalidateCompileEnv | client-resolved (compile env) | `MG_State::pGLContext->InvalidateCompileEnv();` | +| 789 | pGLContext | InvalidateCompileEnv | client-resolved (compile env) | `MG_State::pGLContext->InvalidateCompileEnv();` | + +### `MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp` (25 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 280 | pGLContext | ValidateProgramName | client-resolved (validation) | `if (!MG_State::pGLContext->ValidateProgramName(program)) {` | +| 283 | pGLContext | GetProgramObject | ProgramPublish | `auto& programObject = MG_State::pGLContext->GetProgramObject(program);` | +| 288 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 369 | SharedPtr& framebuffer, GLenum buffer,` | +| 376 | SharedPtr& framebuffer, GLenum buffer,` | +| 383 | SharedPtr& framebuffer, GLenum buffer,` | +| 390 | SharedPtr& framebuffer, GLenum buffer,` | +| 412 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 475 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();` | +| 543 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 596 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 706 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, index);` | +| 712 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, index);` | +| 717 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, index);` | +| 739 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(static_cast(index));` | +| 765 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, index);` | +| 770 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::ShaderStorage, index);` | +| 816 | pGLContext | RecordError | client-resolved (error queue) | `MG_State::pGLContext->RecordError(` | +| 849 | SharedPtr& texture, TextureUploadTarget uploadTarget,` | +| 886 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& vao = *MG_State::pGLContext->GetBoundVertexArray();` | +| 1010 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& vao = *MG_State::pGLContext->GetBoundVertexArray();` | +| 1112 | SharedPtr& readFramebuffer,` | +| 1113 | SharedPtr& drawFramebuffer,` | +| 1337 | pGLContext | GetTransformFeedbackPausedPrimitiveCounter | XfbOp | `primitives += MG_State::pGLContext->GetTransformFeedbackPausedPrimitiveCounter() -` | +| 1384 | pGLContext | GetTransformFeedbackPausedPrimitiveCounter | XfbOp | `MG_State::pGLContext ? MG_State::pGLContext->GetTransformFeedbackPausedPrimitiveCounter() : 0;` | + +### `MobileGL/MG_Backend/DirectVulkan/DirectVulkan.h` (7 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 36 | SharedPtr& framebuffer, GLenum buffer,` | +| 38 | SharedPtr& framebuffer, GLenum buffer,` | +| 40 | SharedPtr& framebuffer, GLenum buffer,` | +| 42 | SharedPtr& framebuffer, GLenum buffer,` | +| 76 | SharedPtr& readFramebuffer,` | +| 77 | SharedPtr& drawFramebuffer,` | +| 103 | SharedPtr& texture, TextureUploadTarget uploadTarget,` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.cpp` (23 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 161 | SharedPtr MakePlaceholderTextureObject(TextureTarget target,` | +| 505 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);` | +| 508 | SharedPtr fallbackHolder;` | +| 554 | pGLContext | GetFramebufferBindingSlot | FboAttach | `auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();` | +| 781 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `const auto& samplerOverride = MG_State::pGLContext->GetTextureUnitObject(unit).GetSamplerObject();` | +| 809 | SharedPtr& outTexture) {` | +| 820 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);` | +| 846 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);` | +| 873 | SharedPtr texture;` | +| 1015 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(imageUnit);` | +| 1188 | pGLContext | GetBufferBindingPointCount | ObjectBind(BufferRange) | `static_cast(MG_State::pGLContext->GetBufferBindingPointCount(bufferTarget));` | +| 1193 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(bufferTarget, frontendBinding);` | +| 1294 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto& imageBinding = MG_State::pGLContext->GetImageTextureBinding(imageUnit);` | +| 1303 | SharedPtr placeholder;` | +| 1392 | SharedPtr UniformManager::GetFallbackTexture(` | +| 1433 | SharedPtr UniformManager::GetFallbackMultisampleTexture(` | +| 1477 | SharedPtr texture;` | +| 1597 | SharedPtr UniformManager::GetUnboundStorageImageTexture(` | +| 1661 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(unit);` | +| 1849 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `auto* texture = MG_State::pGLContext->GetImageTextureBinding(imageUnit).Texture.get();` | +| 1937 | pGLContext | GetImageTextureBinding | ObjectBind(Image) | `const auto& image = MG_State::pGLContext->GetImageTextureBinding(imageUnit);` | +| 2012 | pGLContext | GetBufferBindingPointCount | ObjectBind(BufferRange) | `static_cast(MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform));` | +| 2017 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, frontendBinding);` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/UniformManager.h` (7 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 165 | SharedPtr& outTexture);` | +| 184 | SharedPtr GetFallbackTexture(` | +| 191 | SharedPtr GetFallbackMultisampleTexture(` | +| 213 | SharedPtr GetUnboundStorageImageTexture(TextureTarget target,` | +| 305 | SharedPtr m_fallbackTexture2D;` | +| 308 | SharedPtr> m_fallbackMultisampleTextures;` | +| 317 | SharedPtr> m_unboundStorageImageTextures;` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.cpp` (9 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 50 | BufferBackendOps | - | Buffer ops delta | `using MG_State::GLState::BufferBackendOps;` | +| 104 | BufferBackendOps | - | Buffer ops delta | `const BufferBackendOps g_vulkanBufferBackendOps = {` | +| 130 | BufferBackendOps | - | Buffer ops delta | `MG_State::GLState::SetBufferBackendOps(&g_vulkanBufferBackendOps);` | +| 137 | BufferBackendOps | - | Buffer ops delta | `if (MG_State::GLState::GetBufferBackendOps() == &g_vulkanBufferBackendOps) {` | +| 138 | BufferBackendOps | - | Buffer ops delta | `MG_State::GLState::SetBufferBackendOps(nullptr);` | +| 255 | SharedPtr& bufferObject) {` | +| 496 | SharedPtr&& resource) {` | +| 566 | SharedPtr& bufferObject,` | +| 614 | SharedPtr& bufferObject,` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferManager.h` (4 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 146 | SharedPtr& bufferObject,` | +| 151 | SharedPtr& bufferObject,` | +| 165 | SharedPtr&& resource);` | +| 184 | SharedPtr& bufferObject);` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.cpp` (11 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 57 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `if (MG_State::pGLContext->IsCapabilityEnabled(MobileGL::CapabilityInput::FramebufferSrgb)) return;` | +| 238 | SharedPtr& outTexture) {` | +| 261 | SharedPtr& outTexture) {` | +| 319 | SharedPtr& texture) {` | +| 325 | SharedPtr& storageTexture = storageOwner ? storageOwner : texture;` | +| 350 | SharedPtr& storageTexture = storageOwner ? storageOwner : texture;` | +| 372 | SharedPtr liveTexture;` | +| 392 | SharedPtr liveTexture;` | +| 404 | SharedPtr liveTexture;` | +| 409 | SharedPtr& outTexture) {` | +| 461 | SharedPtr liveTexture;` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/VkClearManager.h` (4 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 127 | SharedPtr& texture);` | +| 135 | SharedPtr& outTexture);` | +| 148 | SharedPtr& outTexture);` | +| 150 | SharedPtr& outTexture);` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.cpp` (5 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 342 | SharedPtr& renderbuffer) {` | +| 613 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(MobileGL::CapabilityInput::FramebufferSrgb);` | +| 965 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(MobileGL::CapabilityInput::FramebufferSrgb);` | +| 1111 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(MobileGL::CapabilityInput::FramebufferSrgb));` | +| 1595 | SharedPtr liveTexture;` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/VkRenderPassManager.h` (1 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 353 | SharedPtr& renderbuffer);` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.cpp` (2 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 808 | pGLContext | GetTextureObject | Texture state | `const auto& liveTexture = MG_State::pGLContext->GetTextureObject(texture.GetExternalIndex());` | +| 951 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(MobileGL::CapabilityInput::FramebufferSrgb);` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp` (140 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 462 | pGLContext | GetViewportIndexed | RenderStateBlob | `const FloatVec4& stored = MG_State::pGLContext->GetViewportIndexed(index);` | +| 467 | pGLContext | GetDepthRangeIndexed | RenderStateBlob | `const FloatVec2& depthRange = MG_State::pGLContext->GetDepthRangeIndexed(index);` | +| 522 | pGLContext | GetBlendColor | RenderStateBlob | `const FloatVec4& blendColor = MG_State::pGLContext->GetBlendColor();` | +| 555 | pGLContext | GetPolygonOffsetUnits | RenderStateBlob | `const Float constantFactor = MG_State::pGLContext->GetPolygonOffsetUnits();` | +| 556 | pGLContext | GetPolygonOffsetFactor | RenderStateBlob | `const Float slopeFactor = MG_State::pGLContext->GetPolygonOffsetFactor();` | +| 569 | pGLContext | GetLineWidth | RenderStateBlob | `Float lineWidth = MG_State::pGLContext->GetLineWidth();` | +| 648 | pGLContext | GetStencilState | RenderStateBlob | `const StencilFaceState& frontStencil = MG_State::pGLContext->GetStencilState(StencilFace::Front);` | +| 649 | pGLContext | GetStencilState | RenderStateBlob | `const StencilFaceState& backStencil = MG_State::pGLContext->GetStencilState(StencilFace::Back);` | +| 1242 | pGLContext | RecordError | client-resolved (error queue) | `MG_State::pGLContext->RecordError(code, MakeUnique("DirectVulkan", func, message));` | +| 1246 | pGLContext | RecordError | client-resolved (error queue) | `MG_State::pGLContext->RecordError(code, MakeUnique("DirectVulkan", func, message));` | +| 1302 | pGLContext | RecordError | client-resolved (error queue) | `MG_State::pGLContext->RecordError(` | +| 2770 | pGLContext | GetClampReadColor | RenderStateBlob | `const GLenum clampMode = MG_State::pGLContext->GetClampReadColor();` | +| 2987 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();` | +| 3419 | SharedPtr{}` | +| 3446 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PrimitiveRestart) \|\|` | +| 3447 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PrimitiveRestartFixedIndex);` | +| 3924 | pGLContext | GetCurrentVertexAttribute | CurrentAttrib | `const auto& currentValue = MG_State::pGLContext->GetCurrentVertexAttribute(location);` | +| 4058 | pGLContext | GetRenderStateParameters | RenderStateBlob | `const RenderStateParameters& rsp = MG_State::pGLContext->GetRenderStateParameters();` | +| 4074 | SharedPtr& indexBufferShared =` | +| 4813 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `if (!MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Multisample)) return kFullCoverage;` | +| 4814 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `if (!MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::SampleMask)) return kFullCoverage;` | +| 4815 | pGLContext | GetRenderStateParameters | RenderStateBlob | `return MG_State::pGLContext->GetRenderStateParameters().SampleMaskValue;` | +| 4826 | pGLContext | GetRenderStateParameters | RenderStateBlob | `const RenderStateParameters& p = MG_State::pGLContext->GetRenderStateParameters();` | +| 4938 | pGLContext | GetRenderStateParameters | RenderStateBlob | `const RenderStateParameters& rsp = MG_State::pGLContext->GetRenderStateParameters();` | +| 4982 | pGLContext | GetPipelineStateVersion | RenderStateBlob | `const Uint renderStateVersion = MG_State::pGLContext->GetPipelineStateVersion();` | +| 5160 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `auto cullFaceEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::CullFace);` | +| 5161 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `auto depthTestEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest);` | +| 5163 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::PolygonOffsetFill) &&` | +| 5166 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard);` | +| 5168 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ColorLogicOp) && m_logicOpFeatureEnabled;` | +| 5169 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `auto stencilTestEnabled = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::StencilTest);` | +| 5175 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();` | +| 5187 | pGLContext | GetStencilState | RenderStateBlob | `const StencilFaceState& frontStencil = MG_State::pGLContext->GetStencilState(StencilFace::Front);` | +| 5188 | pGLContext | GetStencilState | RenderStateBlob | `const StencilFaceState& backStencil = MG_State::pGLContext->GetStencilState(StencilFace::Back);` | +| 5190 | pGLContext | GetPolygonModeFront | RenderStateBlob | `MG_Util::ConvertPolygonModeToVkEnum(MG_State::pGLContext->GetPolygonModeFront());` | +| 5257 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::SampleShading),` | +| 5258 | pGLContext | GetMinSampleShadingValue | RenderStateBlob | `.minSampleShading = MG_State::pGLContext->GetMinSampleShadingValue(),` | +| 5264 | pGLContext | GetPatchVertices | RenderStateBlob | `.patchControlPoints = static_cast(MG_State::pGLContext->GetPatchVertices()),` | +| 5268 | pGLContext | GetCullFaceMode | RenderStateBlob | `? MG_Util::ConvertCullFaceModeToVkEnum(MG_State::pGLContext->GetCullFaceMode(), invertClockwise)` | +| 5282 | pGLContext | GetDepthMask | RenderStateBlob | `.depthWriteEnable = depthTestEnabled && MG_State::pGLContext->GetDepthMask(),` | +| 5287 | pGLContext | GetDepthFunc | RenderStateBlob | `.depthCompareOp = MG_Util::ConvertDepthTestFuncToVkEnum(MG_State::pGLContext->GetDepthFunc()),` | +| 5288 | pGLContext | GetLogicOp | RenderStateBlob | `.logicOp = MG_Util::ConvertLogicOperationToVkEnum(MG_State::pGLContext->GetLogicOp()),` | +| 5324 | pGLContext | GetPatchDefaultOuterLevel | RenderStateBlob | `const FloatVec4& defaultOuterLevel = MG_State::pGLContext->GetPatchDefaultOuterLevel();` | +| 5325 | pGLContext | GetPatchDefaultInnerLevel | RenderStateBlob | `const FloatVec2& defaultInnerLevel = MG_State::pGLContext->GetPatchDefaultInnerLevel();` | +| 5377 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();` | +| 5405 | pGLContext | GetBlendFuncIndexed | RenderStateBlob | `MG_State::pGLContext->GetBlendFuncIndexed(i, srcRGB, dstRGB, srcAlpha, dstAlpha);` | +| 5406 | pGLContext | GetBlendEquationIndexed | RenderStateBlob | `MG_State::pGLContext->GetBlendEquationIndexed(i, colorEquation, alphaEquation);` | +| 5407 | pGLContext | IsCapabilityEnabledIndexed | RenderStateBlob | `const Bool blendEnabled = MG_State::pGLContext->IsCapabilityEnabledIndexed(CapabilityInput::Blend, i);` | +| 5412 | pGLContext | GetColorMaskIndexed | RenderStateBlob | `MG_State::pGLContext->GetColorMaskIndexed(m_independentBlendFeatureEnabled ? i : 0);` | +| 5828 | pGLContext | GetRenderStateParameters | RenderStateBlob | `const auto& parameters = MG_State::pGLContext->GetRenderStateParameters();` | +| 5889 | pGLContext | GetRenderStateParametersVersion | RenderStateBlob | `const Uint paramsVersion = MG_State::pGLContext->GetRenderStateParametersVersion();` | +| 5903 | pGLContext | GetRenderStateParameters | RenderStateBlob | `const RenderStateParameters& p = MG_State::pGLContext->GetRenderStateParameters();` | +| 5979 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();` | +| 6003 | pGLContext | GetProgramForDraw | ObjectBind(Program) | `const auto& program = *MG_State::pGLContext->GetProgramForDraw();` | +| 6051 | pGLContext | IsTransformFeedbackActive | XfbOp | `MG_State::pGLContext->IsTransformFeedbackActive() &&` | +| 6065 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& vao = *MG_State::pGLContext->GetBoundVertexArray();` | +| 6070 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();` | +| 6081 | pGLContext | GetPipelineStateVersion | RenderStateBlob | `const Uint renderStateVersion = MG_State::pGLContext->GetPipelineStateVersion();` | +| 6082 | pGLContext | GetTextureBindGeneration | ObjectBind(Texture) | `const Uint64 bindGeneration = MG_State::pGLContext->GetTextureBindGeneration();` | +| 6090 | pGLContext | GetRenderStateParameters | RenderStateBlob | `const RenderStateParameters& rsp = MG_State::pGLContext->GetRenderStateParameters();` | +| 6264 | pGLContext | GetSamplingResolutionGeneration | TexParam | `const Uint64 samplingResolutionGeneration = MG_State::pGLContext->GetSamplingResolutionGeneration();` | +| 6385 | pGLContext | GetProgramForDraw | ObjectBind(Program) | `const auto& drawProgram = *MG_State::pGLContext->GetProgramForDraw();` | +| 6397 | pGLContext | GetFramebufferBindingSlot | FboAttach | `MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();` | +| 6404 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& vao = *MG_State::pGLContext->GetBoundVertexArray();` | +| 6405 | pGLContext | GetProgramForDraw | ObjectBind(Program) | `const auto& program = *MG_State::pGLContext->GetProgramForDraw();` | +| 6445 | pGLContext | IsTransformFeedbackActive | XfbOp | `if (m_transformFeedbackFeatureEnabled && MG_State::pGLContext->IsTransformFeedbackActive() &&` | +| 6459 | pGLContext | GetTextureBindGeneration | ObjectBind(Texture) | `const Uint64 lodBindGeneration = MG_State::pGLContext->GetTextureBindGeneration();` | +| 6465 | pGLContext | GetSamplingResolutionGeneration | TexParam | `const Uint64 lodSamplingGeneration = MG_State::pGLContext->GetSamplingResolutionGeneration();` | +| 6583 | pGLContext | GetTextureBindGeneration | ObjectBind(Texture) | `const Uint64 bindGeneration = MG_State::pGLContext->GetTextureBindGeneration();` | +| 6602 | pGLContext | GetSamplingResolutionGeneration | TexParam | `const Uint64 samplingGeneration = MG_State::pGLContext->GetSamplingResolutionGeneration();` | +| 6763 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest) \|\|` | +| 6764 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::StencilTest);` | +| 6905 | pGLContext | GetPipelineStateVersion | RenderStateBlob | `snap.renderStateVersion = MG_State::pGLContext->GetPipelineStateVersion();` | +| 6906 | pGLContext | GetTextureBindGeneration | ObjectBind(Texture) | `snap.bindGeneration = MG_State::pGLContext->GetTextureBindGeneration();` | +| 6932 | pGLContext | GetSamplingResolutionGeneration | TexParam | `snap.samplingResolutionGeneration = MG_State::pGLContext->GetSamplingResolutionGeneration();` | +| 6969 | pGLContext | GetProgramForDispatch | ObjectBind(Program) | `const auto& program = *MG_State::pGLContext->GetProgramForDispatch();` | +| 7021 | pGLContext | GetProgramForDispatch | ObjectBind(Program) | `const auto& program = *MG_State::pGLContext->GetProgramForDispatch();` | +| 7065 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto indirectBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DispatchIndirect).GetBoundObject();` | +| 7139 | pGLContext | GetScissorBox | RenderStateBlob | `? MakeDefaultFramebufferScissorRect(MG_State::pGLContext->GetScissorBox(),` | +| 7142 | pGLContext | GetScissorBox | RenderStateBlob | `: MakeClampedScissorRect(MG_State::pGLContext->GetScissorBox(), renderPassEntry->extent);` | +| 7190 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) {` | +| 7193 | pGLContext | GetFramebufferBindingSlot | FboAttach | `auto* fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject().get();` | +| 7201 | pGLContext | GetClearColor | RenderStateBlob | `.color = MG_State::pGLContext->GetClearColor(),` | +| 7202 | pGLContext | GetClearDepth | RenderStateBlob | `.depth = MG_State::pGLContext->GetClearDepth(),` | +| 7203 | pGLContext | GetClearStencil | RenderStateBlob | `.stencil = MG_State::pGLContext->GetClearStencil()` | +| 7210 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ScissorTest)) {` | +| 7233 | pGLContext | GetColorMaskIndexed | RenderStateBlob | `const BoolVec4 colorMask = MG_State::pGLContext->GetColorMaskIndexed(drawBufferIndex);` | +| 7260 | pGLContext | GetDepthMask | RenderStateBlob | `if ((mask & GL_DEPTH_BUFFER_BIT) != 0 && MG_State::pGLContext->GetDepthMask()) {` | +| 7273 | pGLContext | GetStencilState | RenderStateBlob | `MG_State::pGLContext->GetStencilState(StencilFace::Front).WriteMask;` | +| 7302 | pGLContext | GetDepthMask | RenderStateBlob | `if ((deferredMask & GL_DEPTH_BUFFER_BIT) != 0 && !MG_State::pGLContext->GetDepthMask()) {` | +| 7306 | pGLContext | GetStencilState | RenderStateBlob | `const Uint32 stencilWriteMask = MG_State::pGLContext->GetStencilState(StencilFace::Front).WriteMask;` | +| 7322 | pGLContext | GetColorMaskIndexed | RenderStateBlob | `const BoolVec4 colorMask = MG_State::pGLContext->GetColorMaskIndexed(drawBufferIndex);` | +| 7343 | pGLContext | GetColorMaskIndexed | RenderStateBlob | `const BoolVec4 colorMask = MG_State::pGLContext->GetColorMaskIndexed(drawBufferIndex);` | +| 7372 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) {` | +| 7414 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ScissorTest)) {` | +| 7445 | pGLContext | GetDepthMask | RenderStateBlob | `const auto depthClearAllowed = [&]() -> Bool { return MG_State::pGLContext->GetDepthMask(); };` | +| 7447 | pGLContext | GetStencilState | RenderStateBlob | `const Uint32 stencilWriteMask = MG_State::pGLContext->GetStencilState(StencilFace::Front).WriteMask;` | +| 7459 | pGLContext | GetColorMaskIndexed | RenderStateBlob | `const BoolVec4 colorMask = MG_State::pGLContext->GetColorMaskIndexed(static_cast(drawbuffer));` | +| 7516 | pGLContext | GetColorMaskIndexed | RenderStateBlob | `const BoolVec4 colorMask = MG_State::pGLContext->GetColorMaskIndexed(static_cast(drawbuffer));` | +| 7534 | pGLContext | GetDepthMask | RenderStateBlob | `if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0 && MG_State::pGLContext->GetDepthMask() &&` | +| 7542 | pGLContext | GetStencilState | RenderStateBlob | `MG_State::pGLContext->GetStencilState(StencilFace::Front).WriteMask;` | +| 7561 | pGLContext | GetFramebufferBindingSlot | FboAttach | `auto* fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject().get();` | +| 7598 | SharedPtr& framebuffer, GLenum buffer, GLint drawbuffer,` | +| 7622 | SharedPtr& framebuffer, GLenum buffer, GLint drawbuffer,` | +| 7645 | SharedPtr& framebuffer, GLenum buffer, GLint drawbuffer,` | +| 7660 | SharedPtr& framebuffer, GLenum buffer, GLint drawbuffer,` | +| 8042 | SharedPtr& renderbuffer) {` | +| 8519 | pGLContext | GetFramebufferBindingSlot | FboAttach | `auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();` | +| 8520 | pGLContext | GetFramebufferBindingSlot | FboAttach | `auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();` | +| 8524 | SharedPtr& readFbo,` | +| 8525 | SharedPtr& drawFbo,` | +| 8552 | pGLContext | IsCapabilityEnabled | RenderStateBlob | `if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ScissorTest)) {` | +| 8553 | pGLContext | GetScissorBox | RenderStateBlob | `const IntVec4& scissor = MG_State::pGLContext->GetScissorBox();` | +| 9147 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());` | +| 9147 | pGLContext | GetActiveTextureUnit | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());` | +| 9155 | pGLContext | GetFramebufferBindingSlot | FboAttach | `auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();` | +| 9866 | pGLContext | GetFramebufferBindingSlot | FboAttach | `auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();` | +| 10621 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();` | +| 10622 | pGLContext | GetPixelStoreParameters | PixelStoreBlob | `const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false);` | +| 10652 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());` | +| 10652 | pGLContext | GetActiveTextureUnit | ObjectBind(Texture) | `auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());` | +| 10657 | SharedPtr& textureObject,` | +| 10896 | pGLContext | GetTextureUnitObject | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());` | +| 10896 | pGLContext | GetActiveTextureUnit | ObjectBind(Texture) | `auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());` | +| 11137 | pGLContext | GetBoundTransformFeedbackName | XfbOp | `const Uint name = MG_State::pGLContext->GetBoundTransformFeedbackName();` | +| 11151 | pGLContext | IsTransformFeedbackActive | XfbOp | `!MG_State::pGLContext->IsTransformFeedbackActive()) {` | +| 11157 | pGLContext | IsTransformFeedbackPaused | XfbOp | `if (MG_State::pGLContext->IsTransformFeedbackPaused()) {` | +| 11160 | pGLContext | GetTransformFeedbackProgram | XfbOp | `const auto& program = MG_State::pGLContext->GetTransformFeedbackProgram();` | +| 11199 | pGLContext | GetBufferBindingPoint | ObjectBind(BufferRange) | `auto& point = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::TransformFeedback,` | +| 11230 | pGLContext | GetTransformFeedbackGeneration | XfbOp | `const Uint64 generation = MG_State::pGLContext->GetTransformFeedbackGeneration();` | +| 11253 | pGLContext | GetTransformFeedbackProgram | XfbOp | `const auto& program = MG_State::pGLContext->GetTransformFeedbackProgram();` | +| 11901 | pGLContext | GetRenderStateParameters | RenderStateBlob | `const RenderStateParameters& rsp = MG_State::pGLContext->GetRenderStateParameters();` | +| 11979 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& vao = *MG_State::pGLContext->GetBoundVertexArray();` | +| 11989 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 11995 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();` | +| 12080 | pGLContext | GetBoundVertexArray | ObjectBind(VAO) | `const auto& vao = *MG_State::pGLContext->GetBoundVertexArray();` | +| 12090 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 12160 | pGLContext | GetBufferBindingSlot | ObjectBind(Buffer) | `auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();` | +| 12650 | pGLContext | GetProvokingVertexMode | RenderStateBlob | `MG_State::pGLContext->GetProvokingVertexMode() == ProvokingVertexMode::FirstVertex)` | +| 14813 | SharedPtr liveTexture;` | + +### `MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h` (12 hits) + +| line | kind | member | delta | snippet | +|---|---|---|---|---| +| 184 | SharedPtr& framebuffer,` | +| 186 | SharedPtr& framebuffer,` | +| 188 | SharedPtr& framebuffer,` | +| 190 | SharedPtr& framebuffer,` | +| 195 | SharedPtr& readFbo,` | +| 196 | SharedPtr& drawFbo,` | +| 256 | SharedPtr& texture,` | +| 378 | SharedPtr program;` | +| 379 | SharedPtr nearestSampler;` | +| 380 | SharedPtr linearSampler;` | +| 388 | SharedPtr program;` | +| 1390 | SharedPtr& renderbuffer);` | diff --git a/scripts/gen_pipe.py b/scripts/gen_pipe.py new file mode 100644 index 00000000..21788eb6 --- /dev/null +++ b/scripts/gen_pipe.py @@ -0,0 +1,638 @@ +#!/usr/bin/env python3 +# MobileGL - scripts/gen_pipe.py +# 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 +"""The seven MGPipe generators, G1..G7 (plan B section 4.1). + +Reads the three hand-maintained sources of truth + + MobileGL/MG_Pipe/PipeCalls.def the call catalogue + MobileGL/MG_Pipe/PipeFields.def per-payload field lists for the verify comparator + MobileGL/MG_Pipe/Coverage.def accessor -> call mapping for the read inventory + +plus the vendored copy of the backend read inventory + + scripts/data/backend_read_inventory.md + +and writes MobileGL/MG_Pipe/generated/*.inc. The outputs are COMMITTED; CI regenerates +them and fails on a diff, which is what keeps the seven generators from drifting apart +from the catalogue (they all consume the same .def). + + python3 scripts/gen_pipe.py # write the generated files, print the summary + python3 scripts/gen_pipe.py --check # fail if regenerating would change anything +""" + +import argparse +import os +import re +import sys + +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +PIPE_DIR = os.path.join(REPO_ROOT, "MobileGL", "MG_Pipe") +GENERATED_DIR = os.path.join(PIPE_DIR, "generated") +INVENTORY = os.path.join(REPO_ROOT, "scripts", "data", "backend_read_inventory.md") + +GENERATED_BANNER = """// MobileGL - MobileGL/MG_Pipe/generated/{name} +// 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 + +// {title} +// +// GENERATED by scripts/gen_pipe.py from {sources} - DO NOT EDIT. +// Regenerate with `python3 scripts/gen_pipe.py`; CI runs it and diffs the result. +// This file is included from MG_Pipe/MGPipe.h inside namespace MobileGL::MG_Pipe. +""" + +# G7. The pipeline subset of RenderStateParameters, BY MEMBER NAME, taken from the fields +# VulkanRenderer::ComputePipelineStateHash hashes today +# (MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp:4805-4906 at dev@81b17c0b, +# including ResolveEffectiveSampleMask, which the hash folds in twice - once as the +# effective enable bit and once as the mask word). +# +# Names only: offsets are NOT computed here. The chunk table with real offsets is +# MGPipeRenderStateSpans.cpp, built in C++ with offsetof, because a python guess at the +# layout of a struct it cannot see is exactly the kind of drift the G7 setter-consistency +# test exists to catch (plan B section 4.5.2). +PIPELINE_STATE_MEMBERS = [ + "CullFaceEnabled", + "DepthTestEnabled", + "PolygonOffsetFillEnabled", + "RasterizerDiscardEnabled", + "ColorLogicOpEnabled", + "StencilTestEnabled", + "PrimitiveRestartEnabled", + "PrimitiveRestartFixedIndexEnabled", + "DepthMask", + "SampleShadingEnabled", + "MultisampleEnabled", + "SampleMaskEnabled", + "SampleMaskValue", + "MinSampleShadingValue", + "PatchVertices", + "PatchDefaultOuterLevel", + "PatchDefaultInnerLevel", + "PolygonModeFront", + "CullFaceModeSetting", + "DepthFunc", + "LogicOp", + "StencilStates", + "BlendStates", + "ColorMasks", +] + + +def read(path): + with open(path, "r", encoding="utf-8") as handle: + return handle.read() + + +class Call(object): + def __init__(self, index, name, payload, cls, flags): + self.Index = index # 1-based; this is the wire opcode + self.Name = name + self.Payload = payload + self.Class = cls + self.Flags = flags + + @property + def IsScreen(self): + return self.Class == "kScreen" + + @property + def Signature(self): + """(parameter declaration list, argument list) for this call.""" + params = ["const %s* payload" % self.Payload] + args = ["payload"] + if "kVarTail" in self.Flags: + params.append("const void* varTail") + params.append("Uint32 varTailCount") + args.append("varTail") + args.append("varTailCount") + if "kReplySlot" in self.Flags: + params.append("MGPReplySlot* reply") + args.append("reply") + return ", ".join(params), ", ".join(args) + + +CALL_RE = re.compile(r"^\s*X\(\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*([\w|]+?)\s*\)\s*\\?\s*$") + + +def parse_calls(): + text = read(os.path.join(PIPE_DIR, "PipeCalls.def")) + documented = re.search(r"#define MGP_CALL_LIST_DOCUMENTED_COUNT (\d+)", text) + if not documented: + sys.exit("PipeCalls.def: MGP_CALL_LIST_DOCUMENTED_COUNT is missing") + calls = [] + inside = False + for line in text.splitlines(): + if line.startswith("#define MGP_CALL_LIST(X)"): + inside = True + continue + if not inside: + continue + match = CALL_RE.match(line) + if match: + calls.append(Call(len(calls) + 1, match.group(1), match.group(2), match.group(3), + match.group(4).split("|"))) + # The macro ends at the first line without a continuation backslash. + if not line.rstrip().endswith("\\"): + inside = False + count = int(documented.group(1)) + if len(calls) != count: + sys.exit("PipeCalls.def: parsed %d calls but MGP_CALL_LIST_DOCUMENTED_COUNT says %d" + % (len(calls), count)) + seen = set() + for call in calls: + if call.Name in seen: + sys.exit("PipeCalls.def: duplicate call %s" % call.Name) + seen.add(call.Name) + return calls + + +def parse_verify_payloads(): + text = read(os.path.join(PIPE_DIR, "PipeFields.def")) + match = re.search(r"#define MGP_VERIFY_PAYLOAD_LIST\(P\)(.*?)\n\n", text, re.S) + if not match: + sys.exit("PipeFields.def: MGP_VERIFY_PAYLOAD_LIST is missing") + payloads = re.findall(r"P\((\w+)\)", match.group(1)) + for payload in payloads: + if ("#define MGP_FIELDS_%s(F)" % payload) not in text: + sys.exit("PipeFields.def: %s is in the payload list with no field macro" % payload) + return payloads + + +def parse_coverage(): + text = read(os.path.join(PIPE_DIR, "Coverage.def")) + accessors = [] + block = re.search(r"#define MGP_COVERAGE_ACCESSOR_LIST\(X\)(.*?)\n\n", text, re.S) + if not block: + sys.exit("Coverage.def: MGP_COVERAGE_ACCESSOR_LIST is missing") + for name, call in re.findall(r"X\((\w+)\s*,\s*(\w+)\)", block.group(1)): + accessors.append((name, call)) + deltas = [] + block = re.search(r"#define MGP_COVERAGE_DELTA_LIST\(X\)(.*?)\n\n", text, re.S) + if not block: + sys.exit("Coverage.def: MGP_COVERAGE_DELTA_LIST is missing") + for kind, call in re.findall(r"X\(([^,]+),\s*(\w+)\)", block.group(1)): + deltas.append((kind.strip(), call)) + return accessors, deltas + + +INVENTORY_ROW_RE = re.compile(r"^\|\s*(\d+)\s*\|([^|]*)\|([^|]*)\|([^|]*)\|") + + +def parse_inventory(): + if not os.path.exists(INVENTORY): + sys.exit("missing %s - copy it from MobileGL-CS/docs/CS_Refactor/" % INVENTORY) + rows = [] + current_file = None + for line in read(INVENTORY).splitlines(): + heading = re.match(r"^### `([^`]+)`", line) + if heading: + current_file = heading.group(1) + continue + match = INVENTORY_ROW_RE.match(line) + if match and current_file: + rows.append({ + "file": current_file, + "line": int(match.group(1)), + "kind": match.group(2).strip(), + "member": match.group(3).strip(), + "delta": match.group(4).strip(), + }) + return rows + + +def banner(name, title, sources): + return GENERATED_BANNER.format(name=name, title=title, sources=sources) + + +def gen_tables(calls): + screen = [c for c in calls if c.IsScreen] + context = [c for c in calls if not c.IsScreen] + out = [banner("PipeTables.inc", "G1: the two MGPipe interface tables.", "PipeCalls.def")] + for struct_name, group, what in (("MGPipeScreen", screen, "share group"), + ("MGPipeContext", context, "context")): + out.append("// %s: %d calls. A null entry means the backend does not implement this\n" + "// call and the frontend keeps its own path (plan B section 4.1).\n" + "struct %s {" % (what, len(group), struct_name)) + for call in group: + params, _ = call.Signature + out.append(" void (*%s)(%s);" % (call.Name, params)) + out.append("};\n") + out.append("inline constexpr SizeT kMGPipeScreenCallCount = %d;" % len(screen)) + out.append("inline constexpr SizeT kMGPipeContextCallCount = %d;" % len(context)) + out.append("inline constexpr SizeT kMGPipeCallCount = %d;" % len(calls)) + out.append("") + out.append("// A table that is not exactly its call count of function pointers has grown a") + out.append("// member that no generator knows about.") + out.append("static_assert(sizeof(MGPipeScreen) == kMGPipeScreenCallCount * sizeof(void (*)()),") + out.append(" \"MGPipeScreen is not exactly its catalogue's function pointers\");") + out.append("static_assert(sizeof(MGPipeContext) == kMGPipeContextCallCount * sizeof(void (*)()),") + out.append(" \"MGPipeContext is not exactly its catalogue's function pointers\");") + out.append("static_assert(kMGPipeScreenCallCount + kMGPipeContextCallCount == kMGPipeCallCount);") + out.append("static_assert(kMGPipeCallCount == MGP_CALL_LIST_DOCUMENTED_COUNT,") + out.append(" \"the catalogue and its documented count disagree\");") + return "\n".join(out) + "\n" + + +def gen_thunks(calls): + out = [banner("PipeThunks.inc", "G2: monolith thunks over the two tables.", "PipeCalls.def")] + out.append("// One inline call through the installed table. These are the names MG_Impl call") + out.append("// sites move onto, replacing gBackendFunctionsTable.GL.* one at a time. An") + out.append("// unimplemented (null) entry is the caller's business to check, exactly as it is") + out.append("// with the table this replaces.\n") + for call in calls: + params, args = call.Signature + table = "gMGPipeScreen" if call.IsScreen else "gMGPipeContext" + out.append("inline void MGP_%s(%s) {" % (call.Name, params)) + out.append(" %s.%s(%s);" % (table, call.Name, args)) + out.append("}") + out.append("") + return "\n".join(out) + + +def gen_wire(calls): + out = [banner("PipeWire.inc", "G3: wire records, size assertions and the applier's bounds gate.", + "PipeCalls.def")] + out.append("""// Every record is a fixed header plus its payload, padded to the stream's 8-byte +// granularity. The size assertion is stated as a COMPOSITION so it fires on any padding +// the compiler inserts between the header and the payload while staying honest about the +// tail padding the alignment requires. +// +// The applier's precondition is checked BEFORE dispatch, on every record, in every build: +// a record that is shorter than its own type, longer than what is left in the buffer, or +// not a multiple of 8 is protocol corruption and is fatal. There is no recovery path - +// silently applying a truncated record is how a corrupt stream becomes a wrong picture. + +struct MGPWireRecHeader { + Uint16 Op; // MGPWireOp + Uint16 Flags; // MGPipeCallFlags of the call, for asserts and tracing + Uint32 Size; // bytes of this record including the header and the variable tail +}; +static_assert(sizeof(MGPWireRecHeader) == 8, "the wire header is 8 bytes"); +static_assert(std::is_trivially_copyable_v); + +// The opcode is the call's position in PipeCalls.def. Reordering that file is a protocol +// break; appending to it is not. +enum class MGPWireOp : Uint16 { + kInvalid = 0,""") + for call in calls: + out.append(" %s = %d," % (call.Name, call.Index)) + out.append(" kOpCount = %d," % (len(calls) + 1)) + out.append("};\n") + for call in calls: + out.append("struct alignas(8) MGPWireRec_%s {" % call.Name) + out.append(" MGPWireRecHeader Header;") + out.append(" %s Payload;" % call.Payload) + out.append("};") + out.append("static_assert(sizeof(MGPWireRec_%s) ==" % call.Name) + out.append(" ((sizeof(MGPWireRecHeader) + sizeof(%s) + 7u) & ~SizeT(7u))," % call.Payload) + out.append(" \"MGPWireRec_%s gained padding; the wire format moved\");" % call.Name) + out.append("") + out.append("""[[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)); + std::abort(); +} + +#define MGP_WIRE_CHECK_BOUNDS(RecType, CallName) \\ + do { \\ + if (!(size >= sizeof(RecType) && size <= remaining && (size % 8) == 0)) { \\ + MGPipeWireProtocolFatal(CallName, size, remaining); \\ + } \\ + } while (0) + +// Returns whether the record was applied. P0 is a SKELETON: every case validates its +// bounds and then reports "not applied", because no applier exists until P5 wires +// MG_Remote/Server/PipeApplier.cpp to the real backend tables. The switch and the opcode +// enum come from the same list, so a call added to the catalogue cannot be forgotten here; +// the default arm is for the opcode that never came from this catalogue at all - a byte +// off a corrupt stream - and it is fatal for the same reason the bounds check is. +inline Bool MGPipeApplyWireRecord(MGPWireOp op, const void* record, Uint64 size, Uint64 remaining) { + (void)record; + switch (op) {""") + for call in calls: + out.append(" case MGPWireOp::%s:" % call.Name) + out.append(" MGP_WIRE_CHECK_BOUNDS(MGPWireRec_%s, \"%s\");" % (call.Name, call.Name)) + out.append(" return false;") + out.append(""" case MGPWireOp::kInvalid: + case MGPWireOp::kOpCount: + default: + MGPipeWireProtocolFatal("", size, remaining); + } +} + +#undef MGP_WIRE_CHECK_BOUNDS""") + return "\n".join(out) + "\n" + + +def gen_verify(payloads): + out = [banner("PipeVerify.inc", "G4: the MOBILEGL_PIPE_VERIFY field-wise comparators.", + "PipeFields.def")] + out.append("""// Field by field, never memcmp over a whole payload: RenderStateParameters is documented +// in DirectGLES.cpp to false-DIFFER on padding under memcmp (harmlessly there, fatally +// here - a comparator with false positives is a comparator nobody reads). Each function +// reports the FIRST differing field by name, which with the draw serial is what the verify +// harness prints. +// +// Floating-point fields are compared by BITS, so a NaN patch level - which +// glPatchParameterfv accepts and ComputePipelineStateHash already hashes bitwise - equals +// itself instead of tripping every draw. + +#include "../PipeFields.def" +""") + out.append("template ") + out.append("struct MGPipeHasFieldVerifier : std::false_type {};\n") + for payload in payloads: + out.append("inline Bool MGPipeVerify(const %s& a, const %s& b, const char** outField);" + % (payload, payload)) + out.append("") + for payload in payloads: + out.append("template <>") + out.append("struct MGPipeHasFieldVerifier<%s> : std::true_type {};" % payload) + out.append("") + out.append("""template +inline Bool MGPipeFieldEqual(const T& a, const T& b) { + if constexpr (MGPipeHasFieldVerifier::value) { + const char* unusedField = nullptr; + return MGPipeVerify(a, b, &unusedField); + } else if constexpr (std::is_floating_point_v) { + return std::memcmp(&a, &b, sizeof(T)) == 0; + } else if constexpr (std::is_scalar_v || std::is_enum_v) { + return a == b; + } else if constexpr (requires(const T& x, const T& y) { x == y; }) { + return a == b; + } else { + // MEMCMP FALLBACK. Only reached by the payload members that are still MG_State / + // MG_Backend value structs (RenderStateParameters, PixelStoreParameters, + // DynamicBackendParameters) and by MGHostSpan. Those are exactly the types P0.5 + // moves into MGPipeValueTypes.h, at which point they get field lists of their own + // and this branch stops being reachable from any payload. + return std::memcmp(&a, &b, sizeof(T)) == 0; + } +} + +template +inline Bool MGPipeFieldEqual(const T (&a)[N], const T (&b)[N]) { + for (SizeT i = 0; i < N; ++i) { + if (!MGPipeFieldEqual(a[i], b[i])) return false; + } + return true; +} + +#define MGP_VERIFY_FIELD(FieldName) \\ + if (!MGPipeFieldEqual(a.FieldName, b.FieldName)) { \\ + if (outField != nullptr) *outField = #FieldName; \\ + return false; \\ + } +""") + for payload in payloads: + out.append("inline Bool MGPipeVerify(const %s& a, const %s& b, const char** outField) {" + % (payload, payload)) + out.append(" MGP_FIELDS_%s(MGP_VERIFY_FIELD)" % payload) + out.append(" return true;") + out.append("}") + out.append("") + out.append("#undef MGP_VERIFY_FIELD") + out.append("") + out.append("inline constexpr SizeT kMGPipeVerifiedPayloadCount = %d;" % len(payloads)) + return "\n".join(out) + "\n" + + +def gen_filled(accessors, calls): + call_names = set(c.Name for c in calls) + out = [banner("PipeFilled.inc", "G5: PipeInputs field ids and the per-verb poison generations.", + "Coverage.def and PipeCalls.def")] + out.append("""// One field id per GLContext accessor the backends actually read (plan B section 6.2: +// PipeInputs is organized by MEMO KEY, not by read point, which is why the field set is +// small and stable across the whole migration). +// +// The poison is a per-verb GENERATION, not a bit. A bitmap cannot see the dangerous case: +// a field filled by the previous DRAW and then read by the glTexSubImage that follows is +// stale, and its bit is already set. So every verb bumps CurrentVerbSerial, filling a +// field stamps it with that serial, and reading a non-sticky field whose stamp is older is +// Fatal{UnmigratedPipeInput} (section 6.2.2). +// +// P0 is the skeleton: the enum, the tables and the assertion helper exist, PipeInputs +// itself lands in P1. +""") + out.append("enum class MGPipeInputField : Uint16 {") + for name, _ in accessors: + out.append(" %s," % name) + out.append(" kFieldCount,") + out.append("};") + out.append("") + out.append("inline constexpr SizeT kMGPipeInputFieldCount = static_cast(MGPipeInputField::kFieldCount);") + out.append("static_assert(kMGPipeInputFieldCount == %d, \"the PipeInputs field set moved\");" % len(accessors)) + out.append("") + out.append("inline constexpr const char* kMGPipeInputFieldNames[kMGPipeInputFieldCount] = {") + for name, _ in accessors: + out.append(" \"%s\"," % name) + out.append("};") + out.append("") + out.append("// Fields whose value is valid ACROSS verbs. Every entry is false in P0 and each") + out.append("// true has to be argued for in P1 when the fillers land: a sticky field is a field") + out.append("// the poison cannot protect.") + out.append("inline constexpr Bool kMGPipeInputFieldSticky[kMGPipeInputFieldCount] = {") + for name, _ in accessors: + out.append(" false, // %s" % name) + out.append("};") + out.append("") + out.append("// Which call is expected to have filled a field by the time a verb reads it. Names") + out.append("// come from Coverage.def, so this table and the coverage table cannot disagree.") + out.append("inline constexpr const char* kMGPipeInputFieldFilledBy[kMGPipeInputFieldCount] = {") + for name, call in accessors: + marker = "" if call in call_names else " // pseudo-call: not filled by a forward record" + out.append(" \"%s\",%s" % (call, marker)) + out.append("};") + out.append("") + out.append("""struct MGPipeFilledState { + Uint64 CurrentVerbSerial; + Uint64 FilledGen[kMGPipeInputFieldCount]; +}; + +[[noreturn]] inline void MGPipeInputPoisonFatal(MGPipeInputField field, const char* verb) { + MGLOG_F("MGPipe: Fatal{UnmigratedPipeInput, \\"%s@%s\\"}", + kMGPipeInputFieldNames[static_cast(field)], verb); + std::abort(); +} + +inline Bool MGPipeInputFieldIsFresh(const MGPipeFilledState& state, MGPipeInputField field) { + const SizeT index = static_cast(field); + return kMGPipeInputFieldSticky[index] ? state.FilledGen[index] != 0 + : state.FilledGen[index] == state.CurrentVerbSerial; +}""") + return "\n".join(out) + "\n" + + +def gen_coverage(accessors, deltas, rows, calls): + call_names = set(c.Name for c in calls) + pseudo = {"kClientResolved", "kReverseChannel", "kStructuralHandle"} + accessor_map = dict(accessors) + delta_map = dict(deltas) + for _, call in accessors + deltas: + if call not in call_names and call not in pseudo: + sys.exit("Coverage.def: %s is not a call in PipeCalls.def and not a pseudo-call" % call) + + mapped = 0 + by_pseudo = {name: 0 for name in pseudo} + unmapped_rows = [] + per_accessor = {} + for row in rows: + call = None + if row["member"] and row["member"] != "-": + call = accessor_map.get(row["member"]) + if call is None: + call = delta_map.get(row["delta"]) + if call is None: + unmapped_rows.append(row) + continue + if call in pseudo: + by_pseudo[call] += 1 + else: + mapped += 1 + key = row["member"] if row["member"] and row["member"] != "-" else row["delta"] + per_accessor.setdefault(key, [call, 0])[1] += 1 + + out = [banner("PipeCoverage.inc", "G6: backend read inventory -> MGPipe call coverage.", + "Coverage.def and scripts/data/backend_read_inventory.md")] + out.append("""// The acceptance rule (plan B section 10.3-5): regenerate, `git diff --exit-code`, and +// ZERO unmapped rows. P0 permits unmapped rows and only counts them; the count below is +// the number the later gate has to drive to zero. +// +// Three pseudo-calls stand for read points that never become a forward record: +// kClientResolved (the frontend answers it), kReverseChannel (it becomes one of the ten +// MGPipeCallbacks) and kStructuralHandle (the row is a signature carrying a +// SharedPtr that becomes an MGPipeHandle parameter). +""") + out.append("struct MGPipeCoverageEntry {") + out.append(" const char* Accessor;") + out.append(" const char* Call;") + out.append(" Uint32 ReadPoints;") + out.append("};") + out.append("") + out.append("inline constexpr MGPipeCoverageEntry kMGPipeCoverage[] = {") + for key in sorted(per_accessor): + call, count = per_accessor[key] + out.append(" {\"%s\", \"%s\", %d}," % (key, call, count)) + out.append("};") + out.append("") + out.append("inline constexpr SizeT kMGPipeCoverageEntryCount = %d;" % len(per_accessor)) + out.append("inline constexpr Uint32 kMGPipeInventoryReadPoints = %d;" % len(rows)) + out.append("inline constexpr Uint32 kMGPipeInventoryMappedToCall = %d;" % mapped) + out.append("inline constexpr Uint32 kMGPipeInventoryClientResolved = %d;" % by_pseudo["kClientResolved"]) + out.append("inline constexpr Uint32 kMGPipeInventoryReverseChannel = %d;" % by_pseudo["kReverseChannel"]) + out.append("inline constexpr Uint32 kMGPipeInventoryStructuralHandle = %d;" + % by_pseudo["kStructuralHandle"]) + out.append("inline constexpr Uint32 kMGPipeInventoryUnmapped = %d;" % len(unmapped_rows)) + out.append("static_assert(kMGPipeCoverageEntryCount == sizeof(kMGPipeCoverage) / sizeof(kMGPipeCoverage[0]));") + out.append("static_assert(kMGPipeInventoryMappedToCall + kMGPipeInventoryClientResolved +") + out.append(" kMGPipeInventoryReverseChannel + kMGPipeInventoryStructuralHandle +") + out.append(" kMGPipeInventoryUnmapped ==") + out.append(" kMGPipeInventoryReadPoints,") + out.append(" \"every inventory row must land in exactly one bucket\");") + return "\n".join(out) + "\n", unmapped_rows, mapped, by_pseudo + + +def gen_span_table(): + out = [banner("PipeSpanTable.inc", "G7: the render-state pipeline subset, by member name.", + "the field list in scripts/gen_pipe.py")] + out.append("""// D-B1 rejected three CSOs and demanded this table instead, so the table needs its own +// completeness trip wire: MG_Test walks every public RenderState setter and asserts that +// the pipeline-subset hash moves IF AND ONLY IF m_pipelineStateVersion moves. That test +// and MGPipeRenderStateSpans.cpp land with P2; what P0 pins is the MEMBER LIST, taken from +// what VulkanRenderer::ComputePipelineStateHash hashes today, so the later offsets are +// derived from a list that was reviewed rather than invented. +// +// Deliberately absent, and each absence is a question P2 has to answer before the chunk +// table freezes: +// - FramebufferSrgb and DepthClamp have NO STORAGE at all (RenderState.cpp's SetCapability +// falls to "not supported currently" and IsCapabilityEnabled returns false), so six +// backend read points are constant false today. Pipeline state or dead capability? +// - ProvokingVertexModeSetting is Vulkan pipeline state but is not hashed today. +// - FrontFaceModeSetting, ClipOrigin and ClipDepthMode are pipeline state on Vulkan and +// are handled elsewhere in the payload path rather than in the memo word. +// +// The complement of this list is the DYNAMIC subset - the half whose whole purpose is that +// glViewport must not mint a new CSO. + +inline constexpr const char* const kMGPipePipelineStateMembers[] = {""") + for member in PIPELINE_STATE_MEMBERS: + out.append(" \"%s\"," % member) + out.append("};") + out.append("inline constexpr SizeT kMGPipePipelineStateMemberCount = %d;" % len(PIPELINE_STATE_MEMBERS)) + out.append("static_assert(kMGPipePipelineStateMemberCount ==") + out.append(" sizeof(kMGPipePipelineStateMembers) / sizeof(kMGPipePipelineStateMembers[0]));") + out.append("") + out.append("// Filled in by MG_Pipe/MGPipeRenderStateSpans.cpp (P2), which computes the offsets") + out.append("// in C++ with offsetof rather than guessing them in python.") + out.append("extern const MGPStateChunk kMGPipePipelineChunks[];") + out.append("extern const MGPStateChunk kMGPipeDynamicChunks[];") + return "\n".join(out) + "\n" + + +def write(path, text, check, changed): + existing = read(path) if os.path.exists(path) else None + if existing == text: + return + changed.append(os.path.relpath(path, REPO_ROOT)) + if not check: + with open(path, "w", encoding="utf-8", newline="\n") as handle: + handle.write(text) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check", action="store_true", + help="do not write; exit 1 if regenerating would change anything") + args = parser.parse_args() + + calls = parse_calls() + payloads = parse_verify_payloads() + accessors, deltas = parse_coverage() + rows = parse_inventory() + + if not os.path.isdir(GENERATED_DIR): + os.makedirs(GENERATED_DIR) + + coverage_text, unmapped, mapped, pseudo = gen_coverage(accessors, deltas, rows, calls) + changed = [] + write(os.path.join(GENERATED_DIR, "PipeTables.inc"), gen_tables(calls), args.check, changed) + write(os.path.join(GENERATED_DIR, "PipeThunks.inc"), gen_thunks(calls), args.check, changed) + write(os.path.join(GENERATED_DIR, "PipeWire.inc"), gen_wire(calls), args.check, changed) + write(os.path.join(GENERATED_DIR, "PipeVerify.inc"), gen_verify(payloads), args.check, changed) + write(os.path.join(GENERATED_DIR, "PipeFilled.inc"), gen_filled(accessors, calls), args.check, changed) + write(os.path.join(GENERATED_DIR, "PipeCoverage.inc"), coverage_text, args.check, changed) + write(os.path.join(GENERATED_DIR, "PipeSpanTable.inc"), gen_span_table(), args.check, changed) + + screen = sum(1 for c in calls if c.IsScreen) + print("gen_pipe: %d calls (%d screen, %d context), %d verify payloads, %d PipeInputs fields" + % (len(calls), screen, len(calls) - screen, len(payloads), len(accessors))) + print("gen_pipe: inventory %d rows: %d -> call, %d client-resolved, %d reverse-channel, " + "%d structural handle, %d UNMAPPED" + % (len(rows), mapped, pseudo["kClientResolved"], pseudo["kReverseChannel"], + pseudo["kStructuralHandle"], len(unmapped))) + for row in unmapped: + print("gen_pipe: UNMAPPED %s:%d %s %s" % (row["file"], row["line"], row["kind"], row["member"])) + + if changed: + if args.check: + print("gen_pipe: OUT OF DATE: %s" % ", ".join(changed), file=sys.stderr) + return 1 + print("gen_pipe: wrote %s" % ", ".join(changed)) + else: + print("gen_pipe: generated files are up to date") + return 0 + + +if __name__ == "__main__": + sys.exit(main())