Commit Graph
2175 Commits
Author SHA1 Message Date
Adrià d0cf19b2cb Merge pull request #12233 from beardsciences/fix/nvidia-mesh-window-buffer
fix(shader): keep mesh register windows out of local memory on NVIDIA
2026-10-30 02:31:36 +01:00
Adrià f46f453a56 Merge pull request #12724 from Gonic0/fix/shader-single-lane-elected-max
fix(shader): give one-lane waves an elected atomic max per lane
2026-10-30 00:18:15 +01:00
Adrià 5e6c22e772 Merge pull request #12208 from XairRrR/fix/shader-srt-read-below-loaded-pointer
fix(shader): poison an SRT read below a pointer loaded from memory
2026-10-29 21:52:04 +01:00
Adrià 1b9d8c99a2 Merge pull request #12529 from imellag/revert/shader-scalar-buffer-bounds
revert(shader): check scalar buffer bounds in 64 bits again (#10337)
2026-10-29 21:20:19 +01:00
Christian 6f79055a00 fix(shader): keep mesh register windows out of local memory on NVIDIA
On NVIDIA's proprietary driver, function arrays that live in local memory
and are read under a data-dependent branch return wrong values in
subgroups 1-3 of a mesh workgroup; subgroup 0 is correct. A wave64
geometry program whose relative addressing keeps its VGPRs in the vector
window therefore loses register writes in every wave but the first. In
the clipping geometry program of Sony's PS5 PSP emulator (PPSA07550) the
empty waves never cleared their candidate primitive slots and wrote
degenerate triangles over wave 0's, so the 3D menu drew spikes.

On that driver a mesh program's window now lives in a device-local
storage buffer of 256 workgroup slots of 65536 dwords, claimed through a
per-slot lock and released after the final barrier. The pool is created
before the first draw that uses it records anything, and its header is
zeroed in a batch of its own that is submitted and waited for; the pool
is published only after that wait, and a failure releases it. Direct
register accesses keep one function variable per register; a direct
store also writes the buffer when a buffer read can see it, relative
reads load the buffer, relative writes store only to it, and a direct
read that may follow a relative write loads the buffer and refreshes the
variable. A buffer entry is zeroed only when a buffer read can see it
before a write. APS5_NO_MESH_WINDOW_BUFFER=1 keeps the arrays.
2026-10-29 08:56:39 -04:00
Adrià 1ac12cc534 Merge pull request #10803 from slopgobblercobbler/fix/spirv-instruction-word-bounds
fix(shader): validate SPIR-V instruction word count boundaries and phi patching
2026-10-29 13:48:55 +01:00
Adrià c33bb03591 Merge pull request #11820 from milescoviello/perf/ir-inline-operands
perf(shader): keep up to three IR operands inside the value
2026-10-29 12:58:07 +01:00
Dean 70003eb812 Merge pull request #12666 from JJenkx/windows/shader-cache-native-file-times
fix(shader): read shader cache file times natively on Windows
2026-10-29 23:56:36 +13:00
Adrià 9f04702aee Merge pull request #10831 from cgrd/fix/buffer-vsharp-type-stores
fix(shader): drop vector stores through TYPE 2/3 and FORMAT 0 buffer V#s as measured
2026-10-29 11:21:59 +01:00
Dean 4c7d275bef Merge pull request #11684 from oneandonlydean/feat/agc-unclamped-normalized-blend-v2
feat(agc): blend into an unclamped normalized color target in the pixel shader
2026-10-29 20:48:48 +13:00
Adrià 27e4c68eb6 Merge pull request #12280 from Filip722/perf/shader-identity-export-mapping
perf(shader): bypass identity fragment export mappings
2026-10-29 08:48:14 +01:00
Nicolas GonzalezandClaude Opus 5.5 5ff5075544 fix(shader): give one-lane waves an elected atomic max per lane
On a one-lane wave (vertex and local programs without vertex subgroups,
merged hull programs), a wave64 f32 max half-wave scan threw at its
v_permlanex16_b32. When the scan's lane 31 and 63 reads only elect,
through v_cmpx_eq_f32 against each lane's own key, the lanes that
buffer_atomic_umax or smax that maximum at a wave-uniform address with an
unused result, each invocation now takes its own key: every active lane
writes it, and memory ends with the wave's maximum because the keys are
proven non-negative and not NaN. The permutation, whose value then
reaches no other result, keeps its own value.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-29 02:04:56 -03:00
Dean b433c40f2f Merge pull request #12251 from Michael-KD/fix/div-scale-fixup-f32-denormal-modes
fix(shader): translate v_div_scale_f32 and v_div_fixup_f32 in f32 denormal modes 1 and 2
2026-10-29 16:25:43 +13:00
Dean aae2682289 Merge pull request #12191 from JJenkx/fix/agc-d16-float16-image-store
fix(shader): keep the half bits of a 16-bit float image store's D16 data
2026-10-29 16:25:20 +13:00
JJenkxandClaude Opus 5.5 e2f86a2047 fix(shader): read shader cache file times natively on Windows
The MinGW std::filesystem::last_write_time reads and sets file times
through the UCRT stat and utime calls, which convert through local time
using TZ. With a TZ other than the Windows zone, including the one libc
applies from the guest environment, times shifted by the difference:
under TZ=IST-5:30 on an Eastern host a temporary written that second
was 9.5 hours old, so housekeeping deleted it, and a TZ west of the host
set the active cache stamp hours in the past. Housekeeping now reads
times with GetFileAttributesExW, refreshes the stamp with SetFileTime
and compares in system_clock. The housekeeping test runs its child
with a TZ 12 hours from the host zone.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 21:05:40 -04:00
Dean 35f59e44eb Merge pull request #12228 from MarcZX10/fix/rectlist-tessellation-clip-distance-components
fix(shader): count vertex clip distances in the rect-list tessellation limits
2026-10-29 12:10:29 +13:00
Dean d43e6bd663 Merge pull request #12185 from Gonic0/fix/recompiler-user-data-call-across-branches
fix(recompiler): link user-data calls whose target copy precedes forward branches
2026-10-29 07:27:53 +13:00
XairRrR 91bbd2fca5 fix(shader): poison an SRT read below a pointer loaded from memory
A read at a negative offset from a pointer the walker loaded from guest
memory (a null pointer behind a guarded branch, offset -4) wrapped below
zero in AddSignedAddress, and the walk threw "cannot evaluate" instead of
recording the read as inaccessible. Uncharted: The Lost Legacy threw it
while loading its first level. The read is now recorded as an SRT poison
at its address wrapped to 48 bits, like any other unreadable address, so
the binding faults only if the shader takes that path.

verifyGuardedNullPointers gains the case for both branch outcomes; it
throws the title's error without the change.

AI-assisted: yes
2026-10-28 18:47:47 +01:00
Emanuele 1fd69ab767 revert(shader): check scalar buffer bounds in 64 bits again (#10337)
The 32-bit OpUMulExtended form of #10337 raises the GPU time per frame on MoltenVK by 50 to 90 percent in God of War Sons of Sparta and Planet of Lana. Its extended BufferDescriptorRuntime fixture stays: the 64-bit check makes the same bounds decision.
2026-10-28 18:44:34 +01:00
Adrià beb20cfcdf Merge pull request #11719 from milescoviello/perf/srt-evaluator-requirements
perf(shader): reuse the evaluator's requirement list across values
2026-10-28 17:35:37 +01:00
Adrià ecfad8e66c Merge pull request #11711 from danielcadev/fix/runtime-formatted-store-expansion
fix(shader): bound runtime formatted-store expansion
2026-10-28 17:35:31 +01:00
Dean d5b654d21c Merge pull request #11949 from 00xDisaster/fix/windows-include-path-quoting
build(windows): quote the forced standard-stream include path
2026-10-29 03:34:16 +13:00
Adrià 123f7f3b60 fix(shader): keep multi-dword TYPE 2/3 stores throwing and move the failed-capture fixture to TYPE 1 2026-10-28 14:42:32 +01:00
cgrdandClaude Opus 5.5 4620e29426 fix(shader): drop vector stores through TYPE 2/3 and FORMAT 0 buffer V#s as measured
A buffer V# whose TYPE is not 0 threw "buffer descriptor uses an
unsupported type" for every access. Measured on a PS5 (CFI-1115B,
firmware 12.20): through a V# with TYPE 2 or 3 and FORMAT 0 or a valid
format, buffer_store_dword and buffer_store_format_x write nothing and
don't fault, so a buffer a shader only stores to through one is bound as
a null V#. Loads and atomics through those V#s leave the destination VGPR
unchanged, which a null V# can't reproduce, so they still throw. Through
a TYPE 0, FORMAT 0 V#, vector loads return 0 and stores are dropped, and
those buffers are bound as a null V# too. TYPE 1 faults the GPU for every
access measured and keeps throwing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 14:42:23 +01:00
Dean 0c4d871f18 Merge pull request #12253 from Michael-KD/fix/fma-f32-denormal-modes
fix(shader): translate the fused f32 fma forms in f32 denormal mode 2
2026-10-29 00:32:38 +13:00
Dean 7d223dd6da Merge pull request #12252 from Michael-KD/fix/f32-rounding-frexp-denormal-modes
fix(shader): translate f32 fract, floor, ceil and frexp_mant in f32 denormal modes 1 and 2
2026-10-29 00:32:22 +13:00
Filip Michalski 90c9d5c8d3 chore(shader): update identity exports to current main 2026-10-28 11:43:04 +01:00
Dean 3a40945e62 Merge pull request #12298 from oneandonlydean/revert-11199-helper-lane-masks
revert(shader): restore pixel helper lanes in guest lane masks (#11199)
2026-10-28 22:57:31 +13:00
Dean e40163ef16 Merge pull request #11967 from milescoviello/perf/ir-instruction-pool
perf(shader): allocate a program's instruction list nodes from a pool
2026-10-28 22:53:40 +13:00
Dean 94341855b4 Merge pull request #11916 from Bram910/fix/shader-pack-mapping-ownership
fix(shader): preserve ownership of mapped shader pack entries
2026-10-28 22:53:02 +13:00
Dean 3917fa85ce Merge pull request #12188 from Gonic0/fix/shader-lds-lock-fences-moltenvk
fix(shader): fence the LDS lock's critical section on MoltenVK
2026-10-28 22:51:00 +13:00
Dean 7c7cfacc2d Merge pull request #12173 from MarcZX10/fix/shader-tessellation-instance-patches-zero-control-points
fix(shader): do not divide by zero control points when keying tessellated draws
2026-10-28 22:50:20 +13:00
Adrià a444a18d93 Merge pull request #11507 from hugodearaujo/fix/agc-short-cube-sampling
fix(agc): sample cube descriptors with fewer than six slices as six faces
2026-10-28 10:14:50 +01:00
Dean b332780eec revert(shader): restore pixel helper lanes in guest lane masks (#11199)
This reverts merge 7fc0ae6008. On NVIDIA, where helper lanes take part in ballots, clearing helper bits from every pixel ballot breaks derivatives along triangle edges (bright lines and specks on a cloud surface in PPSA21564). See the report on #11199.
2026-10-28 22:11:10 +13:00
Dean 87b810a1db Merge pull request #12211 from XairRrR/fix/shader-paired-wave-zero-branch
fix(recompiler): take a paired wave64 zero branch when some invocations are inactive
2026-10-28 21:32:27 +13:00
Adrià 0fe94e2883 fix(shader): drop unreachable phi counter check, move debt line off section end 2026-10-28 09:17:36 +01:00
slopgobblercobbler fccfd18dcd fix(shader): validate SPIR-V instruction word count boundaries and phi patching 2026-10-28 09:17:32 +01:00
Adrià 7fc0ae6008 Merge pull request #11199 from QRMOE/fix/shader-pixel-helper-lane-masks
fix(shader): keep pixel helper lanes out of guest lane masks
2026-10-28 08:54:16 +01:00
Dean Galvin 00c06d63e8 Merge branch 'main' into feat/agc-unclamped-normalized-blend-v2
# Conflicts:
#	core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
2026-10-28 20:13:06 +13:00
Filip Michalski 2b4efb1dc0 perf(shader): bypass identity fragment export mappings 2026-10-28 07:00:18 +01:00
Dean 00e85fea39 Merge pull request #10348 from oneandonlydean/perf-D2-v47
fix(agc): clamp an exported pixel depth to the viewport depth range
2026-10-28 18:25:05 +13:00
Michael Krumland-DunningandClaude Opus 5.5 254d7b416f fix(shader): translate the fused f32 fma forms in f32 denormal mode 2
v_fma_f32, v_fmac_f32, v_fmamk_f32 and v_fmaak_f32 threw "f32 denormal
mode 2 is not implemented" whenever FLOAT_MODE flushed f32 denormal
inputs but kept denormal results.

On RDNA2 (gfx1036) at round to nearest even, mode 2 flushes the inputs
and rounds the fused result once with gradual underflow, which the host's
Fma gives under DenormPreserve 32. The translation now matches all 5,596
measured triples for the four forms at IEEE 0 and 1. Mode 1 keeps
throwing: there the hardware flushes a result that is tiny after rounding
with an unbounded exponent even when the addend is nonzero.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 22:05:57 -07:00
Michael Krumland-DunningandClaude Opus 5.5 126b7b98a0 fix(shader): translate f32 fract, floor, ceil and frexp_mant in f32 denormal modes 1 and 2
All four threw "f32 denormal mode N is not implemented" whenever
FLOAT_MODE kept only one side of the f32 denormals.

On RDNA2 (gfx1036), in every f32 round mode and at IEEE 0 and 1, mode 2
gives the results of mode 0 and mode 1 those of mode 3 with a denormal
result flushed, which only v_fract_f32 of a positive denormal produces.
The translation now matches all 118 measured inputs in modes 1 and 2.
Mode 1 of the three that run host float operations on a kept denormal
needs DenormPreserve 32, as v_min_f32 does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 22:05:57 -07:00
Michael Krumland-DunningandClaude Opus 5.5 2295183c6c fix(shader): translate v_div_scale_f32 and v_div_fixup_f32 in f32 denormal modes 1 and 2
Both threw "f32 denormal mode N is not implemented" whenever FLOAT_MODE
kept only one side of the f32 denormals, so any LLVM-compiled f32
division in such a shader failed to translate.

On RDNA2 (gfx1036) at round to nearest even, v_div_fixup_f32 follows bit
0 of the field, and v_div_scale_f32 flushes its inputs in mode 2 as in
mode 0 and in mode 1 keeps them and flushes a denormal result, with the
VCC of mode 3. The translation now matches all 2,304 measured rows in
modes 1 and 2 at IEEE 0 and 1. Directed rounding still throws, as an
underflowing v_div_fixup_f32 result rounds to the smallest denormal there.

Division and F32DenormalFlush no longer require modes 1 and 2 to be
refused; Division checks v_div_scale_f32 in them against its measured
vectors instead.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 22:05:56 -07:00
Adrià e5c952d51f Merge pull request #9806 from frankea/fix/hull-masked-control-point
fix(shader): evaluate a control point masked out of the packed hull register
2026-10-28 05:35:24 +01:00
Adrià ed94f8443f Merge pull request #11699 from Filip722/perf/shader-pixel-entry-mask
perf(shader): reuse starting pixel lane predicates
2026-10-28 04:59:08 +01:00
MarcZX10 02f4f788d5 fix(shader): count vertex clip distances in the rect-list tessellation limits
BuildRectListShaders sized the per-vertex tessellation interface from the
position and the exported parameters only. The generated control and evaluation
shaders also carry the vertex shader's ClipDistance array (up to eight scalars)
as input and output builtins, so for a vertex shader that writes clip distances
the interface was up to eight components larger than the checked size and could
exceed maxTessellationControlPerVertexInputComponents and its siblings. The
sibling barycentric geometry shader already counts them.

The clip distance count is now read before the check, and both the parameter
capacity and the component total include it, so parameters are trimmed to what
fits and an interface that cannot fit is rejected.
2026-10-28 03:46:30 +00:00
XairRrR 86f97e8ac4 fix(recompiler): take a paired wave64 zero branch when some invocations are inactive
A wave64 program held as 32 invocations x 2 lanes decided s_cbranch_execz,
s_cbranch_vccz and s_cbranch_scc0 by balloting the per-lane "zero"
condition and requiring all 64 bits set. OpGroupNonUniformBallot sets the
bit of an inactive invocation to 0 (SPIR-V specification,
OpGroupNonUniformBallot: "The bit is set to one if the corresponding
invocation is active and the Predicate for that invocation evaluated to
true; otherwise, it is set to zero"), and an invocation that returned
from the entry point is no longer active. When one invocation stopped,
e.g. after a BDA fault, the zero branch was never taken for the others:
a loop leaving through s_cbranch_execz never ended.

The RDNA2 ISA branches when the whole register is zero ("if (EXEC == 0)",
"if (VCC == 0)", "if (SCC == 0)", SOPP S_CBRANCH_EXECZ / VCCZ / SCC0), so
the branch now asks whether any active invocation still has a lane with
the bit set, as the wave32 path does: not any(own lane set || other
lane set). Inactive invocations then count as lanes with the bit clear.
The non-zero kinds keep any(own || other).

The execution test runs a wave64 program in which lane 0 faults and
stops its invocation, then takes each zero and non-zero branch; before
this change the other lanes fell through all three zero branches.

AI-assisted: yes
2026-10-28 04:37:56 +01:00
Nicolas GonzalezandClaude Opus 5.5 2f25ab919f fix(shader): fence the LDS lock's critical section on MoltenVK
SPIRV-Cross emits the lock's acquire compare-exchange and release
exchange as relaxed Metal atomics, so the plain LDS loads and stores of
a locked 64-bit update were not ordered against the lock and contended
ds_add_u64 lost updates on an Apple M5 Pro. On MoltenVK the driver now sets
SpirvTarget::ldsLockFences, and the lock brackets its critical section
with workgroup-scope acquire and release OpMemoryBarriers, which become
threadgroup atomic_thread_fence with no execution barrier.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 23:37:56 -03:00
JJenkxandClaude Opus 5.5 c3c947e9c1 fix(shader): keep the half bits of a 16-bit float image store's D16 data
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 22:18:04 -04:00