Repository navigation
fix(recompiler): give wave64 vertex programs on 32-lane hosts a 32-lane s3 - #3546
Merged
adriadam10 merged 3 commits intoOct 11, 2026
Merged
Conversation
…ne s3 A wave64 vertex program runs at one guest lane per invocation, so on a 32-lane host subgroup its lanes 32-63 have no invocation. The vertex entry still set s3 = 64 | 64 << 8, telling the program that its wave holds 64 vertices and 64 primitives. An NGG culling program then keys a full-wave OR on lane < s3's primitive count, lanes 32-63 hold real cull results, and its v_readlane_b32 of lane 63 cannot be built (boykopovar#2783). The entry now sets s3 = 32 | 32 << 8 for a wave64 program on a 32-lane host, the wave the host runs; the console launches waves with fewer vertices and primitives. The host subgroup width reaches the vertex input the way it reaches the compute and mesh inputs (BuildShaderStageInputInfo from HostSubgroupSize), and the source, prepared-shader and disk cache keys already carry it for every stage. Programs that do not read s3 are unchanged; wave32 programs and other host widths keep the full wave. The keys are then 0 in lanes 32-63, so the read is sound. On a 32-lane host, when the existing half-wave scan rules fail, the scan may also: - run under s_mov_b64 exec, -1 (EXEC constant true); - use bound_ctrl:1 row_shr steps when 0, the fill, is the identity (OR, add, unsigned max), also into another register, whose old value never shows since such a step writes every lane; - take keys shown to be 0 in lanes 32-63: built from 0 through selects under a lane id or v_mbcnt of -1 below at most 32, v_cmp_ne_u32 against 0, ORs, DPP and permlanes, in stages whose entry EXEC is every lane. Lane 63 is then the identity and lane 31 the subgroup reduction of the host's invocations. A shuffle from lane 31 is not enough: an RTX 5070 runs the vertex warps of a non-indexed triangle list with 30 of their 32 invocations. A v_readlane_b32 outside the host subgroup of another value that is 0 in lanes 32-63 reads 0. Only reads that used to throw change, besides lane 31 of such scans. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wave64 vertex program with the culling scan of boykopovar#2783 (lane < s3's primitive count, row_shr bound_ctrl:1 OR scan, v_permlanex16_b32, v_readlane_b32 of lanes 31 and 63) compares the wave OR with the ballot of its keys and collapses its triangle when they differ. On a 32-lane device every pixel of the grid must be covered. Before the s3 change the program does not build on 32 lanes; with a shuffle from lane 31 instead of the subgroup reduction no pixel is covered, as the device leaves lanes of each vertex warp idle. Other subgroup widths skip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The half-wave scan rules for 32-lane hosts made lane 31 of a matched scan a subgroup reduction and threw on a device without subgroup arithmetic, where the read used to build as a shuffle of the scan. Such a device now keeps the shuffle for lane 31; lane 63 still reads the identity, which needs no arithmetic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 11, 2026
9 tasks done
9 tasks done
Collaborator
|
Checked the bound_ctrl:1 |
10 tasks done
9 tasks done
9 tasks done
oneandonlydean
added a commit
that referenced
this pull request
Oct 12, 2026
…up fixture The fixture read lane 63 of v0, which the program never writes. Since #3546 a wave64 vertex program on a narrower host subgroup builds that read as 0, so the synchronous registration the test expects to fail succeeds. v5 holds the vertex index, which is not 0 in lanes 32-63, so the read still throws.
This was referenced Oct 12, 2026
Closed
10 tasks done
Tamalero
added a commit
to Tamalero/AnyPS5
that referenced
this pull request
Oct 16, 2026
…n prepare The registered-workgroup control registers a non-NGG copy of its fixture in synchronous mode and expected the lane-63 refusal to prove that preparation ran at registration. Since boykopovar#3546 a wave64 vertex program's lane-63 read builds on a 32-lane host, so the refusal never comes and agc_driver_prepared_shaders_registered_workgroup_sync fails on main on every host narrower than 64 lanes. The control now registers an LDS write followed by s_barrier, which a vertex program cannot build on any host width, so it also runs on 64-lane hosts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes #2783. A wave64 NGG vertex program on a 32-lane host subgroup stops on current
main:Titles that stop there on NVIDIA, and get past it with this change:
The program keys a full-wave OR on
lane < s3's primitive countand reads lanes 31 and 63. At one guest lane per invocation, lanes 32-63 have no invocation on a 32-lane host, but the vertex entry sets3 = 64 | 64 << 8.s3: for a wave64 vertex program on exactly 32 host lanes,emitEntryProloguesetss3 = 32 | 32 << 8, a wave the console can launch. The host subgroup width reachesShaderVertexInputInfoas it already reaches the compute and mesh inputs. Every cache key already carriesHostSubgroupSize. Wave32 programs and 64-lane hosts are unchanged.The read: with the narrowed
s3, the keys are 0 in lanes 32-63. When the strict rules don't match on a 32-lane host,MatchHalfWaveScanalso accepts:s_mov_b64 exec, -1;bound_ctrl:1row_shrsteps whose fill is the reduction's identity;Lane 63 then reads the identity, and lane 31 the subgroup reduction. Without subgroup arithmetic, lane 31 stays a shuffle.
TechnicalDebt: the two entries (vertex entry
s3, lanes outside the host subgroup) describe this.Related: #2261 (open) would route registered NGG programs to the mesh path, which would also cover this. #1825 only routes unregistered programs that use LDS or
s_barrier, and this program is registered.Tested
Windows 11 (26200), WinLibs GCC 15.2.0, Release, RTX 5070 Laptop (NVIDIA 616.92, 32-lane subgroups), on
main0f863c1:agc_shader_memory(verifyNarrowVertexWave): fails onmainwith the lane-63 error and passes with this branch. It also checks the cases that must still throw: a bound of 33, a count from user data, the vertex index instead of the lane, and a step into another register withoutbound_ctrl:1.agc_driver_ngg_vertex_wave_reduction: fails onmainwith the lane-63 error and passes with this branch. It skips on other subgroup widths.agc_driver*andagc_shader*on NVIDIA: 306/306.windows_exceptions, fails the same way onmain(CI run 38133926187), along with three Linux tests there.tools/check_conventions.py --base origin/mainpasses.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code