Skip to content

feat(shader): implement the missing 32-bit LDS atomics and fix ds_min/max_f32 - #320

Merged
boykopovar merged 2 commits into
boykopovar:mainfrom
adriadam10:feat/lds-atomics
Oct 3, 2026
Merged

boykopovar merged 2 commits into
boykopovar:mainfrom
adriadam10:feat/lds-atomics

Conversation

@adriadam10

Copy link
Copy Markdown
Collaborator

What

New (these threw "DS opcode is not supported"): ds_rsub_u32, ds_rsub_rtn_u32, ds_inc_u32, ds_dec_u32, ds_mskor_b32, ds_mskor_rtn_b32, ds_cmpst_b32, ds_cmpst_rtn_b32, ds_cmpst_f32, ds_cmpst_rtn_f32, ds_add_f32, ds_add_rtn_f32, ds_min_rtn_f32, ds_max_rtn_f32, ds_wrap_rtn_b32, ds_nop.

Fix: ds_min_f32 / ds_max_f32 take a single data operand on gfx10, but the translation read a second one (source2, the unused data1 field, which is usually v0) and stored data0 when that compared below/above memory. They now keep min/max(memory, data).

The reference emulators don't implement these, so the semantics were measured on an RDNA2 GPU (Radeon 660M, gfx1035) with raw kernels through ROCm HSA. A Python model of all eight forms matched 512/512 hardware results (old = memory before, d0/d1 = data0/data1):

  • rsub: d0 - old
  • cmpst_b32: old == d0 ? d1 : old; cmpst_f32 does the same with an ordered float compare
  • mskor: (old & ~d0) | d1
  • wrap: old >= d0 ? old - d0 : old + d1
  • add_f32: IEEE add
  • min/max_f32: IEEE minNum/maxNum, so a NaN in memory or data loses to the number, and −0 < +0

inc/dec reuse the existing ops.

Implementation: SharedAtomicFMin32/FMax32 now return the old value (U32) like the other shared atomics, so the _rtn forms share them; they no longer need the scratch-dword workaround. New IR ops SharedAtomicRsub32/FAdd32/Cmpst32/CmpstF32/Mskor32/Wrap32 are emitted through the existing AtomicUpdate compare-exchange loop, with the same workgroup acquire/release barrier Atomic32 uses for LDS.

Test agc_driver_lds_atomics: one wave32 shader, each of the 17 ops on its own LDS dword per lane. It checks the memory afterwards and every _rtn result. Inputs are random words, small integers (inc/dec/wrap/rsub edges), float specials (±0, ±1, NaN, inf), and equal/unequal pairs for cmpst. The expected values come from a C++ model of the hardware semantics above. Float ops are not checked on lanes with f32 denormals, since those follow the driver's float controls.

Tested

Linux, full ctest (99) passes.

  • RADV (Radeon 660M) and lavapipe: the test passes.
  • Without the change it fails with "DS opcode is not supported".

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: ci: run the shader execution tests on lavapipe #251 (test harness); review only the last commit
  • AI-assisted: yes

adriadam10 and others added 2 commits October 1, 2026 22:45
MixPrecision and SdwaDestination dispatch hand-assembled RDNA through the
recompiler and compare every lane with a CPU model, but were never in ctest.
Move them to tests/execution/, which is globbed into one test per file with
the same target names, so new execution tests add a file and no CMake lines.
They exit 77 (skipped) when the host has no Vulkan device; the Linux runner
installs lavapipe and sets ANYPS5_REQUIRE_VULKAN=1 so they cannot skip there.

Co-Authored-By: Claude <noreply@anthropic.com>
…/max_f32

ds_rsub(_rtn)_u32, ds_inc_u32, ds_dec_u32, ds_mskor(_rtn)_b32,
ds_cmpst(_rtn)_b32/f32, ds_add(_rtn)_f32, ds_min/max_rtn_f32,
ds_wrap_rtn_b32 and ds_nop threw in the decoder. The new ones run as a
compare-exchange loop over the old value, with the semantics measured on an
RDNA2 GPU.

ds_min_f32 and ds_max_f32 take one data operand on gfx10, but compared the
memory with a second register (the unused data1 field, usually v0) and
stored data0 when that compare held. They now keep the minimum/maximum of
memory and data like the hardware: a NaN loses to a number, -0 < +0. Both
IR ops return the old value, so the _rtn forms share them.

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

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

Progress report (709d7fe → c8af889)

📈 GPU shader instructions: 54.03% (+1.37%, +16 implemented instructions)

✅ 16 implemented
Encoding Name
DS DS_ADD_F32
DS DS_ADD_RTN_F32
DS DS_CMPST_B32
DS DS_CMPST_F32
DS DS_CMPST_RTN_B32
DS DS_CMPST_RTN_F32
DS DS_DEC_U32
DS DS_INC_U32
DS DS_MAX_RTN_F32
DS DS_MIN_RTN_F32
DS DS_MSKOR_B32
DS DS_MSKOR_RTN_B32
DS DS_NOP
DS DS_RSUB_RTN_U32
DS DS_RSUB_U32
DS DS_WRAP_RTN_B32

@github-actions github-actions Bot added build shader core/shader agc libSceAgc* ci .github labels Oct 2, 2026
This was referenced Oct 2, 2026
@adriadam10 adriadam10 mentioned this pull request Oct 2, 2026
9 tasks done
@boykopovar
boykopovar merged commit 926906e into boykopovar:main Oct 3, 2026
3 checks passed
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.
@adriadam10
adriadam10 deleted the feat/lds-atomics branch October 5, 2026 06:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* build ci .github shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants