The recompiler asked for DenormPreserve only for 16-bit and 64-bit floats,
so a device that flushes 32-bit denormals by default (RADV, MoltenVK) flushed
them whatever the guest's float mode said. target.float32DenormPreserve is set
from shaderDenormPreserveFloat32 when denormBehaviorIndependence allows a 32-bit
mode, is part of the cache key and of the request serialization (version 41),
and makes the module declare DenormPreserve 32 unless the 32-bit flush is
already taken in hardware. APS5_NO_HW_F32_PRESERVE=1 turns it off.
A guest wave's lanes see each other's LDS writes without a barrier, so
the recompiler adds a subgroup-scope OpControlBarrier between an LDS
write and the next LDS access when the guest wave is one host subgroup
(WaveLdsScope). AMD's proprietary Windows driver 32.0.21042.62 (Vulkan
driver 2.0.353, "AMD proprietary shader compiler", Radeon 0x13c0)
compiles that barrier to s_barrier, as the pipeline's internal ISA
shows (26 s_barrier for the guest's 9 in a small LDS reduction). In a
workgroup of several waves, the waves that enter a branch holding LDS
accesses then execute extra s_barriers, which pair with the other
waves' barriers, and those waves read LDS before it is written. LLPC
compiles a subgroup-scope control barrier to a wave barrier instead.
PS5LM Plus (PPSA99581) answered with garbage on that driver: the first
compute dispatch of its first graph, an RMS norm with a 512-thread
wave32 LDS tree reduction, scaled every row except its first 32
elements by a partial sum, while NVIDIA matched a NumPy reference and
the inputs were identical.
On that driver the barrier is now a subgroup-scope OpMemoryBarrier,
which it compiles to no instruction. A 32-lane subgroup is one hardware
wave there, which runs its lanes in lockstep and issues its LDS
instructions in order; Vulkan does not guarantee that, so it is
recorded as debt. Other drivers keep the control barrier. The flag is
part of the shader cache key, and agc_shader_replay takes
--wave-lds-memory-barriers to replay a request the way that driver
compiles it.
agc_driver_lds_tree_reduction sums over eight wave32 waves the way the
guest does, with its last four steps inside one wave and no s_barrier
between them: before, thread 32 read 1936 instead of 32896 on the AMD
driver; it passes on AMD and NVIDIA now. agc_subgroup_barrier checks
the driver quirk and the emitted barriers on every runner. PS5LM Plus
now answers " Paris. It is the largest city in Europe ..." on the AMD
iGPU without flash attention, as on NVIDIA and the CPU.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A registered type-3 hull program holds the local and the hull part of a
tessellation draw in one binary, addressed through SPI_SHADER_PGM_LO_LS
(SH 0x148) with no SPI_SHADER_PGM_LO_HS: the local part runs on the lanes
below s3[7:0] and writes LDS, an s_barrier follows, and the hull part runs
on the lanes below s3[15:8] and may store the registers the local part
filled. The draw decode asked 0x148 for a type-5 local half, and
PPSA11386 (007 First Light) stopped in its level load with "registered
program refers to an incompatible shader binary type".
The draw decode now gives a registered type-3 program both the local and
the hull role and marks the tessellation configuration mergedHull. The
local stage only passes gl_VertexIndex and gl_InstanceIndex in a 16-byte
record. The hull stage runs the whole program, one invocation per output
control point: v2 and v5 come from the record of control point
min(gl_InvocationID, inputs - 1), v3 is the relative vertex id, and the
LDS becomes a per-control-point output array that a control point writes
in its own record and reads in any record of its patch after the barrier.
Registers live across the barrier stay in the invocation.
The hull stage of a merged program runs at one lane per invocation on
every device (HostSubgroupSize 1). Its lanes are gl_InvocationID, not host
subgroup invocations, so the s_cbranch_execz around the local part tests
the invocation's own lane. With the device's subgroup it took a subgroup
ballot, and lavapipe, whose subgroup stages lack the tessellation control
stage, rejected the draw ("subgroup capability 61 is unsupported for
shader stage 2").
The lowering refuses more input than output control points, LDS writes
outside the own record, reads outside the patch's records, barriers that
some invocations do not reach or that sit in a loop, reads or writes of
another lane's register, outputs beyond the device limits, and fetch
subroutines. Type 3 is prepared with its stage group like type 5 and
takes its float mode from RSRC1_HS. The hull user-data address (SH
0x102/0x103) is optional for a merged program: s[0:1] take it, unchecked,
when the draw sets it and are zero otherwise.
Both stages refer to one binary, so a draw lists each code and header
region once (AppendProgramMemory); a request's memory regions must not
overlap. An indirect draw's record words go into every program whose user
data holds the register, so the hull stage of a merged program reads
them too.
mergedHull is part of the recompile cache key, the draw decode comparison
and the request serialization (version 39).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 <noreply@anthropic.com>
A pixel shader that reads per-vertex parameters (v_interp_mov of P10 and
P20) on a device without VK_KHR_fragment_shader_barycentric now works on
the mesh path too. The mesh program stages each such parameter in
workgroup memory and writes the values of the three vertices of every
primitive as a per-primitive output, in the order of the triangle
indices; the pixel shader reads them from there.
The emulation throws when the staging, the output components or the
output memory exceed the device's mesh limits, or when the mesh
configuration uses a provoking vertex other than the first.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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 <noreply@anthropic.com>
With DB_SHADER_CONTROL.STENCIL_OP_VAL_EXPORT_ENABLE the pixel shader
supplies the value that REPLACE_OP and the add and subtract operations
use in place of DB_STENCILREFMASK.STENCILOPVAL. The bit was refused as a
whole, which stopped Onimusha: Way of the Sword (PPSA27836) at a
DB_SHADER_CONTROL 0x45 draw with DB_STENCIL_CONTROL 0x40040 (REPLACE_OP
on depth pass, both faces), write mask 0xff and compare ALWAYS.
The export is accepted with the 32_GR and 32_ABGR Z formats. When no
reachable operation through a non-zero write mask reads the value, it
cannot change the result and is ignored. Without a test value export, a
reachable REPLACE_OP becomes REPLACE with the exported value as
FragStencilRefEXT (or the bounded replay on devices without the
extension), provided both faces compare without the test value and use
no ONES or REPLACE_TEST; early tests throw. The add and subtract
operations, and REPLACE_OP with a test value export, still throw.
The value is read from bits 8-15 of the second MRTZ channel: the
title's pixel shader shifts it left by 8 right before exp mrtz
(v_lshlrev_b32 v0, 8, v0). That is the only evidence for the layout and
it is listed as technical debt. The pixel stage info carries the new
flag into the cache key, the draw keys and request serialization
version 31.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Devices with DenormFlushToZero for f32 run the mixed denormal-flush programs with and
without the hardware mode at startup; when every result matches, add, sub, mul, fma,
floor and ceil in the default rounding mode drop their software flush.
A dispatch over maxComputeWorkGroupCount was split into vkCmdDispatchBase
chunks whose base reached the limit, which VUID-vkCmdDispatchBase-baseGroupX-00421
forbids, and NVIDIA refused the split outright.
Compute modules now declare WorkgroupOffset specialization constants (default 0)
that the recompiler adds to WorkgroupId and GlobalInvocationId and keeps
unmaterialized. Each chunk is recorded with vkCmdDispatch from group 0, bound to
a pipeline variant specialized with the chunk's base. The NVIDIA throw is gone;
device-memory LDS splits still throw.
Fixes#7375
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.
Resolves the shader debt file bullet by bullet: main's EmitSetAttribute entry, and this branch's PERSP_SAMPLE/LINEAR_SAMPLE and pixel-position entries.
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 <noreply@anthropic.com>
A remapped vertex shader reads the scale and offset from the push block, but
the pipeline's push constant stages only counted programs with push data of
their own, so a shader with none got no stage in a non-GPL layout and its
scale and offset never arrived. The result now says it reads them, and the
push stages include the vertex stage for it. The draw test gains a fetchless
remapped vertex shader with no push data.
The remap mode stays a specialization constant and cache key input. Scale and
offset are two push words at byte 104, patched per draw, so draws that differ
only in depth range share one pipeline. The recipe, journal and serializer
carry the mode only.
Only the remap mode changes what the vertex shader compiles to. Scale and
offset are specialization constants that each materialization takes from
its own request, so they no longer split the compiled variant per depth
transform. The result memo hashes them with the snapshot instead, so one
prepared vertex shader does not hand the module specialized for one
transform to a draw with another. sameVertexInfo repeated its first
comparison without paClVsOutCntl; the repeat is dropped.
A test pins the version 27 layout (the remap fields are the last bytes
of a vertex request), a version 25 replay, truncation and trailing-data
refusal, and that scale and offset leave the cache key alone while the
mode does not. agc_depth_range_remap materializes one prepared vertex
shader with two transforms and checks that each gets its own module.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without VK_EXT_depth_range_unrestricted a viewport depth range outside
[0, 1] threw. Minecraft (PPSA17221) draws its world with scale 1 and
offset 0 in negative-one-to-one clip space, so its depth is the raw z/w
clamped by PA_SC_VPORT_ZMIN/ZMAX. On such a device the vertex shader now
scales clip z so z/w is the console's depth, the pipeline clamps depth at
the console's bounds, and two clip distances restore the near and far
planes that depth clamping removes. Mode, scale and offset travel as
pipeline specialization constants from the draw's vertex stage info, in
both the per-draw and the registration-module path; mesh and tessellation
draws with such a range still throw.
The console's depth clamp bounds are clipped to [0, 1]: exact for a
normalized depth target or none, and reported once for a tested or
written float depth target, where a depth below 0 is stored as 0
(Minecraft clamps its float depth target to [-1, 1]). The shader
validator accepts the remap's two extra clip distances next to the one
a program may already export.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Main's #6402 stores to image tables: a table with a written entry takes
the fresh path, as it does on main, and so does one without a sampled
table binding, whose entries the fresh path rejects. The shared place
step uses the binding as a pointer. Main's #6614 frees descriptor sets
from shared pools one by one, while this branch keeps only dedicated
pools, so both stay. #7101's bulk key read counts its unmapped-page
skips as unmapped, and its key words sit next to this branch's write
stamp and compared runs.
NVIDIA's compiler predicates a branched LDS load at no cost, while the
selected form costs about 2.5 instructions per dword and splits a
ds_read_b128 into four loads (RTX 4060 measurements by @karanvora2599 in
loads therefore keep the branch of main, in workgroup, function and device
memory alike, and the sentinel dword that only they read goes away: the LDS
array is the program's dwords plus the lock again, and the device-memory
decision reserves the lock alone. The sentinel budget tests become lock
budget tests at that boundary, and the guarded-load test pins the branch.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A load selected instead of branched still accesses memory, so its fallback
element must be one no write can race with: scratch keeps element 0, LDS in
workgroup or function memory reads a sentinel dword appended after the
program's LDS and lock, and storage buffers read element 0xfffffffe, which
robustBufferAccess2 turns into a zero load without a memory access. The
device enables robustBufferAccess2 when it has it and the target carries the
flag; without it, and for LDS in device memory, buffer loads keep their
selections. The aligned high word of a buffer read re-reads the low word's
element instead of word 0. agc_guarded_memory_loads traces the emitted loads
in both device modes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Main took request serialization versions 31 (output primitive size) and
32 (spanned waves), and #6683 uses 33, so the pixel iteration fields move
to version 34. A version 32 request keeps its spanned-waves flag and reads
no pixel iteration.
Prepared captures pass their source handle to the plan reload. When the handle
matches the request, its artifact serves the draw, so the rebuilt resource
program is not kept even if the push layout differs from the artifact's.
A handle that does not match the source is rejected before the entry changes.
Same change as f1b42dae4 on top of c00b0dfb9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
APS5_SPANNED_WAVE is now read once by the driver, which sets
SpirvTarget::spannedWaves only on a device with 32-lane compute subgroup
control and computeFullSubgroups. The recompiler no longer reads the
environment. The field is part of the cache key and of the request
serialization (version 32), so a cached or replayed module keeps the mode
it was built in.
A spanned module numbers its lanes SubgroupId * 32 +
SubgroupLocalInvocationId, which needs two full 32-lane subgroups. The
artifact records this as fullSubgroups (disk cache and pipeline identity
included), and the driver creates those pipelines with
REQUIRE_FULL_SUBGROUPS. Compute pipeline captures accept the flag with a
required subgroup size.
The two-lane fallback now catches only SpannedWaveUnsupported, thrown for
a shuffle wider than 32 bits; other emit errors surface as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves#4943's draw relocation against this branch: tagVariantRuns, which now builds a draw variant's runs and words, also records the captured words it shares with a table's supplied blocks, per region as before. A variant whose runs relocation splits keeps sharing for the draw cache's word compare, and skips the vouched validation, which already requires one run per region.
The ancillary input's sample number (bits 11:8) was the SampleId
builtin in every pixel shader. Vulkan enables sample shading for a
shader that uses SampleId, so a shader that reads the sample field or
the whole ancillary word ran once per sample on a multisampled draw
even when the guest runs it once per pixel (PS_ITER_SAMPLE clear), and
FragCoord, and with it POS_X_FLOAT and POS_Y_FLOAT, moved to the
sample. The sample number is now SampleId only when the request says
the guest iterates the shader, and otherwise 0, the iterated sample
number of a pixel the hardware does not iterate.
The other inputs that depend on the sample follow where the console
evaluates them:
- PERSP_SAMPLE and LINEAR_SAMPLE are interpolated with
InterpolateAtOffset at sample 0's offset in a shader the guest does
not iterate, and in an iterated one at the offset of the host's
SampleId, read from a table of the guest's locations, because Vulkan
leaves plain barycentrics under sample shading at an
implementation-defined position (lavapipe uses the pixel centre).
- PERSP_CENTER and LINEAR_CENTER are interpolated at offset 0 in an
iterated shader.
- POS_X_FLOAT and POS_Y_FLOAT follow SPI_BARYC_CNTL: the centre is
floor(FragCoord) + 0.5 under sample shading, a sample position in a
shader the guest does not iterate is the centre plus sample 0's
offset, and POS_FLOAT_ULC gives the upper-left corner.
The offsets need InterpolationFunction (sampleRateShading) and the
fragment-shader barycentric path; devices without them keep the host's
default positions. The shader disk cache format goes to 39.
Brings the series onto main 4e9a2a871 (#1193, #1201, #1202, #6060,
#6333 and the descriptor word tracking of 682b07eb0).
Resolutions:
- ShaderMemory: the column memo's supplied and spilled blocks now sit
on #1193's page vector, page pool and word masks. Pages are found by
binary search, since a spilled block can add many pages, and a block
placed into a lazily read page fills the rest of the 16-word blocks it
touches, so a later lazy read never overwrites placed words.
- ResourceMaterializer and SharedValues: the selected-record path and
the words index carry availableDwordMask, as the snapshot hash does.
A selected record's dwords past the end of its buffer count as read
zeros, which is what a scalar buffer load returns there, so a key past
the heap still selects a null image without reading the heap.
- DescriptorCache: kept table sets live beside #1201's recycled sets.
- Tests and CMake: both sides' tests are kept.