mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
- MG_Pipe/DirtySurface.def: 73 rows, one per distinct mutator the scanner finds, each answering
"what publishes this". The answer vocabulary is a MGPipeDirty bit name or one of five
non-bit answers, and each of the five is documented in the file's header rather than left to
be inferred: kImmediate, kReverseChannel, kNoBackendRead, kExplicitDestroy and
kPulledEveryVerb. Where a mutator has more than one true answer the row carries the COARSER
one - the one that cannot under-fire.
- gen_pipe_dirty_surface.py --check is the gate and it fails in BOTH directions: an unmapped
mutator renders stale, and a row naming a mutator the scan no longer finds keeps a real hole
looking covered. It also rejects an answer that is neither a documented non-bit answer nor a
bit name read out of Tracker.h's own kMGPipeDirtyNames, so a renamed bit cannot leave a row
silently pointing at nothing.
- --self-test runs three canned negative controls - a withheld mutator, a stale row, a bad
answer - and each must trip; trips == 0 is itself an error, the shape
check_include_closure.py and gen_pipe.py --self-test already use. ROADMAP.md's rule is that
every gate must be able to go red for the reason it exists.
- --summary keeps working unchanged, because the CI file that still calls it belongs to
another package until it lands.
- The human report prints the mapped answer where it printed UNMAPPED.
- FillPoints.def: the verdict on the eight statically over-approximated rows, recorded per
group in the def's own comment. All eight are KEPT and the reason is the same in all three
groups - each row names a concrete backend path (the depth/stencil read emulation's paused
capture, VkClearManager::PreCompensateSrgbClearColor's GL_FRAMEBUFFER_SRGB read, the shader
blit's viewport / provoking vertex / binding-point reads), and the only evidence that could
retire one is dynamic. A corpus that never reaches a path proves nothing about it, and a row
dropped on that basis turns a rare path into Fatal{UnmigratedPipeInput} in a shipped build.
The contract's new FramebufferSrgb storage in fact makes one of the eight MORE load-bearing
than it was, not less: it used to read a compile-time constant.
335 lines
24 KiB
Modula-2
335 lines
24 KiB
Modula-2
// MobileGL - MobileGL/MG_Pipe/FillPoints.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 per-verb fill points of the PipeInputs strangler (ARCHITECTURE.md 9.2, phase A; the
|
|
// P1 brief D7). Three hand-maintained lists, read by scripts/gen_pipe.py (G5b) into
|
|
// generated/PipeFillPoints.inc:
|
|
//
|
|
// MGP_FILL_VERB_LIST every verb the frontend calls through GLFunctionsTable, with its class
|
|
// MGP_FILL_CLASS_LIST the verb classes
|
|
// MGP_FILL_FIELD_LIST the may-read table: which PipeInputs fields a class of verb may read
|
|
//
|
|
// The verb set IS the function-pointer member set of MG_Backend::GLFunctionsTable
|
|
// (MG_Backend/BackendObject.h), in declaration order: gen_pipe.py parses that struct and
|
|
// refuses a row set that is not exactly its member set in that order, so the MGPipeVerb enum
|
|
// and the table cannot drift apart. MG_Impl spells MGP_FILL(Verb) immediately before every
|
|
// call through the table (83 statements over these 69 verbs); Present and SetSwapInterval go
|
|
// through BackendObject virtuals and read no frontend state, so they are not verbs here.
|
|
//
|
|
// The seven sticky fields (Coverage.def, MGP_COVERAGE_STICKY_LIST) are implicit in every
|
|
// class and are not listed. The verify lane is the oracle for this table: a
|
|
// Fatal{UnmigratedPipeInput, "Field@Verb"} found there is fixed by adding the (class, field)
|
|
// row, never by marking the field sticky.
|
|
//
|
|
// ---------------------------------------------------------------------------------------
|
|
// THE VERDICT ON THE EIGHT STATICALLY OVER-APPROXIMATED ROWS (P2 brief C.1, MEASUREMENTS.md
|
|
// section 4). Every one of them is KEPT, and the reason is the same in all three groups: the
|
|
// row is not a guess, it names a concrete backend path, and the only evidence that could
|
|
// retire it is DYNAMIC - a corpus that never reaches the path proves nothing, because a row
|
|
// removed on that basis turns a rare path into Fatal{UnmigratedPipeInput} in a shipped build.
|
|
//
|
|
// kReadback + IsTransformFeedbackActive / IsTransformFeedbackPaused
|
|
// KEPT. The depth/stencil read emulation draws (ScopedEmulationDrawState, DirectGLES.cpp)
|
|
// and pauses an active capture around its own draw, so a glReadPixels of a depth or
|
|
// stencil attachment reads the transform-feedback state exactly as a draw does. Reached
|
|
// only when the emulation is armed, which is a driver-shaped decision, so no desktop
|
|
// corpus can decide it.
|
|
//
|
|
// kTextureOp + IsCapabilityEnabled, kDispatch + IsCapabilityEnabled
|
|
// KEPT. Magma's GenerateMipmap materialises a texture's queued clear before it blits and
|
|
// PrepareStorageImageTextures does the same for every storage image a dispatch writes;
|
|
// both go through VkClearManager::PreCompensateSrgbClearColor, which reads
|
|
// GL_FRAMEBUFFER_SRGB. The P2 contract gave that capability real storage for the first
|
|
// time, so this row went from reading a compile-time constant to reading real state -
|
|
// which is the opposite of a row that could be dropped.
|
|
//
|
|
// kBlitOrCopy / kTextureOp + the shader blit's viewport and vertex/buffer bindings
|
|
// (GetViewportIndexed, GetDepthRangeIndexed, GetProvokingVertexMode, GetBufferBindingPoint)
|
|
// KEPT. TryBlitToDefaultFramebufferWithShader is a real draw of a backend-owned helper
|
|
// program: ApplyGLViewportState -> ComputeGLViewport reads viewport 0 and its depth range,
|
|
// GetOrCreateBlitPipeline -> SelectProvokingVertexMode reads the provoking vertex, and
|
|
// BindProgramUniformBuffers' block resolvers read the frontend binding points. It is taken
|
|
// when a blit's destination is the default framebuffer and the driver cannot do it
|
|
// natively - again a driver-shaped decision.
|
|
//
|
|
// What WOULD retire a row: the poison build already answers "was this field read at this
|
|
// verb" exactly (MOBILEGL_PIPE_POISON_OMIT withholds one field's stamp for one verb and a
|
|
// read of it aborts naming the pair). Turning that into a retirement gate means running the
|
|
// omission across the full CTS caselist on both devices, not the desktop corpus, and that is
|
|
// recorded as P3a work rather than done here on evidence that cannot support it.
|
|
// ---------------------------------------------------------------------------------------
|
|
//
|
|
// gen_pipe.py's block regexes end at a blank line: keep the empty line after each macro.
|
|
//
|
|
// clang-format off
|
|
|
|
// X(Verb, Class) - one row per function-pointer member of MG_Backend::GLFunctionsTable (BackendObject.h),
|
|
// in declaration order. gen_pipe.py parses that struct and refuses a row set that is not exactly its member set.
|
|
#define MGP_FILL_VERB_LIST(X) \
|
|
X(DrawArrays, kDraw) \
|
|
X(DrawElements, kDraw) \
|
|
X(DrawElementsBaseVertex, kDraw) \
|
|
X(MultiDrawArrays, kDraw) \
|
|
X(MultiDrawElements, kDraw) \
|
|
X(MultiDrawElementsBaseVertex, kDraw) \
|
|
X(MultiDrawElementsIndirect, kDraw) \
|
|
X(MultiDrawArraysIndirect, kDraw) \
|
|
X(MultiDrawElementsIndirectCount, kDraw) \
|
|
X(MultiDrawArraysIndirectCount, kDraw) \
|
|
X(DrawRangeElementsBaseVertex, kDraw) \
|
|
X(DrawRangeElements, kDraw) \
|
|
X(DrawElementsInstancedBaseVertexBaseInstance, kDraw) \
|
|
X(DrawElementsInstancedBaseVertex, kDraw) \
|
|
X(DrawElementsInstancedBaseInstance, kDraw) \
|
|
X(DrawElementsInstanced, kDraw) \
|
|
X(DrawArraysInstancedBaseInstance, kDraw) \
|
|
X(DrawArraysInstanced, kDraw) \
|
|
X(DrawElementsIndirect, kDraw) \
|
|
X(DrawArraysIndirect, kDraw) \
|
|
X(Clear, kClear) \
|
|
X(ClearBufferfi, kClear) \
|
|
X(ClearBufferfv, kClear) \
|
|
X(ClearBufferuiv, kClear) \
|
|
X(ClearBufferiv, kClear) \
|
|
X(ClearNamedFramebufferfv, kClear) \
|
|
X(ClearNamedFramebufferfi, kClear) \
|
|
X(ClearNamedFramebufferiv, kClear) \
|
|
X(ClearNamedFramebufferuiv, kClear) \
|
|
X(BlitFramebuffer, kBlitOrCopy) \
|
|
X(BlitNamedFramebuffer, kBlitOrCopy) \
|
|
X(CopyTexImage2D, kBlitOrCopy) \
|
|
X(CopyTexSubImage2D, kBlitOrCopy) \
|
|
X(CopyImageSubData, kBlitOrCopy) \
|
|
X(GenerateMipmap, kTextureOp) \
|
|
X(ReadPixels, kReadback) \
|
|
X(GetTexImage, kReadback) \
|
|
X(GetTextureImage, kReadback) \
|
|
X(DispatchCompute, kDispatch) \
|
|
X(DispatchComputeIndirect, kDispatch) \
|
|
X(MemoryBarrier, kQuery) \
|
|
X(MemoryBarrierByRegion, kQuery) \
|
|
X(BindImageTexture, kTextureOp) \
|
|
X(GetIntegeri_v, kQuery) \
|
|
X(ShaderStorageBlockBinding, kProgramOp) \
|
|
X(FenceSync, kQuery) \
|
|
X(ClientWaitSync, kQuery) \
|
|
X(WaitSync, kQuery) \
|
|
X(DeleteSync, kQuery) \
|
|
X(GetSyncStatus, kQuery) \
|
|
X(IsTimerQuerySupported, kQuery) \
|
|
X(BeginTimeElapsedQuery, kQuery) \
|
|
X(EndTimeElapsedQuery, kQuery) \
|
|
X(QueryCounterTimestamp, kQuery) \
|
|
X(IsQueryResultAvailable, kQuery) \
|
|
X(GetQueryResult64, kQuery) \
|
|
X(DeleteBackendQuery, kQuery) \
|
|
X(BeginOcclusionQuery, kQuery) \
|
|
X(EndOcclusionQuery, kQuery) \
|
|
X(BeginXfbPrimitivesQuery, kQuery) \
|
|
X(EndXfbPrimitivesQuery, kQuery) \
|
|
X(PatchParameteri, kQuery) \
|
|
X(BeginTransformFeedback, kXfbSpan) \
|
|
X(EndTransformFeedback, kXfbSpan) \
|
|
X(PauseTransformFeedback, kXfbSpan) \
|
|
X(ResumeTransformFeedback, kXfbSpan) \
|
|
X(BindTransformFeedback, kXfbSpan) \
|
|
X(DeleteTransformFeedback, kXfbSpan) \
|
|
X(GetGpuTimestampNs, kQuery)
|
|
|
|
// X(Class) - the nine verb classes (ARCHITECTURE.md:153 names eight; kProgramOp is split out because
|
|
// ShaderStorageBlockBinding is the one non-draw verb that syncs Espryt's render state and textures).
|
|
#define MGP_FILL_CLASS_LIST(X) \
|
|
X(kDraw) X(kDispatch) X(kClear) X(kBlitOrCopy) X(kTextureOp) X(kReadback) X(kXfbSpan) X(kProgramOp) X(kQuery)
|
|
|
|
// X(Class, Field) - the may-read table. A field named here is filled and stamped at every verb of the class;
|
|
// a read of a field NOT named here is Fatal{UnmigratedPipeInput, "Field@Verb"} in a poison build.
|
|
// Derived from the verified reachability of every backend read (both backends, union), P1 brief D7.
|
|
#define MGP_FILL_FIELD_LIST(X) \
|
|
/* kDraw: every draw entry of both backends */ \
|
|
X(kDraw, GetBoundVertexArray) \
|
|
X(kDraw, GetProgramForDraw) \
|
|
X(kDraw, GetBufferBindingSlot) \
|
|
X(kDraw, GetBufferBindingPoint) \
|
|
X(kDraw, GetTouchedBufferBindingPointCount) \
|
|
X(kDraw, GetTextureUnitObject) \
|
|
X(kDraw, GetTextureContextId) \
|
|
X(kDraw, GetTextureBindGeneration) \
|
|
X(kDraw, GetMaxTouchedTextureUnit) \
|
|
X(kDraw, GetSamplingResolutionGeneration) \
|
|
X(kDraw, GetImageTextureBinding) \
|
|
X(kDraw, GetCurrentVertexAttribute) \
|
|
X(kDraw, GetRenderStateParameters) \
|
|
X(kDraw, GetRenderStateParametersVersion) \
|
|
X(kDraw, GetPipelineStateVersion) \
|
|
X(kDraw, GetViewport) \
|
|
X(kDraw, GetViewportIndexed) \
|
|
X(kDraw, GetDepthRangeIndexed) \
|
|
X(kDraw, GetScissorBox) \
|
|
X(kDraw, IsCapabilityEnabled) \
|
|
X(kDraw, IsCapabilityEnabledIndexed) \
|
|
X(kDraw, GetBlendColor) \
|
|
X(kDraw, GetBlendFuncIndexed) \
|
|
X(kDraw, GetBlendEquationIndexed) \
|
|
X(kDraw, GetColorMaskIndexed) \
|
|
X(kDraw, GetLogicOp) \
|
|
X(kDraw, GetDepthFunc) \
|
|
X(kDraw, GetDepthMask) \
|
|
X(kDraw, GetStencilState) \
|
|
X(kDraw, GetCullFaceMode) \
|
|
X(kDraw, GetPolygonModeFront) \
|
|
X(kDraw, GetPolygonOffsetFactor) \
|
|
X(kDraw, GetPolygonOffsetUnits) \
|
|
X(kDraw, GetLineWidth) \
|
|
X(kDraw, GetMinSampleShadingValue) \
|
|
X(kDraw, GetProvokingVertexMode) \
|
|
X(kDraw, GetPatchVertices) \
|
|
X(kDraw, GetPatchDefaultOuterLevel) \
|
|
X(kDraw, GetPatchDefaultInnerLevel) \
|
|
X(kDraw, GetPrimitiveRestartIndex) \
|
|
X(kDraw, GetFramebufferBindingSlot) \
|
|
X(kDraw, IsTransformFeedbackActive) \
|
|
X(kDraw, IsTransformFeedbackPaused) \
|
|
X(kDraw, GetTransformFeedbackProgram) \
|
|
X(kDraw, GetTransformFeedbackGeneration) \
|
|
X(kDraw, GetBoundTransformFeedbackLifetimeId) \
|
|
X(kDraw, GetTransformFeedbackCapturedVertices) \
|
|
/* kDispatch: the patch fields are Espryt's SyncCurrentProgram -> */ \
|
|
/* AttachPassthroughTessControlStage (Managers.cpp) */ \
|
|
X(kDispatch, GetProgramForDispatch) \
|
|
X(kDispatch, GetBufferBindingSlot) \
|
|
X(kDispatch, GetBufferBindingPoint) \
|
|
X(kDispatch, GetTouchedBufferBindingPointCount) \
|
|
X(kDispatch, GetTextureUnitObject) \
|
|
X(kDispatch, GetTextureContextId) \
|
|
X(kDispatch, GetTextureBindGeneration) \
|
|
X(kDispatch, GetMaxTouchedTextureUnit) \
|
|
X(kDispatch, GetSamplingResolutionGeneration) \
|
|
X(kDispatch, GetImageTextureBinding) \
|
|
X(kDispatch, GetFramebufferBindingSlot) \
|
|
/* Magma's PrepareStorageImageTextures materialises a queued clear for every */ \
|
|
/* storage image the dispatch writes, and the clear pre-compensates its colour */ \
|
|
/* against GL_FRAMEBUFFER_SRGB (VkClearManager::PreCompensateSrgbClearColor). */ \
|
|
X(kDispatch, IsCapabilityEnabled) \
|
|
X(kDispatch, GetPatchVertices) \
|
|
X(kDispatch, GetPatchDefaultOuterLevel) \
|
|
X(kDispatch, GetPatchDefaultInnerLevel) \
|
|
/* kClear */ \
|
|
X(kClear, GetRenderStateParameters) \
|
|
X(kClear, GetRenderStateParametersVersion) \
|
|
X(kClear, GetViewport) \
|
|
X(kClear, IsCapabilityEnabled) \
|
|
X(kClear, GetFramebufferBindingSlot) \
|
|
X(kClear, GetClearColor) \
|
|
X(kClear, GetClearDepth) \
|
|
X(kClear, GetClearStencil) \
|
|
X(kClear, GetScissorBox) \
|
|
X(kClear, GetColorMaskIndexed) \
|
|
X(kClear, GetDepthMask) \
|
|
X(kClear, GetStencilState) \
|
|
X(kClear, GetTextureUnitObject) \
|
|
X(kClear, GetTextureContextId) \
|
|
X(kClear, GetSamplingResolutionGeneration) \
|
|
X(kClear, GetTextureBindGeneration) \
|
|
X(kClear, GetMaxTouchedTextureUnit) \
|
|
X(kClear, GetImageTextureBinding) \
|
|
/* kBlitOrCopy */ \
|
|
X(kBlitOrCopy, GetFramebufferBindingSlot) \
|
|
X(kBlitOrCopy, IsCapabilityEnabled) \
|
|
X(kBlitOrCopy, GetScissorBox) \
|
|
X(kBlitOrCopy, IsTransformFeedbackActive) \
|
|
X(kBlitOrCopy, IsTransformFeedbackPaused) \
|
|
X(kBlitOrCopy, GetRenderStateParameters) \
|
|
X(kBlitOrCopy, GetRenderStateParametersVersion) \
|
|
X(kBlitOrCopy, GetViewport) \
|
|
X(kBlitOrCopy, GetActiveTextureUnit) \
|
|
X(kBlitOrCopy, GetTextureUnitObject) \
|
|
X(kBlitOrCopy, GetTextureContextId) \
|
|
X(kBlitOrCopy, GetSamplingResolutionGeneration) \
|
|
X(kBlitOrCopy, GetTextureBindGeneration) \
|
|
X(kBlitOrCopy, GetMaxTouchedTextureUnit) \
|
|
X(kBlitOrCopy, GetImageTextureBinding) \
|
|
X(kBlitOrCopy, GetColorMaskIndexed) \
|
|
X(kBlitOrCopy, GetDepthMask) \
|
|
X(kBlitOrCopy, GetStencilState) \
|
|
/* Magma's shader blit to the default framebuffer */ \
|
|
/* (TryBlitToDefaultFramebufferWithShader) is a real draw of a backend-owned */ \
|
|
/* helper program: it sets the dynamic viewport through ApplyGLViewportState */ \
|
|
/* -> ComputeGLViewport (viewport 0 and its depth range), picks the pipeline's */ \
|
|
/* provoking vertex through GetOrCreateBlitPipeline -> SelectProvokingVertexMode, */ \
|
|
/* and binds the helper's descriptors through BindProgramUniformBuffers, whose */ \
|
|
/* buffer-block resolvers read the frontend binding points. */ \
|
|
X(kBlitOrCopy, GetViewportIndexed) \
|
|
X(kBlitOrCopy, GetDepthRangeIndexed) \
|
|
X(kBlitOrCopy, GetProvokingVertexMode) \
|
|
X(kBlitOrCopy, GetBufferBindingPoint) \
|
|
/* kTextureOp */ \
|
|
X(kTextureOp, GetActiveTextureUnit) \
|
|
X(kTextureOp, GetTextureUnitObject) \
|
|
X(kTextureOp, GetImageTextureBinding) \
|
|
X(kTextureOp, GetTextureContextId) \
|
|
X(kTextureOp, GetSamplingResolutionGeneration) \
|
|
X(kTextureOp, GetTextureBindGeneration) \
|
|
X(kTextureOp, GetMaxTouchedTextureUnit) \
|
|
/* Magma's GenerateMipmap materialises the texture's queued clear before it */ \
|
|
/* blits (MaterializePendingClearForTexture -> PreCompensateSrgbClearColor, */ \
|
|
/* which reads GL_FRAMEBUFFER_SRGB), and a depth texture takes the shader path */ \
|
|
/* (GenerateDepthMipmapWithShader -> BindProgramUniformBuffers), whose sampler */ \
|
|
/* resolver reads the draw framebuffer for the feedback-loop check and whose */ \
|
|
/* buffer-block resolvers read the frontend binding points. */ \
|
|
X(kTextureOp, IsCapabilityEnabled) \
|
|
X(kTextureOp, GetFramebufferBindingSlot) \
|
|
X(kTextureOp, GetBufferBindingPoint) \
|
|
/* kReadback */ \
|
|
X(kReadback, GetPixelStoreParameters) \
|
|
X(kReadback, GetBufferBindingSlot) \
|
|
X(kReadback, GetFramebufferBindingSlot) \
|
|
X(kReadback, GetActiveTextureUnit) \
|
|
X(kReadback, GetTextureUnitObject) \
|
|
X(kReadback, GetClampReadColor) \
|
|
X(kReadback, IsCapabilityEnabled) \
|
|
X(kReadback, GetRenderStateParameters) \
|
|
X(kReadback, GetRenderStateParametersVersion) \
|
|
X(kReadback, GetViewport) \
|
|
X(kReadback, GetTextureContextId) \
|
|
X(kReadback, GetSamplingResolutionGeneration) \
|
|
X(kReadback, GetTextureBindGeneration) \
|
|
X(kReadback, GetMaxTouchedTextureUnit) \
|
|
X(kReadback, GetImageTextureBinding) \
|
|
/* The depth/stencil read emulation draws (ScopedEmulationDrawState, */ \
|
|
/* DirectGLES.cpp) and pauses an active capture around its own draw, so a */ \
|
|
/* readback reads the transform-feedback state exactly as a draw does. */ \
|
|
X(kReadback, IsTransformFeedbackActive) \
|
|
X(kReadback, IsTransformFeedbackPaused) \
|
|
/* kXfbSpan */ \
|
|
X(kXfbSpan, GetTransformFeedbackProgram) \
|
|
X(kXfbSpan, GetBufferBindingPoint) \
|
|
X(kXfbSpan, GetTouchedBufferBindingPointCount) \
|
|
X(kXfbSpan, GetTransformFeedbackCapturedVertices) \
|
|
X(kXfbSpan, IsTransformFeedbackActive) \
|
|
X(kXfbSpan, IsTransformFeedbackPaused) \
|
|
X(kXfbSpan, GetTransformFeedbackGeneration) \
|
|
X(kXfbSpan, GetBoundTransformFeedbackLifetimeId) \
|
|
/* kProgramOp: ShaderStorageBlockBinding syncs Espryt's render state and textures */ \
|
|
X(kProgramOp, GetRenderStateParameters) \
|
|
X(kProgramOp, GetRenderStateParametersVersion) \
|
|
X(kProgramOp, GetViewport) \
|
|
X(kProgramOp, IsCapabilityEnabled) \
|
|
X(kProgramOp, GetFramebufferBindingSlot) \
|
|
X(kProgramOp, GetTextureUnitObject) \
|
|
X(kProgramOp, GetTextureContextId) \
|
|
X(kProgramOp, GetSamplingResolutionGeneration) \
|
|
X(kProgramOp, GetTextureBindGeneration) \
|
|
X(kProgramOp, GetMaxTouchedTextureUnit) \
|
|
X(kProgramOp, GetImageTextureBinding) \
|
|
/* kQuery: Magma's transform feedback query end reads the paused counter */ \
|
|
/* (DirectVulkan.cpp); every other verb in the class reads nothing and */ \
|
|
/* its fill is a serial bump */ \
|
|
X(kQuery, GetTransformFeedbackPausedPrimitiveCounter)
|
|
|
|
// clang-format on
|