Repository navigation
perf(shader): check f32 NaN sources only for NaN results, narrow window reads - #12548
josealissonbr wants to merge 5 commits into
Conversation
…ow reads The f32 add, sub, mul and fma NaN rule selected over every source after every operation. NanPropagateF32 keeps the same bits but checks the sources in a branch taken only when the host result is NaN, which every NaN source produces under SignedZeroInfNanPreserve. The SRT evaluator evaluates the opcode with the same rule. VectorWindowIndexNarrower lists the few values a relative read index can take and, when every relative access of a window is such a read, reads constant slots instead, so the window takes no indexed access. Together they take the two particle passes of the PPSA01325 title screen from 16.6 and 14.7 ms to about 8.8 and 6.3 ms of GPU time per frame on an RTX 5070; either change alone made no gain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e-nan-propagation
…e-nan-propagation
|
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.
|
MarcZX10
left a comment
There was a problem hiding this comment.
Reviewed — changes requested
The performance evidence is present, but this branch overlaps open PRs #4287 and #10720 without recording an agreed integration order; combining it with #10720 produces a source conflict.
What I ran
- Reviewed exact head
4dee5b5e3296c643731e19eca13ddad80d1e49abagainst base319621ba2e578bd053b0baba2877fe70520e0737; current-main merge treedb3ff82497c56456347fdd03f9822d02a37bb553is clean. - Ran
git diff --check refs/review/current-main...refs/review/12548— pass, no output. - Compared both declared related PRs: #4287 is open and changes the same
nanResultF32behavior; #10720 is open and changes the same operand-access logic. Pairwise merge with #4287 is clean, but pairwise merge with #10720 reports a content conflict inOperandAccess.cpp. - Inspected the new NaN-propagation emitter/evaluator and finite-set vector-window narrowing logic. No separate correctness defect was established in that source inspection.
- Refreshed hosted checks: Windows, Ubuntu/Windows relinker, relinker ASan, conventions, eligibility, labels and report pass; Linux product and specialized jobs are skipped; macOS relinker checks are pending.
Results
| Check | Result |
|---|---|
| Current-main merge preview | Clean |
| Diff check | Pass |
| Merge preview with #4287 | Clean tree; same NaN-result logic is being proposed |
| Merge preview with #10720 | Content conflict in OperandAccess.cpp |
| Performance report requirements | Body includes paired same-scene summaries, four attached traces and trace_digest.py output; captures report no dropped events |
| Hosted Windows and Ubuntu/Windows relinker, ASan, policy | Pass |
| Hosted Linux product | Skipped |
| Hosted macOS relinker | Pending |
| Focused Vulkan/performance runs | Reported by author; not independently run |
Raw combined-merge output with #10720
CONFLICT (content): Merge conflict in core/shader/recompiler/Translation/src/TranslationContext/OperandAccess.cpp
Negative control
| Assertion | Change under test | Result |
|---|---|---|
| The NaN and vector-window changes preserve behavior and improve the reported title workload | Author reports current-main versus change runs, full Vulkan CTest, and validation-layer comparison | Evidence and raw summaries are included in the PR body; I did not run the GPU control locally |
Findings
Blocking
- Coordinate and declare the overlapping work. #4287 remains open and changes the same
nanResultF32NaN selection behavior; the PR says this is offered in its place, but no agreement or supersession order is recorded. #10720 also remains open, and a combined merge preview with its current head conflicts inOperandAccess.cpp. The repository requires authors of overlapping PRs to agree on landing order and record it withDepends on. Please coordinate with the affected authors and update the checklist/body to state the agreed order or replacement plan; rebase and rerun overlap checks where required. This request is about integration, not pressure to close either PR.
Non-blocking
The performance report is substantially complete: paired captures use the same title, scene, machine, driver and resolution; the four trace files and digest output are attached. I did not independently validate those trace binaries or reproduce the Vulkan measurements.
Sources
- NaN propagation and window narrowing changes at this PR head
- Window-index analysis at this PR head
- Open competing NaN-result PR #4287
- Open related fast-f32 PR #10720
- Contribution rule for overlapping PRs
Not verified
I did not run the full CTest suite, Vulkan validation tests, GPU capture, the SRT tape path, or console/title tests. Hosted product Linux is skipped and macOS relinker checks were still pending. Author-reported test runs and trace measurements are not my own runs.
To get this to a good state
Record an author-agreed integration order/replacement plan for #4287 and #10720, then rebase and rerun the overlap check. I will review the updated head and retest the affected integration paths.
adriadam10
left a comment
There was a problem hiding this comment.
Checked the diff against the existing nanResultF32 rule. The NaN branch keeps the same bits (the legacy mul/mad forms zero the factors before the call, the invalid-product guard for a NaN addend is kept, the SRT evaluator applies the same order), and the window narrower falls back whenever any access is a store or an index is unbounded. The numbers are clear. Two things block the merge:
- Overlap, no order.
OperandAccess.cpp:345-351rewrites the samenanResultF32body that #4287 reduces (waiting for title numbers, with @oneandonlydean's ordering "#4287 first, then #10720 rebases onto it"), and #10720 changes the same lines behindAPS5_FAST_F32. Please say which of the three lands and how: either list #4287/#10720 in Depends on and rebase on the one that goes first, or state that this replaces #4287 and why the branch version is better, and show that it still merges with #10720 (it conflicts today, as MarcZX10 reported). - Unrelated behaviour change.
SrtEvaluator.cpp:372turnsif (value.ArgumentCount() < *arguments) return true;intoreturn false;for every opcode inevaluatedArguments, not onlyNanPropagateF32. Drop it, or explain why it is needed and add a test where it changes the result.
Smaller: docs/dev/debt/shader.md is accurate, but the 16-value limit of the narrower and the compare-chain cost are unmeasured; a line in Tested on a program with many listed reads would settle that.
…NanPropagateF32 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
✅ NVIDIA GPU test: passedPR head
Intermittent validation messages (1, seen once in the parallel run, not reproduced when the test was re-run alone 2 times; not counted against this PR)Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (2, not counted against this PR)Validated but not compared (1, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 394 tests)Skipped (41)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
|
An AMD RDNA 4 run on Windows of Windows 11 Pro 10.0.22631, AMD Radeon RX 9070 XT (
AI-assisted: yes (Claude Code) |
|
@josealissonbr An AMD RDNA 2 run of this head and one note from reading it. Setup. RX 6700 XT (gfx1031), AMD driver 32.0.21045.16010, Windows 11 26200, Ryzen 5 5600X, WinLibs GCC 15.2.0, Release. Head Tests, with Persona 5 Royal (PPSA05110, own dump), first gameplay area at 3840x2160. Four legs alternating base and PR, each with a 4 s trace at 560 s; other processes used a median 2.7-3.2 % CPU over each window ±20 s. Summary-line env:
GPU time per present didn't change: +0.14, -0.21 and -0.06 ms in the three neighbouring pairs (1-2, 2-3, 3-4). Frame time here is set by the CPU side (the GPU is busy 39 ms of a 57 ms frame, and the queue worker's On the CPU side the PR legs come out worse in all three pairs: one present fewer per window (66 against 67), 0.5-2.2 ms per frame more queue-worker time and a 0.5-1.7 ms higher p90. Part of that is one 72-75 ms The emulator dumped a 960x540 frame every 20 presents for our input driver, 3-4 per window at 0.5-0.8 ms each. p99 is left out: with 66-67 frames it is the slowest single frame (base: 246 ms for the window's first frame, and 75 ms; PR: the 148 and 141 ms stall frames). Note: the AI-assisted: yes (Claude Code) |
…/recompiler-rare-nan-propagation # Conflicts: # docs/dev/debt/shader.md
✅ NVIDIA GPU test: passedPR head
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (4, not counted against this PR)Validated but not compared (1, not in the main baseline's validation pass)Validation IDs already present on main (3 IDs in 392 tests)Skipped (48)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
adriadam10
left a comment
There was a problem hiding this comment.
Checked the new head against our last review. The SrtEvaluator.cpp change is fixed in 965b877: the short-argument path is back to return true and only NanPropagateF32 takes the new case.
One point is still open:
- Overlap, no order.
OperandAccess.cpp(thenanResultF32rewrite) still conflicts with #4287 and with #10720 (git merge-treeagainst current heads), and the body saysDepends on: none. Please say which of the three lands and how: either list the one that goes first in Depends on and rebase onto it, or state that this replaces #4287 and why, and show that it still merges with #10720.
Smaller, optional: a line in Tested on a program with many listed reads would settle the 16-value limit of the narrower and its compare-chain cost.
|
Independent AMD Linux correctness evidence for head Hardware: Radeon RX 6650 XT, RADV/Mesa 26.2.6; physical Vulkan with required Baseline Two conflicts were resolved without dropping main's behavior:
Matched focused gate: baseline 10/10, change 11/11, zero skipped, failed, or Bugsnax PPSA01503 v02.000.000: the same private 3840×2160 cave-entrance frame captured from main replays with both builds using One of this scene's expensive fragment programs compiles to 58,926 SPIR-V words on baseline and 65,918 with this PR: fewer selects but more control flow. Cold single-pass replay takes 46.549 versus 76.785 seconds, including preparation and pacing; those are not steady-state GPU times or native FPS. This confirms a real frame still renders exactly while also showing why static size and cold replay cannot establish a performance gain. Normal game-launch, same-scene FPS measurement remains outstanding; this is not full-suite, full-game, Windows or PS5-equivalence qualification. Evidence archive, SHA-256 |
|
Native Bugsnax follow-up on the same RX 6650 XT: these repeats do not establish a stable FPS gain in this scene. This supplements the earlier pixel-exact replay/correctness result; it does not contradict the reported NVIDIA result for another title. Clean main Machine: Ryzen 9 7950X, 32 threads, 128 GiB RAM, RX 6650 XT, RADV Mesa 26.2.6-arch1.1, Linux 7.2.11-arch1-1. Bugsnax PPSA01503 v02.000.000, 3840×2160 render resolution, saved rainy cave entrance. Separate fresh caches and writable runtimes; the same verified inactive save seed and Cross at30/50/70 seconds, held2s. No input after about73s. Reviewed snapshots at120/165/195 show the same cave/camera/scaffold/vegetation, with normal animated rain/lens/wind differences. Each whole arm uses its own FIFO turn and closes ordinarily at210s. Official
Order was A1 → B1 → B2 → A3 for the four qualified measurements. The first pair is +4.71%; the reversed comparison is −1.71%. Main itself varies from20.00 to21.12 FPS, while the change gives20.94 and20.76. The small average difference sits inside that main-run spread: I cannot claim a repeatable improvement or a general regression from these two runs per variant. All four qualified runs exit0, no trace drops, protected input/save/SDK/executable/module hashes unchanged, no remaining native processes, and the temporary udmabuf limit restored to64. The matched correctness gate on these exact clean-main/change sources passes10/11 groups (8/9 physical-GPU plus2 CPU), zero skipped or VUID errors, with actual Khronos instance/device insertion in both logs. Exclusions, retained rather than overwritten: the initial harness rejected a valid symlinked library before measurement; the resolved-path check was corrected without changing source/SDK. An additional main run between B2 and A3 is excluded because an independent HTTP2 CPU test overlapped its capture by16.89s; it was allowed to close normally and replaced by A3 with no other builds/tests/trace processing during that run. Timing receipts establish no such overlap for any of the four qualified windows. No focus was forced; directX11 freshness checks are unavailable where the window was inactive, and that limitation is retained beside the reviewed compositor images. Four native traces and exact summary lines, commands, reviewed screenshots, full logs, SDK provenance, matched gate/JUnit and exclusion receipts, SHA-256 |
|
Native title measurement for public head I integrated this PR alone onto main Same machine: RX 6650 XT, RADV Mesa 26.2.6, Linux 7.2.11, Ryzen 9 7950X. Both runs use 3840×2160, the same six-file save seed/input route, fresh private shader/Mesa caches, and the first tutorial kitchen at the ingredient prompt (score 0, timer 01:40). Both before/after-window screenshots were reviewed; camera/positions match, while costume tints vary between runs. This is not pixel-equivalence evidence. Both traces use Official
Conclusion: +1.90% mean FPS in one pair, with worse p99, does not establish a useful title-level speedup. I am not claiming one or repeating this unchanged pair; it falls below the predeclared 5% threshold for a reverse-order main repeat. The candidate exits normally after 333.9 s; observer/harness also exit 0. Protected inputs, executable/modules and the 799-file SDK bank are unchanged; the shared import limit is restored, and no private native process remains. Thirteen host-work samples covering 278–304 s contain no compiler/test/profiler processes. Five focused candidate tests pass without skips or VUIDs, with the Khronos instance/device validation layer actually inserted. The shader that dominates the kitchen's GPU time was also captured privately and recompiled offline: main 24,683 SPIR-V words / 639 selects / 71 conditional branches; this PR 27,442 / 479 / 215. These counts are diagnostic, not a performance claim. Traces, official digests, integration diff and focused test logs. SHA-256: |
|
@josealissonbr I am evaluating a narrow normal-finite-constant This proof differs from conditional source checking. If your PR goes first, I would express the same narrow predicate at the existing structured node rather than carry a competing legacy helper edit. Could you confirm the preferred ordering or whether you would like the domain proof offered as a credited follow-up? No measured gain is claimed here. AI-assisted: yes (Codex). The concrete change is now available as draft #14892, including the isolated diff, arithmetic proof, matched focused hardware checks, a pinned-current-main native refresh and separately scoped ordinary gameplay evidence. The merge-order question remains open; that branch includes none of these overlapping PRs. |
|
Radeon gameplay validation of the complete existing change by josealissonbr, at author head I integrated it on plain main Each study used six prospectively fixed, balanced independent run-level pairs and one warmup per arm. The positive initial screen selected the candidate for one fresh, separately frozen confirmation with the balanced launch order reversed. Results are reported separately, without pooling or additional pairs:
The primary metric is each run’s original Scope: Sons of Sparta PPSA28997 v01.008.001, restored camp, physical internal 2560×1440, presentation 1920×1080, Radeon 760M / private RADV Mesa 26.2.6 + libdrm 2.4.133 / Linux 7.2.12. Exact arithmetic, normal GPL with optimized LTO, explicit pipeline journal OFF, default heap setting, and Matched focused coverage passed all 11 groups per arm, including nine actual Vulkan execution groups with the Khronos layer loaded on the Radeon, plus shader-memory and vector-window groups. The author’s bounded-relative-index execution fixture was also compiled against plain main for a matched control. No skipped tests, missing-layer messages or VUID errors appeared. These are this integration’s tests, separate from the earlier contributor’s Radeon integration coverage. Timing-only evidence with frozen protocols, exact source/configuration hashes, original summary lines, pinned upstream full digests, all unchanged original timing traces, and full remote byte/readback verification: initial screen and independent confirmation. Game-derived shaders, saves, screenshots, ISA, captures, logs and binaries remain private. AI-assisted validation and analysis: yes (Codex). Credit for the optimization remains with josealissonbr. |
|
Closing this PR. I no longer have the GPU (RTX 5070) that this work was developed, tested and measured on, so I can't test it or follow up on review anymore, and the PR is no longer useful from my side. Thanks for the reviews. |
|
@josealissonbr @boykopovar I would like to take over the integration, Linux/Radeon testing and review follow-up for this work, while retaining josealissonbr’s authorship and credit. Would you be comfortable reopening #12548 with that arrangement, or would you prefer a credited successor PR from my fork? I cannot reproduce the original RTX 5070 measurements, so those would remain explicitly attributed historical evidence. There is independent evidence worth preserving: our complete-PR validation found a 2.43% mean frame-time reduction in a six-pair screen and 3.40% in a separate fresh six-pair confirmation (95% CI 2.90–3.91%). That is Sons of Sparta’s physical-1440p camp on Radeon 760M/private RADV 26.2.6, main Proposed continuation:
I rechecked main at AI-assisted: yes (Codex). |
What
The f32 add, sub, mul and fma NaN rule (
nanResultF32) selected over every source after every operation: one compare and one select per source on each of these instructions. The new IR opcodeNanPropagateF32keeps the same result bits but checks the sources in a branch taken only when the host result is NaN. That is exact because every module declares f32SignedZeroInfNanPreserve, so a NaN source always gives a NaN host result, and the tiny-product flush needs finite factors and a zero addend. Immediate sources are emitted as integer constants, since a float constant bitcast quiets a signaling NaN on NVIDIA. The SRT evaluator evaluates the opcode with the same rule, and the passes that inspect selects (masked select, redundant value, tessellation lowering, denormal flush) know it.VectorWindowIndexNarrowerlists the few values a relative read index can take (adds, shifts, masks, bit-field extracts and selects of known values, at most 16) and, when every relative access of a window is such a read and every listed value is inside the window, reads the listed constant slots and selects between them, so the window needs no indexed access. It changes nothing when any window access is a store or has an index it cannot bound.Each change alone made no difference in the measured scene; together they shorten the two particle passes of the PPSA01325 title screen. The behaviour is generic: no title, shader or address is matched.
docs/dev/debt/shader.mdrecords both, including that the SRT tape compiler does not know the new opcode and falls back to the interpreter.Tests:
ShaderMemorycovers the evaluator with NaN, signaling NaN, IEEE and non-IEEE sources and the invalid product;VectorWindowcovers the narrowed shape of a dynamic function access; the new GPU testexecution/RelativeIndexingBoundedValuesruns relative reads whose index takes a small set of values and checks every lane.Tested
Windows 11 Pro 26200, Intel Core i5-14400, NVIDIA GeForce RTX 5070 (driver 616.64), MinGW GCC 15.2 Release build.
ANYPS5_REQUIRE_VULKAN=1andANYPS5_NO_SHADER_CACHE=1: 2862 of 2862 passed on this head (4dee5b5,main319621b merged), and 2857 of 2857 on 0c620ad (maine4da19a merged). The 65maincommits between them change one file of the recompiler (ControlFlow/src/GraphBuilder.cpp) and add one AGC test; neither touches the code of this change.agc_tests withVK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation(Khronos layer built from b2b44c0, API 1.4.366),ANYPS5_REQUIRE_VULKAN=1andANYPS5_NO_SHADER_CACHE=1,ctest -R "^agc_" -V, onmaine4da19a and on 0c620ad, same machine and GPU: 1138 of 1138 and 1139 of 1139 passed (the extra one is the new test). Neither log reportswas not found but was requested by env var VK_INSTANCE_LAYERS; the same 20 tests log validation messages on both, with the same IDs, and this change adds no newVUID-ID.--windows --windows-gui --to-intel, from start without input to the title screen, 3840x2160.maine4da19a against 0c620ad (this branch before the merge ofmain319621b), both built in the same session on the same machine. Each capture is 20 s from 110 s after start (APS5_TRACE_SECONDS=110:20, no other switch, log not redirected), after one warm-up run of each build; captures ran alternating main 1, change 1, main 2, change 2. All four windows are on the looping title screen animation.Summary lines:
{"commit":"e4da19aff06e","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"110:20"},"gpu":"NVIDIA GeForce RTX 5070","driver":"NVIDIA 616.64","os":"Windows 10.0.26200","cpu":"Intel(R) Core(TM) i5-14400","cpu_threads":16,"memory_mib":32452,"title_id":"PPSA01325","title":"ASTRO's PLAYROOM","content_version":"01.905.000","resolution":"3840x2160","window":{"spec":"seconds 110:20","first_present":2171,"ms":20022.129},"presents":283,"frame_ms":{"p50":70.459,"p90":73.799,"p99":77.083,"max":81.656},"gpu_busy_ms_per_present":56.152,"worker_busy_pct":{"queue worker 0x0":99.375,"queue worker 0x3d":99.712,"queue worker 0x52":0.000},"top_phases_ms_per_frame":{"Driver.Packet":140.625,"Driver.Packet.memory_wait":56.227,"Driver.Packet.dispatch":26.511,"Driver.Dispatch":25.967,"Vulkan.Dispatch":23.921,"Vulkan.Dispatch.shader_resources":23.665,"Driver.Packet.draw":19.713,"Driver.Draw":19.604},"trace_dropped":false,"trace":"aps5-trace-e4da19aff06e-40620.json.gz"} {"commit":"0c620add72d2","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"110:20"},"gpu":"NVIDIA GeForce RTX 5070","driver":"NVIDIA 616.64","os":"Windows 10.0.26200","cpu":"Intel(R) Core(TM) i5-14400","cpu_threads":16,"memory_mib":32452,"title_id":"PPSA01325","title":"ASTRO's PLAYROOM","content_version":"01.905.000","resolution":"3840x2160","window":{"spec":"seconds 110:20","first_present":2036,"ms":20054.828},"presents":351,"frame_ms":{"p50":56.749,"p90":59.722,"p99":64.314,"max":66.495},"gpu_busy_ms_per_present":41.454,"worker_busy_pct":{"queue worker 0x0":99.196,"queue worker 0x3d":99.723,"queue worker 0x52":0.000},"top_phases_ms_per_frame":{"Driver.Packet":113.493,"Driver.Packet.memory_wait":52.147,"Driver.Packet.dispatch":16.624,"Driver.Dispatch":16.070,"Graphics.Draw":15.212,"Vulkan.Dispatch":14.220,"Vulkan.Dispatch.shader_resources":13.968,"Driver.Packet.draw":13.903},"trace_dropped":false,"trace":"aps5-trace-0c620add72d2-22844.json.gz"} {"commit":"e4da19aff06e","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"110:20"},"gpu":"NVIDIA GeForce RTX 5070","driver":"NVIDIA 616.64","os":"Windows 10.0.26200","cpu":"Intel(R) Core(TM) i5-14400","cpu_threads":16,"memory_mib":32452,"title_id":"PPSA01325","title":"ASTRO's PLAYROOM","content_version":"01.905.000","resolution":"3840x2160","window":{"spec":"seconds 110:20","first_present":2173,"ms":20028.327},"presents":284,"frame_ms":{"p50":70.110,"p90":72.849,"p99":76.116,"max":88.590},"gpu_busy_ms_per_present":55.493,"worker_busy_pct":{"queue worker 0x0":99.379,"queue worker 0x3d":99.696,"queue worker 0x52":0.000},"top_phases_ms_per_frame":{"Driver.Packet":140.232,"Driver.Packet.memory_wait":55.518,"Driver.Packet.dispatch":26.931,"Driver.Dispatch":26.399,"Vulkan.Dispatch":24.206,"Vulkan.Dispatch.shader_resources":23.942,"Driver.Packet.draw":19.938,"Driver.Draw":19.831},"trace_dropped":false,"trace":"aps5-trace-e4da19aff06e-35696.json.gz"} {"commit":"0c620add72d2","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"110:20"},"gpu":"NVIDIA GeForce RTX 5070","driver":"NVIDIA 616.64","os":"Windows 10.0.26200","cpu":"Intel(R) Core(TM) i5-14400","cpu_threads":16,"memory_mib":32452,"title_id":"PPSA01325","title":"ASTRO's PLAYROOM","content_version":"01.905.000","resolution":"3840x2160","window":{"spec":"seconds 110:20","first_present":2410,"ms":20011.073},"presents":312,"frame_ms":{"p50":58.020,"p90":61.977,"p99":70.254,"max":1754.371},"gpu_busy_ms_per_present":42.432,"worker_busy_pct":{"queue worker 0x0":99.261,"queue worker 0x3d":99.474,"queue worker 0x52":0.000},"top_phases_ms_per_frame":{"Driver.Packet":127.268,"Driver.Packet.memory_wait":52.553,"Graphics.Draw":22.970,"Driver.Packet.draw":21.051,"Driver.Draw":20.935,"Graphics.Draw.draw_and_resource_release":20.802,"Driver.Packet.prepare_memory":18.271,"Driver.Packet.dispatch":17.595},"trace_dropped":false,"trace":"aps5-trace-0c620add72d2-15132.json.gz"}tools/trace_digest.pyon main 1 and change 1: GPU busy drops from 56.1 to 41.4 ms per frame and frame p50 from 70.5 to 56.7 ms (about 14.2 to 17.6 FPS). The queue worker that waits for the GPU (queue worker 0x3d) spends 54.3 ms per frame inDriver.Packet.memory_waitonmainand 50.4 ms on this change, and the presenter waits 15.4 against 4.9 ms. The 1754 ms frame in change 2 is oneVulkan.GraphicsPipelineLibrarycreation (1643 ms) for a pipeline the title first uses there: with the faster build, 110 s after start falls later in the title animation (present 2494 against 2171 onmain), and no other frame of the change captures exceeds 192 ms.Before this merge, a local GPU pass timer (not part of this change) measured the two particle passes on
maine2a5118 at 16.6 and 14.7 ms of GPU time per frame, 17.7 and 16.0 ms with the NaN branch alone, and 8.6-9.0 and 6.2-6.5 ms with both changes.tools/trace_digest.py, main 1seconds 110:20 from present 2171: 283 presents, frame p50 70.5 / p90 73.8 / p99 77.1 / max 81.7 ms, GPU busy 56.2 ms per present
NVIDIA GeForce RTX 5070, NVIDIA 616.64, Windows 10.0.26200, 3840x2160, commit e4da19a, dropped: no
{"commit":"e4da19aff06e","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"110:20"},"gpu":"NVIDIA GeForce RTX 5070","driver":"NVIDIA 616.64","os":"Windows 10.0.26200","cpu":"Intel(R) Core(TM) i5-14400","cpu_threads":16,"memory_mib":32452,"title_id":"PPSA01325","title":"ASTRO's PLAYROOM","content_version":"01.905.000","resolution":"3840x2160","window":{"spec":"seconds 110:20","first_present":2171,"ms":20022.129},"presents":283,"frame_ms":{"p50":70.459,"p90":73.799,"p99":77.083,"max":81.656},"gpu_busy_ms_per_present":56.152,"worker_busy_pct":{"queue worker 0x0":99.375,"queue worker 0x3d":99.712,"queue worker 0x52":0.0},"top_phases_ms_per_frame":{"Driver.Packet":140.625,"Driver.Packet.memory_wait":56.227,"Driver.Packet.dispatch":26.511,"Driver.Dispatch":25.967,"Vulkan.Dispatch":23.921,"Vulkan.Dispatch.shader_resources":23.665,"Driver.Packet.draw":19.713,"Driver.Draw":19.604},"trace_dropped":false,"trace":"aps5-trace-e4da19aff06e-40620.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
Largest slices (ms per frame, inclusive, all threads): Driver.Packet 136.7, Driver.Packet.memory_wait 56.2, batch 56.1, Driver.Packet.dispatch 26.5, Driver.Dispatch 25.9, Vulkan.Dispatch 23.9, Vulkan.Dispatch.shader_resources 23.6, Driver.Packet.draw 18.2, Driver.Draw 18.1, Driver.Packet.prepare_memory 17.9
Longest waits
Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)
tools/trace_digest.py, change 1seconds 110:20 from present 2036: 351 presents, frame p50 56.7 / p90 59.7 / p99 64.3 / max 66.5 ms, GPU busy 41.5 ms per present
NVIDIA GeForce RTX 5070, NVIDIA 616.64, Windows 10.0.26200, 3840x2160, commit 0c620ad, dropped: no
{"commit":"0c620add72d2","dirty":false,"env":{"APS5_TRACE_DIR":"<path>","APS5_TRACE_SECONDS":"110:20"},"gpu":"NVIDIA GeForce RTX 5070","driver":"NVIDIA 616.64","os":"Windows 10.0.26200","cpu":"Intel(R) Core(TM) i5-14400","cpu_threads":16,"memory_mib":32452,"title_id":"PPSA01325","title":"ASTRO's PLAYROOM","content_version":"01.905.000","resolution":"3840x2160","window":{"spec":"seconds 110:20","first_present":2036,"ms":20054.828},"presents":351,"frame_ms":{"p50":56.749,"p90":59.722,"p99":64.314,"max":66.495},"gpu_busy_ms_per_present":41.454,"worker_busy_pct":{"queue worker 0x0":99.196,"queue worker 0x3d":99.723,"queue worker 0x52":0.0},"top_phases_ms_per_frame":{"Driver.Packet":113.493,"Driver.Packet.memory_wait":52.147,"Driver.Packet.dispatch":16.624,"Driver.Dispatch":16.07,"Graphics.Draw":15.212,"Vulkan.Dispatch":14.22,"Vulkan.Dispatch.shader_resources":13.968,"Driver.Packet.draw":13.903},"trace_dropped":false,"trace":"aps5-trace-0c620add72d2-22844.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
Largest slices (ms per frame, inclusive, all threads): Driver.Packet 109.6, Driver.Packet.memory_wait 52.1, batch 41.4, Driver.Packet.dispatch 16.6, Driver.Dispatch 16.0, Vulkan.Dispatch 14.2, Vulkan.Dispatch.shader_resources 13.9, Graphics.Draw 13.8, Driver.Packet.prepare_memory 12.9, Driver.Packet.draw 12.4
Longest waits
Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)
Traces (slice timings only; they open in ui.perfetto.dev):
aps5-trace-0c620add72d2-22844.json.gz
aps5-trace-e4da19aff06e-35696.json.gz
aps5-trace-e4da19aff06e-40620.json.gz
aps5-trace-0c620add72d2-15132.json.gz
Overlap: this is offered in place of #4287, which reduces the same
nanResultF32select chain to one quiet and one fallback select and has been waiting for title numbers; this change keeps the NaN rule out of the common path instead and brings those numbers. #10720 changes the same lines behindAPS5_FAST_F32; #12233 touches the neighbouring line ofdocs/dev/debt/shader.mdand #12324 a line ofRecompiler.cppnext to the new pass.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)🤖 Generated with Claude Code