Skip to content

perf(shader): flush f32 denormals in hardware when a startup probe matches the emulation - #6503

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
ThatsLeo:perf/shader-hw-f32-flush
Oct 19, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
ThatsLeo:perf/shader-hw-f32-flush

Conversation

@ThatsLeo

@ThatsLeo ThatsLeo commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

What

In FLOAT_MODE f32 denormal mode 0 the recompiler flushes f32 denormals in software around every flushing instruction: both inputs, and for products the tiny-before-rounding check (flushTinyProduct, a second scaled multiply plus compares and selects). In Stray's 1,751 shader requests this makes v_mul_f32, the most common f32 instruction, about 96 SPIR-V words, against about 19 for v_mad_f32.

On a device whose hardware flush gives the same bits, this PR lets the hardware do it. It follows what came out of #4977:

  • Per-device probe. A device with shaderDenormFlushToZeroFloat32 and denormBehaviorIndependence other than NONE runs ProbeFloat32FlushToZero when it is created. It recompiles the three programs of agc_driver_f32_denormal_flush (the mixed module with every flushing instruction in one entry point, as @MarcZX100 asked, and the two tiny-product programs) with no float mode, IEEE_MODE off and IEEE_MODE on, once with the current emulation and once with DenormFlushToZero 32 declared, dispatches each and compares the results bit for bit (NaN with NaN). The reference is the current emulation, so the probe checks the device against the semantics the tests already pin.

  • Only when every result matches, SpirvTarget::float32FlushToZero is set (it is part of the cache key and of the serialized request, version 33). Every module then declares DenormFlushToZero 32, and these instructions drop their software flush in the default rounding mode only:

    • v_add_f32, v_sub_f32, v_subrev_f32, v_mul_f32, v_floor_f32, v_ceil_f32;
    • the fused v_fma_f32, v_fmac_f32, v_fmamk_f32 and v_fmaak_f32 (roundsProductSeparately false).

    VOP3P forms (v_fma_mix_f32), the directed rounding modes and every other instruction (min/max/med3, frexp, legacy forms, compares, v_div_fixup, v_mullit) keep the emulation: the mixed check in AGC: vkQuake's ray-traced shadow compute shader becomes 519,500 SPIR-V words; Intel ANV compiles it for minutes under GpuMutex and crashes (PPSA99010, Linux) #4977 showed differences for them.

  • A device without the mode (NVIDIA per @nikowolf123's tables, lavapipe here) never runs the probe and is unchanged. APS5_NO_HW_F32_FLUSH skips the probe. The driver prints one [float-controls] line with the outcome and its time. The probe runs before the pipeline journal replay starts, and its pipelines are neither recorded in the journal nor counted in ComputePipelinesCreated.

Tested

Fedora 44, GCC 16.2.1, Release, Intel Arc (Meteor Lake iGPU, Mesa 26.2.3 ANV) and lavapipe (llvmpipe, LLVM 22.1.8).

  • Probe on the Arc: [float-controls] f32 denormal flush: hardware (0 of 5472 probe results differ from the emulated flush, 35-50 ms). On lavapipe the device offers no f32 flush mode, so nothing runs.
  • agc_driver_f32_denormal_flush now runs every check, including the directed rounding modes and the refused denormal modes, once with the emulated flush and, on a device that offers the mode, again with the hardware flush. It also requires the hardware-flush module to declare the mode and to be smaller. Both pass on the Arc.
  • Serialization: request_serializer and agc_shader_memory cover the new field (round trip, cache key, version 33, version 32 requests read without it, downgraded requests).
  • ctest (1493 tests) on main fc623324a: the Arc, where the probe enables the hardware flush for the whole suite, and lavapipe both pass all but application_heap_lazy (fix(libc): open closed standard descriptors on /dev/null when libc loads #6107).
  • Stray (PPSA02101), boot phase before the save menu, presents 20 to 50 (APS5_TRACE_FRAMES=20:30, no other switch), 3840x2160, warm caches, two captures each, same session. The base is main 0029e62f9 with one local change the dump needs (PlayGo answers BAD_CHUNK_ID for the missing playgo-* files, as before fix(libScePlayGo): report missing chunk metadata #5494); the PR commit is the same change on that base. The GPU's minimum frequency was set to 2250 MHz for both (rps_min_freq_mhz).
frame p50 / p90 (ms) GPU busy per present (ms)
main 622.3 / 639.3, 617.2 / 648.9 478.8, 481.7
this PR 550.9 / 570.3, 555.2 / 568.7 404.9, 421.4

The compute program that dominates the scene (0x2011300000, 8x8 wave64 at 3840x2160) went from 343 to 307 ms per dispatch in an APS5_PROFILE_GPU=1 run.

{"commit":"27b682d212ab","dirty":false,"env":{"APS5_TRACE_FRAMES":"20:30"},"gpu":"Intel(R) Arc(tm) Graphics (MTL)","driver":"Intel open-source Mesa driver Mesa 26.2.3","os":"Linux 7.2.8-200.fc44.x86_64","resolution":"3840x2160","window":{"spec":"frames 20:30","first_present":20,"ms":18603.607},"presents":30,"frame_ms":{"p50":622.325,"p90":639.309,"p99":653.111,"max":653.111},"gpu_busy_ms_per_present":478.831,"worker_busy_pct":{"queue worker 0x0":97.278,"queue worker 0x20":96.682},"top_phases_ms_per_frame":{"Driver.Packet":1198.055,"Driver.Packet.memory_wait":586.464,"Driver.Packet.device_lock":498.398,"Driver.Present":280.698,"VideoOut.Flip":271.082,"VideoOut.Flip.present":271.023,"Driver.Present.render_fence_wait":268.857,"Vulkan.Retire":268.854},"trace_dropped":false,"trace":"aps5-trace-27b682d212ab-1374847.json.gz"}
{"commit":"27b682d212ab","dirty":false,"env":{"APS5_TRACE_FRAMES":"20:30"},"gpu":"Intel(R) Arc(tm) Graphics (MTL)","driver":"Intel open-source Mesa driver Mesa 26.2.3","os":"Linux 7.2.8-200.fc44.x86_64","resolution":"3840x2160","window":{"spec":"frames 20:30","first_present":20,"ms":18632.346},"presents":30,"frame_ms":{"p50":617.225,"p90":648.886,"p99":662.958,"max":662.958},"gpu_busy_ms_per_present":481.746,"worker_busy_pct":{"queue worker 0x0":97.216,"queue worker 0x20":96.535},"top_phases_ms_per_frame":{"Driver.Packet":1199.739,"Driver.Packet.memory_wait":587.963,"Driver.Packet.device_lock":500.560,"Driver.Present":280.986,"VideoOut.Flip":271.206,"VideoOut.Flip.present":271.147,"Driver.Present.render_fence_wait":258.166,"Vulkan.Retire":258.163},"trace_dropped":false,"trace":"aps5-trace-27b682d212ab-1375362.json.gz"}
{"commit":"c165fee308a4","dirty":false,"env":{"APS5_TRACE_FRAMES":"20:30"},"gpu":"Intel(R) Arc(tm) Graphics (MTL)","driver":"Intel open-source Mesa driver Mesa 26.2.3","os":"Linux 7.2.8-200.fc44.x86_64","resolution":"3840x2160","window":{"spec":"frames 20:30","first_present":20,"ms":16565.012},"presents":30,"frame_ms":{"p50":550.915,"p90":570.300,"p99":578.791,"max":578.791},"gpu_busy_ms_per_present":404.894,"worker_busy_pct":{"queue worker 0x0":95.470,"queue worker 0x20":96.875},"top_phases_ms_per_frame":{"Driver.Packet":1058.273,"Driver.Packet.memory_wait":520.520,"Driver.Packet.device_lock":428.690,"Driver.Present":221.739,"VideoOut.Flip":213.932,"VideoOut.Flip.present":213.873,"Driver.Present.render_fence_wait":201.451,"Vulkan.Retire":201.448},"trace_dropped":false,"trace":"aps5-trace-c165fee308a4-1377819.json.gz"}
{"commit":"c165fee308a4","dirty":false,"env":{"APS5_TRACE_FRAMES":"20:30"},"gpu":"Intel(R) Arc(tm) Graphics (MTL)","driver":"Intel open-source Mesa driver Mesa 26.2.3","os":"Linux 7.2.8-200.fc44.x86_64","resolution":"3840x2160","window":{"spec":"frames 20:30","first_present":20,"ms":16616.641},"presents":30,"frame_ms":{"p50":555.163,"p90":568.674,"p99":573.219,"max":573.219},"gpu_busy_ms_per_present":421.421,"worker_busy_pct":{"queue worker 0x0":97.159,"queue worker 0x20":96.726},"top_phases_ms_per_frame":{"Driver.Packet":1070.065,"Driver.Packet.memory_wait":520.591,"Driver.Packet.device_lock":438.947,"Driver.Present":223.880,"VideoOut.Flip":216.228,"VideoOut.Flip.present":216.166,"Driver.Present.render_fence_wait":196.777,"Vulkan.Retire":196.775},"trace_dropped":false,"trace":"aps5-trace-c165fee308a4-1378392.json.gz"}

Traces and trace_digest.py output: ThatsLeo:evidence/hw-f32-flush (main/ and pr/).

  • Code size: over Stray's 1,751 requests, a local switch dropping the software flush for the same instructions (plus v_fma_mix_f32) took the f32 instructions' SPIR-V from 61.8M to 41.5M words (-33%): v_mul_f32 96 to 38 words, v_add_f32 76 to 46, v_sub_f32 57 to 37.

Related open PRs: zmeizy's series for f32 denormal modes 1 and 2 (#6010 and the others) changes f32DenormalFlushFor too; this PR acts only in mode 0. #5782 adds a flush to the v_mad_f32 family; the fused-only condition here keeps the non-fused forms emulated whatever it decides.

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 under Silent stubs in their library's technical debt file
  • 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
  • Unblocks: none
  • GPUs tested: Intel Arc (Meteor Lake iGPU), Mesa 26.2.3 ANV, Fedora 44 (probe passes, hardware flush on); lavapipe (llvmpipe, LLVM 22.1.8), Mesa 26.2.3, Fedora 44 (no f32 flush mode, unchanged)
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 6679 core/shader/recompiler/tests/RequestSerializerTests.cpp L210, L213-214, L249, L252-253, L274, L397-402, L411, L415-416, L430, L432
PR 6767 core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp L341-343
PR 6801 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp L4734, L4852, L5089, L5093, L5099
core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp L797, L826, L835, L878
core/shader/recompiler/tests/RequestSerializerTests.cpp L210, L214, L249, L253, L274, L277, L398, L401, L411, L416, L430, L432
PR 7332 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp L4734, L4852, L4943, L4952, L5001-5002, L5010, L5017-5018, L5024-5025, L5031-5032, L5038-5039, L5089, L5093, L5099
core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp L797, L826, L835, L878
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvModuleSetup.cpp L341-343
PR 7410 core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp L797, L826, L835, L878
core/shader/recompiler/Translation/include/Translation/TranslationContext.hpp L27
core/shader/recompiler/Translation/src/InstructionTranslator.cpp L571

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 1341 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 3247 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 4040 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4072 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Translation/include/Translation/InstructionTranslator.hpp
core/shader/recompiler/Translation/include/Translation/TranslationContext.hpp
core/shader/recompiler/Translation/src/InstructionTranslator.cpp
PR 4472 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4891 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 5426 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5441 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5480 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5499 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5500 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 5609 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 5656 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/Recompiler.hpp
docs/dev/ARCHITECTURE.md
PR 5941 core/shader/recompiler/Translation/src/VectorInstructions.cpp
PR 6009 core/shader/recompiler/Translation/src/VectorInstructions.cpp
PR 6044 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ARCHITECTURE.md
PR 6146 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6177 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitter.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
core/shader/recompiler/tests/RequestSerializerTests.cpp
PR 6185 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6186 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/Recompiler.hpp
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6202 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6235 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/tests/RequestSerializerTests.cpp
PR 6292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6360 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6440 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 6499 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ARCHITECTURE.md
PR 6501 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6535 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6539 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 6611 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
PR 6612 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
core/shader/recompiler/Translation/src/InstructionTranslator.cpp
core/shader/recompiler/tests/RequestSerializerTests.cpp
PR 6615 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6639 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/tests/RequestSerializerTests.cpp
PR 6644 core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
PR 6645 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 6647 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6683 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
core/shader/recompiler/tests/RequestSerializerTests.cpp
PR 6701 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6718 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6747 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6759 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6762 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/tests/RequestSerializerTests.cpp
PR 6763 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ARCHITECTURE.md
PR 6788 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6820 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6868 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6879 core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 6924 core/shader/recompiler/Recompiler.cpp
docs/dev/ENVIRONMENT.md
PR 6931 docs/dev/ARCHITECTURE.md
PR 6937 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6941 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitter.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
core/shader/recompiler/tests/RequestSerializerTests.cpp
PR 6946 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
docs/dev/ARCHITECTURE.md
docs/dev/ENVIRONMENT.md
PR 6947 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitter.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
docs/dev/ARCHITECTURE.md
docs/dev/ENVIRONMENT.md
PR 6960 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6963 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6970 core/shader/recompiler/Recompiler.hpp
PR 6977 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
core/shader/recompiler/Translation/include/Translation/InstructionTranslator.hpp
core/shader/recompiler/Translation/src/InstructionTranslator.cpp
PR 6989 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6995 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7002 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7085 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7154 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
PR 7223 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
PR 7233 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7340 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
docs/dev/ENVIRONMENT.md
PR 7369 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7381 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 7388 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 7390 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
PR 7392 core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
PR 7395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/shader/recompiler/Recompiler.cpp
PR 7427 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 7484 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitter.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
docs/dev/ENVIRONMENT.md
PR 7489 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7505 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7514 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ENVIRONMENT.md
PR 7618 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7622 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp
core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/CacheKey.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/Recompiler.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitter.hpp
core/shader/recompiler/SpirvBackend/include/SpirvBackend/SpirvEmitterState.hpp
core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
docs/dev/ARCHITECTURE.md
docs/dev/ENVIRONMENT.md
PR 7689 core/shader/recompiler/Recompiler.hpp

@alfalfa-star

Copy link
Copy Markdown
Contributor

Tested on AMD RDNA4 with RADV, which the description doesn't cover yet: RX 9070 XT (GFX1201), Mesa 26.2.4, Arch Linux (kernel 7.2.9), GCC Release build of 396372fa4 merged onto main 03c4cb5e7.

Probe: hardware flush is on here too, [float-controls] f32 denormal flush: hardware (0 of 5472 probe results differ from the emulated flush, 16-22 ms). RADV reports shaderDenormFlushToZeroFloat32 with denormBehaviorIndependence 32_BIT_ONLY.

ctest (1267 tests, -j4) on main + this PR: 1266 pass. The one failure is agc_driver_tessellation (SEGFAULT, the same with APS5_NO_HW_F32_FLUSH=1; #4683). agc_driver_f32_denormal_flush, request_serializer and agc_shader_memory pass.

Kena: Bridge of Spirits (PPSA01802), walking in the forest after loading a save. The base is main 03c4cb5e7 plus this PR, and a few local stand-ins Kena needs on main to get that far: skipped failing draws, null descriptors for unreadable SRT pointers, sampler and V# relaxations. One binary, with APS5_NO_HW_F32_FLUSH=1 for the off side. The shader cache was warm on both sides, 3200x1800 render targets, 125 s runs, APS5_PROFILE_GPU=1. GPU time per call of the programs that run in every frame:

program off (ms/call) on (ms/call)
0x200bd60000 (compute) 3.148 2.050 -35%
0x2007260000 (compute) 1.132 0.688 -39%
0x20099d0000 (compute) 1.006 0.672 -33%
0x2006f10000 0.323 0.238 -26%
0x2006f00000 0.483 0.364 -25%
0x2006fa0000 0.200 0.157 -22%
0x2009460000 0.343 0.294 -14%
0x20072b0000 0.360 0.322 -11%

Draw time per present depends too much on where the walk is to compare between runs (43-60 ms on, 47-71 ms off in the same intervals). On main Kena is limited by the CPU side here (frame p50 110-330 ms against 58-94 ms of GPU busy per present in APS5_TRACE_SECONDS=95:10 windows), so the frame rate on main doesn't change with this PR. The saving shows once the rest of the frame is faster.

In one of seven boots with the hardware flush (none of four without), the title stopped submitting GPU work right after the shader cache loaded. Three further boots with the flush were fine, so I can't tie it to this PR.

AI-assisted: yes (Claude Code)

@ThatsLeo

Copy link
Copy Markdown
Contributor Author

Thanks @alfalfa-star, that covers the vendor the description was missing: the same 0 of 5472 on RADV, and the Kena compute timings.

About the boot that stopped submitting: the probe only runs while the device is created, and when any result differs it leaves the emulated flush on, so it shouldn't affect anything after the shader cache loads. To rule it out, could you attach that boot's log if you still have it? The [float-controls] line and the last few hundred lines before the stop would show whether the probe ran normally and where the submissions ended.

AI-assisted: yes

@SoRadGaming

SoRadGaming commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Probe and frame-rate results on AMD's proprietary Windows driver, which this PR hadn't been tested on yet.

Host: Windows 11 Pro 26200, Radeon RX 9070 XT (amdvlk 32.0.23033.1002, LLPC 26.3.1), Ryzen 7 5800X3D, WinLibs GCC 15.2.0 r7, Release.

Probe. The device reports shaderDenormFlushToZeroFloat32 true and independence 32_BIT_ONLY. At startup it logs:

[float-controls] f32 denormal flush: hardware (0 of 5472 probe results differ from the emulated flush, 55 ms)

Frame rate.

hub FPS
APS5_NO_HW_F32_FLUSH=1 7.14 / 7.16
this PR 7.32 / 7.32 / 7.32 / 7.17

That is about +2 %. This scene is limited by queue worker 0 rather than the GPU, so the shader savings show only partly. No errors in the 5 launches with the hardware flush.

AI-assisted (Claude Code); the builds and runs are on my machine.

@alfalfa-star

Copy link
Copy Markdown
Contributor

@ThatsLeo I still had it. Both logs are attached here: the stalled boot in full (418 lines), and the first 500 lines of a good boot with the same binary and environment for comparison.

Probe. It ran normally, once per Vulkan device the build creates, the same as in every other boot:

98:  [float-controls] f32 denormal flush: hardware (0 of 5472 probe results differ from the emulated flush, 20 ms)
221: [float-controls] f32 denormal flush: hardware (0 of 5472 probe results differ from the emulated flush, 17 ms)

Where it stopped. It stopped about 10 s into loading, after both probes:

  • The pipeline cache loaded (111 MiB), the swapchain was created, and the title read its UserSettings, Autosave and AutosaveA saves.
  • It then created the first storage textures and did one StorageTexture upload submit / done.
  • Next it created a 128x128, 8-mip cube (tile=7) and its tile=3 copy (0x2034950000 ... layers=5 dim=2 tile=3).
  • The last GPU-related line is the first [shader-disk-cache] (10 s): 3267 hits, 0 misses report.
  • After that, the log has only Pad script lines until my 125 s timeout killed it (exit 124). On teardown it printed four storage image ... destroyed with GPU results pending, including the frame buffer 0x8fc0000000.

The good boot reaches the same point at line ~395. There it creates a second tile=3 cube copy, does another upload submit / done, and goes on for about 4,100 more lines before its first cache report.

No GPU reset in the kernel log, no crash and no skipped draws or dispatches. The run was killed, so I have no thread backtrace. It also never reached the 95 s trace window, so there is no trace. That fits your reading: the stop is well after the probe, and I can't tell from the log what it was waiting on.

AI-assisted: yes (Claude Code)

@johnmaia

Copy link
Copy Markdown
Contributor

Runs on three more devices: RDNA 3 with RADV, NVIDIA on Windows, and Intel on Windows. Head 0ffb5f4, merged onto main 82ddae7 (on Windows also with #6452, which the MinGW test build needs).

Device Probe ctest, main → this PR
AMD Radeon RX 7600 (RDNA 3, RADV NAVI33, Mesa 25.2.8), Ubuntu 24.04, GCC 13.3 hardware (0 of 5472 probe results differ ...), 26–29 ms, in every device creation of the run 1286 / 1287 → 1286 / 1287
NVIDIA RTX 4080 (617.42), Windows 11 26300, WinLibs GCC 15.2.0 r7 not run: the device reports shaderDenormFlushToZeroFloat32 = false, so the emulated flush stays 1320 / 1321 → 1320 / 1321
Intel UHD Graphics 770 (101.6129), same Windows PC hardware (0 of 5472 ...) in all 488 device creations of the run see below

agc_driver_f32_denormal_flush passes on all three.

The other failures are not this PR's:

  • RX 7600: agc_driver_graphics, which fails the same way on main (Linux GCC13.3: agc_driver_graphics reports VOP2 for an all-zero vertex request #6792; test(agc): keep the vertex terminate case's code in static storage #6789 fixes it).
  • Windows: guest_kqueue, which fails only in our elevated SSH session, on main as well.
  • UHD 770: main fails the same five image-table and bindless tests as this PR (agc_driver_bindless_images, image_sint8, image_table, image_table_cache_bounds, image_table_reuse), plus guest_kqueue.
  • UHD 770, flakes:
    • In the full run with this PR, video_out_hdr_present, patched_libraries, agc_driver_pm4_replay_tool and guest_shader_link also failed. The first three pass on rerun.
    • guest_shader_link sometimes hangs on this GPU without this PR too. Run alone 15 times with a 60 s timeout, it timed out 4 times on main and 6 times with this PR. I read that as the same flake.

AI-assisted: yes (Claude Code)

@milescoviello

Copy link
Copy Markdown
Contributor

A size data point from Crimson Desert (PPSA25286). Its three biggest compute pipelines are the ones that push it past its memory budget while the driver compiles them.

I replayed the title's requests for those three programs with agc_shader_replay, built from main 82ddae7 + #6785 + this PR at 0ffb5f4. Each request was replayed twice: once as dumped (float32FlushToZero false), and once with float32FlushToZero set on its target by a local, unproposed switch in the replay tool. Everything else in the request was identical.

Program SPIR-V words, emulated flush with float32FlushToZero change
A 1,357,366 1,038,828 −23.5%
B 1,691,292 1,349,782 −20.2%
C 1,764,684 1,410,819 −20.1%

The emulated sizes match what the title builds in-game today (1,357,369 words for A).

On this machine (RTX 4070 Ti, NVIDIA 595.99.02, Ubuntu 24.04) the Linux driver also reports shaderDenormFlushToZeroFloat32 = false, as johnmaia saw on Windows. So the hardware path does not apply here, and I could not compare vkCreateComputePipelines time or memory for the smaller modules. With the emulated flush, NVIDIA takes 18.5 s / 1.37 GB, 90 s / 4.1 GB and 101 s / 4.5 GB for A, B and C.

AI-assisted: yes

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 16, 2026
@odntht

odntht commented Oct 16, 2026

Copy link
Copy Markdown

Another row for the probe table: Gen9 on Linux.

Setup: Intel HD Graphics 520 (Skylake GT2, Gen9), Mesa 25.2.8 ANV, Ubuntu 24.04 (container), kernel 6.12.111, GCC 13.3, Release. Head 0ffb5f460 merged onto main 82ddae75b (clean; it conflicts with the current main f474fb776). ANYPS5_GPU="HD Graphics 520"; the log shows it selected.

  • Probe: [float-controls] f32 denormal flush: hardware (0 of 5472 probe results differ from the emulated flush, ...) in every process. It takes 296 ms on the first, cold run and 69 to 85 ms after that (12 processes), against 35 to 50 ms on the Arc in the description.
  • agc_driver_f32_denormal_flush, agc_driver_f16_denormal_flush, agc_driver_ldexp_f32_denormal, agc_driver_vcmp_f32_denormals, agc_denormal_flush_elimination, agc_shader_memory and request_serializer pass 3 of 3.

AI-assisted: yes (Claude Code). The runs are on my machine.

@johnmaia

Copy link
Copy Markdown
Contributor

Follow-up on the guest_shader_link timeouts on the Intel UHD 770 in my run above: they were the test process hanging at exit, which #6986 fixes. With #6986 on main f643e61 it passed 15 of 15, against 4 of 15 timed out on main without it. Nothing there is down to this PR.

AI-assisted: yes (Claude Code)

@blawesom

blawesom commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Rebase handoff, with a serializer version collision resolved — main and this PR each added one shader-target bool after the same tail, both gated at serialization version 32 in their own trees. The merged serializer keeps spannedWaves at v32 and moves float32FlushToZero to a new v33 (writer, reader bound, and the version/future assertions in the tests all follow); the test fixtures' expected tails are rebuilt for the merged layout (one shared header plus both bools).

Verified on Linux (CachyOS, kernel 7.2.9, GCC 16.2.1, Ninja, Release, BUILD_TESTING=ON, AMD RX 9070 XT / RADV):

  • builds; tools/check_conventions.py --base origin/main passes; request_serializer passes with the new versioning
  • one open failure: agc_shader_memory fails its layout-pair reuse check ("the second layout ignored the reuse switch") on the merged branch — I could not attribute it cleanly because your own head does not build against current main (its PipelineJournal size pins are stale), so there is no clean control; the serializer tests and everything else pass apart from the known main regression (agc_driver_tessellation segfault, feat(shader): read hull inputs at a control-point index computed from data #4727)

To pick it up:

git fetch https://github.com/blawesom/AnyPS5 rebase/6503
git checkout <your branch>
git merge --ff-only FETCH_HEAD

Head is 1d180c293 (amended: the serializer v33 fix and the test updates were folded in after the first push).

@ThatsLeo
ThatsLeo force-pushed the perf/shader-hw-f32-flush branch from 0ffb5f4 to c850cb9 Compare October 17, 2026 13:56
@ThatsLeo

Copy link
Copy Markdown
Contributor Author

Rebased onto main 47a266cf9 (c850cb9), with the same resolution as @blawesom's handoff: spannedWaves stays at version 32 and float32FlushToZero moves to version 33. request_serializer now also checks that a version 32 request reads back without the flag. Thanks for preparing it.

agc_shader_memory passes on the rebased head, on the Arc and on lavapipe, so the layout-pair reuse failure is specific to that merge. The full suite:

Thanks also @odntht for the Gen9 row and @johnmaia for tracking the guest_shader_link timeouts to #6986. The probe has now matched the emulated flush on Intel Gen9, Gen12 and Arc (ANV and Windows), RDNA 3 and RDNA 4 (RADV), and AMD's Windows driver. On NVIDIA the drivers don't offer the f32 flush, so it stays emulated there.

AI-assisted: yes

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 17, 2026
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed perf Performance change labels Oct 18, 2026
@ThatsLeo
ThatsLeo force-pushed the perf/shader-hw-f32-flush branch from c850cb9 to dded725 Compare October 18, 2026 23:14
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 18, 2026
@adriadam10 adriadam10 self-assigned this Oct 19, 2026
ThatsLeo and others added 3 commits October 19, 2026 07:00
…tches the emulation

Devices with DenormFlushToZero for f32 run the mixed denormal-flush programs with and
without the hardware mode at startup; when every result matches, add, sub, mul, fma,
floor and ceil in the default rounding mode drop their software flush.
@adriadam10
adriadam10 force-pushed the perf/shader-hw-f32-flush branch from dded725 to 59449dc Compare October 19, 2026 05:14
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@adriadam10
adriadam10 merged commit 9f5d4bd into boykopovar:main Oct 19, 2026
10 checks passed
oneandonlydean pushed a commit that referenced this pull request Oct 20, 2026
APS5_NO_GPL opened a device without VK_EXT_graphics_pipeline_library and
the five extensions that go with it. The AMD Windows driver shares its
on-disk pipeline cache between devices with and without those extensions,
so a compute pipeline compiled on one kind of device and loaded on the
other ran with the wrong descriptor layout. The startup f32 flush probe
(#6503) runs the same compute pipelines on every device. In a parallel
test run, every APS5_NO_GPL test therefore page-faulted at 0xbeefdeadbeef,
an address read from the probe's own 0xdeadbeef fill. Each fault reset the
GPU, and twice the reset hung the machine.

The device now enables the extensions and their features whenever the
GPU supports them. APS5_NO_GPL only turns off their use, so every device
on a GPU builds the same pipelines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
oneandonlydean pushed a commit that referenced this pull request Oct 21, 2026
#6503 took request serialization 36 for its float32 flush flag, so this branch's sample order moves to 37. The version relabels in ShaderMemory.cpp and RequestSerializerTests.cpp drop both trailing flags and the sample order.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* build docs perf Performance change shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants