Skip to content

fix(shader): shuffle both wave64 halves of a scalar with one vector shuffle - #6677

Closed
mariuszkreft wants to merge 1 commit into
boykopovar:mainfrom
mariuszkreft:fix/shader-wave64-vector-shuffle
Closed

mariuszkreft wants to merge 1 commit into
boykopovar:mainfrom
mariuszkreft:fix/shader-wave64-vector-shuffle

Conversation

@mariuszkreft

Copy link
Copy Markdown

What

Found by driving GTA V (PPSA04264) on Linux/RTX 3090 (NVIDIA 580.159.03): creating the compute pipeline for shader 69ec8524477a669e segfaults inside NVIDIA's SPIR-V compiler (SIGSEGV in libnvidia-gpucomp.so+0xe3facc, exit 139, no AnyPS5 message). This is the same offset as the ballot crash of #6654, but this pipeline still crashes after its ballots are rewritten as subgroup OR reductions.

A wave64 program held at two lanes per invocation on a 32-lane host emitted every wave shuffle as two scalar OpGroupNonUniformShuffles, one per half, with the same physical lane (SpirvValueEmitContext::Shuffle). The crashing pipeline has 103 such pairs. Replacing the shuffles with OpUndef makes it compile, and removing any 9 of the 72 shuffles of one DPP stretch does too, so no single construct is at fault: the trigger looks like the number of subgroup operations a driver analysis walks.

  • SpirvValueEmitContext::Shuffle (SpirvEmitter.cpp) now shuffles a scalar argument as one OpGroupNonUniformShuffle of a two-component vector holding both halves, extracts the two halves and selects the one the source lane is in, as before. Vector arguments keep the two shuffles. The result is the same for every lane, with half the subgroup operations.
  • With this change the captured pipeline compiles in agc_compute_pipeline_replay (3 of 3 runs), and the 15 other GTA V captures with shuffles compile within 10% of their old times.
  • What in the driver triggers the crash is not known, and a larger shader may still hit it; this is recorded as a technical-debt entry in docs/dev/debt/shader.md. Only the NVIDIA 580 driver was tested.

Tests: wave32_wide_subgroup gains CheckTwoLaneShuffle, which links two half contexts, interprets the emitted SPIR-V and requires one shuffle for both halves and the right value from either half for 32 requested lanes spread over both halves.

Possibly related: #6294 (Borderlands 4, NVIDIA 595, segfault in the same compiler). Not verified against it.

Tested

Linux, on the host, in the slice worktree. The loop ran this gate itself; the tail of it is
at the bottom of this pull request. No title and no GPU were involved.

cmake -S . -B build-full -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON
cmake --build build-full --parallel 12
ctest --test-dir build-full --output-on-failure -j 12
python3 tools/check_conventions.py --base origin/main

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw; no silent stubs added
  • No notes, investigation .md files or images added to the repository
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: PPSA04264 Game bring-up #1203 — reported against this title, not verified on it
  • GPUs tested: n/a (host-side change; no GPU execution)
  • AI-assisted: yes
Verification log tail
=== slice gta5-shader-wave64-vector-shuffle: fix/shader-wave64-vector-shuffle @ 310a88f70 on origin/main 5d9dcc42d ===
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON
ninja -C build   -> rc=0 (3304 steps)

=== affected test ===
1/1 Test #750: wave32_wide_subgroup .............   Passed
100% tests passed, 0 tests failed out of 1

=== fails before (SpirvEmitter.cpp reverted to origin/main) ===
1/1 Test #750: wave32_wide_subgroup .............***Failed
a wave64 shuffle of a scalar was not issued once for both halves

=== ctest -R 'agc|shader|wave|subgroup|spirv|Wave32' -j16 ===
99% tests passed, 3 tests failed out of 605
	393 - agc_driver_flip_stores_small_results (Failed)
	608 - agc_driver_store_idle_results (Failed)
	609 - agc_driver_store_over_untracked (Failed)
rerun alone: agc_driver_flip_stores_small_results, agc_driver_store_idle_results passed
agc_driver_store_over_untracked: pre-existing on origin/main 5d9dcc42d (recorded in slices gta5-shader-ballot-subgroup-or, gta5-shader-ballot-once-per-block)
skipped: agc_host_import_sparse, agc_driver_sparse_dma_buf_import, agc_driver_stencil_ref_export

=== conventions ===
All conventions checks passed.

=== RESULT ===
PASS

…huffle

A wave64 program held at two lanes per invocation on a 32-lane host now
shuffles a scalar as one OpGroupNonUniformShuffle of a two-component vector
holding both halves, then picks the half the source lane is in. It used to
issue one scalar shuffle per half with the same physical lane. The result is
the same for every lane; vector arguments keep the two shuffles.

NVIDIA 580.159.03 (RTX 3090) segfaults in libnvidia-gpucomp (+0xe3facc)
creating GTA V's compute-69ec8524477a669e. With its ballots rewritten as
subgroup OR reductions (boykopovar#6654) the pipeline still crashed with 103 pairs of
half shuffles left; replacing the shuffles with undef avoids the crash, and
removing any 9 of the 72 shuffles of one DPP stretch does too, so the
trigger looks like the number of subgroup operations a driver analysis
walks. With one vector shuffle per pair the pipeline compiles, and the 15
other GTA V captures with shuffles compile within 10% of their old times.
The vector shuffle is recorded in docs/dev/debt/shader.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 6010 docs/dev/debt/shader.md L104

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 4339 core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
PR 4650 core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
PR 5021 core/libs/tests/Wave32WideSubgroup.cpp
core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
docs/dev/debt/shader.md
PR 5371 docs/dev/debt/shader.md
PR 5426 docs/dev/debt/shader.md
PR 5500 docs/dev/debt/shader.md
PR 5941 docs/dev/debt/shader.md
PR 5958 docs/dev/debt/shader.md
PR 6009 docs/dev/debt/shader.md
PR 6044 core/libs/tests/Wave32WideSubgroup.cpp
PR 6177 core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
PR 6222 core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
docs/dev/debt/shader.md
PR 6235 docs/dev/debt/shader.md
PR 6412 docs/dev/debt/shader.md
PR 6416 core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp

@github-actions github-actions Bot added shader core/shader docs labels Oct 16, 2026
@milescoviello

Copy link
Copy Markdown
Contributor

A data point from Borderlands 4 (PPSA01494), the NVIDIA compiler crash in #6294. Linux, RTX 4070 Ti, NVIDIA 595.99.02, 600 s with scripted pad input:

Build Runs Result
main bf20538 1 EXIT-139 at 323 s, backtrace ends in _nv002nvvm
main 673f740 + this PR (c07c27a7e2 merge) 3 EXIT-139 at 346, 224 and 216 s; the one symbolized backtrace ends in _nv002nvvm

So on 595.99 this change doesn't avoid the Borderlands 4 crash, which matches what I saw for #6654's rewrite there. Like #6654, it says nothing about GTA V on 580.

AI-assisted: yes (Claude Code)

@savvaniss

Copy link
Copy Markdown
Contributor

GPU execution data for this change: the first run on GPUs, as far as I can see. It covers a 32-wide host (Intel) and a 64-wide host (AMD).

Windows 11 Pro 26300, WinLibs GCC 15.2.0 r7, Release, every execution test built, ANYPS5_NO_SHADER_CACHE=1, serial CTest, main 47a266cf9. Intel UHD Graphics 770 (8086:a780, 32-wide subgroups), driver 32.0.101.7088. AMD Radeon RX 9070 XT (1002:7550, 64-wide subgroups), driver 32.0.31041.1004, with VkCache emptied first.

UHD 770 RX 9070 XT
This head 310a88f70 merged into main: wave32_wide_subgroup, ×3 pass pass
This PR's test file on main without the fix, ×3 fails fails
Full CTest with this PR alone 1392 of 1398, same failures as main 1396 of 1398, same failures as main

main alone: UHD 770 1392 of 1398, RX 9070 XT 1396 of 1398. The failures are image tables (#6647) and sample_multisample_target (#6971) on Intel, and quad_pixel_mask (#6255) and layered_volume_vertex_no_gpl (#4768) on AMD.

This PR conflicts with #6638 in core/libs/tests/Wave32WideSubgroup.cpp, so one of them needs a rebase after the other lands.

AI-assisted: yes (Claude Code)

@mariuszkreft

Copy link
Copy Markdown
Author

Closed by the contribution loop's guard: this pull request has no checklist and was not opened by the loop's reviewed path. The work will be reopened as a pull request after the local gate and the review pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants