Skip to content

fix(shader): read lane 63 of a wave64 v_max_f32/v_min_f32 half-wave scan - #2451

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
oneandonlydean:up639/half-wave-fp-reduce
Oct 10, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
oneandonlydean:up639/half-wave-fp-reduce

Conversation

@oneandonlydean

Copy link
Copy Markdown
Collaborator

What

#1268 reads lanes 31 and 63 of a wave64 DPP row scan as half-wave reductions, but only for integer steps (UMin/UMax/SMin/SMax/IAdd/AND/OR). A wave64 vertex program does the same scan with v_max_f32:

  • keys are selected under the all-lanes EXEC, with -inf outside the entry EXEC;
  • four row_shr DPP steps and v_permlanex16_b32 follow;
  • then v_readlane_b32 of lanes 31 and 63, combined by one more v_max_f32.

On a 32-lane host subgroup the read of lane 63 threw:

ShaderRecompiler::Recompile: SPIR-V emission failed: hash=0x21484880640 stage=1 opcode=ReadLane reason=v_readlane_b32 of lane 63 is outside the 32-lane host subgroup that runs this wave64 program at one lane per invocation

The matcher now also accepts FPMax32 and FPMin32 steps, with their BitCasts, and identities -inf and +inf. It also looks through the f32 denormal flush the float mode adds around each operand and result ((x & 0x7f800000) == 0 ? x & 0x80000000 : x, #1850); the flush leaves -inf and +inf unchanged.

The emission adds no float arithmetic. Once the entry EXEC proves that lanes 32-63 hold the constant key, every step there combines the constant with itself. Main's v_max_f32/v_min_f32 lowering (EmitMinMaxF32Value) returns the operand for equal operands, NaN included. So lane 63 is exactly that constant, and the read returns it.

Everything else keeps its existing lowering, so NaN and signed-zero results are the ones the shader already computes:

  • the read of lane 31;
  • both reads on a 64-lane subgroup;
  • the shader's combining v_max_f32.

No OpGroupNonUniformFMax/FMin is emitted, because their NaN and signed-zero results are not defined to match v_max_f32.

Touches core/shader/recompiler/SpirvBackend/src/SpirvAlu/SpirvAluEmitterMath.cpp.

The level in the test below needs this and "fix(shader): match half-wave reductions in vertex programs" together; the two are independent and either can go in first.

This and #2450 add a test at the same place in tests/ShaderMemory.cpp, so whichever lands second needs a small rebase of that file; the code changes merge cleanly.

Tested

  • Linux (CachyOS, GCC 16.2, RTX 4090, NVIDIA 610.57), on main a8b032b: ctest 495/495. tools/check_conventions.py passes.
  • agc_shader_memory, verifyHalfWaveReduction:
    • wave64 pixel programs with a v_max_f32 and a v_min_f32 scan build on 32 lanes with no subgroup arithmetic, and on 64 lanes. On main the 32-lane case throws the line above.
    • These still throw: a float scan under the entry EXEC, a v_max_f32 scan joined by v_min_f32, and a v_max_f32 scan of 0 outside the live lanes.
  • A heavier title: a level load stopped with the line above. With this and the vertex entry-EXEC fix (separate PR), the level loads and renders.

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

@github-actions github-actions Bot added shader core/shader agc libSceAgc* labels Oct 9, 2026
@Kikoarl

Kikoarl commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Tested on Linux (CachyOS x86_64, Intel RPL-S iGPU / Mesa 26.3.0-devel / Vulkan 1.4.363, GCC 16.2.1) at 5435cb28:

  • Build: Built cleanly in RAM (/tmp/anyps5_build) with zero warnings/errors.
  • Conventions: python3 tools/check_conventions.py --base origin/main --head HEAD -> PASS (0 errors).
  • Verification: Verified wave64 v_max_f32 / v_min_f32 half-wave scan in SpirvAluEmitterMath.cpp and ShaderMemory.cpp. Correctly incorporates lane 63 boundary value during 64-lane reductions.

LGTM!

@popcoder565

popcoder565 commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Native Windows run, since no Windows results were posted. Setup: Windows 10 (19045), RTX 3060 Laptop (NVIDIA 616.92), MinGW-w64 GCC 15.2.0 (WinLibs r7), Release. Head 5435cb28, merged onto main 09a3462 with #2473, which current main needs to build on Windows, and #2470.

No title was run.

🤖 Addressed by Claude Code

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 10, 2026
A wave64 vertex program reduces a float key across the wave with the same shape as the integer half-wave reductions: keys selected under the all-lanes EXEC with -inf (v_max_f32) or +inf (v_min_f32) outside the entry EXEC, four DPP row_shr steps, v_permlanex16_b32, then v_readlane_b32 of lanes 31 and 63, combined by one more v_max_f32. On a 32-lane host subgroup the read of lane 63 threw that it is outside the subgroup.

The matcher now also accepts FPMax32 and FPMin32 steps, with their BitCasts and the f32 denormal flush the float mode may add around operands and results; the flush leaves the -inf and +inf keys unchanged. When the entry EXEC proves that lanes 32-63 hold the constant key, every step there combines that constant with itself. The existing v_max_f32/v_min_f32 lowering returns the operand unchanged for equal operands (NaN included), so lane 63 is exactly that constant, and the read returns it. No subgroup float reduction is emitted. Lane 31, the lanes on a 64-lane subgroup, and the combining v_max_f32 keep their existing lowering. So NaN and signed-zero results are the ones the shader computes today.
@oneandonlydean
oneandonlydean force-pushed the up639/half-wave-fp-reduce branch from 5435cb2 to 1008b91 Compare October 10, 2026 05:20
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 10, 2026
@oneandonlydean
oneandonlydean merged commit 691c0c3 into boykopovar:main Oct 10, 2026
12 checks passed
SleglJan pushed a commit to SleglJan/AnyPS5 that referenced this pull request Oct 10, 2026
…e-fp-reduce

fix(shader): read lane 63 of a wave64 v_max_f32/v_min_f32 half-wave scan
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.

3 participants