Repository navigation
perf(shader): flush f32 denormals in hardware when a startup probe matches the emulation - #6503
Conversation
|
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.
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.
|
|
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 Probe: hardware flush is on here too, ctest (1267 tests, Kena: Bridge of Spirits (PPSA01802), walking in the forest after loading a save. The base is
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 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) |
|
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 AI-assisted: yes |
|
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 Frame rate.
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. |
|
@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: Where it stopped. It stopped about 10 s into loading, after both probes:
The good boot reaches the same point at line ~395. There it creates a second 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) |
396372f to
0ffb5f4
Compare
|
Runs on three more devices: RDNA 3 with RADV, NVIDIA on Windows, and Intel on Windows. Head 0ffb5f4, merged onto
The other failures are not this PR's:
AI-assisted: yes (Claude Code) |
|
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
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 AI-assisted: yes |
|
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
AI-assisted: yes (Claude Code). The runs are on my machine. |
|
Follow-up on the AI-assisted: yes (Claude Code) |
|
Rebase handoff, with a serializer version collision resolved — Verified on Linux (CachyOS, kernel 7.2.9, GCC 16.2.1, Ninja, Release,
To pick it up: Head is |
0ffb5f4 to
c850cb9
Compare
|
Rebased onto
Thanks also @odntht for the Gen9 row and @johnmaia for tracking the AI-assisted: yes |
c850cb9 to
dded725
Compare
…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.
dded725 to
59449dc
Compare
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>
#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.
What
In
FLOAT_MODEf32 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 makesv_mul_f32, the most common f32 instruction, about 96 SPIR-V words, against about 19 forv_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
shaderDenormFlushToZeroFloat32anddenormBehaviorIndependenceother thanNONErunsProbeFloat32FlushToZerowhen it is created. It recompiles the three programs ofagc_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_MODEoff andIEEE_MODEon, once with the current emulation and once withDenormFlushToZero 32declared, 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::float32FlushToZerois set (it is part of the cache key and of the serialized request, version 33). Every module then declaresDenormFlushToZero 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;v_fma_f32,v_fmac_f32,v_fmamk_f32andv_fmaak_f32(roundsProductSeparatelyfalse).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_FLUSHskips 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 inComputePipelinesCreated.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).
[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_flushnow 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.request_serializerandagc_shader_memorycover the new field (round trip, cache key, version 33, version 32 requests read without it, downgraded requests).ctest(1493 tests) onmainfc623324a: the Arc, where the probe enables the hardware flush for the whole suite, and lavapipe both pass all butapplication_heap_lazy(fix(libc): open closed standard descriptors on /dev/null when libc loads #6107).APS5_TRACE_FRAMES=20:30, no other switch), 3840x2160, warm caches, two captures each, same session. The base ismain0029e62f9with one local change the dump needs (PlayGo answersBAD_CHUNK_IDfor the missingplaygo-*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).mainThe compute program that dominates the scene (
0x2011300000, 8x8 wave64 at 3840x2160) went from 343 to 307 ms per dispatch in anAPS5_PROFILE_GPU=1run.{"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.pyoutput: ThatsLeo:evidence/hw-f32-flush (main/andpr/).v_fma_mix_f32) took the f32 instructions' SPIR-V from 61.8M to 41.5M words (-33%):v_mul_f3296 to 38 words,v_add_f3276 to 46,v_sub_f3257 to 37.Related open PRs: zmeizy's series for f32 denormal modes 1 and 2 (#6010 and the others) changes
f32DenormalFlushFortoo; this PR acts only in mode 0. #5782 adds a flush to thev_mad_f32family; the fused-only condition here keeps the non-fused forms emulated whatever it decides.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)