Skip to content

fix(recompiler): v_readfirstlane_b32 of an empty EXEC reads lane 0 - #445

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
oneandonlydean:fix/recompiler-readfirstlane-empty-exec
Oct 3, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
oneandonlydean:fix/recompiler-readfirstlane-empty-exec

Conversation

@oneandonlydean

Copy link
Copy Markdown
Collaborator

What

With no lane active, v_readfirstlane_b32 reads lane 0 on hardware (the RDNA ISA gives the lane as the first set bit of EXEC, or lane 0 when EXEC is zero). EmitReadFirstLane shuffled from the ballot's first lane without that case:

  • one host lane per invocation: OpGroupNonUniformBallotFindLSB of an empty ballot is undefined in SPIR-V (on my RTX 4090 it gave lane 31);
  • a wave64 program over two 32-lane halves: FirstLane takes FindILsb(high) + 32 when the low word is empty, and with FindILsb(0) = -1 that is lane 31.

The emitter now selects lane 0 when both ballot words are zero, and the first active lane otherwise.

The v_writelane half of this area went in with #254. #322 (v_readlane lane wrap) and #411 (v_writelane and EXEC) edit the functions right below this one; this branch merges cleanly with each of them, alone and both together.

Tested

  • Linux (CachyOS, x86-64, RTX 4090, subgroup size 32): full build with BUILD_TESTING=ON and ctest pass.
  • New execution test agc_driver_read_first_lane_tests (tests/execution/ReadFirstLane.cpp, exits 77 without Vulkan): a wave32 and a wave64 compute shader each do v_readfirstlane_b32 of 0x100 + lane with EXEC empty (expects lane 0) and with only lanes 4 and 5 (wave32) or 40 and 41 (wave64) active, and store both results from every lane. On this GPU the wave64 shader takes the two-lane path. Against main it fails with "read first lane wave32: lane 0 result 0 is 287, expected 256" (lane 31).
  • PPSA21564: on my integration branch since 2026-09-30, through repeated passes of my visual regression check (checkpoint frames, flicker, shimmer, video tearing, FPS). I have no title symptom tied to this; it is from the ISA.

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 (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

With no lane active the hardware reads lane 0. The emitter shuffled from
the ballot's first lane: OpGroupNonUniformBallotFindLSB of an empty
ballot is undefined, and the two-lane wave64 path computed lane 31
(FindILsb of the empty high word plus 32). Lane 0 is now selected when
the ballot is empty.

Tests: agc_driver_read_first_lane_tests runs v_readfirstlane_b32 in a
wave32 and a wave64 compute shader with EXEC empty and with a later
first active lane.
@github-actions github-actions Bot added shader core/shader agc libSceAgc* labels Oct 3, 2026
@oneandonlydean
oneandonlydean merged commit 7fc6f0e into boykopovar:main Oct 3, 2026
3 checks passed
@oneandonlydean
oneandonlydean deleted the fix/recompiler-readfirstlane-empty-exec branch October 3, 2026 22:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant