Repository navigation
perf(recompiler): emit wave-uniform values once for both lanes of a two-lane invocation - #304
Merged
boykopovar merged 1 commit intoOct 3, 2026
Conversation
This was referenced Oct 2, 2026
Merged
This was referenced Oct 2, 2026
oneandonlydean
force-pushed
the
perf/recompiler-two-lane-uniform-values
branch
from
October 2, 2026 08:12
8e9e30b to
984a89c
Compare
8 of 9 tasks
…wo-lane invocation A wave64 compute or mesh program on a 32-wide host runs two lanes per invocation, and the emitter emitted every IR value once per lane, scalar ones included: each SGPR value, scalar memory load and loop phi existed twice in the module, raising register pressure for every such program. WaveUniformValues (SpirvAnalysis) finds the values every lane of the wave computes alike: user data, constants, resources, ballots, readlane and readfirstlane results, scalar loads through a wave-uniform address, and what follows from them alone. Lane ids, builtins other than the workgroup id, lane exchanges and every other memory or image read differ by lane, as does anything computed from them. For such a value without side effects, the second lane reuses the first lane's definition. Both lanes are one invocation, so they share control flow and the definition dominates every use of the second lane. agc_shader_memory checks the analysis and that a two-lane module computes a scalar product as often as the one-lane module. The agc_driver_wave_uniform execution test runs a wave64 program with a scalar loop, a ballot and a readfirstlane and checks every lane's result. It skips on a subgroup narrower than 32, such as lavapipe's.
oneandonlydean
force-pushed
the
perf/recompiler-two-lane-uniform-values
branch
from
October 3, 2026 07:02
984a89c to
a791e7d
Compare
oneandonlydean
added a commit
to oneandonlydean/AnyPS5
that referenced
this pull request
Oct 3, 2026
…oykopovar#304 boykopovar#305 boykopovar#306 boykopovar#322 boykopovar#409 boykopovar#416, boykopovar#276 boykopovar#277 boykopovar#275 boykopovar#307, shader ops boykopovar#226 boykopovar#255 boykopovar#260 boykopovar#280 boykopovar#308 boykopovar#309 boykopovar#311 boykopovar#318 boykopovar#320 boykopovar#324 boykopovar#326 boykopovar#379 boykopovar#384 boykopovar#399, boykopovar#261, kernel boykopovar#335 boykopovar#341 boykopovar#349 boykopovar#355, libs boykopovar#227 boykopovar#244 boykopovar#245 boykopovar#273 boykopovar#274 boykopovar#279 boykopovar#286 boykopovar#287 boykopovar#289 boykopovar#296 boykopovar#300 boykopovar#328 boykopovar#358 boykopovar#402) into astrobot Where our PR merged in a revised form, main's version is taken; where astrobot carries the same work plus later local commits, astrobot's code stays and main's additions appear once. Conflicts: - AvPlayer (Export.cpp both libs, include/AvPlayer.hpp, src/Player.cpp, src/Source.cpp, AvPlayerLibrary.cmake, CMakeLists.txt, SceTypes.hpp, tests/GuestAvPlayer.cpp): main's boykopovar#307 (our reviewed squash, which carries 9c8e7fd e9ea5c8 adcede3 fb4eeb9 953057c ac456ee). It links the shared ffmpeg-core target, so astrobot's second FFmpeg build goes: 3rdparty/FFmpeg submodule, core/libs/FFmpeg.cmake and src/Exports.cpp. - libScePngEnc: main's boykopovar#227 (supersedes OverkillLabs' encoder). - libScePad Export.cpp: main's boykopovar#287 tilt correction (supersedes 49d45f5). - libSceAudioPropagation Export.cpp: astrobot's object model, which defines every function boykopovar#289 declares as a stub. - libSceSaveData.native Export.cpp: both (SaveDataHoldWrites and boykopovar#289's unknown exports). - VideoOutDriver.cpp: astrobot's (2546bd1 counters around boykopovar#416's lastFlipVblank store, same value). tests/Flip.cpp: boykopovar#416's pacing test and boykopovar#417's after-GPU test both kept. - boykopovar#278 files (DisplayFormat, VulkanDevice, GpuColorTransfer, ResidentPresent, agc CMakeLists): main's comment-free boykopovar#278 plus astrobot's P5 present-direct (b29728d), which removed DetileImage, ResidentPresent.hpp and the recorder-test hook; the resident present test stays its own executable. - VulkanDevice.cpp/.hpp: astrobot's GDS, local memory probe, sample dumps and P5 code; boykopovar#303/boykopovar#409 comments as main has them; main's shader clock (boykopovar#324) and DepthSurface-free includes. - RequestSerializer.cpp: version stays 8 (6 mesh passthrough, 7 nonConstantImageOffsets, 8 astrobot's later field). boykopovar#303's auto-merged second write and read of nonConstantImageOffsets removed. - SpirvEmitter.hpp, SpirvEmitterState.hpp, Recompiler.hpp/.cpp, StageIO.hpp, SpirvOptimizer.cpp, InstructionTranslator.cpp, ShaderReplay.cpp: local fields kept, boykopovar#303/boykopovar#305 comments as main has them. - SpirvAluEmitterMath.cpp: v_readlane keeps the split-wave path and takes boykopovar#322's wrap at the wave size for the others. - SpirvEmitter.cpp: astrobot's single-lane layout check. - SpirvFlowEmitter.cpp: boykopovar#304 as merged (the second lane emits a shared value itself when the first lane defined nothing). - SpirvImageEmitter.cpp: main's Offset operand placed before MinLod. - boykopovar#304: main's WaveUniformValues (SpirvAnalysis.cpp) replaces astrobot's copy in SpirvWaveExchange.cpp. - Recorder.hpp/.cpp, Draw.hpp, Graphics Draw.cpp, ShaderResources.cpp: astrobot's boykopovar#276/boykopovar#277 with its later snapshot work (per-use budgets, stale-snapshot refresh, profile counters, boykopovar#410, boykopovar#424, fan GS); boykopovar#409/boykopovar#306 comments as main has them. - PacketMemory.cpp: boykopovar#409 as merged plus astrobot's two opt-outs. - tests: ShaderMemory.cpp takes boykopovar#303's and boykopovar#304's new tests; Recorder.cpp takes boykopovar#409's afterRecordedWorkTests and drops main's copies of the boykopovar#276/boykopovar#277 tests astrobot already has; Structurizer.cpp keeps boykopovar#413's case. boykopovar#143 (NGG passthrough) stays as on astrobot: upstream did not touch its files since the boykopovar#258 revert.
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
A wave64 compute or mesh program on a 32-wide host runs two lanes per invocation (
laneCount == 2), andEmitStructuredBlockemits every IR value once per lane. That includes the values the whole wave shares: each SGPR value, scalar memory load and loop phi exists twice in the module, which raises register pressure for every such program.Changes:
WaveUniformValues(inSpirvAnalysis) finds the values every lane of the wave computes alike. It is a fixed point over the program:ReadConst/ReadConstBuffer,Ballot,ReadLaneandReadFirstLane.LaneId, builtins other than the workgroup id, attributes and interpolation parameters, DPP/permlane/bpermute/swizzle,WriteLane, append/consume, and every buffer, shared, address or image access.ScalarAddressresource) counts as uniform when its operands are, since the wave performs it once.I checked that no emitter puts the first lane's definition inside a conditional of its own. The conditionals some emitters open (BDA reads,
EmitValueOrZeroIfCondition, BDA fault returns, storage image mip switches) close at their merge block, and the result is anOpPhithere. The per-lane loop emits each instruction unconditionally for both lanes. A forward reference that the second lane allocated earlier throughResultgets anOpCopyObjectfromDefine, as before.On my integration branch, four ray-traced lighting programs and one other wave64 compute program of PPSA21564 compile to about 12% fewer SPIR-V words with this change. On the RTX 4090, the two-lane modules of those programs had needed more than 255 registers and spilled to local memory.
Tested
agc_shader_memory_tests:verifyWaveUniformValueschecks the analysis on a small program. User data, a sum of it, an address resource, a scalar load through a uniform address, a value computed from that load and a ballot are uniform.LaneId, a sum with it, a scalar load at a lane-dependent offset, a global load, a value computed from the global load and a per-lane compare are not.verifyTwoLaneUniformValuesrecompiles a wave64 program (s_load_dwordx2,s_mul_i32,v_add_nc_u32,buffer_store_dword) for 32- and 64-wide hosts and requires the same number ofOpIMulin both. Without the emitter change it fails (a two-lane invocation computes a scalar value once per lane); with the change it passes.agc_driver_wave_uniformexecution test (tests/execution/WaveUniform.cpp, so ctest runs it like the other tests there): a 64-thread wave64 program with ans_buffer_load_dwordx2, a scalar loop whose phis feed a multiply,s_bcnt1_i32_b64of a VCC ballot and av_readfirstlane_b32checks every lane's stored result. It passes on the RTX 4090. As a check of the test itself, wrongly treating builtins as uniform makes it fail (thread 1 stored 1022, expected 1002). Like the other execution tests it exits 77 (skipped) without a Vulkan device, and it also skips on a subgroup narrower than 32 (lavapipe's is 8), where a wave64 does not fit in two lanes.agc_driver_bda_device_testsandagc_driver_mesh_testspass.agc_driver_recompiler_testsfails withmissing register at DWORD 0x207on main as well.ctest: 106/106 pass, including the execution tests.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