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.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MsqQQF7ugn7MqZXcmnmz1z
iterationRP's Program 203 declares shared vec2 prefixSumCache[32] for a
512-invocation workgroup indexed by gl_SubgroupID; any device narrower
than 16 lanes partitions into more than 32 subgroups and the pack writes
shared memory out of bounds (heap corruption on lavapipe's CPU
rasterizer, ssim 0.028 on the CI retrace). Fix it where the fault lies -
in the fixture - and keep the GL contract sound everywhere else:
- FixIterationRPSubgroupScratchPass: fingerprint-gated SPIR-V pass that
grows exactly that array to ceil(invocations/width) entries on sub-16-lane devices; every other module passes through byte-identical.
- DeriveNumSubgroupsPass stays default-on for the Adreno topology bug
and is made spec-sound: pipelines request REQUIRE_FULL_SUBGROUPS
whenever the workgroup shape makes the flag legal (computeFullSubgroups
enabled, local_size_x a multiple of the native width, subgroup count
within maxComputeWorkgroupSubgroups).
- EmulateSubgroupsPass: 32-lane virtual-subgroup lowering kept in-tree
as a last resort, enabled only by MOBILEGL_MAGMA_EMULATE_SUBGROUP=1 on
devices with no native subgroup support; fails closed on extended
subgroup instructions and on modules whose added scratch would exceed
maxComputeSharedMemorySize.
- IterationRPFirstReductionScenario skips gracefully outside the pack's
16..256-lane source domain; the new IterationRPScratchFixScenario runs
the fixture-shaped reduction on any width and asserts the exact
width-independent total. DriverPost keeps reporting FAIL on
out-of-domain devices.
- Program203 -> IterationRP rename throughout; the per-trace
num_subgroups_quirk plumbing is removed from the trace replayer, JNI
chain, and CI workflows.