In wave32 a compare that wrote vcc_lo kept only the ballot word and rebuilt the
lane's bit from it as (word >> lane) & 1, and every read of vcc_lo as a lane
mask (v_cndmask, carry-in) did the same. s_*saveexec_b32 and the 32-bit scalar
logic ops and s_mov_b32 that write exec_lo or vcc_lo also rebuilt the lane bit
from the new word. Wave64 already kept the lane bits.
Every writer keeps Vcc equal to the lane's bit of VccLo and Exec equal to the
lane's bit of ExecLo, so in wave32:
- a compare into vcc_lo sets Vcc to the compared value, and reading vcc_lo as
a mask returns Vcc;
- s_*saveexec_b32 combines the old Exec with the source's lane bit, and tags
the saved mask in the destination SGPR with the old Exec;
- s_and/or/xor/not/andn2/orn2/nand/nor/xnor_b32 and s_mov_b32 that involve
exec_lo or vcc_lo combine the sources' lane bits the way sU64Mask does for
the 64-bit forms, and tag an SGPR destination.
The words are still computed as before for scalar reads, SCC and branches;
where nothing reads them they are dead.
agc_driver_wave32_exec_lane_bits runs a wave32 program with v_cmp into vcc_lo,
v_cndmask, s_and_saveexec_b32 and exec restores through s_or_b32 and s_mov_b32,
checks every lane, and checks that its SPIR-V has no subgroup ballot left.
M0 is unsigned, so an index at or past the window reads v0 and drops its
write, v_swaprel_b32 writes neither side when either is out of range, and
the window array starts zeroed like unwritten SSA registers; the window
load counts as a lane source so a wave64 program's second half keeps its
own registers. agc_driver_relative_indexing runs in wave32 and wave64 and
pins the rows measured on gfx1035 in the review of #3856, and the
TechnicalDebt entry states the measured behaviour, the window's end at the
last named register and the sizes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`v_movrels_b32`, `v_movreld_b32`, `v_movrelsd_b32`, `v_movrelsd_2_b32`
and `v_swaprel_b32` lowered to a compare-and-select chain over every
register from the base to the program's last allocated register, so a
program addressing 154 registers 104 times turned 3176 ISA words into
649,515 SPIR-V words that Intel's compiler did not finish. The registers
from the lowest relative base up now live in a function-scope array per
invocation (`LoadVectorWindow`/`StoreVectorWindow`), so an indexed access
is one load or store; plain accesses to those registers go through the
same array, DPP-modified relative writes keep their `DppUpdateU32` merge,
an index past the window reads 0 and drops its write, and programs
without relative addressing are unchanged. The same program recompiles
to 392,333 words with 4,992 selects instead of 32,383.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A select of exec and zero gives a mask that is the same for every lane. Reading
it back went through a lane number, which Metal does not have in a vertex
shader, so MoltenVK rejected the shader. The select now stores the lane bit and
marks the pair as a mask, a constant mask word reads as a constant, and the
constant folder removes the bit test on an all-zero or all-one word.
agc_scalar_select_mask runs the translated IR over a wave and pins the lane
values for scc set and clear, a source that differs per lane, the two wave64
halves differing, a destination that is also a source, and constant and
literal sources.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Decodes SOP1 0x21 s_swappc_b64 (link pair = pc + 4) and resolves its call/return pairing through the existing s_setpc_b64 path: static call regions in GraphBuilder and the vertex-entry fetch-shader call inlined from the parsed fetch-shader plan. s_call_b64 (SOPK 0x16) and s_rfe_b64 (SOP1 0x22) throw at decode. Unmodeled cases throw; see docs/dev/TechnicalDebt.md.
A wave64 graphics program on a 32-lane host runs one guest lane per
invocation, yet an EXEC written as a value keeps all 64 bits: the NGG
prologue's s_lshr_b64 exec, -1, 64 - count reads count 0 and sets -1.
A waterfall loop that copies EXEC with s_mov_b64 and retires one key
per pass then never empties its copy, since no ballot sets the lanes
the host lacks, and its v_readlane picks lane 32 and up.
readRawU32 now ANDs EXEC with a ballot of true when it is read as a
value (s_mov_b64, ALU sources, src_execz), and s_*_saveexec and
s_*_wrexec store that masked copy. EXEC's own words, threadBit and the
saveexec combination keep the raw words, so s_orn2_saveexec_b64 vcc,
exec still sets all 64 bits. On subgroups under 32 lanes the EXEC_LO
copies of agc_driver_scalar_sop1 now hold only the subgroup's lanes.
Without fetch-inactive or bound_ctrl, a v_permlane16_b32/v_permlanex16_b32
lane whose source lane is inactive keeps its old value on hardware;
AnyPS5 wrote 0 there. bound_ctrl alone now writes 0.
Co-Authored-By: Claude <noreply@anthropic.com>
v_pk_fmac_f16 read its accumulator through the permuted destination and
wrote without the DPP write mask. v_movrels/movrelsd/movrelsd_2 dropped
the source permute, and the relative writes dropped the DPP write mask.
Co-Authored-By: Claude <noreply@anthropic.com>
s_lshl1..4_add_u32 never wrote SCC; hardware sets it when the shifted-out
bits or the add carry are non-zero. s_bfe_i32/u32 went through
OpBitField{S,U}Extract, which RADV gets wrong when the width runs past
bit 31; build the field from shifts instead.
Adds an execution test for 38 SOP2 opcodes with hardware-captured results.
Co-Authored-By: Claude <noreply@anthropic.com>
v_movrelsd moves VGPR[src + M0] to VGPR[dst + M0]. The _2 form and
v_swaprel take the source offset from M0[9:0] and the destination
offset from M0[25:16]; v_swaprel and v_swap exchange the two registers.
They reuse the select-over-registers lowering of v_movrels/v_movreld,
now shared as readRelativeVector/writeRelativeVector.
Co-Authored-By: Claude <noreply@anthropic.com>
V_WRITELANE_B32 writes the selected lane regardless of EXEC, but the
translation routed it through the exec-masked VGPR write, so a spill to an
inactive lane was dropped. The WriteLane test now writes lane 5 while only
lane 0 is active.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The geometry path's merged ES/GS programs (VGT_SHADER_STAGES_EN.GS_EN)
were compiled as mesh-stage programs whose input metadata was never
filled in, so every such draw failed with "mesh shader translation
configuration is not supported".
Recompiler:
- BuildShaderStageInputInfo takes the draw's MeshConfiguration; the mesh
input helpers (primitive size, step and counts) are implemented.
- The prologue emulates one NGG subgroup per workgroup: s2 GS_TG_INFO,
s3 the merged wave info per wave, the GS vertex offsets (ES thread
index times VGT_ESGS_RING_ITEMSIZE, odd strip triangles swapped), the
primitive id, and the ES vertex and instance ids. An indexed draw's
vertex id is read from the index buffer through a raw V# the driver
puts in hidden user words 4..7.
- s_sendmsg GS_ALLOC_REQ becomes MeshAllocate. The epilogue zeroes the
allocation first, clamps it to the output limits, culls primitives no
thread exported and decodes the 9-bit vertex indices.
- The draw parameters live in the last six push dwords. A BDA fault in
a mesh program no longer stops the invocation (as for other programs
with barriers, the fault is recorded and the read returns zero), so
every invocation reaches the subgroup barriers and the epilogue.
- The cache keys cover the mesh configuration; requests serialize its
new esgsItemSize field (version 4).
Driver:
- DecodeShaderStages reads VGT_ESGS_RING_ITEMSIZE (now in the draw key)
and sizes the subgroup for its ES vertices, GS primitives and outputs.
- Stages compile below the mesh draw parameters, which every mesh draw
pushes; the mesh stage always takes part in the push range.
- The index-buffer V# is set per draw (per record on the CPU-resolved
indirect path); the index-buffer alias check spares read-only
elements.
- The workgroup and LocalSize checks count two guest lanes per
invocation on 32-wide subgroups; mesh draws may carry a first vertex
or instance.
agc_driver_mesh_tests (EXCLUDE_FROM_ALL, needs VK_EXT_mesh_shader)
recompiles a hand-assembled merged ES/GS program in the PS5 compiler's
layout and draws indexed and non-indexed triangle lists in one- and
two-wave subgroups and a triangle strip across odd-starting subgroups,
reading the pixels back. agc_shader_replay prints a request's draw and
mesh configuration.
Only the lines this branch added lose their comments; the commits before
keep them, with the reasoning behind each change, for whoever digs into
that code later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- MIMG word0 bit 12 (UNORM) is accepted on every op without a sampler
(compute image stores set it, as the ISA asks); sample and gather still
reject it, and rejections name the instruction words.
- The image_sample_*_a opcodes keep the plain address layout, as in Kyty.
- The _cl LOD clamp becomes the MinLod image operand (or clamps an
explicit level) on devices with shaderResourceMinLod; a gather with a
clamp fails loudly.
- v_add_co_ci_u32, v_sub_co_ci_u32 and v_subrev_co_ci_u32 in VOP3 form
take their carry-in from the SGPR pair of their third source, not VCC:
a glyph upload's reciprocal division corrected its quotient on the
wrong lanes and UI text came out stippled.
- Pixel exports follow each MRT slot's component mapping and write only
the components of their export format (32_R, 32_GR, 32_AR).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
s_getpc_b64 produced the next instruction's offset from the start of the
shader, so code that addresses data stored behind itself built V#s at a
tiny address. Astro Bot's video-frame NV12->RGB pass draws its quad with
an NGG shader that fetches the four vertices from after its own code
(s_getpc_b64; s_add_u32 0x98; s_addc_u32) - the buffer resolved to 0xf0,
every vertex read as zero, the quad never rasterized and the PlayStation
Studios logo video stayed black.
The result is now GetShaderBase + offset. The resource walk already
evaluates GetShaderBase per request from the code address, so the V#
names the real data and the same code at another address still shares
its compiled variant.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ButHENCbXXt8h4EZuWUabJ
- s_*_saveexec reads its source before writing the old EXEC into the
destination, so the aliased form (vcc, vcc) computes exec & vcc; the
per-lane exec bit comes from the lane's own bit. Every Bink 2 decoder
kernel uses that form; this was the video corruption.
- buffer atomics add/sub/or/xor with a zero operand become an atomic
load or nothing.
- DescriptorBinding carries bufferWritten/bufferAtomic/imageWritten;
results expose a variantId and share SPIR-V copy-on-write; source
keys use a length plus 64-bit hash; captures can be reused.
- wave-LDS ordering barriers, v_movrels/v_movreld lowering, SMEM with a
VCC base, f16 conversion opcode fix, a per-function SPIRV-Tools pass
list, env-gated IR dumps and probes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177gthrWEBBfBu5xJDXgBfo