A shader source loading its resource plan from the disk cache decoded
its own copy, also when another source had already loaded the same
plan: the same code registered under another plan key (another host
subgroup size or interface) stores byte-identical plan entries. In Ghost
of Tsushima's cache, 14,043 plan entries hold 8,170 distinct payloads.
Loaded plans now go through a table of weak references keyed by two
hashes of the payload, its size and the shader address the plan carries,
so equal payloads for one shader decode once and share the plan, its
compact form and its compiled SRT tape while any source uses it.
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
The second half of a pipelined draw (stage materialization, cache insert,
enqueue) runs on a finisher thread, overlapping the next draw's packet
work; draws whose stages could write memory are held back. Commits skip
work whose inputs did not change since the last commit, color metadata
passes are ordered through the pipeline, resource and storage-image proofs
are reused within one collect epoch until a write breaks them, and a shape
entry is refreshed in place at the second sight of the same read user words.
APS5_VERIFY_DRAW_PIPELINE checks reused proofs against a full check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A FLAT_SHADE input without pass-through that a pixel shader reads
through v_interp_mov_f32 P20 and not P10 stayed an explicit per-vertex
input and read vertex 2 minus vertex 0. For a flat value that is the same
on every vertex the difference is 0, and with the first provoking vertex
nothing refused it.
ps5-opengl lowers every flat input to a load of one vertex: vertex 1
(P10) by default, which #4030 reads as the provoking vertex, and vertex 2
(P20) for triangles when GL's flatshade_first is set. SuperTuxKart's
deferred point lights are drawn that way, so all twelve flat inputs of
pointlight.frag read 0, the attenuation 0 * -inf was NaN, nothing was
discarded, and the starting-line referee's light quad blacked out the
scene behind it (#8364).
A P20-only read now returns the provoking vertex's value, as P10 and P0
do. The lane was not measured on the console either, so FragmentParameter
records it and a pipeline using the last provoking vertex throws. An input
read through both P10 and P20 keeps its per-vertex differences.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each GCN instruction is re-encoded to its RDNA form and decoded by the
existing RDNA decoder, so the PS4 path shares the recompiler. Covers the
legacy VS stage (program at the VS slot, user SGPRs from s0, vertex and
instance index in v0-v3), fetch shaders linked as user-data calls, MIMG
(image_sample and friends), PS4 buffer descriptors (GCN DATA_FORMAT /
NUM_FORMAT, 44-bit base) and T# image descriptors (GCN tiling in word 7).
The GCN switch is keyed into the shader disk cache. PS5 behaviour is
unchanged: every PS4-only rule is gated on GcnGuestShaders().
fdfawef324 notes that current LLVM spills SGPRs into VGPRs it allocates like any other
register, so one window register can be an ordinary register early in a
program and hold spill lanes later. ReadLaneEliminator only followed a
spilled lane across M0-indexed window stores for slots never used as
ordinary registers, so RESIDENT EVIL 4 (PPSA07411) and LEGO Horizon
Adventures (PPSA14632) kept their reloaded descriptors as run-time
values and stopped. A known lane can only come from a v_writelane chain,
so keep it across a computed-index store instead; a constant-index store
of the slot still ends the spill. This is fdfawef324's suggestion on
#6277.
That such a store misses the spill cannot be proven for these programs,
so it is checked at run time where a replaced read depends on it: a
backward liveness over each slot and lane whose known value a replaced
read used puts a WindowLaneGuard before every computed-index store at
which that lane is live, and the guard records a WindowLaneSpill fault
when the store changes that lane of the slot. The driver reads the fault
buffer back for every program with such a guard and reports the dispatch
or draw as failed, so a broken assumption is no longer silent. This also
covers the pure spill slots main already folds.
Co-authored-by: fdfawef324 <[email protected]>
Co-Authored-By: Claude <[email protected]>
A CB_TARGET_MASK that writes fewer than four components of a 4_4_4_4
target leaves the other nibbles of each packed word as they were, which
an R16_UINT attachment cannot do. Such draws now close the attachment's
color write mask, and the pixel shader merges the written nibbles into
the target through a storage image: inside a pixel interlock it reads
the word, keeps the bits the mask leaves alone and stores it back.
The merge (export index, word mask, sample count) is part of the static
shader key, request version 40 and shader disk cache format 42; the
storage image is appended to the shader's images with a single runtime
mode, and its T# is encoded from the target's surface at draw time, so
the draw resolves it to the target's resident image, which Draw
requires. Merging shaders declare early fragment tests; shaders that
kill under late tests, export depth, stencil or coverage, use
alpha-to-coverage or a quad sample mask are refused, as are
multisampled, mipmapped, volume and array targets and more than one
merged target per draw.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
StencilReplaySafe refused every program with usesDma, including pixel
shaders whose only BDA accesses are reads (Onimusha: Way of the Sword,
PPSA27836: one global load, then image and buffer reads). Replaying such
a shader per reference value is idempotent: its only side effect is the
first-fault record, a compare-exchange that keeps the first fault; a
fault is never retried but thrown by CheckFault, and replayed draws with
a BDA table already wait and check faults before publishing. Written-page
tracking exists only in programs with bdaWrites, which stay refused.
Compiled shaders now carry bdaWrites, and ShadersWriteMemory counts a BDA
page table as a memory write only for a program that writes through it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Capture evaluated every flattened scalar slot even when a clean scalar
branch skipped it, so an inaccessible direct user-data root refused the
draw. Record flat-slot uses on resource control-flow blocks and evaluate
only the reachable ones; keep refusing taken and unresolved paths.
Fixes#8272.
Back 1D and 1D-array depth comparison textures with 2D Vulkan images and views of height 1. Drivers such as AMD RADV reject 1D depth images (VK_ERROR_FORMAT_NOT_SUPPORTED). The shader recompiler maps 1D depth comparison samplers to 2D image descriptors, and pins the normalized vertical coordinate to 0.5 to sample the center of row 0 without hitting borders.
A data-only hit (#494) reused a cached stage only when every user data word
of the stage was unchanged. A stage records the user words it depends on when
it is captured: the words its SRT walk reads as values or bases
(SrtUserDataUse), the words its binding layout forwards into shader data
(userDataRegisterReads), and the vertex fetch table words of its vertex stage
info. A cached stage is now a candidate when only words outside that set
changed; its captured memory is validated as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A pixel shader that reads barycentrics on a device without
VK_KHR_fragment_shader_barycentric used to need a vertex shader so a
generated geometry stage could follow it, which a mesh pipeline cannot
have. The mesh program is now compiled a second time with the layout
its pixel shader needs: it writes its clip position as a varying and the
three corner positions of each primitive as per-primitive outputs, and
the pixel shader solves for the weights, for both smooth and linear
interpolation.
The layout is part of the mesh cache key, the stored artifact and the
captured request. Without VK_EXT_mesh_shader the draw still throws, now
saying so. Per-vertex parameters on the mesh path still throw.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Since 2688b7646 a read and written V# element is staged in device memory
up to 16 MiB when the direct dispatch's threads times the V# stride cover
at least half of it. A raw buffer has stride 0, so it never passed and
kept the 2 MiB written window: GL storage buffers compiled by Mesa stay
bound in place in the host import however hard a dispatch sweeps them.
The PS5 OpenGL Showcase's particle update (262,144 threads, each loading
and storing its own 32-byte record of an 8 MiB SSBO) took about 53 ms of
GPU per frame on an RTX 4060 bound in place; staged it takes about 0.1 ms
plus 2.2 ms of bulk copies.
The recompiler already records the furthest byte each buffer resource's
accesses reach past their base (maxByteExtent). The binding plan now
carries it per guest buffer element (DescriptorBinding::bufferExtent,
shader disk cache format 37), and a raw element uses it as its record
size in the sweep test. Strided elements are unchanged.
The data-hit verification keeps this branch's pending-span query on its ShaderMemory and takes #5958's CaptureInvocation, which retries a capture with converted samples.
Keeps this branch's tagVariantRuns declaration next to main's dumpRequest suffix parameter in Driver.hpp, and reads #6232's pointer-chain test words from its table's snapshot, where this branch keeps them.
#7527's selected-record limit test reads a record's descriptor words through the
table snapshot's shared words list; this branch keeps a snapshot's words per column,
so the test reads them from its column instead.
10_11_11_UNORM (format 30) images are read and written through an
R32_UINT view, so the hardware sampler cannot filter them. image_sample
of a 2D or 2D array view now fetches the one or four texels at LOD 0,
decodes them and blends them in the shader, with bilinear weights on
the 8-bit grid, for point or bilinear filtering with wrap or
clamp-to-edge addressing.
A shader gets the converted sample mode only after a converted
descriptor reaches one of its sampled images: that capture throws
ConvertedSampleRequired and the driver prepares the program again
with GuestContext::convertedSamples.
The R10_G11_B11 float format is sampled natively since #7271 and keeps
its refusal on the converted path.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The new samplerCoordinatesIgnored field adds 40 bytes to DescriptorBinding
under libstdc++ on Linux x86_64, where the size assert is compiled. The
encoder and decoder already carry the field.
AI-assisted: yes
An S# that forces unnormalized coordinates was refused when it samples a
T# the shader declares 3D, 2D-array, cube or multisampled, since Vulkan
allows unnormalized coordinates only with a single-level 1D or 2D view.
A null T# reads zeros at any coordinate, and a T# whose channels all
select constants is emitted as those constants without a sample, so when
the S# samples no other T# its coordinates cannot change a result. Such
an S# is now marked in its binding (samplerCoordinatesIgnored) and the
driver creates its sampler without FORCE_UNNORMALIZED. The flag is part
of the binding content key and of the shader disk cache, whose format
version is raised to 38. An S# that also samples a real T# while a null
T# of such a shape shares it is still refused. PPSA01317 pairs an
unnormalized clamped bilinear S# with a null 3D T# and a 3D T# whose
channels all select 0 for image_sample_lz in a compute shader during
boot.
AI-assisted: yes
Every IR value kept two lists of its users: uses (the users) and
operandUses (the users with their operand index). AddArgument,
ReplaceArgument and ReplaceAllUsesWith updated both, and each value
allocated and freed both. Only operandUses is kept now. Uses() is a view
of the users in operandUses, and HasUses, UseCount and Invalidate read
it. sizeof(IrValue) drops from 152 to 128 bytes; the plan value encoder
writes no use lists, so only its size check changes.
The two lists held the same users in the same order, except after
ReplaceArgument on a user that took the value in more than one
operand: uses dropped the user's first entry and operandUses the
replaced operand's entry. Uses() now follows operandUses there too.
shader_ir_use_rewrite counted the allocations of both lists: its
allocation failure check now fails the one reserve ReplaceAllUsesWith
makes, and its growth check allows the allocations of one list, which
main exceeds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The flag that adds the vertex stage to the push constant stages was not part
of the encoded result, so a journalled remapped vertex shader replayed
without it. Encode and decode it with the invocation and cover it in the
result round trip and the push stage test.