Main took request serialization version 48 for the fragment barycentric
built-ins flag (#9193). The depth export clamp bounds are now written at
version 49 and read only for versions from 49, so a version 48 request as main
writes it still decodes, with default bounds. Tests: version 49 is the
emitted prefix, 50 the first unsupported one, a depth exporting request
without its bounds decodes at 48, 47 and 46 (the last two without main's
barycentric byte), and a version 48 two-sample dithered request decodes as
written.
Main took request serialization version 47 for the two-sample dithered
alpha-to-coverage value. The depth export clamp bounds are now written at
version 48 and read only for versions from 48, so a version 47 request as main
writes it still decodes, with default bounds. Tests: version 48 is the
emitted prefix, 49 the first unsupported one, and a version 47 two-sample
dithered request (with and without depth export) decodes as written.
The I/J reads take the BaryCoordSmooth input, but gl_FragCoord was declared
only for the offset kinds, so its w was read as the constant 0 and the
fragment function never read [[position]]; Apple GPUs then return the
weights unnormalized. Declare it with the built-in, and select on
gl_FragCoord.w > 0, which a compiler cannot fold as it can
min(b, b + |w|).
MoltenVK exposes VK_KHR_fragment_shader_barycentric but its MSL step
rejects PerVertexKHR, so the extension is disabled there and a pixel
shader whose I/J VGPRs outlive their v_interp_p1/p2 pairs takes the
generated barycentric stages, which accept only triangle lists without
primitive restart on a device without geometry shaders.
A new target flag, fragmentBarycentricBuiltins, enables the extension
for its built-ins only. The host interpolation check then keeps the
leftover I/J reads (all perspective or all linear, at the fragment's
default position) and reads them from BaryCoordKHR or
BaryCoordNoPerspKHR, while the interpolated attributes stay hardware
varyings. Raw vertex parameters and I/J at an offset or a sample
position still take the generated stages. Apple GPUs return
unnormalized perspective weights unless the fragment function reads
[[position]], so each perspective read keeps a neutral use of
gl_FragCoord.w, loaded as a float. Devices with the full extension are
unaffected; the request serialization moves to version 45.
Co-authored-by: yripper <[email protected]>
Symptom / Log:
- 26102026_05_33_48.log:L3022-L3034: [gpu] driver failure: compute shader 0x248a05b00: runtime image descriptor is invalid
Root Cause:
- core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp:ReadRuns/ReadColumn/Select/TableSnapshotter::Run/Materialize:L882-L1220 — table materialization did not propagate descriptorScope across table entries and did not rebuild earlier runs or records when a later run or indirect record waited on the GPU and invalidated cached Raw words.
Change:
- Set descriptorScope to the record/descriptor size during ReadRuns, ReadColumn, Select, and TableSnapshotter::Run, rebuild the full table or snapshot across up to 3 attempts when rawInvalidations changes mid-pass, force PendingWrite::Sync on the final attempt via SrtDescriptorForceSync, and refuse if the epoch still fails to stabilize.
Metric / Verification:
- Materialize and SnapshotResourceTables rebuild earlier runs/records when a late wait invalidates Raw words and refuse unstable snapshots.
Symptom / Log:
- 26102026_05_33_48.log:L3022-L3034: [gpu] driver failure: compute shader 0x248a05b00: runtime image descriptor is invalid
Root Cause:
- core/libs/prx/libSceAgcDriver/Execution/src/ShaderMemory.cpp:ShaderMemory::read:L302-L361 and ShaderMemory::readWords:L441-L463 — per-dword PendingWrite decisions on wordwise pages allowed leading dwords of a 32-byte T# to be cached with PendingWrite::Raw before a later dword waited on the GPU, and page->valid.All() in readWords() copied wordwise pages without re-checking descriptorSpanBytes or pendingWrite policy.
Change:
- Propagate the remaining descriptor span size through SrtRuntime::descriptorScope during SRT tape and evaluator reads, restrict the page->valid.All() fast-path in readWords() to non-wordwise pages, re-evaluate cached wordwise words against checkSpanBytes, invalidate cached Raw words when a read waits on the GPU, and require a stable rawInvalidations epoch across up to 3 attempts (forcing PendingWrite::Sync on the final attempt and throwing if the epoch fails to stabilize).
Metric / Verification:
- ShaderMemory and SrtWalker reads never mix pre-wait Raw words with post-wait words across a descriptor span and refuse unstable snapshots.
Resolve the overlap with main's WindowLaneGuard and image-resource changes
by keeping both sides: DescriptorFault sits next to WindowLaneGuard in the
opcode list, metadata and side-effect switch, and its emitter next to
EmitWindowLaneGuard; optionalDescriptor is serialized after
remappedDepth1D; undefinedImageWords follows the new image helpers; the
descriptor web debt entry follows main's new entries.
The ImageResource field-count assertion is derived from the merged
structure (44 fields: main's 43 plus optionalDescriptor). sizeof
(ImageResource) stays 96 and sizeof(MemoryInfo) stays 80, so those
assertions are unchanged. The undefined-format test keeps its captured
regions in a variable, as MemoryRegionView no longer binds a temporary.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An image_store with DIM 2D_ARRAY through a 1D T# had no runtime image
mode, so the dispatch stopped with "image descriptor is incompatible
with the static runtime image interface". #2348 added a 1D mode for
DIM 2D stores only.
Write-only 2D-array storage images now get the same 1D mode as the 2D
case. On RDNA2 (gfx1036, hardware oracle) such a store writes texel x
and ignores y when the slice is 0, and writes nothing for any other
slice, with DEPTH 0 or 3, so the store is skipped unless the slice is 0.
New execution test agc_driver_image_store_line_through_plane_array.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ImpliesNot returned at a negated condition, so a predicate !!a against
the condition !a, or !!a in either order of a conjunction, never reached
the negated-predicate and conjunction cases, which prove it. Lane-bit
lowering makes such a predicate from a complemented own-lane ballot of
!a, and the address evaluator then read both arms of a select on !a
instead of the one the predicate implies. Each case now falls through
when it does not prove the negation. Three hull reads under that
complemented ballot bit, alone and conjoined on either side, check it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Crimson Desert (PPSA25286) draws with VGT_SHADER_STAGES_EN 0x60212d:
local, hull, domain and NGG geometry stages, the hull halves merged in one
registered type-3 program, the domain and geometry halves in one type-2
program. The domain half runs as the tessellation evaluation stage and
captures each vertex's ESGS item, and a mesh shader runs the geometry half
on the captured items.
Registered type-3 merged hulls come from #6679; this adds 16-bit LDS
reads and writes in tessellation stages, which Crimson Desert's merged
hull uses.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bugsnax (PPSA01503) draws with DB_ALPHA_TO_MASK 0xe701 (quad offsets
3, 1, 2, 3 without OFFSET_ROUND) on a two-sample target and stopped on
the refusal of every dither but the eight-sample 0x18701. The pixel
shader now applies an eight-level per-quad table for it, measured
natively on gfx1032, and the request's dither byte selects that table
(serialization version 47). It is accepted only for two matched raster
and DB_EQAA samples, the two measured location pairs and FP16_ABGR or
32_ABGR exports.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A buffer V#'s presence came from the dispatch's V# snapshot as the
specialization constant BufferBase + 2, which buildSpecializedModule
turns into a literal. One guest program then compiled to two SPIR-V
modules, one with the buffer bound and one with it still null, each a
separate pipeline. descriptor() now reads presence from the V# words at
run time, as registration modules already do, and the formatted-buffer
helper takes it from its valid parameter.
AI-assisted. Tested by me on my own machine.
A local program that keeps the relative patch index only on inactive
lanes, or in a vector-window slot nothing reloads, was refused as a
data use. Follow the index through the values that carry it, and
refuse only when a side effect still observes it.
A resource block behind v_cmpx_gt_u32 exec, s, v0 and s_cbranch_execz keeps
its condition only if the compare can decide it, but UGreaterThan32 and
ULessThan32 needed both operands and v0 is a lane value. When the uniform
operand is 0 (or 0xffffffff on the other side) no lane can pass the compare,
so the walk can skip the block without the lane value.
UGreaterThan32 now yields false when its first operand is 0 or its second is
0xffffffff, and ULessThan32 when its first is 0xffffffff or its second is 0,
in the SRT interpreter and in the SRT tape alike; MayDecide keeps such a
condition when either operand is evaluable. Fully evaluable compares are
unchanged.
Destruction AllStars (PPSA02630) dispatches compute shader 0x200a220000 with
a thread count of 0 and leaves the V# in the skipped block stale (one
dispatch holds 00000000 00000000 00000000 ffffffff), which stopped it at
about 40 s with "buffer descriptor uses an unsupported type".
Co-Authored-By: Claude Opus 5.5 <[email protected]>
image_get_resinfo returns 0 in its third component when the instruction's
DIM has no layers (1D, 2D, 2D MSAA) and the T# is not 3D, as measured on
RDNA 2 in #7550. Keep 1D-array T#s on their first layer and MSAA T#s
refused, and record the unmeasured forms in the shader debt file.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
image_get_resinfo with DIM 1D on a cube, 3D or 2D-array T# was refused
with "image address has too few coordinate components": the tracker made
a 1D image that only the query uses, and no 1D mode matches such a T#.
The returned size, layer count and levels come from the T#; DIM only sets
the number of address registers. A 1D image used only by dimension
queries now decodes the T#'s own dimension and accepts 2D, 1D array,
2D array, 3D and cube views.
The flag is stored in the shader disk cache, so FormatVersion moves to 38.
PPSA01317 queries a 64x64 cube with DIM 1D in a pixel shader.
AI-assisted: yes