v_cvt_f16_u16 SDWA only accepted word and dword source selects. Silent
Hill Townfall (PPSA33286) converts the top byte into the low half of a register
and keeps the high half (src0_sel:BYTE_3 dst_sel:WORD_0
dst_unused:UNUSED_PRESERVE), so its shaders failed to recompile. The
generic SDWA source path already zero-extends byte selects, which is the
u16 value the conversion expects, so the rule now accepts every source
select, with whole and partial destinations.
agc_driver_sdwa_cvt_f16_u16 runs four byte selects with dword, WORD_0
and WORD_1 destinations on the GPU and checks the converted halves and
the preserved bits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d55dfe8b33c6ee418f40a92e49236f8679a6470b)
The registered-shader decode ran before the deferral decision, so a single
instruction the decoder does not implement ended the title at registration
instead of deferring the shader to its first use.
Give the decoder's unsupported-instruction failures their own type
(UnsupportedInstructionError): the five family-scoped SOP opcode defaults and
the MIMG opcode lookup (which now names the opcode) raise it, and the decode in
PrepareRegistered defers the shader with that reason when it hits one. Any
other exception still throws, so the corrupt-code contract is unchanged.
PPSA26344 hits this: unsupported MIMG opcode 229 (0xE5), issue #1941. The
failing word is reproducible offline from the title's own shader store
(cache_ps5/all_shaders.xpps, code word 260), which is how the fixture was
checked before it was written.
Scope: this defers the scalar-family and MIMG opcode gaps. The vector (VOP*)
and memory (SMEM/MUBUF/MTBUF/FLAT/DS) opcode lookups, the modifier-level
"not supported" throws and the malformed/truncated throws still abort.
Widening the deferral to those is a policy call for the maintainers.
Stacked on #1836 (the deferral machinery); re-stacked on its current head so
this diff no longer carries #1836's earlier broad catch. Once #1836 lands only
the decode change remains.
Written with AI assistance.
v_not_b32_sdwa takes the same dst_sel and dst_unused operands as the
other 32-bit VOP1 SDWA forms (RDNA2 ISA 6.3.1 "SDWA"; LLVM
VOP1Instructions.td gives V_NOT_B32 a VOP1_SDWA encoding with dst_sel
and dst_unused). The rule now allows byte and word destinations with
every unused mode, which the existing SDWA writeback implements; the
execution test checks WORD_0 with UNUSED_PRESERVE.
Uncharted: Legacy of Thieves Collection (PPSA05684) stopped at a v_not_b32_sdwa with dst_sel:WORD_0 dst_unused:UNUSED_PRESERVE.
GFX10 image format 2 (8_SNORM, Mesa gfx10_format_table) resolves to
VK_FORMAT_R8_SNORM and is marked sampleable in the recompiler's format
table.
Uncharted: Legacy of Thieves Collection (PPSA05684) stopped at a compute shader sampling an 8_SNORM image.
ReadRawComputeShader decodes a growing prefix of guest memory and reads
more when the decoder throws std::out_of_range, which the vector, scalar
and instruction-length checks throw for an instruction cut off by the end
of the span. The memory (SMEM, DS, MUBUF, MTBUF, FLAT), image and export
decoders threw std::runtime_error instead, so a two-word instruction
across a window boundary failed the read: vkQuake terminated on "truncated
FLAT instruction". These now throw std::out_of_range too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decode the three VOP3 16-bit interpolation instructions (attribute, channel
and high-half select in the src0 field, the I/J VGPR in src1, P0 or the first
step's result in src2) and translate them with a new
GetInterpolationParameterF16 IR value. It reads the selected 16-bit half of
each vertex's parameter through VK_KHR_fragment_shader_barycentric, so P10 and
P20 are vertex differences rounded to f16; both steps are a multiply and an
add, as in the f32 forms.
An input in FP16_INTERP_MODE whose low half is defaulted is still declared at
its slot when its high half is read, and the high half takes its default from
USE_DEFAULT_ATTR1 and DEFAULT_VAL_ATTR1. Without barycentrics, on an input
without FP16_INTERP_MODE or on a pass-through input the instructions throw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The decoder threw on the GFX10.1 DS src2 opcodes 0x80-0x8b, 0x8d,
0x92, 0x93 and 0x95. Each reads DS[B], where B is the address plus
four times the 15-bit signed offset in offset1[6:0]:offset0, and
applies the matching operation to DS[A]: add, sub, rsub, inc, dec,
signed and unsigned min and max, and, or, xor, f32 min, max and add
through the 32-bit LDS atomics, and a plain write for
ds_write_src2_b32. The form that takes the offset from the address
bits (offset1[7]), the 64-bit forms and GDS still throw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decodes IMAGE_GATHER4H_PCK (MIMG opcode 98) and IMAGE_GATHER8H_PCK
(opcode 99), implements gather4h_pck through translation, the SPIR-V
backend and a GPU execution test, and documents gather8h_pck as a
desktop null-op.
Semantics were measured on gfx1031 hardware with hand-encoded MIMG
words (LLVM rejects the PCK gather mnemonics on every RDNA1/RDNA2
target). The measured rule for gather4h_pck: the four-texel horizontal
window is floor(u*width-0.5)+{-1,0,+1,+2} clamped to the width, the row
is floor(v*height) clamped, the raw bits of the window texels are
concatenated into a little-endian bitstream (texel k at bit offset
k*texelBits), and output register j - the j-th set DMASK bit in
ascending order - receives stream word j, zero past the end of the
stream. Exactly popcount(dmask) consecutive VGPRs are written from vdst
ascending; texels wider than 32 bits write zeros (hardware limit); rows
past the height read zeros; negative coordinates clamp to zero. The
window anchor matches the repo's existing PS5-validated HorizontalOffsets
rule, so this reuses the established convention.
The decoder accepts multi-bit DMASKs for these two opcodes only and
sizes their data span as popcount(dmask) (measured), unlike gather4h
which always writes four components. Translation carries the packed
flag through imageMemoryInfoFromInstruction; the SPIR-V emitter
reassembles the bitstream per lane with per-texel OpImageFetch and
byte assembly. D16 data throws until measured.
gather8h_pck never returns texel data on this hardware: across dmasks
0x1-0xFF including the upper nibble, RGBA8/RGBA16/RGBA32 formats, 1D/2D
and 3D descriptors, normalized and unnormalized sampling, sentinel-
prefilled destination registers confirm it only ever zero-fills
popcount(dmask) registers. It decodes but throws at translation.
The measured semantics and the gather8h_pck null-op are recorded in
docs/dev/TechnicalDebt.md.
Reuse the prerequisite call-region analysis for signed relative calls, including callees after s_endpgm. Lower the saved link as shader base plus PC+4 and reject unsupported targets and call shapes.
Add decoder, IR, CFG and Vulkan execution regressions. Seven focused CMake tests pass; five layouts cover 15 dispatches and 3,200 output comparisons on lavapipe. Record ISA sources and native RDNA2/PS5 limits in TechnicalDebt.
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.
perf(agc): baseline shader recompilation on registration
In main, baseline recompilation depends on the resource configuration and runs on the first use of each corresponding variant.
This branch prepares IR, the static interface, baseline SPIR-V, and available ABI variants during shader registration. The compiled artifact is separate from per-draw and per-dispatch data.
Before the first execution with a new resource configuration, the runtime reads descriptors, validates compatibility, prepares the binding plan, specializes existing SPIR-V by substituting constants and removing unused branches and computations, materializes invocation data, and creates the Vulkan pipeline. These results are cached.
Variants that depend on linked graphics stages are prepared when the corresponding ABI is resolved. Unregistered compute shaders undergo baseline preparation on their first dispatch, emit an error log, and cache the result.
Subsequent executions reuse prepared artifacts, specializations, and pipelines while updating invocation data and resources as needed.
Format 43 packs R as a 10-bit and G and B as 11-bit unsigned floats, which
Vulkan has no format for. Keep it in an R32_UINT image like the other
packed formats without a host equivalent, and convert texels in the shader
on image loads and stores; sampling it still throws.
s_sethalt with SIMM16[0] clear is a no-op; s_sethalt 1, s_sendmsghalt, a reached s_code_end, s_rfe_b64, the ds_gws_* instructions and ds_ordered_count throw with the reason at translation. Linear decoding stops at s_code_end like at s_endpgm, so code padded with s_code_end after a final backward branch decodes. Encodings from LLVM SOPInstructions.td and DSInstructions.td, checked against the gfx10 MC tests; semantics from the RDNA 2 ISA guide.
The LOD warning lwe adds to the texel status is texture-streaming feedback,
which is not reported for the T# MIN_LOD_WARN either. Accept lwe on sample
and gather instructions as it already is on loads and stores; lwe on
image_get_lod still throws.
NVIDIA 610.62 cannot sample R8G8_SRGB, and lavapipe can sample neither
it nor R8_SRGB, so an 8_8 sRGB T# made an image the device does not
support: creating it crashes the NVIDIA driver and lavapipe loads zeros.
The device's format support is read once. An 8 or 8_8 sRGB texture the
device cannot sample is created and viewed as R8_UNORM or R8G8_UNORM,
and SpirvTarget::srgbDecodeFormats makes the materializer mark the
image, so image loads map every channel through a 256-entry table of
the sRGB EOTF rounded to nearest even at bf16 precision, which is what
RDNA2 (gfx1035) returns; both channels of 8_8 are sRGB, and DST_SEL 0
and 1 map to themselves. Samples and gathers of such an image throw,
and its bindless table entries are unmapped like converted formats.
APS5_SRGB_SHADER_DECODE=1 takes this path on every device. Shader disk
cache entries and keys carry the mark: cache format version 7.
DS opcodes 0x6e and 0x6f exchange two qwords at offset0 and offset1
scaled by 8 (st64: 512) and return both old values. Each address is
exchanged with its own 64-bit LDS exchange on the locked 64-bit path.
Equal offsets and a destination past v255 throw, as for the b32 forms;
64-bit GDS throws like the other 64-bit DS atomics.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
image_atomic_* with a two-dword data mask (four for cmpswap) is the
64-bit form of an integer image atomic. The decoder rejected it with
"invalid MIMG data mask". PPSA14632 (Unreal Engine) uses
image_atomic_umax this way in a compute shader during its first frames.
Swap, add, sub, signed and unsigned min/max, and, or, xor and cmpswap
now decode with 64-bit data and translate to new ImageAtomic*64 IR
opcodes. An image updated by them gets its own descriptor binding
range, declared as an R64ui image of 64-bit integers, and the module
uses Int64ImageEXT from SPV_EXT_shader_image_int64. The driver enables
shaderImageInt64Atomics when the device offers it and binds an
R64_UINT view of the 32_32 UINT, SINT or FLOAT surface. Without the
feature, recompiling such a shader throws.
64-bit inc, dec and the float atomics still throw, as does any other
access in the same shader to an image it updates with 64-bit atomics.
The new agc_driver_image_atomics64 test runs every operation on 32_32
UINT, SINT and FLOAT surfaces and checks the returned and stored values
against edge cases across the dword boundary.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GFX10 format 6 (8_SINT) T#s threw "sampled image descriptor uses an
unsupported format": neither the recompiler's format table nor the
driver's texture format table had a row for it. Format 6 now reads as
a SINT image through an R8_SINT view, so image_load sign-extends each
texel (0x80 reads -128) and image-table entries of format 6 decode.
8_SINT storage still throws.