Initialize the desktop replay Request from the three iterationRP environment flags before loading MobileGL. Without this, the Request defaults caused the replay core to unset CI's exported flags, leaving all repairs disabled despite the workflow configuration.
Run the lavapipe Program 203 golden test with the same subgroup scratch, derived topology, and missing-barrier repairs as the iterationRP retrace matrix. This keeps the prerequisite integration job from failing before retrace jobs can start.
Program 203 reuses prefixSumCache for a second subgroup reduction before every workgroup invocation has consumed the first result. Add a fingerprint-gated SPIR-V pass that inserts the missing Workgroup acquire-release barrier while preserving native subgroup operations.
Keep the repair opt-in behind MOBILEGL_ITERATIONRP_FIX_BARRIER, cover insertion, pass-through, and idempotence, and enable it together with the existing iterationRP subgroup repairs for the matching Linux and Android CI retraces.
Add a deterministic iterationRP Program 203 fixture that dispatches the original shader and compares every RG16F texel against fixed half-float golden bits. This catches both a wrong exposure result and collateral writes without retaining a serial reference shader.
Make MobileGLIntegrationTest runnable as a standalone Android executable by linking the shared MobileGL library and backing EGL with an AImageReader window; desktop keeps its static-library pbuffer path.
Validation: Adreno 830 passes with 0/262656 mismatches; lavapipe reproduces the current reduction defect with 1/262656 mismatches at the exposure texel.
Final audit round over the DirectGLES scratch/shadow mechanisms; three verified
defects fixed:
- ~ScopedEmulationDrawState restored the APPLICATION's per-buffer colour masks,
not what SyncRenderState actually pushed: a widened attachment's alpha-off
doctoring (g_syncedColorMaskAlphaWidenMask) was dropped while the memo still
claimed it applied, so the next sync early-outed and draws wrote fragment
alpha into the widened buffer - breaking the stored-alpha==1.0 invariant the
widen discipline exists to protect. The restore now re-applies the doctoring.
- The same restore loop gated on the core glColorMaski name only, while the sync
push falls back to glColorMaskiEXT/OES: EXT/OES-only devices were left holding
buffer 0's mask broadcast across every draw buffer with the shadow recording
the divergent set (never repaired). The restore now uses the same three-way
pointer fallback.
- ResolveThenBlit ran its resolve-into-scratch staging blit under the
application's scissor: a box not covering the scratch-origin rect clipped the
resolve silently (no GL error), and the second blit then copied stale scratch
renderbuffer texels into the destination. The staging blit now runs scissor-off
(shadow-tracked, like ScopedScissorDisable); the caller-visible blit keeps its
native scissor semantics.
Six more verified defects from the residual memo/cache mechanisms:
- Program resource cache (DirectVulkan reflection): glShaderStorageBlockBinding
deliberately does not bump the backend state version, and the SSO pipeline
composite is unnamed so the by-name in-place patch can never reach its slot -
the composite kept serving pre-rebind SSBO bindings. The cache now keys on the
program's block-binding version; a binding-only change re-applies the overrides
by name instead of re-running spirv-reflect. SetShaderStorageBlockBinding also
gains the equality bail-out its uniform-block sibling has, so the composite
mirror's replay stops churning the version every draw.
- LinkProgram's allowVSOnlyPrograms function-static latch never set its own
initialized flag (dead memo, re-read every call) - and completing it would have
frozen a per-backend capability across re-initialization. Replaced with a fresh
per-link read from the null-checked active backend.
- Query object registry: drained at full library teardown (DestroyAllQueryObjects,
mirroring DestroyAllSyncObjects) - undeleted queries and their backend wrappers
leaked across Destroy/Initialize cycles, stale ids stayed IsQuery == GL_TRUE in
the re-initialized library, and a later delete could hand the old backend's
wrapper to a different backend's DeleteBackendQuery.
- Converted vertex streams and the host-side EBO max-index scan now SyncGpuWrites
before reading the coherent mapping: XFB/SSBO/image writes are merely recorded
at that point, so the conversion read pre-write bytes (the restart-index
rewrite already synced; these two host reads did not).
- Zero-stride converted bindings: both converters rejected stride 0, making the
factory's documented single-element conversion unreachable and silently
dropping every draw using such a binding; the stride is substituted with the
element size for the one-element case.
- DemoteFloat64Pass block relayout: measurement queued into the module eagerly,
so a mid-struct failure left a half-relaid-out block (compacted offsets before
the failing member, 64-bit offsets after) while claiming the block was left
alone. Decoration writes are now collected and committed only when the whole
block measures successfully.
Audit of every memoization implementation; sixteen verified defects fixed:
DirectGLES backend:
- Broadcast draw-buffer memo: cleared at MakeCurrent/DestroyEGLContext like its
sibling shadows; its identity+version key is only monotonic within one GLContext,
so a library teardown + re-init could false-hit on a recycled FBO address.
- Backend texture id re-mint (RecreateBackendTexture) now bumps an attachment
generation that the SyncCurrentFBO gate and every FBO twin compare, so driver
FBOs re-attach instead of keeping the deleted texture name; the attachment walk
re-enters until the generation is quiescent (a walk itself can re-mint).
- Buffer id re-mint (persistent-map adoption, immutable-store retire) now bumps a
generation the VAO twin sync compares, forcing a full re-emit of the baked
glVertexAttribPointer / element-array bindings that frontend versions cannot see.
- VAO element-array sync memo: bound-object identity joins the wrapping Uint16
slot version (same pairing the ResolvedDrawBuffers IBO memo already uses).
DirectVulkan backend:
- EBO slice memo gains the mapped-buffer guard its vertex-binding sibling has: a
shadow-backed persistent map mutates with no epoch bump, so a hit must decline.
- VkClearManager::MergeClearPayload keeps colorEncoding/colorInt/colorUint with
the color, so deferred glClearBufferiv/uiv no longer degrade to all-zero float.
- GetOrCreateComputePipeline no longer memoizes a failed creation (same contract
as PipelineFactory): a transient driver failure was permanently disabling every
dispatch of that program.
- Explicit-LOD-0 verdict memo keys on the sampling-resolution generation; sampler
filter/aniso/LOD setters bump only that counter, so the old key served a stale
verdict (wrong SPIR-V variant) after glTexParameter/glSamplerParameter changes.
- SetupDraw fast path declines instead of re-arming on a moved sampling-resolution
generation (the snapshot bakes the LOD verdict into its pipeline), and
recomputes the XfbCapture bit so the first draw after glBeginTransformFeedback
cannot bind the undecorated variant and silently capture nothing.
- VertexInputStateFactory eviction epoch is drawn from a process-wide source: VAO
state-pointer memos outlive the factory across renderer recreation, and a fresh
factory restarting at epoch 1 would dereference a dead factory's entry.
- Cached render passes re-read the live renderbuffer clear payload at begin (the
clear VALUE is not in the pass hash; the entry's inline snapshot replayed the
creation-time color and dropped the newly queued one).
- FramebufferObject gains a never-reused lifetime id, keyed into the render-pass
fast-path memo and the SetupDraw snapshot beside the raw pointer + Uint16
version pair, which address reuse plus fresh version counts could equal.
- SyncTextureResource's preserved-content image goes through the deferred-release
ring on both failure paths instead of a synchronous destructor under the GPU.
MG_State frontend:
- Layer-1 compile memo is env-disciplined like layers 2/3: a node computed against
a dead CompileEnv (e.g. pre-capability fallback limits) no longer answers
glCompileShader forever once the environment's content changes.
- Pipeline composite cache rebuilds from each stage program's last-link shader
snapshot (new LinkedShaderRef list + pinned link inputs) instead of the live
attach list and current compile nodes: post-link glAttachShader/glCompileShader
must not leak into the composite while the (lifetimeId, linkVersion) signature
still hits - GL's "as last linked" rule.