docs(shader): record negative texture coordinates on RDNA2

This commit is contained in:
brumskysb committed 2026-11-02 10:23:56 -07:00
1 parent 1d91f2966d
commit 2f101ee4a0
1 file changed
+1 -1
+1 -1
View File
@@ -184,7 +184,7 @@
- [v_cvt_pkrtz_f16_f32 and v_pack_b32_f16 denormals](../../../core/shader/recompiler/Translation/src/ConvertInstructions.cpp) follow the `FLOAT_MODE` f16 denormal field: `v_cvt_pkrtz_f16_f32` flushes each denormal half result to its signed zero when bit 1 is clear (0 and 1), and `v_pack_b32_f16` flushes each denormal half input to its signed zero when bit 0 is clear (0 and 2). Measured on RDNA2 (gfx1031, Radeon RX 6700 XT, AMD Windows driver 32.0.21045.5002 through HIP) at IEEE=0 and IEEE=1 with denorm16 0..3 and f32 denormals kept, over 256 pairs of 16 f32 values around the f16 denormal range for `v_cvt_pkrtz_f16_f32` and 256 pairs of 16 halves (zeros, denormals, minimum normals, ±1, the largest finite value, infinities and quiet and signaling NaNs) for `v_pack_b32_f16`. Shaders without a known float mode keep the previous lowering. The PS5's gfx1013 is not measured.
- [Color comparison sampling](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) (`image_sample_c`/`image_sample_c_lz`, excluding native R32 float/R16 unorm) returns zero for every function on sampled floating-point GFX10 formats (codes 1–77 except 7 and 22) and corresponding GFX11 formats measured on gfx1100. Point and bilinear results match on gfx1100. The Vulkan shader still lowers the comparisons but overrides the result with zero, including NaNs and TRUNC_COORD. Scaled, integer and converted formats, image tables, min/max reduction, unnormalized coordinates and unsupported addressing throw; only 2D, 2D-array and cube views are accepted. PS5 gfx1013, cube behavior, arrays except codes 1 and 56, and anisotropic comparisons remain unverified. Comparison bilinear weights remain full float rather than hardware fixed point
- [Shared sampling lowering](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) rounds array layers half to even and retains unordered NOT_EQUAL arithmetic, following adriadam10's RDNA2 gfx1035 measurements in [#907](https://github.com/boykopovar/AnyPS5/pull/907#pullrequestreview-5439697732), not measurements on a PS5. Constant-zero color comparison tests cannot verify layer selection or nonzero NaN comparisons. Converted unorm sampling shares the layer selection and tests it with distinct values in four layers
- [Negative color-texture comparison wrap indices](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) (libSceAgcDriver) use explicit `OpSRem` normalization to implement modulo; the negative-offset point-wrap regression fails with direct `OpSMod` and passes with normalization on Mesa 22.3.6 llvmpipe (LLVM 15.0.6), but negative-index wrap has not been measured on PS5 hardware.
- [Negative color-texture comparison wrap indices](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) (libSceAgcDriver) use explicit `OpSRem` normalization to implement modulo; the negative-offset point-wrap regression fails with direct `OpSMod` and passes with normalization on Mesa 22.3.6 llvmpipe (LLVM 15.0.6), but negative-index wrap was measured on RDNA2 (gfx1036) over coordinates four periods deep: wrap reads floor(u*w) mod w on both axes, mirror reflects every other period, mirror-once clamps after one, clamp-last pins to texel 0, and the comparison form reads the same texels as the plain form. The PS5's gfx1013 is not measured.
- [Converted unorm images](../../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) (10_11_11_UNORM, read and written through an R32_UINT view) are sampled in the shader, the way color comparisons are emulated: `image_sample` of a 2D or 2D array view fetches the one or four texels at LOD 0, decodes them and blends them, for point or bilinear filtering with equal minification and magnification filters and wrap or clamp-to-edge addressing. A shader gets these sample modes only once a converted descriptor reaches one of its sampled images: that capture throws `ConvertedSampleRequired`, the driver records the program and prepares it again with `GuestContext::convertedSamples`, so such a shader is prepared twice per run and its first capture is repeated. The record lives with the registered program and is not kept across runs. Bilinear weights are rounded to the hardware's 8-bit grid (`roundEven(fraction * 256) / 256`, ties to even), as measured with `image_sample_lz` on RDNA2 (gfx1035) for 10_11_11 UNORM texels by adriadam10 in #5958; point sampling, decoding, swizzles and wrap and clamp-to-edge addressing matched the same measurement. A sample that is not `_lz` needs an image with one mip level; gathers, LOD queries, clamp-to-border and mirrored addressing, other filters, min/max reduction, unnormalized coordinates, images selected at run time, 16-bit data and a DST_SEL of the missing W channel throw.
- [64-bit buffer atomics without shaderBufferInt64Atomics](../../../core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp) (`buffer_atomic_or_x2`, `buffer_atomic_and_x2`, `buffer_atomic_xor_x2`) whose result no instruction reads compile to one 32-bit atomic on each dword when the device has no `shaderBufferInt64Atomics`, for example on MoltenVK, if every 64-bit atomic of the program has that form and the same opcode. The pair is not atomic: a plain load that races it can see one half applied, and two different 64-bit bitwise operations on one word can end in a value that no serial order gives. Two programs that run together and apply different operations to one word are not covered. The address checks are those of the 64-bit form. Any other 64-bit buffer atomic, one whose result is read, or a program with two opcodes fails to compile on such a device. Not measured against a native 64-bit atomic on hardware; the compile cases are in `agc_shader_memory_tests`.
- [64-bit LDS atomics](../../../core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvMemoryInstructions.cpp) (`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 of `ds_add_u32` and 128 lanes of `ds_add_u64` on 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-bit `flat_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_mskor` and `ds_wrxchg`, are extrapolated from `ds_add_u32` and 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_b64` is two `ds_mskor` halves, 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_b16` and their `_d16_hi` forms) and plain `ds_read_*`, `ds_write_*`, `ds_append_u32` and `ds_consume_u32` are 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_layout` with `shaderSharedInt64Atomics` would 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; with the workgroup lock, in device memory and in the invocation-private LDS of other stages alike, the recompiler records the `Unaligned` shader fault with the LDS byte address, leaves both dwords unchanged and returns 0, but the lane and the rest of its wave keep running. When the LDS lives in device memory (compute LDS beyond the device's shared-memory limit) and the device has `shaderBufferInt64Atomics`, no lock is used: a 64-bit atomic is a compare-exchange loop on a 64-bit view of the storage buffer and a 32-bit atomic is a native buffer atomic, so a mixed-width pair on one slot relies on the device ordering both widths on one buffer address; only `ds_add_u32` against `ds_add_u64` was measured, on gfx1036 (AMD Windows driver 32.0.21042.62) and an RTX 4070 SUPER. The lock is not used there because AMD's Windows driver compiles an acquire or release on a storage buffer, at workgroup scope or wider, to `s_barrier`, and those barriers inside the lock's divergent loop pair with the program's own `s_barrier`s. The fence after each LDS atomic compiles to `s_barrier` on that driver as well, so a wave that skips an LDS atomic that other waves of its workgroup execute misses a barrier they reach; its effect was not measured.