From 9c773182bbb44e2c88b100049c214cf426a70977 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 5 Sep 2026 14:07:33 -0400 Subject: [PATCH] [Feat] (MGPipe): land the interface skeleton - the complete call catalogue, the payload PODs and the seven generators - Plan B section 4 makes the frontend/backend boundary explicit and gives the backend its own state machine. This is the P0 deliverable of section 11: the whole catalogue exists from day one, placeholders included, because the wire opcode is a call's position in PipeCalls.def and record numbering must never churn. - MG_Pipe/PipeCalls.def is the single source of truth: 68 unique calls as X(Name, Payload, Class, Flags). Its header reconciles that number with the plan's headline counts (section 4.4, appendix A), which double count - "CSO 15" names bind_sampler_states and set_sampler_views that the "set_* 17" list also names, "screen 14" tabulates the query family that section 4.3 assigns to the context, and the "transfer 12" row enumerates 11 calls. Each reconciliation is written down next to the count rather than resolved silently. - MGPipeHandles.h: the 8-byte {slot, gen} pair, dense per-kind slots, the reserved null and default-framebuffer handles, and the ShaderCso composite band (sections 4.2, 5.6.3). The two generations are documented as strictly separate, with the interface rule that no call may require the client to know MGGen. - MGPipeTypes.h: every payload of section 4.5 as a flat POD with explicit padding, a trivial-copyability assertion and an exact sizeof assertion, because the wire records are memcpy'd and a field silently changing width is a protocol break no test would see. MGPCaps embeds DynamicBackendParameters by inclusion so a caps field added there needs no second edit here; its assertion is stated as a composition because that struct still carries SizeT. ResidualValueBlock is pinned at MGL_RESIDUAL_BLOCK_SIZE 1248, the ratchet that only ever goes down and reaches static_assert(... == 0) in P13 (section 6.3). - MGPipeHostSpan.h keeps the one shape that changes with the transport isolated behind one predictable branch, with the kFromServerIndexMirror sentinel D-B7 needs. - MGPipeCallbacks.h names the reverse channel as ten callbacks plus the forward terminator in the context table, replacing 95 poke sites across 17 methods (section 7.1). - scripts/gen_pipe.py runs G1-G7 off those .def files. G1 asserts each table is EXACTLY its call count of function pointers; G3 pads every wire record to the stream's 8-byte granularity and checks size >= sizeof && size <= remaining && size % 8 == 0 before dispatch, fatally; G4 compares field by field (padding excluded, floats by bits) because a comparator with false positives is one nobody reads - DirectGLES.cpp says the same thing about its own memcmp of RenderStateParameters; G5 turns the accessor list into per-verb poison generations rather than a written-once bitmap, which is the only version that can see a field left over from the previous draw (section 6.2.2); G6 joins the 477 read points of the vendored backend_read_inventory.md against Coverage.def and reports 0 UNMAPPED (299 to a call, 167 signatures that become handle parameters, 6 reverse channel, 5 client-resolved); G7 pins the pipeline subset BY MEMBER NAME from what VulkanRenderer::ComputePipelineStateHash hashes today, computing no offsets in python. - The generated files are committed so the build never depends on python; CI regenerates and diffs them. --- CMakeLists.txt | 4 + MobileGL/MG_Pipe/Coverage.def | 106 +++ MobileGL/MG_Pipe/MGPipe.h | 93 ++ MobileGL/MG_Pipe/MGPipeCallbacks.h | 61 ++ MobileGL/MG_Pipe/MGPipeHandles.h | 98 ++ MobileGL/MG_Pipe/MGPipeHostSpan.h | 57 ++ MobileGL/MG_Pipe/MGPipeTypes.h | 754 ++++++++++++++++ MobileGL/MG_Pipe/PipeCalls.def | 148 ++++ MobileGL/MG_Pipe/PipeFields.def | 235 +++++ MobileGL/MG_Pipe/generated/PipeCoverage.inc | 108 +++ MobileGL/MG_Pipe/generated/PipeFilled.inc | 308 +++++++ MobileGL/MG_Pipe/generated/PipeSpanTable.inc | 67 ++ MobileGL/MG_Pipe/generated/PipeTables.inc | 105 +++ MobileGL/MG_Pipe/generated/PipeThunks.inc | 290 ++++++ MobileGL/MG_Pipe/generated/PipeVerify.inc | 565 ++++++++++++ MobileGL/MG_Pipe/generated/PipeWire.inc | 885 +++++++++++++++++++ scripts/data/backend_read_inventory.md | 626 +++++++++++++ scripts/gen_pipe.py | 638 +++++++++++++ 18 files changed, 5148 insertions(+) create mode 100644 MobileGL/MG_Pipe/Coverage.def create mode 100644 MobileGL/MG_Pipe/MGPipe.h create mode 100644 MobileGL/MG_Pipe/MGPipeCallbacks.h create mode 100644 MobileGL/MG_Pipe/MGPipeHandles.h create mode 100644 MobileGL/MG_Pipe/MGPipeHostSpan.h create mode 100644 MobileGL/MG_Pipe/MGPipeTypes.h create mode 100644 MobileGL/MG_Pipe/PipeCalls.def create mode 100644 MobileGL/MG_Pipe/PipeFields.def create mode 100644 MobileGL/MG_Pipe/generated/PipeCoverage.inc create mode 100644 MobileGL/MG_Pipe/generated/PipeFilled.inc create mode 100644 MobileGL/MG_Pipe/generated/PipeSpanTable.inc create mode 100644 MobileGL/MG_Pipe/generated/PipeTables.inc create mode 100644 MobileGL/MG_Pipe/generated/PipeThunks.inc create mode 100644 MobileGL/MG_Pipe/generated/PipeVerify.inc create mode 100644 MobileGL/MG_Pipe/generated/PipeWire.inc create mode 100644 scripts/data/backend_read_inventory.md create mode 100644 scripts/gen_pipe.py 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())