72 KiB
72 KiB
shader technical debt
Unknown function info
- Packed pixel ancillary word (shader recompiler) - the sample index at bits 8-11 and the render target index at bits 16-28 follow #295, and their equality with the SampleId and Layer builtins is inferred, not measured. A value passed through a select, move or phi is rebuilt from those two fields with bits 0-7, 12-15 and 29-31 zero. That is exact only because every read of the word must be an extract inside one modelled field; any other read throws. The other bits are unmodelled and their RDNA 2 layout is unknown: the RDNA 3.5 ISA is reported to give prim type at bits 0-1, VRS rates at bits 2-5 and eye id at bit 7, which was not checked against RDNA 2
- Color comparison descriptor forms: emulated color comparison supports UNORM, SNORM and float formats for 2D, 2D-array and cube views. 8-bit sRGB (
Format8Srgb,Format8_8Srgb,Format8_8_8_8Srgb) returns 0 for every compare function, measured withimage_sample_c_lzon gfx1035 over references -0.5..1.5 and texel codes 0..255. Anisotropic filters are refused. Bilinear cube comparisons of float, unorm and snorm formats withoutDISABLE_CUBE_WRAPare refused; on that hardware a 4x4 R32 float cube blends across faces (46 of 90 edge rows differ from a per-face clamp; face 0, ref 0.1, u=3.9 v=2.5 is 0.6016 and u=1.5 v=0.1 is 0.3984), and withDISABLE_CUBE_WRAPthe per-face clamp matches. An 8-bit sRGB bilinear cube without that bit returns 0, since every texel comparison is already 0 for every compare function and the selected face cannot change the result. Cube-edge sampling behavior and console coverage on PS5's gfx1013 remain unverified. - s_setkill (core/shader/recompiler) - decoded as
s_endpgmfor any immediate: measured on RDNA2 hardware (gfx1035), a store before it is visible and nothing after it runs (#771); whether the KILL status bit it sets is observable differently froms_endpgmis unverified - s_sethalt (core/shader/recompiler) - with SIMM16[0] clear it is a no-op: the RDNA 2 ISA guide describes that value as resuming a halted wave, and a wave that executes it is not halted; measured on a gfx1035 (RDNA 2) wave:
s_sethalt 0followed by a VALU op completes and gives the normal result. Only SIMM16[0] is read; with it set the instruction throws - s_subvector_loop_begin and s_subvector_loop_end (core/shader/recompiler) - follow AMD's RDNA 2 instruction set reference and a gfx1036 (wave64) measurement, pinned in SubvectorLoop.cpp;
S0in the end instruction'selif(S0 == 0)is read as its SGPR operand. A wave32 shader and a destination other than an SGPR throw because neither is measured. Measured on PC RDNA2 (Radeon 610M), not on a PS5. The specialization pass keeps a constant-condition branch whose untaken target is a loop header, since folding it removes the loop's back edge, which SPIR-V requires exactly once per loop header (SPIRV-Tools rejects the module and the NVIDIA driver 615.78.08 crashes compiling it); a loop whose back-edge block becomes unreachable in another way is not handled - Vertex entry merged wave info (recompiler) -
s3of every vertex-path shader is set towaveSize | waveSize << 8(vertex and primitive count of a full wave, wave index 0), or to32 | 32 << 8for a wave64 program on a 32-lane host subgroup, which runs it at one lane per invocation and holds no lanes 32-63. On a device whosesubgroupSupportedStageslacks the vertex stage (lavapipe, MoltenVK), vertex and local programs run as one-lane waves:s3is1 | 1 << 8, the lane id is 0, a ballot is the invocation's own bit, a shuffle or broadcast returns the invocation's value, an elect is true and a subgroup reduction is the value itself, so noGroupNonUniformcapability is declared. Layout taken from the prologue Mesa's ACO emits for NGG vertex shaders (s_bfe_u64 execfroms3), not measured on hardware. The driver gives every graphics program user data froms8, so the condition does not tell NGG from legacy vertex shaders: they just never reads3. A draw whose last wave is partial still reports a full wave, and so does a host wave with idle invocations (an RTX 5070 ran every vertex warp of a non-indexed triangle list with 30 of its 32 invocations). - s_barrier in vertex-path programs (recompiler) - emits no instruction when the program has no LDS or GDS access and no memory write or atomic, so no wave of the subgroup can observe another; the
GS_ALLOC_REQsuch a barrier orders on NGG passthrough programs is already dropped on the vertex path. The Vertex execution model has no workgroup barrier. Programs with LDS, GDS or memory writes throw. Not measured on hardware
Functional
- MaskedSelectEliminator can discard inactive source lanes through
ReadFirstLaneonly when its read mask implies the write mask and its result is unobserved outside the read mask. A loop-local read mask requires result uses in the same block without phi transport. The empty-EXEC lane-zero fallback follows AMD's Vega ISA, section 12.8, V_READFIRSTLANE_B32, page 127 and the existing SPIR-V emitter. The optimization is covered by synthetic IR tests but has not been compared with console hardware; a proof that needs a read mask of a kind that is already being solved is refused. - Shader modules built at registration (shader recompiler): on devices with graphics pipeline libraries a vertex or pixel module reads buffer V# words, image modes, swizzles and mip ranges, depth-compare state, export mappings and vertex component selectors at run time instead of specializing on them per draw, so its libraries can be built when the shader is registered. A module that would exceed
SpirvTarget::registrationWordLimit(200000 SPIR-V words) is emitted specialized as before: on RADV (Radeon 660M, Mesa 26.2.4) a sample of 60 Astro's Playroom pixel shaders compiled 1.9 to 2.2 times slower in registration form below 141k words and 3.8 to 5.7 times slower above 280k words (none in between), so the limit sits in that gap; it was not measured on other drivers. Texel-offset folding, the format word of a buffer accessed with the descriptor's format (buffer_load_format_*; as runtime values every format's decoder is compiled, which made CI's lavapipe JIT ten times slower) and the vertex attribute numeric class stay specialized, so shaders with the first two, and pixel shaders whose image needs a multisampled descriptor, get no registration result; these, and every draw whose fixed-function state differs from the prebuild's, still build their libraries at the draw. Compute and the other stages stay specialized: in registration form theagc_driver_dword_pattern_fillcompute shader hung CI's lavapipe (Mesa 25.2.8) when built withANYPS5_ENABLE_SPIRV_TOOLS=ON, not on RADV, and the cause was not found. - SPIRV-Tools optimization skips CCP and Simplification for modules with storage-only 8- or 16-bit types because the pinned folder produces illegal narrow constants. This also disables unrelated constant folding; the restricted full pipeline mirrors the pinned performance recipe.
- SRT signed comparison evaluates
SLessThan32as signed 32-bit less-than, matching the existing SPIR-V backend and SPIR-VOpSLessThan. Signed boundary descriptor expressions are tested on the host; native RDNA2/PS5 execution and title behavior remain unverified. - MIMG
tfeandlwenever write the status VGPR after the data. On hardware it is written only when a texel fetch hits an unmapped page of a partially resident texture, or, forlweon sample/gather, when the LOD is below the T#MIN_LOD_WARN; texture memory is always resident here, the LOD warning (texture-streaming feedback, like the T#MIN_LOD_WARNitself) is never reported, andlweonimage_get_lodthrows. - Packed horizontal gathers (
image_gather4h_pck, MIMG opcode 98): the four window texels are read raw and concatenated into a little-endian bitstream, with DMASK bit i writing stream word i to the next destination register; measured on RDNA2 (gfx1031) at every edge: the anchor is floor(x-0.5) with elements at -1, 0, +1 and +2 clamped horizontally into the width, the row is floor(y), negative rows clamp to row 0, rows at or past the height return zeros, sampler addressing modes do not affect the window, and formats wider than 32 bits per texel write zeros in every register. The emitter fetches each window texel at LOD 0 and packs its bits likeimage_load_pck, so the sampler's filter, addressing and LOD state are not used; formats whose bits are not recoverable from the view and D16 data throw. The anchor and row multiply the normalized coordinate by the image size without contraction. The stream rule was measured for R8_UNORM, RG8_UNORM and RGBA8; only RGBA8 has an execution test.image_gather8h_pck(opcode 99) never returns texel data on this hardware - across dmasks 0x1 to 0xFF including the upper nibble, RGBA8, RGBA16 and RGBA32 formats, 1D, 2D and 3D descriptors and normalized and unnormalized sampling it only zero-fills popcount(dmask) registers (sentinel-prefilled destinations) - so it decodes but throws at translation; its eight-texel semantics remain unmeasured. - MIMG BY2/BY4 loads (
image_load[_mip]_by2/by4, opcodes 0x42, 0x43, 0x4a and 0x4b) were measured on RDNA2 (gfx1035) with the hardware oracle for 2D images with identity swizzle. They read N texels (BY2: 2, BY4: 4) fromx & ~(N - 1)on row y, texel by texel and component by component within each texel, with each texel converted likeimage_load. Every register is zero when the mip level is past the last level,x + N - 1is outside the level's width (unaligned x, unsigned), or y is outside its height. Data only comes back when the N texels fit in one dword: R8 and R16 (DMASK 0x3) or RG8 (0xf) for BY2, and R8 (0xf) for BY4. With 32-bit texels (R32, RG32, RG16, RGBA8) the hardware returns zeros. Other formats, masks, dimensions, swizzles, D16/A16 and indirect descriptors throw. The stores (0x52, 0x53, 0x5a and 0x5b) decode but throw at translation. Measured on hardware, they write from the unaligned x up to the end of its dword, which is not implemented. - 2D stores to 1D storage images: an
image_storewith DIM 2D through a 1D T# writes texel x and ignores y, as measured on RDNA2 (gfx1035) for y from 0 to 4 and 0xffffffff, single- and four-component formats and a 1x1 8_8_8_8_UNORM T#: the T# type decides the addressing, DIM only how many address VGPRs are read. Storage loads, atomics andimage_store_mipthrough such a pair still throw - MIMG PCK2/PCK4 loads (
image_load[_mip]_pck2/pck4, opcodes 0x70, 0x71, 0x73 and 0x74) were measured on RDNA2 (gfx1035) with the hardware oracle for 2D images with identity swizzle. With data mask 0x1, they write one dword: the raw bits of the N texels (PCK2: 2, PCK4: 4) fromx & ~(N - 1)on row y, concatenated (element k at bitk * texel bits) and zero-extended. The dword is zero when the mip level is past the last level,x + N - 1is outside the level's width (unaligned x, unsigned), or y is outside its height. Data only comes back when N texels fit in 32 bits: R8, R16 or RG8 for PCK2, R8 for PCK4. Wider texels (R32, RG16, RGBA8) and masks other than 0x1 write zeros on hardware. Those masks throw at decode, and formats outside UNORM/UINT/SINT R8, R16 and RG8 throw. #1314's earlier measurement saw only zeros because it used masks 0x3 and 0xf. The stores (0x76, 0x77, 0x79 and 0x7a) decode but throw at translation. They are not measured, and opcode 0x76 on 32_32_32_32_FLOAT hung the GPU (#1314). - 3D image accesses bound to a 2D or 2D-array T# follow CouchRiv's hardware oracle measurement on RDNA2 (gfx1036, #2023) and adriadam10's (gfx1035, #2140), not the PS5's gfx1013. On a 2D T#,
image_sample_lzignores r and reads the (u,v) texel,image_loadandimage_load_mipread the (x,y) texel when z is 0 and return zero otherwise, andimage_get_resinforeturns (width, height, 1, levels). On a 2D-array T#, these accesses behave like dim 2D-array: r or z is the layer, andimage_get_resinforeturns the layer count as its third component. Every other access of such an image, including samples with an LOD, bias, derivatives, offset or comparison, gathers,image_get_lodand stores, keeps throwing because it is not measured. - Unnormalized S#s are accepted only when direct and used only by explicit-LOD
image_samplewithout comparison, derivatives or_a, on a single-level, single-layer 1D or 2D view of mip 0, even when the image is also sampled through a normalized S#. - Unnormalized texel offsets are added to the f32 coordinates: exact on RDNA2 (gfx1035), which applies the offset before the clamp (measured by adriadam10).
- Image atomics on 32_SINT and 32_FLOAT surfaces act on the raw dword through an R32_UINT storage view; their float results are assumed to match those measured on 32_UINT surfaces. Other formats throw.
s_memtimeon RADV with a GFX10.3 to GFX11.7 GPU (RDNA 2, RDNA 3, RDNA 3.5) reads the Vulkan device clock instead of the subgroup clock, which ACO reads from the 20-bitSHADER_CYCLESregister there and which wraps every 2^20 cycles. The device clock is a monotonic 64-bit counter, but it counts the 100 MHz reference clock (the same one ass_memrealtime), not shader cycles, sos_memtimedeltas are about 20 times smaller than on hardware. The families are matched by the name RADV reports (NarrowSubgroupClock); a family added to Mesa later reads the subgroup clock until it is listed. AMD's proprietary Windows driver also exposes a 20-bit subgroup clock on device 0x1114 (Radeon 860M, measured in #1615), device 0x744c (Radeon RX 7900 XT, measured in #1632), and device 0x747e (Radeon RX 7800 XT, measured in #1632); other AMD proprietary devices remain unverified.s_memtimeands_memrealtimeon llvmpipe read only the low 32 bits of the host's nanosecond clock at either scope: Mesa'semit_clockbuilds the high dword with a left shift by 32, so it is always 0 and both counters wrap every 2^32 ns (about 4.3 s). The shader clock test compares them modulo 2^32 thereds_wrxchg2_rtn_b32,ds_wrxchg2st64_rtn_b32,ds_wrxchg2_rtn_b64andds_wrxchg2st64_rtn_b64are translated as two separate 32-bit or 64-bit atomic swaps per invocation. RDNA2 hardware (measured on gfx1035) performs every lane's first exchange before any lane's second one, so when lanes of the same wave or of different waves overlap addresses, the result can differ from hardware.offset0 == offset1throws, although on hardware it is two exchanges in order.ds_condxchg32_rtn_b64uses two 32-bit atomic conditional exchanges. RDNA2 (gfx1035) measurements confirmed aligned return values, conditional bit clearing and 16-bit offsets; out-of-range accesses return zero without modifying LDS. Misaligned accesses fault the wave on gfx1035; the recompiler does not model this fault. Radeon 780M (gfx1103, RDNA3) measurements confirmed aligned return values, conditional bit clearing, EXEC masking and operand overlap in wave32 and wave64; contended cases passed per-dword conservation checks. Contended lane ordering and gfx1013 remain unmeasured; pair-wide indivisibility is not assumed, and concurrent 64-bit LDS atomics use a separate lock. GDS throws. VGPR pairs extending beyond v255 throw; allocated-VGPR EXEC nullification is not emulated.- 64-bit image atomics (
image_atomic_*with a two-dword data mask) needVK_EXT_shader_image_atomic_int64and act on 32_32 UINT, SINT and FLOAT surfaces through an R64_UINT storage view. 64-bitimage_atomic_inc,image_atomic_decand the float atomics throw, as does any other access in the same shader to an image it updates with 64-bit atomics. - 8_SRGB and 8_8_SRGB loads on a device that cannot sample R8_SRGB or R8G8_SRGB return the sRGB EOTF rounded to nearest even at bf16 precision, as adriadam10 measured on RDNA2 (gfx1035) in #866; the PS5's gfx1013 is not measured.
- Samples and gathers of 8_SRGB and 8_8_SRGB textures throw on a device that cannot sample R8_SRGB or R8G8_SRGB.
- Bindless image table entries of 8_SRGB and 8_8_SRGB textures read zeros, like converted formats, on a device that cannot sample R8_SRGB or R8G8_SRGB.
- Shader MODE register access reads the initial rounding fields from
GuestContext::floatMode; missing metadata retains the legacy round-to-nearest-even assumption. Immediate rounding writes are accepted only when they preserve that state. State-changing or SGPR-sourced MODE writes throw until runtime mode tracking and affected arithmetic are implemented. Non-rounding register reads still throw;s_setreg_b32writes to other hardware registers are still ignored. Tests follow the public RDNA2/LLVM contract, not console measurements. - A flat SRT read through a null or unmapped pointer loaded from memory raises a BDA fault only if the program executes it, then stops or reads zero like a BDA read.
- Repeated
s_loads of one flat SRT dword through a null or unmapped pointer report one of their pcs, and ans_loadwhose inaccessible dwords are all unused, or only address repeated loads, reports no fault of its own. - A value read through a null or unmapped pointer still throws at record time if a branch condition, uniform fill or table base needs it, or if the pointer comes from user data and is not null, even if the console skips the read.
- A V# or flat SRT word read through a null pointer taken from user data is zero and counts as a poisoned SRT read, so a dispatch that reads it is not cached. The console faults if the program executes the read; PPSA28416 branches around it with
s_cbranch_execz. The fault is not modeled. - A V#, T# or S# loaded through a null or unmapped pointer read from memory is zero when the work is recorded, and the capture throws if no flat SRT slot records that read.
- 2D accesses through a 1D T# read the texel the 1D addressing selects and ignore the second coordinate, as simonbbby measured on RDNA2 (gfx1036) in #2237 for
image_sample_lzand mip-lessimage_loadwith point filtering and wrap, clamp and border addressing; the PS5's gfx1013 is not measured. A plainimage_sample(implicit LOD) through such a pair takes its LOD from the derivatives of the first coordinate alone, measured on RDNA2 (gfx1036) with the hardware oracle on the quads of a compute kernel (wave32 and wave64, IEEE off, DX10 clamp on, denormals preserved, round to nearest even): on a 16-texel, 5-mip 32_UINT T# with point filters it picks the same mip and texel as DIM 1D for first-coordinate steps of 0, 1, 2, 4 and 8 texels per lane along x or along y, whatever the second coordinate does (normalised steps up to 16 per lane), while the same rows on a 16x16 2D T# move the mip with the second coordinate. Outside the pixel stage a plainimage_sampleis emitted with LOD 0 for every image type, so this pair reads mip 0 there, where the kernel measurement shows the quad's derivatives being used. Other 2D accesses through a 1D T# (storage, depth, packed, BY loads, mips, explicit LOD, bias, offsets, derivatives, comparison) still throw. - Emulated comparison of a 2D-array access through a 2D T# samples the 2D image and ignores the slice, as the native modes of a 2D-array access through a 2D T# already do. This follows the T# type deciding the addressing; it is not measured on hardware for comparison samples
- Scalar aperture base sources (core/shader/recompiler) -
src_shared_base,src_shared_limit,src_private_baseandsrc_private_limit(scalar source codes 235 to 238) are not modelled: decoding throwsunsupported scalar source operand codewith the code and the program counter, so a shader that reads one does not compile. Operand 235 (src_shared_base) was reported on #1182 from PPSA26344; the flat aperture routing does not read the base, so a shader that builds its own flat addresses from it stops at decode. - Flat aperture bases (core/shader/recompiler) - the layout follows the gfx9+ one in the AMD KFD driver's
kfd_flat_memory.c(MAKE_LDS_APP_BASE_V9,MAKE_LDS_APP_LIMIT): each aperture is a 4 GB window whose base is the high dword, so a flat address is routed by its whole high dword, as measured on RDNA2 (gfx1035:src_shared_base0x00010000_00000000,src_shared_limit0x00010000_ffffffff). The PS5's base values, high dwords 0x80000000 (shared,SharedApertureHigh) and 0x70000000 (private,PrivateApertureHigh), are inferred from GTA V's traversal addresses, not measured on a console or read from SH_MEM_BASES - Flat accesses outside the aperture routing (core/shader/recompiler) - only compute stages route flat accesses, so graphics-stage flat accesses to the shared or private aperture still go to BDA. In compute, a 64-bit flat atomic whose address is in the shared aperture takes the workgroup lock (
ldsLock) for its two-dword update, and every such atomic requests that lock, so a shader with one pays for it even on global addresses (the path is exercised byagc_driver_global_atomics_float). Inferred, not measured on LDS or scratch: an aperture access that is not inside one dword is done byte by byte, an unaligned aperture atomic records the BDA unaligned fault, and LDS or scratch accesses past their end are dropped or read zero. - On a host subgroup narrower than 32 lanes,
threadBitreads an EXEC written as a value (s_mov_b32/s_mov_b64) at bit (subgroup invocation id) of EXEC_LO, so lanes at or above the subgroup size read a lower lane's bit. - EXEC read as a value (
s_mov_b64 sN, exec, thes_*_saveexeccopy, ALU sources) holds only lanes with a host invocation; the console's also holds the wave's other lanes, e.g. 32-63 of a wave64 on 32 host lanes. v_readlane_b32and fetch-inactivev_permlane16_b32/v_permlanex16_b32read undefined data from a lane that the host subgroup does not hold or whose host invocation is inactive; the console reads the lane's VGPR.- Without fetch-inactive,
v_permlane16_b32andv_permlanex16_b32take a source lane that the host subgroup does not hold as inactive and keep the old value (0 with bound_ctrl); the console reads that lane's VGPR. - On a host subgroup narrower than the wave,
v_readlane_b32of a constant lane outside it builds only as lane 63 of a half-wave scan (DPP row_shr 1/2/4/8,v_permlanex16_b32, EXEC -1) or, in a program whose entry EXEC is every lane (vertex, local and tessellation stages), as 0 when the value is 0 in lanes 32-63; other such reads throw. A value is 0 there when it is built from 0 through selects under a lane index (lane id orv_mbcntof -1) below at most 32,v_cmp_ne_u32against 0, OR, DPP and permlanes; the counts ins3give such a bound on a 32-lane host. On a 32-lane host a half-wave scan may also run unders_mov_b64 exec, -1, use bound_ctrl:1 steps for an OR, add or unsigned max, and take keys that are 0 in lanes 32-63; its lane 31 is then the subgroup reduction of the host's invocations (a shuffle of the scan on a device without subgroup arithmetic): idle invocations add the identity, where the console would add the keys of the laness3claims. - NGG mesh entry uses the guest wave size (32 or 64) for wave indices, per-wave vertex and primitive counts, and the workgroup wave count in
s3. The four-bit wave fields follow Mesa's get_wave_id_in_tg, get_tgsize and get_thread_id_in_tg; partial waves and groups exceeding 15 waves throw. Indexed and non-indexed wave32 mesh rendering with 32, 64 and 128 threads was checked by pixel readback on Windows with an NVIDIA RTX 4070 Laptop GPU. The guest register values have not been compared with console hardware. - Mesh shader grid size (shader recompiler) - every mesh shader loads
NumWorkgroups.xand declares no vertices when it is 0, which no dispatch produces. The read is a workaround for RADV (Mesa 26.2.4, seen on an RX 9070 XT, GFX12): binding a mesh pipeline whose shader reads neitherNumWorkgroupsnorDrawIndexkeeps the user SGPR count and draw-id flag of the vertex pipeline bound before it in the same command buffer (radv_bind_pre_rast_shaderupdates them only when the shader has theAC_UD_VS_BASE_VERTEX_START_INSTANCEslot), soradv_emit_userdata_meshopens a 3-registerSET_SH_REGand writes one value, the CP takes the followingDISPATCH_MESH_DIRECTas register data and the GPU hangs (gfx ring timeout, MODE1 reset). Kena: Bridge of Spirits (PPSA01802) hits it on its layered volume draws. ReadingNumWorkgroupsgives every mesh shader the grid-size slot, so the count and the values match; drop the read once a fixed Mesa is the minimum - Shader recompilation currently occurs right before it was transferred to Vulkan with caching, but should be moved to the relinker stage. For this purpose, shader/recompiler was written completely independently from libs/prx.
- v_interp_p1ll_f16, v_interp_p1lv_f16 and v_interp_p2_f16 read the 16-bit halves of each vertex's parameter through
VK_KHR_fragment_shader_barycentricand throw without it, as fixed-function interpolation cannot split a 32-bit attribute. P10 and P20 are the vertex differences rounded to f16 to nearest even, which is not confirmed on hardware. v_interp_p1ll_f16 and v_interp_p1lv_f16 round the exactP10 * I + P0once to f32 and v_interp_p2_f16 rounds the exactP20 * J + S2once to f16, as measured on RDNA2 (#697); the exact sums are computed in f64, so these instructions need the Float64 capability. They follow the shader's float mode, measured on RDNA2 (gfx1035) in all eight combinations ofIEEE_MODEand kept/flushed f32 and f16 denormals over 768 special and 1,024 random rows. With IEEE=1 a NaN operand is returned quieted; with IEEE=0 it is returned unquieted, except that v_interp_p2_f16's f16 result is always quiet. Flushed f32 denormals flush I/J, v_interp_p2_f16's f32 S2 and the f32 result; flushed f16 denormals flush P0, P10/P20, v_interp_p1lv_f16's f16 S2 and the f16 result. Denormal modes 1 and 2 (flush only inputs or only outputs) and rounding other than to nearest even are not measured and throw. An unknown mode follows IEEE=0 with denormals kept. Lavapipe has no barycentrics, so the draw test is skipped in CI;agc_driver_interpolation_modeschecks the arithmetic in every mode against the hardware rows on any device with Float64. - Without
VK_KHR_fragment_shader_barycentric, a pixel shader whose I/J VGPRs only feedv_interp_p1_f32/v_interp_p2_f32of their own pair reads the attribute Vulkan interpolated. Any other read of them or of a raw vertex parameter takes the weights and the three vertex values from a generated geometry stage, which needsgeometryShaderand a vertex shader before it: tessellation, mesh and rect-list pipelines, primitives other than triangles, more than 32 fragment input locations and interfaces beyond the device's geometry limits throw. - v_fma_f64 and v_div_fmas_f64 with a subnormal operand or result round once, as IEEE 754 fusedMultiplyAdd does with f64 denormals kept, measured on RDNA2 (gfx1035) at IEEE=0 and IEEE=1,
v_div_fmas_f64with VCC clear and set. The f16/f64 denormal field ofFLOAT_MODE(bits 7:6) is not read: when it flushes, the hardware flushes the operands and the result ofv_fma_f64(tininess before rounding) and only the result ofv_div_fmas_f64, while these keep them. Shaders with f64 instructions declareDenormPreservefor 64-bit floats only whenshaderDenormPreserveFloat64is true and Vulkan reportsdenormBehaviorIndependenceasALL; other independence modes are not handled by the current backend because they require coupled execution modes. See VkShaderFloatControlsIndependence. Elsewhere (including lavapipe), the driver may flush f64 subnormals, andagc_driver_f64_fma_subnormalsis skipped. - SINT storage images of 8- and 16-bit components saturate each stored 32-bit value to the component range, as adriadam10 measured for image stores on RDNA2 (gfx1035) in #944 (448 rows match); the PS5's gfx1013 is not measured.
StoreTexelwrites channel 1 of a two-channel format from the data slot whose DST_SEL is Y, for SINT and UINT alike. With DST_SEL X,Y,Z,W, gfx1035 writes it from the W slot, as adriadam10 measured in #944.- v_ldexp_f16 follows the
FLOAT_MODEf16 denormal field: bit 0 preserves the half input and bit 1 the half result. Otherwise a denormal input is flushed to its signed zero, and a result whose exact scaled value is below the minimum normal (2^-14) is flushed to its signed zero before rounding, sov_ldexp_f16 0x07ff, -1gives +0 instead of 0x0400. Measured on RDNA2 (gfx1036) with round to nearest, IEEE 0 and 1 and denorm16 0..3, over every half magnitude 0x0000..0x0fff of both signs scaled by 2^-1 and 2^0 (16384 rows per mode): no mismatch. NaN and infinity handling and destination high-half preservation are unchanged. Shaders without a known float mode keep the previous lowering. - v_mullit_f32 follows the hardware measured with the MODE register at IEEE=1 and denormals kept (a signaling NaN src0 is quieted only with IEEE=1, as measured at IEEE=0 too; a positive denormal src2 counts as positive); with f32 denormals flushed it follows the f32 denormals entry below, and denormal modes 1 and 2 throw.
- v_mad_legacy_f32 and v_mac_legacy_f32 (VOP3 0x140, VOP2 0x06) round the product to f32 before the add, give a +0 product for a zero factor, and flush their f32 denormal sources, their product and their result to a zero of the same sign in every f32 denormal mode, an exception to the
FLOAT_MODEmodel in f32DenormalFlushFor. Measured on gfx1010 (RX 5700 XT) withtools/hw-oracle/template.s, assembled by the AMD Windows driver'samd_comgr_2.dlland launched through itsamdhip64_6.dllinstead oforacle.cand ROCr, over the 32 rows of the fiveFmaLegacy.cppand the fourMacLegacy.cppencodings: identical in wave32 and wave64 at IEEE 0 and 1 with f32 denormal modes 0 to 3, except that the two signaling NaN rows are quieted only with IEEE=1 (#1281, #1290). A sum of two zeros takes its sign on the bits (+0 unless both are -0), as lavapipe returned -0 for +0 + -0. The PS5's gfx1013 is not measured; LLVM decodes the encoding asv_mad_legacy_f32there, as on gfx1010. RDNA2 differs: on gfx1031 (RX 6700 XT, measured by pandather in #1281) the encoding isv_fma_legacy_f32and fused, with the same zero rule and denormal-source flush, so an RDNA2 measurement is not evidence for the console. - v_fma_mixlo_f16 and v_fma_mixhi_f16 round the exact product-sum once to f16 with the f64 arithmetic of v_interp_p2_f16, so they need the Float64 capability. On RDNA2 (gfx1036) they follow the
FLOAT_MODEf16 round and denormal fields, not the f32 round field: -1.5 * 2^-24 + 2^-126 from f32 sources (0xbfc00000, 0x33800000, 0x00800000) gives 0x8001 with f16 denormals kept and round to nearest even, 0x8000 with f16 denormals flushed and 0x8002 with f16 round toward -infinity. With f16 denormals kept, every f16 rounding mode is translated: the exact product-sum is rounded once in the mode's direction. All four modes match RDNA2 (gfx1036) on the 2,233 rows of the #2631 sweep, includingFLOAT_MODE0xcc (toward zero, used by ps5-opengl) and the exact cancellations toward -infinity; the PS5's gfx1013 is not measured. A result beyond 65504 becomes infinity or 65504 as IEEE 754 gives for the mode. Flushed f16 denormals still throw, and packed f16 still throws in every mode but round to nearest even with f16 denormals kept.FP16_OVFLset is not measured. - Signaling NaN inputs of the f32 and f16 unary instructions (
v_sqrt/rcp/rsq/log/exp/sin/cos/fract/floor/ceil/trunc/rndne/rcp_iflag/frexp_mant),v_ldexp_f32/f16,v_frexp_mant_f16,v_div_fixup_f32/f16andv_mullit_f32are returned quieted only with IEEE=1 and unchanged with IEEE=0, measured on RDNA2 (gfx1035) in both modes, and so are those of the f64 forms (v_sqrt/rcp/rsq/fract/floor/ceil/trunc/rndne/frexp_mant/ldexp/div_fixup_f64) andv_cvt_f64_f32, measured on RDNA2 (gfx1036) in both modes; shaders without a known float mode follow IEEE=0.v_pack_b32_f16quiets each signaling NaN half by the same rule, measured on RDNA2 (gfx1036) in both modes. A signaling NaN result ofv_cubesc/cubetc/cubema_f32is quieted by the same rule and keeps the sign the instruction gives it, measured on RDNA2 (gfx1036) in both modes.v_add/sub/subrev/mul/fma/fmac/fmamk/fmaak/mul_legacy_f32andv_fma_mix_f32with f32 sources choose their NaN result on the bits, measured over 1000 operand triples in both modes: the first NaN source in operand order after its modifiers (the subtrahend negated, as the hardware adds its negation), quieted only with IEEE=1, or the default NaN 0xffc00000 for an invalid operation; in the fma forms an invalid product (inf * 0) gives the default NaN even when src2 is a NaN, and in the legacy forms a zero factor makes the product 0 even when the other factor is a NaN (a NaN src2 still wins).v_mad_legacy_f32andv_mac_legacy_f32follow the same rule with their separately rounded product, as measured on gfx1010 (#1281, #1290): a NaN source quieted only with IEEE=1, inf - inf giving 0xffc00000, the zero rule hiding a NaN factor and a NaN src2 winning. The f16 and f64 arithmetic,v_fma_mix_f32with f16 sources and the separately rounded mad/mac forms (not measurable on RDNA2) follow the same rule on hardware but their NaN bits are left to the driver.v_cvt_f16_f32,v_cvt_f32_f64,v_dot2_f32_f16,v_fma_mixlo_f16andv_fma_mixhi_f16always quiet andv_div_scale_f32never does: the f32 signaling NaN 0x7f812345 as src0 or src2 gives 0x7e09 from mixlo and mixhi at IEEE=0 and IEEE=1 on RDNA2 (gfx1036), wherev_fma_mix_f32gives 0x7f812345 at IEEE=0 and 0x7fc12345 at IEEE=1. The payload of an f16 NaN source of mixlo and mixhi is left to the driver's f16 to f32 conversion, which NVIDIA replaces. - Scalar f16 add, sub, subrev and mul denormals follow the
FLOAT_MODEf16 denormal field at nearest-even: bit 0 preserves inputs and bit 1 preserves the result; otherwise a denormal is flushed to its signed zero. Measured on RDNA2 (gfx1036) with IEEE=0/1 and FP16_OVFL=0/1, over 32 operand pairs in all four denormal modes, including signed zeros, denormals, minimum normals, infinities and NaNs. Multiply detects tininess after rounding to 11 significant bits with an unbounded exponent, before gradual half packing:0x3bff * 0x0400flushes, while0x3c01 * 0x03ffrounds to0x0400and is kept. Directed rounding and ternary arithmetic keep their previous denormal lowering. Shaders without a known float mode keep the previous lowering. Scalarv_sub_f16/v_subrev_f16sharefloat16Binarywith add/mul and were measured on gfx1036 over the same operand set as add (1156 non-NaN-input rows per mode); NaN result bits still depend on the driver as recorded above. - f16 unary, frexp and conversion denormals follow the
FLOAT_MODEf16 denormal field:v_ceil_f16,v_floor_f16,v_fract_f16,v_rcp_f16,v_rsq_f16,v_sqrt_f16,v_log_f16,v_exp_f16,v_sin_f16,v_frexp_mant_f16,v_frexp_exp_i16_f16,v_cvt_norm_i16_f16,v_cvt_norm_u16_f16andv_cvt_f32_f16flush a denormal half input to its signed zero when bit 0 is clear (0 and 2), and the f16-result ones flush a denormal half result to its signed zero when bit 1 is clear (0 and 1), after the host rounds it to half. Measured on RDNA2 (gfx1036) at IEEE=0 and IEEE=1 with denorm16 0..3 over every non-NaN f16 input (63,490 rows per mode and instruction): the only mismatches are the 1-ULP driver differences ofv_exp_f16(2 inputs) andv_sin_f16(14), which are the same with denormals kept. NaN inputs are not compared.v_ldexp_f16shares the input rule.v_cvt_pknorm_i16_f16andv_cvt_pknorm_u16_f16are not measured and keep their previous lowering. Shaders without a known float mode keep the previous lowering. - Packed f16 NaN results (
v_pk_add/mul/fma/min/max_f16) follow the hardware measured on RDNA2 (gfx1035) at IEEE=0 and IEEE=1 with f16 round-to-nearest-even and f16 denormals kept: with IEEE=1 a signaling NaN input is quieted and min/max return the other operand only for a quiet NaN; with IEEE=0 NaN inputs pass through unquieted and min/max ignore signaling. Inv_pk_fma_f16an invalid product (inf * 0) gives the default NaN even when src2 is a NaN. A shader without a known float mode is translated as IEEE=0, the mode titles use; a mode with another f16 rounding or that flushes f16 denormals throws. - f32 denormals follow the shader's
FLOAT_MODEf32 denormal field (bits 5:4) when it flushes them (0, and shaders without a known float mode, as in the 0xc0 most titles use), measured on RDNA2 (gfx1035) over 1444 rows per instruction at IEEE=0 and IEEE=1.v_add/sub/subrev/mul/mul_legacy/mullit/fma/fmac/fmamk/fmaak/min/max/min3/max3/med3/fract/floor/ceil/frexp_mant/sin/cos_f32flush their f32 inputs and their result to a zero of the same sign (v_mullit_f32measured on RDNA2 (gfx1036) over 6907 rows per mode at IEEE=0 and IEEE=1, so a positive denormal src2 counts as zero); the f32 compares,v_cvt_i32/u32/rpi_i32/flr_i32_f32,v_frexp_exp_i32_f32,v_cvt_f64_f32,v_cvt_f16_f32,v_cvt_pkrtz_f16_f32,v_cvt_pknorm_i16/u16_f32andv_cvt_pk_u8_f32flush their f32 inputs.v_div_scale_f32flushes all three inputs to signed zeros, measured on RDNA2 (gfx1036) over 2744 operand triples per mode at IEEE=0 and IEEE=1 with f32 denormals flushed and kept; its scale-down behaviour and host denormal preservation remain incomplete.v_div_fixup_f32flushes its src1 and src2 and keeps a denormal src0 and result, measured on RDNA2 (gfx1036) over 6859 rows per mode at IEEE=0 and IEEE=1. Tininess is detected after rounding, measured on RDNA2 (gfx1036) forv_mul_f32andv_mullit_f32at IEEE=0 and IEEE=1: a product is flushed when its value rounded to 24 bits with an unbounded exponent is below 2^-126, so0x3f7ffffe * 0x00800001(2^-126 * (1 - 2^-46)) gives0x00800000and0x3f7fffff * 0x00800000(2^-126 * (1 - 2^-24)) gives zero. The check rounds in the f32 round mode ofFLOAT_MODE(bits 1:0), measured in all four modes, while the products themselves are still rounded to nearest even. This applies tov_mul_f32,v_mul_legacy_f32,v_mullit_f32, and tov_fma_f32,v_fmac_f32,v_fmamk_f32,v_fmaak_f32andv_fma_mix_f32when the addend is zero; with a nonzero addend the fused result is kept, so an exact result just below 2^-126 that rounds to it is not flushed. In the three directed modes the check is computed exactly from the significands. To nearest even (0, the mode of shaders without a known float mode) one factor is multiplied by 2^64, which is exact below 2^64, and the product with the other factor is compared with 2^-62. That multiply is a plainOpFMullike the product, and no f32 rounding mode is requested in the SPIR-V, so the check relies on the driver rounding f32 to nearest even by default, as the products do, and a product that rounds up to 2^-126 is the emitted product.v_fma_mix_f32follows this rule with mixed f16/f32 sources too, measured on RDNA2 (gfx1036) with f16 in either multiplicand slot, low and high half selection, and an f32 or f16 zero addend: f320x00801002times f160x3bffgives0x00800000to nearest at IEEE=0 and IEEE=1, while the neighbours0x00801001and0x00801003give zero and0x00800001. The tiny-product boundary was also measured for both signs in all four f32 round modes at IEEE=0; f16 inputs retain their previous conversion and denormal handling.v_fma_mixlo_f16andv_fma_mixhi_f16flush their f32 sources too and keep a product below 2^-126, measured on RDNA2 (gfx1036).v_mad_legacy_f32andv_mac_legacy_f32flush their inputs, their product and their result in every mode, see its entry; their product is still checked for tininess on its exact value, so one below 2^-126 gives zero even when it rounds to 2^-126, as the console's separately rounded encoding is not measured at this boundary. Denormal modes 1 and 2 throw for the other instructions. Not covered, and not measurable on RDNA2 (no such opcodes) or not measured:v_mac_f32,v_mad_f32,v_madak_f32andv_madmk_f32(rounded separately),v_dot2_f32_f16andv_div_fmas_f32(on hardwarev_div_fmas_f32flushes only its result), f32 interpolation andds_add_f32keep the previous lowering;ds_min/max/cmpst_f32flush both operands by the field, see the LDS float atomics entry. With f32 denormals kept (3) the results depend on the driver's default denormal behaviour, as noDenormPreserveexecution mode is requested;v_ldexp_f32and the f32 compares are the exceptions.v_cmp_X_f32andv_cmpx_X_f32compare the operands as sign-magnitude integers with the NaN cases handled apart, so a denormal is not read as zero, measured on RDNA2 (gfx1036) over 256 operand pairs per opcode at IEEE=0 and IEEE=1 with f32 denormals kept and flushed.v_ldexp_f32preserves subnormal inputs and rounds a subnormal result in the f32 round mode ofFLOAT_MODE, measured on RDNA2 (gfx1036) over 2163 rows in each of the four round modes at IEEE=0 and IEEE=1. An overflow rounds like any other result (infinity, or the largest finite value when the mode rounds that sign toward zero). With f32 denormals flushed it flushes a subnormal input and result, measured over the same rows in each round mode at IEEE=0 only. A device whose driver splitsFmainto a multiply and an add flushes a product below 2^-126 before adding a non-zero addend inv_fma_f32andv_fmac_f32:0x1fffffff * 0x1fffffff + 0x00800000gives0x00800000instead of the fused0x00ffffffon RADV RENOIR (Mesa 26.2.4), whose compiled shader hasv_mad_f32and nov_fma_f32. F32DenormalFlush accepts that sum on such a device. - v_rcp, v_rsq, v_sqrt, v_log, v_exp, v_sin and v_cos (f32 and f16) use the driver's approximation for finite results, not the console's: the low bits can differ. Only NaN, invalid, zero and infinite results and the exact sin/cos zeros are chosen on the bits. Measured on RDNA2 (gfx1036) over every f16 input and 32,832 f32 inputs: RADV returns the hardware's bits for every input of rcp, rsq, sqrt and log and all but one of exp (f16 0x11c5, 1 ULP); NVIDIA (615.71.09) stays within 2 ULP except
v_log_f32near 1.0 (next entry). v_sin and v_cos take the driver's sine or cosine of the radians of the exact cycle, except within 2^-12 of a turn from a zero of the function, where the result is the linear term of the exact distance to the zero: the hardware's constant 6.2831826 (0x40c90fd5) at the integers, which the hardware applies exactly to inputs below 2^-12 of a turn, and the f32 2π at the half and quarter turns, where the hardware is within 7 ULP of it. Outside those bands, a driver result more than 4 ULP from a Taylor series in turns of the exact cycle is replaced by the series, which is within 2 ULP of the true sine and cosine; the hardware is up to 9 ULP from them (gfx1035, 243,092 f32 inputs), so results stay within 13 ULP of the hardware on any driver. RADV returns the hardware's bits for every f16 input and for 32,422 sine and 32,486 cosine results of the 32,832 f32 inputs; the rest are 1 ULP off, except up to 14 ULP on either side of non-zero integers and up to 15 ULP beyond ±256 cycles, where the hardware's own reduction differs from the exact fraction, and up to 6 ULP inside the half- and quarter-turn bands. NVIDIA's own sine is within 1 ULP on 93% of the f32 inputs and up to 1,166 ULP off just outside the bands, its cosine within 163 ULP; the series replaces those results. A denormal f32 input with f32 denormals kept gives the hardware the input times 6.2831826; both hosts flush it. - v_log_f32 and v_log_f16 keep the driver's log2 unless, for inputs within 1/8 of 1.0, it is more than 4 ULP from an eight-term series in x - 1, which then replaces it.
v_log_f16goes through the same f32 lowering and is rounded to half. The series is evaluated for everyv_logand chosen withOpSelectrather than a branch, so its seven fused multiply-adds and two multiplies are an unconditional cost. Measured on RDNA2 over all 3,145,728 f32 inputs within 1/8 of 1.0 (gfx1035, IEEE 0, f32 denormals flushed), the hardware is within 1 ULP of the correctly rounded log2 and the series within 2 ULP of it and within 3 ULP of the hardware (2 except at 0x3f752e47, 0x3f7baab0 and 0x3f7bc4fb), so a kept driver value is within 7 ULP of the hardware and a driver that returns the hardware's bits never triggers the gate; RADV (Mesa 26.2.4) returns the hardware's bits on the 9,280 such inputs measured on gfx1036; NVIDIA (615.71.09) was up to 23,947,836 ULP off at 1 - 2^-24 and is within 6 ULP inside the band after the change; outside it its own log2 stays up to 9 ULP off (measured up to 1.375). Over every f16 input, the change moves onev_log_f16result on NVIDIA (0x3bed, 1 ULP, onto the hardware's bits) and none on RADV. - Ordered not-equal, less-than and greater-than compares (shader recompiler) -
FPOrdNotEqual32is emitted asOpFUnordNotEqual, andFPOrdLessThan32andFPOrdGreaterThan32as their ordered forms, each with both operands checked for NaN on their bits; the results are the same for every input. The AMD Windows driver 32.0.13058.8001 (Radeon 860M, RDNA 3.5) returns 1 for a NaN operand when any float form of not-equal feeds a ballot on a 32-wide subgroup (#825), and so does a less-than or greater-than against an infinity the driver sees as a constant (#3536), presumably rewritten into a not-equal by its compiler - DB_SHADER_CONTROL.PRIMITIVE_ORDERED_PIXEL_SHADER (shader recompiler) - the host pixel interlock spans the whole shader, not only up to
s_sendmsgORDERED_PS_DONE. - POPS collision SGPRs (shader recompiler) - the collision wave ID and intrawave collision mask read 0, so a title skips its POPS wait and runs its per-collision loop once.
- Image loads (shader recompiler) - an image_load reads a sampled binding, also of an image the shader stores to, so in a primitive-ordered pixel shader it misses an earlier overlapping primitive's image_store in the same draw.
- Tessellation rings of local, hull and domain programs become Vulkan tessellation interfaces and are never written to guest memory; a title that reads the off-chip or factor ring back sees no data.
- Inner tessellation factors of fractional-odd triangle patches that are NaN tessellate as 1, as in the D3D11 reference tessellator; the console is not measured.
- Hull programs start from the hull launch registers (user data, ring bases, v0, v1); on the console they start with what the local program leaves, and that it leaves them unchanged is not checked.
- The output modifier (
mul:2,mul:4,div:2) follows the shader's float mode as measured on RDNA2 (gfx1035): the hardware applies it only with IEEE mode off and output denormals of the result's precision flushed (bit 1 of theFLOAT_MODEdenormal field clear), and ignores it otherwise. On f32 results it is applied as the hardware does with f32 denormals flushed; a shader without a known float mode is translated with IEEE mode off and f32 denormals flushed, the mode titles use. On f16 and f64 results it is ignored, and it throws in the modes where the hardware applies it. - The clamp modifier follows
DX10_CLAMP, measured on RDNA2 (gfx1035) for f32, f16, packed f16 and f64 results at IEEE=0 and IEEE=1: withDX10_CLAMP=1 (and in shaders without a known float mode) a NaN result clamps to +0, including the quieted NaN thatv_pk_min_f16andv_pk_max_f16return for a signaling NaN operand with IEEE=1 (measured on RDNA2 gfx1036 at bothIEEE_MODEand bothDX10_CLAMPsettings); withDX10_CLAMP=0 a NaN result is written unchanged and every other value clamps to [0, 1] as before. f64 results follow the same rule on hardware, but a clamped f64 result withDX10_CLAMP=0 throws until it has an execution test. WithDX10_CLAMP=0 the NaN's bits are those the translation already produces for the instruction without clamp, so where those are left to the driver (f32 and scalar f16 arithmetic) the payload is the driver's, not the hardware's input propagation.v_dot2_f32_f16and the integer clamps are unchanged. - FP16_OVFL follows the shader's float mode as measured on RDNA2 (gfx1036) at IEEE=0 and IEEE=1 with f16 denormals kept and flushed: with
FP16_OVFL=1 an f16 result that rounds to ±infinity is written as ±0x7bff unless a source operand is infinite, sov_rcp_f16,v_rsq_f16andv_log_f16of ±0 andv_exp_f16of 16 or more are clamped while an infinite input passes through, andv_div_fixup_f16keeps the infinity it writes for a zero denominator or an infinite numerator and clamps any other infinite quotient, a NaN one included.v_cvt_pkrtz_f16_f32rounds toward zero and never overflows. The sources are checked as the f32 values the translation computes with; the rule is applied tov_fma_mix*_f16with f32 sources and tov_interp_p2_f16without a measurement of infinite f32 sources or parameters. Round modes other than nearest even are not modelled, so the overflow threshold is the nearest-even one (65520). - Directed f16 rounding for
v_cvt_f16_f32,v_cvt_f16_i16,v_cvt_f16_u16andv_ldexp_f16followsFLOAT_MODEbits 3:2 only when f16 denormals are preserved (bits 7:6 = 3), measured on RDNA2 (gfx1036). With f16 denormals flushed (bits 7:6 = 0), or flushed on one side (modes 1 and 2, not measured), the hardware also flushes the f16 result and the directed path does not model that, so those shaders keep round-to-nearest-even. Theclampmodifier with directed rounding is not measured. - Comparison sampling of a color texture (
image_sample_c/image_sample_c_lzon a texture that is not R32 float or R16 unorm) is emulated in the shader, since Vulkan compares only depth formats: the red channel is fetched, compared and, for bilinear filtering, blended (compare, then filter). Only 2D, 2D array and cube views, float/unorm/snorm formats, wrap, clamp-to-edge and clamp-to-border addressing (border red 0 for the black borders, 1 for opaque white) and point or bilinear filtering at one level are implemented, with texel offsets added to the texel indices; LOD biases and clamps are accepted, as they cannot select another level of a single-level sample. Half-border addressing, the border color table, gradients, gathers, other dimensions, anisotropy and mip filtering throw. Under bilinear filtering, taps past a clamp-to-border edge compare the border value and blend with the texel taps, so results near the edge depend on the filter weights. AMD documentation does not say how a color format's reference is handled: it is clamped to [0, 1] for unorm and [-1, 1] for snorm, as Vulkan does for unorm depth, and not clamped for float. 8-bit sRGB (Format8Srgb,Format8_8Srgb,Format8_8_8_8Srgb) returns 0 for every compare function, measured withimage_sample_c_lzon gfx1035 over references -0.5..1.5 and texel codes 0..255; that measurement also returns 0 for 8-bit UNORM, which this path still compares. A bilinear cube of those 8-bit sRGB formats withoutDISABLE_CUBE_WRAPreturns 0 for the same reason; float, unorm and snorm bilinear cubes without that bit still throw. Bilinear weights are full float, not the hardware's fixed-point subtexel weights - Negative color-texture comparison wrap indices (libSceAgcDriver) use explicit
OpSRemnormalization to implement modulo; the negative-offset point-wrap regression fails with directOpSModand passes with normalization on Mesa 22.3.6 llvmpipe (LLVM 15.0.6), but negative-index wrap has not been measured on PS5 hardware. - Converted unorm images (10_11_11_UNORM, read and written through an R32_UINT view): samples, gathers, LOD queries, 16-bit data and a DST_SEL of the missing W channel throw.
- Converted unorm image loads divide by 2^n - 1 with the buffer format path's
OpFDiv, which Vulkan allows to be up to 2.5 ULP off, so some drivers are not bit-exact. - 64-bit LDS atomics (
ds_*_u64,ds_*_b64,ds_*_f64) read and write the two dwords under a workgroup lock stored after the guest LDS, as LDS is a 32-bit array without 64-bit atomics. On RDNA2 (gfx1036) the LDS serialises a 32-bit atomic on a slot's high dword and a 64-bit atomic on that slot: 128 lanes ofds_add_u32and 128 lanes ofds_add_u64on one slot sum exactly. Only that pair was measured. Every 32-bit LDS atomic of a program that contains a 64-bit LDS atomic therefore takes the same lock, including a 32-bitflat_atomic_*whose address lands in the LDS aperture (a 64-bit one takes it already); the flat forms were not measured against a 64-bit atomic either (the other 32-bit LDS atomics,ds_min/max,ds_cmpst, the float forms,ds_inc/dec,ds_mskorandds_wrxchg, are extrapolated fromds_add_u32and were not measured against a 64-bit atomic); a program without a 64-bit LDS atomic keeps its native 32-bit atomics.ds_condxchg32_rtn_b64is twods_mskorhalves, each under the lock but not atomic as a pair, so a 64-bit atomic between the two halves is not excluded. Sub-dword writes (ds_write_b8,ds_write_b16and their_d16_hiforms) and plainds_read_*,ds_write_*,ds_append_u32andds_consume_u32are not taken under the lock, so a plain access racing a 64-bit atomic on the same dwords can see or overwrite one half of it; whether the hardware orders them was not measured. The lock serialises the workgroup's LDS atomics of such a program; the throughput cost was not measured.VK_KHR_workgroup_memory_explicit_layoutwithshaderSharedInt64Atomicswould make both widths native and is not used. A 64-bit atomic at an address that is 4-byte but not 8-byte aligned faults the wave on gfx1036, as on gfx1035. - LDS float atomics (
ds_min/max/cmpst_f32,ds_min/max/cmpst_f64and their_rtnforms) follow the hardware measured on RDNA2 (gfx1036) over 14 x 14 operand sets (zeros, ones, infinities, denormals, quiet and signalling NaNs of both signs and with payloads) in IEEE 0 and 1 with every value 0-3 of the type's denormal field: both operands are flushed to a signed zero when bit 0 of the field is clear (0 and 2; 1 and 3 keep them; the f32 field for the f32 forms, the f16/f64 field for the f64 forms; a shader without a known float mode flushes f32 and keeps f64), then a signalling-NaN data operand is stored quieted, else a signalling-NaN old value is stored quieted, else a quiet-NaN old value is replaced by the data, else a quiet-NaN data operand loses, else the ordered compare with -0 below +0 decides, and the flushed value is what is written, also when the old value wins.cmpstcompares the flushed bits (both zeros equal, NaN never equal) and leaves memory unchanged when they differ. The returned value is always the old value as it was in memory.IEEE_MODEhas no effect. The same bit rule serves the buffer float atomics (AtomicFloatBits). Not measured: a denormal or signalling-NaNdata1ofcmpst(stored as given), andds_add_f32, which keeps the host'sOpFAdd. - V# bases off a DWORD boundary (shader recompiler) - raw accesses use the exact bytes and
s_buffer_loadreads(base & ~3) + (offset & ~3), as measured on gfx1035 (Linux, alignment mode UNALIGNED; the console's mode is unknown). Formatted accesses off their element's alignment and atomics throw. - 32-bit bitfield masks (
S_BFM_B32,V_BFM_B32) use the truncating 32-bit shift specified by the RDNA 1.0 ISA, sections 12.1 (opcode 36) and 12.12 (opcode 867). Width and offset use their low five bits. This matches gfx1035 (Linux) for every width/offset pair, with and without upper operand bits set, and both leave SCC unchanged; not measured on the console. - Vector buffer offsets that are not a multiple of 4 (shader recompiler) - dword accesses count whole dwords from the V# base, where the hardware reads the exact bytes at base + offset.
- Raw
buffer_load_dwordx2is one 64-bit access on the hardware (with or without glc), so it never sees half of abuffer_atomic_swap_x2. Only a glc load that is 8-byte aligned and in bounds, in a program that has the 64-bit storage view (it uses 64-bit buffer atomics), is read with one 64-bit atomic load; every other dwordx2 load is two dword loads and can tear against a concurrent 64-bit write: loads without glc, and loads in a program without 64-bit buffer atomics while another shader writes the pair withswap_x2. - Buffer loads without GLC or DLC (shader recompiler) are not coherent in a program that has GLC or DLC buffer accesses and no
buffer_gl0_inv,buffer_gl1_inv,s_gl1_invors_dcache_inv: only its GLC and DLC loads, stores and atomics use theCoherentbuffer view, so a plain load can return a value cached before another work-group's write. This follows the LLVM AMDGPU memory model for GFX10, in which a load-acquire is a GLC DLC load followed bybuffer_gl1_invandbuffer_gl0_inv; it is not measured on the console or on RDNA hardware. - DS src2 operations (
ds_*_src2_*, GFX10.1 only) readDS[B], withB = A + 4 * sext(offset1[6:0]:offset0), and then updateDS[A]with the matching 32-bit atomic, so the read ofBis not atomic with the update ofA. The form that takes the offset from the address (offset1[7]set), the 64-bit forms and GDS throw: whether the LDS address keepsA[31:17]in that form, and whether the 64-bit forms scale the offset by 4 or 8, is not documented.ds_min_src2_f32,ds_max_src2_f32andds_add_src2_f32throw: on RDNA2 (gfx1035, the oracle's hardware, which has no src2 opcodes)ds_min_f32,ds_max_f32andds_add_f32ignoreIEEE_MODEbut depend on whether f32 denormals are flushed (28, 28 and 12 of 196 special rows differ), and that is not modelled for LDS float atomics yet (#981). - s_swappc_b64 writes the link pair with the same model as
s_getpc_b64: the guest return addresspc + 4as a byte offset overGetShaderBase. Any other use of the link pair (arithmetic, compare, hash, store) throws, apart from the overwrites and shared links that static scalar calls allow; constant-offset folding over the link is not modeled. The special encodings_swappc_b64with SDST 125 still decodes ass_setpc_b64, which matches the hardware: measured on RDNA2 (gfx1036) in wave32 and wave64, it branches to the source pair and leaves a marker SGPR pair untouched, and with an SGPR destination the link isPC + 4. - The s_swappc_b64 fetch-shader call at vertex/local/tessellation/mesh entry assumes the called fetch-shader blob ends in
s_setpc_b64to the link pair (the GNM fetch-shader shape shadPS4'sEmitFetchcopies); the blob is not part of the shader program, so its return closes the pairing by assumption and any in-program reader of the link pair throws. Attribute fetches are inlined from the request's parsed fetch-shader plan through the sameGetAttributepath as the embedded fetch analyzer, honoring the buffer resourcedst_sel; an attribute writing more than four registers, or an entry call without a parsed plan, throws. The vertex/instance offset accumulator inside the fetch blob is not replayed for external blobs. - The scalar aperture sources src_shared_base, src_shared_limit, src_private_base, src_private_limit (core/shader/recompiler) decode to their own operand kinds (scalar source codes 235 to 238, named as in LLVM's
SIRegisterInfo.td), but their value is not modelled: reading one refuses with the source it refused named, because the wave's shared and private aperture bases and limits are unknown. Both the scalar-code read and the raw-operand read refuse; a shader that only carries the value into an address still prepares, one that reads it does not - EmitSetAttribute (shader recompiler) - a primitive with a vertex whose clip position is (0,0,0,0) is clipped away through an extra
gl_ClipDistanceelement on vertex and tessellation evaluation shaders, matching what a Radeon 610M (RDNA2, RADV) and llvmpipe rasterise; NVIDIA 615.78.08 without it draws the vertex at the viewport centre. Measured for triangles (strip and list) and for a point at (0,0,0,0), which the Radeon 610M does not draw, NVIDIA draws at the viewport centre and llvmpipe at the top-left corner; lines with such a vertex are clipped by the same element and were not measured. Mesh, geometry and the generated rect-list and barycentric stages do not write it, a device withoutshaderClipDistancecannot compile vertex shaders (the compile throws), and guest clip distances are still refused by the pipeline validator. The PS5 rasteriser was not measured. - LINE_STIPPLE_TEX (recompiler) reads as 0: line stipple is not emulated. RADV enables the input only as the required pixel input of a shader that reads no interpolants, because it initializes the fewest VGPRs (Mesa
radv_compute_spi_ps_input), and never reads it - Static scalar calls (
s_call_b64ands_swappc_b64) require an ordinary even-aligned SGPR link pair (s0:s1 through s104:s105) and a laters_setpc_b64return reading that exact pair. A pair serves one call, or several calls whose regions are disjoint, each closed by the first return reading the pair after its target. Outside every region of its calls the pair may only be overwritten whole, bys_mov_b32,s_mov_b64, an SOP2 result or a scalar load, as the caller of a user-data callee does before each call. Regions must nest or be disjoint; recursion, other shared links, escaping link values (including overlapping tuples and resource descriptors), invalid targets, interleaved regions, cross-region branches or fall-through, relative SGPR reads and dynamics_setpc_b64in a program with calls throw. The analysis checks all decoded instructions, including unreachable code. Arbitrary indirect calls and general call frames are not implemented. - Relative vector addressing (
v_movrels_b32,v_movreld_b32,v_movrelsd_b32,v_movrelsd_2_b32,v_swaprel_b32) keeps every vector register from the lowest base register the program addresses relatively up to the last register the program names in a zero-initialised function-scope array per invocation (LoadVectorWindow/StoreVectorWindow, one element per register), so an indexed access is one load or store instead of a compare-and-select chain over the window; the host compiler decides whether that array stays in registers or spills to scratch, which is not measured. A register in the window the program never wrote reads 0, as an unwritten SSA register does; the console's value is undefined. M0 is unsigned: an index at or past the window readsv0and drops its write, andv_swaprel_b32writes neither side when either is out of range, as measured on RDNA2 (gfx1035,agc_driver_relative_indexing). The window ends at the last register the program names rather than at the shader's VGPR allocation, which the recompiler does not receive, so a relative write past the named registers but inside the allocation is dropped where the hardware keeps it for a later relative read. Killzone: Liberation's PSP vertex program (PPSA11142, 3176 ISA words, 104 relative accesses over 154 registers) went from 649,515 to 392,333 SPIR-V words with this. Pixel programs whose relative span contains the packed ancillary input instead keep that span in SSA and implement indexed operations with compare/select chains. This keeps ancillary definitions visible to field lowering and masked-write proofs; storing the unmodelled whole word in a private array would require composing unknown bits. A fold after masked-select elimination lowers fields whose consumers become visible only after redundant copies are removed. Whole-word uses still throw. Both representations include implicit entry inputs in the inferred register limit and retain unsigned M0, inactive-write and DPP update rules; wave32/wave64 native Vulkan readbacks cover relative reads, writes, split-offset swaps, inactive writes, DPP masks and out-of-range accesses, but console comparison and the performance cost of selecting over this span are unverified. - s_call_b64 saves
PC + 4overGetShaderBaseand branches toPC + 4 + signext(SIMM16) * 4, following AMD RDNA2 ISA sections 3.6.3, 12.2 and 12.3. Forward callees afters_endpgmare decoded. Measured on RDNA2 (gfx1036) in wave32 and wave64 with SIMM16 = 3: the link pair receivesPC + 4, the branch goes toPC + 4 + signext(SIMM16) * 4, ands_setpc_b64of the link returns to the instruction after the call. Execution on the PS5 (gfx1013) and title compatibility for these bounded scalar calls remain unverified;s_rfe_b64still throws in translation.