Skip to content

perf(agc): give small device-local buffers a pool tier of their own - #8912

Open
Celegans12 wants to merge 1 commit into
boykopovar:mainfrom
Celegans12:perf/agc-small-device-buffer-tier
Open

Celegans12 wants to merge 1 commit into
boykopovar:mainfrom
Celegans12:perf/agc-small-device-buffer-tier

Conversation

@Celegans12

@Celegans12 Celegans12 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

#7056 moved device-local buffers under 1 MiB out of the device tier into the small tier, so the large device buffers' churn stops evicting them. But the small tier's 64 MiB budget is also what the small host buffers live in, so now those two evict each other. Every small device miss is a dedicated vkAllocateMemory and every eviction a vkFreeMemory on the draw commit thread. In Yōtei on our tree, after we merged #7056 the small tier had 2.5x the misses of our previous build (492k vs 192k), and BufferPool::Put took 1.8 s of that thread per 120 s.

  • Small device-local buffers get their own tier (smallDeviceTier) with a budget of max(64 MiB, device tier budget / 8), on top of the device tier's. They get back the device tier's next-class search they had before fix(agc): keep small device buffers in the small pool tier #7056. The large device buffers and the small host buffers no longer evict them, and they don't evict those.
  • Each tier's slot bound goes from a sixth to an eighth of maxMemoryAllocationCount, so four tiers still leave half of it free; the header comment that described three tiers is gone.
  • APS5_BUFFER_POOL_SHARED still puts every device buffer in the device tier. trim and the destructor cover the new tier, and the [bufferpool] line reports it.

Overlaps: #8748 edits the line after the class search in Take. The authors agreed that #8748 lands first and this PR rebases over it. #11920 touches the constructor initializer; its author agreed to rebase if this PR lands first. #8672's idle reclaim and #4472's VRAM rule walk only the device tier, so small device buffers stay out of their reach, as they are on main today.

Tested

The requested current-main comparison in a scene that exercises this tier remains outstanding. The older combined open-world result does not establish a speed gain for this PR; the isolated in-run and title-menu comparisons below show no significant frame-rate gain.

  • Windows 11 Pro, MinGW-w64 GCC 15.2.0, -DBUILD_TESTING=ON, RTX 3080 Laptop, based on main 7ab4b2e: ctest 1824/1828. The 4 failures (log_macros, log_macros_timestamps, guest_kqueue, and agc_driver_image_array_window_past_layer_limit, which doesn't compile with MinGW here) fail on main too.
  • New SmallDeviceOwnTier case in agc_buffer_pool: a released 256 KiB device buffer survives 80 MiB of small host buffer churn and is reused without an allocation. With main's BufferPool it fails: "small host buffer churn evicted a retained small device buffer".
  • Linux (WSL, GCC 13.3, lavapipe), CI's job on the merge with main: 1762/1782; the same 20 fail on main (WSL has no userfaultfd write watching).
  • Isolated switch comparison, run pb-8912 (2026-10-22, our tree on main 557682c, not current main): APS5_NO_SMALL_DEVICE_TIER flipped every 60 s in one warm 1,300 s Yōtei run. Paired B−A was −0.13 ± 0.62 presents/10 s (8 pairs): no significant frame-rate gain. Queue-0 room wait was 94 vs 113 µs/draw, and small-device retention was exercised. This does not satisfy the requested current-main-versus-change scene comparison.
  • main vs this change on the same machine (RTX 3080 Laptop, driver 610.62, Windows 11), Ghost of Yōtei (PPSA26344 v1.008) title menu from the same save with no input, warm caches, interleaved runs in one session, perf-report protocol (only APS5_TRACE_FRAMES=150:60, no APS5_PROFILE_*): main a5a2c94 against main a5a2c94 + this PR. Frame p50 131.9 ms (3 runs) against 132.5 ms (2 runs), GPU busy 77.7 against 81.0 ms per present. That's inside the run-to-run spread (main's three runs: 128.2-135.1 ms), as expected for a scene that doesn't exercise this path; our open-world numbers, from our own tree, are in the comments. Traces and tools/trace_digest.py output per run: pl-main-1, pl-main-2, pl-main-3, pl-8912-1, pl-8912-2.
aps5-summary.jsonl, pl-main-3 and pl-8912-2 (the run closest to each side's mean; `title` written as `ō`, since the summary writer double-encodes it and GitHub mangles that escape)
{"commit":"a5a2c946a3a9","dirty":false,"env":{"APS5_NO_CLEAR_HAND_OVER":"1","APS5_NO_DEFERRED_SETTLES":"1","APS5_NO_PAD_INPUT":"1","APS5_NO_PIPELINE_JOURNAL":"1","APS5_NO_RESIDENT_LINEAR":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_RESOURCE_CACHE_ENTRIES":"1024","APS5_TRACE_FRAMES":"150:60"},"gpu":"NVIDIA GeForce RTX 3080 Laptop GPU","driver":"NVIDIA 610.62","os":"Windows 10.0.26200","cpu":"11th Gen Intel(R) Core(TM) i9-11900H @ 2.50GHz","cpu_threads":16,"memory_mib":32469,"title_id":"PPSA26344","title":"Ghost of Yōtei","content_version":"01.008.000","resolution":"3840x2160","window":{"spec":"frames 150:60","first_present":150,"ms":11388.409},"presents":60,"frame_ms":{"p50":132.237,"p90":378.788,"p99":2488.458,"max":2488.458},"gpu_busy_ms_per_present":76.739,"worker_busy_pct":{"queue worker 0x0":61.336,"queue worker 0x20":12.709,"queue worker 0x52":10.897,"queue worker 0x53":12.001,"queue worker 0x30":43.438,"queue worker 0x38":50.980,"queue worker 0x48":3.931},"top_phases_ms_per_frame":{"Driver.Packet":363.867,"Shader.PrepareArtifact":329.176,"Driver.Packet.dispatch":163.759,"Driver.Packet.memory_wait":130.658,"Driver.Dispatch":116.659,"Driver.Packet.prepare_memory":46.855,"GpuMutex.wait.dispatch":32.057,"Shader.Register":29.105},"trace_dropped":false,"trace":"aps5-trace-a5a2c946a3a9-16452.json.gz"}
{"commit":"c9ab24b2a148","dirty":false,"env":{"APS5_NO_CLEAR_HAND_OVER":"1","APS5_NO_DEFERRED_SETTLES":"1","APS5_NO_PAD_INPUT":"1","APS5_NO_PIPELINE_JOURNAL":"1","APS5_NO_RESIDENT_LINEAR":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_RESOURCE_CACHE_ENTRIES":"1024","APS5_TRACE_FRAMES":"150:60"},"gpu":"NVIDIA GeForce RTX 3080 Laptop GPU","driver":"NVIDIA 610.62","os":"Windows 10.0.26200","cpu":"11th Gen Intel(R) Core(TM) i9-11900H @ 2.50GHz","cpu_threads":16,"memory_mib":32469,"title_id":"PPSA26344","title":"Ghost of Yōtei","content_version":"01.008.000","resolution":"3840x2160","window":{"spec":"frames 150:60","first_present":150,"ms":11964.391},"presents":60,"frame_ms":{"p50":133.365,"p90":391.895,"p99":2939.489,"max":2939.489},"gpu_busy_ms_per_present":81.509,"worker_busy_pct":{"queue worker 0x0":60.509,"queue worker 0x52":10.355,"queue worker 0x53":12.095,"queue worker 0x20":11.279,"queue worker 0x30":42.591,"queue worker 0x48":4.669,"queue worker 0x38":50.579},"top_phases_ms_per_frame":{"Shader.PrepareArtifact":386.964,"Driver.Packet":377.535,"Driver.Packet.dispatch":168.934,"Driver.Packet.memory_wait":133.947,"Driver.Dispatch":118.660,"Driver.Packet.prepare_memory":49.840,"GpuMutex.wait.dispatch":32.522,"Shader.Register":28.438},"trace_dropped":false,"trace":"aps5-trace-c9ab24b2a148-25492.json.gz"}
- Not tested: AMD, Intel, console behaviour.

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: perf(agc): keep device buffers placed in host memory for reuse while the device heap is full #8748
  • Fixes: none
  • Unblocks: none
  • GPUs tested: NVIDIA GeForce RTX 3080 Laptop GPU, driver 610.62, Windows 11 Pro; lavapipe (Mesa 25.2.8, WSL Ubuntu 24.04)
  • AI-assisted: yes

Device-local buffers under 1 MiB are retained in the small tier, so the
large device buffers' churn no longer evicts them. But the small tier's
64 MiB budget is also the small host buffers' budget, and the two now
evict each other. Each small device miss is a dedicated vkAllocateMemory
and each eviction a vkFreeMemory on the thread that builds the draws.

Small device-local buffers now get a tier of their own: a budget of
max(64 MiB, device tier budget / 8) and the device tier's class search
they had before, apart from the large device buffers and the small host
buffers. With four tiers, each tier's slot bound is an eighth of
maxMemoryAllocationCount, so half of it stays free as before. The
[bufferpool] line reports the new tier.

AI-assisted: yes
@github-actions github-actions Bot added perf Performance change perf numbers missing perf pull request without a before and after summary line labels Oct 21, 2026
@github-actions

Copy link
Copy Markdown

This pull request is labeled perf, so it needs the summary line of a capture on main and one with the change, from the same title, scene and machine, as the Performance report form asks. Please paste both aps5-summary.jsonl lines, attach the two traces and add the output of tools/trace_digest.py.

@github-actions

github-actions Bot commented Oct 21, 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 11920 core/libs/prx/libSceAgcDriver/Graphics/src/BufferPool.cpp L43

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 4472 core/libs/prx/libSceAgcDriver/Graphics/include/BufferPool.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/BufferPool.cpp
core/libs/prx/libSceAgcDriver/tests/BufferPool.cpp
PR 6553 core/libs/prx/libSceAgcDriver/tests/BufferPool.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Graphics/include/BufferPool.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/BufferPool.cpp
PR 9712 core/libs/prx/libSceAgcDriver/Graphics/include/BufferPool.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/BufferPool.cpp
core/libs/prx/libSceAgcDriver/tests/BufferPool.cpp
PR 13148 core/libs/prx/libSceAgcDriver/Graphics/src/BufferPool.cpp

@MarcZX10

Copy link
Copy Markdown
Contributor

I reviewed the new tier selection, budget/slot bound, and trim/destructor coverage; the focused SmallDeviceOwnTier test targets the eviction this change is meant to prevent. I could not run agc_buffer_pool locally from this checkout. One evidence gap remains: the performance A/B in the description turns #8911, #8912, and #8913 on/off as a group, so it does not isolate this tier’s effect. The perf-label check also asks for matched main/change captures, both summary lines, trace artifacts, and trace_digest.py output. Please attach those or keep the performance claim scoped to the combined set.

@Celegans12

Copy link
Copy Markdown
Contributor Author

@MarcZX10 thanks for going through the tier selection, the slot bound and the trim/destructor coverage. Good to hear SmallDeviceOwnTier reads as aimed at the right eviction.

Fair point on the A/B: it toggled #8911, #8912 and this PR as a group, on our own Yōtei tree rather than main, so it doesn't isolate the tier. I've rescoped the speed line to the combined set and kept only this tier's own hit/miss counts as PR-specific. The matched main/change captures with summary lines, traces and trace_digest.py output are still owed. I'll post them here once I can run them, but I don't have a date yet.

@github-actions github-actions Bot added the agc libSceAgc* label Oct 21, 2026
@EdouardSence

Copy link
Copy Markdown
Contributor

AMD / Linux (RX 9070 XT, RADV) A/B of #8911 + #8912 + #8913 together on ASTRO BOT: neutral there (no change, no regression). Numbers and trace digest: #8911 (comment)


Tested and written by Claude (AI assistant) on a contributor's machine.
AI-assisted: yes

@CnRJay CnRJay left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each of these pool changes is small and the reasoning is sound (small device-local buffers evicting small host buffers out of one 64 MiB tier; a hit whose VkBuffer has another exact size paying a destroy and bind). The problem is the set.

  • #8911, #8912 and #8913 are measured "together in one in-run open-world A/B (9+9 phases): 6.17 vs 5.16 presents per 10 s", on the author's own Ghost of Yotei tree, not on main, and "not isolated for this PR". The three cannot be told apart, so none of them is shown to pay for itself, and a regression in one would be hidden by gains in the others.
  • At least four more open PRs change the same pool tiers and free list: #8748 (keep host-placed device buffers for reuse), #8888 (size classes for large host buffers), #8672 (reclaim idle device buffers under cache pressure) and #8913 (this series). #8913 itself says it overlaps #8748's free-list change. They conflict textually and, more importantly, interact: a size-class change in #8888 changes what #8913's exact-size scan finds, and a separate small tier changes what #8672 reclaims.
  • Please agree an order with the other authors and measure each step against the previous one on the same scene, summary lines of main and each PR, no APS5_PROFILE_*. Until then the maintainers cannot tell which of the five is the one that helps, and a pool tuned by five independent heuristics is hard to reason about when the next title's churn pattern differs.

@Celegans12

Copy link
Copy Markdown
Contributor Author

You're right about the set. The 6.17 vs 5.16 A/B toggled #8911, #8912 and #8913 together on our own Yōtei tree, so it can't show that any single one of them pays for itself. And the five pool PRs touch the same tiers and free list, so they interact.

Here's an order I'd suggest, for @LeandroLP23, @milescoviello and @eliasbruno124-dev to weigh in on:

  1. perf(agc): keep device buffers placed in host memory for reuse while the device heap is full #8748, since perf(agc): prefer a pooled buffer of the requested exact size #8913 already overlaps its free-list change;
  2. perf(agc): pool large host buffers by size class #8888, whose size classes change what perf(agc): prefer a pooled buffer of the requested exact size #8913's exact-size scan finds;
  3. perf(agc): give small device-local buffers a pool tier of their own #8912, the small device-local tier;
  4. perf(agc): prefer a pooled buffer of the requested exact size #8913, rebased onto the first two;
  5. perf(agc): reclaim idle device buffers under storage cache pressure #8672 last, so it reclaims from the tiers as they end up.

#8911 doesn't touch the pool (it skips the queued-draw read check when a storage flush stores nothing), so it can be measured on its own at any point. If any of you would rather go in a different order, I'm happy to rebase ours around it.

For our three, I'll measure each one alone against main on Ghost of Yōtei in the open world, with the summary lines of both builds and no APS5_PROFILE_*, and post the numbers on each PR. Those runs are queued behind other work on our machine, so I can't give a date yet. Until then I'd treat the earlier combined A/B as a hint, not evidence.

@milescoviello

Copy link
Copy Markdown
Contributor

Thanks @Celegans12, that order works for me, with #8888 second. On the current heads, git merge-tree merges #8888 cleanly with each of #8748, #8912, #8913 and #8672, so the order is about measurement, not conflicts. The interaction I'd watch is the one you named: with #8888 a large host request takes its size class's capacity, so #8913's exact-size scan meets class-sized buffers on that tier. If #8748 lands first, I'll rerun #8888's BO2 menu A/B on top of it and post the numbers there before asking for review again.

AI-assisted: yes (Claude Code)

@Celegans12

Copy link
Copy Markdown
Contributor Author

@CnRJay I ran each of the three pool PRs on its own now. Plain main can't take Ghost of Yōtei to the open world (the loading screen stops presenting after about 160 s and the game later aborts on a blend into a 10_11_11 unorm target main doesn't support), so I can't give you main-vs-PR summary lines in the world. What I can give is our tree (our local merge with main of 2026-10-21) with only this PR's switch, APS5_NO_SMALL_DEVICE_TIER, flipped every 60 s in one 1,300 s run: same scene and session for both halves.

On presents this one is in the noise: 5.75 per 10 s with the tier vs 5.16 without, but paired against neighbouring phases it's -0.13 +- 0.62, so I wouldn't claim anything there. The per-draw cost is clearer: room wait on queue 0 goes from 94 to 113 us per draw without the tier (+18 +- 4 us, the one difference here well past noise), buffer allocation per upload from 3.5 to 12.7 us and the import lookup from 15 to 37 us. With the tier on, small device-local buffers hit about 88k times per phase against 357 misses.

I also tried a boot-scene pair on plain main, but those captures turned out to be pipeline-cold on this machine, so I'm not using them. On the order with #8748, #8888 and #8672: I'm happy to rerun this A/B on top of whichever lands first.

Details

Run pb-8912, our local merge with main 557682c, warm caches (NVIDIA pipeline cache readable, dictionary checked before the run), web-teshio-b save, 1,300 s cap, APS5_AB=60:APS5_NO_SMALL_DEVICE_TIER from present 760, RTX 3080 Laptop (610.62), Windows 11. B = the switch set, i.e. this PR's change off.

phases (presents: begin / settled / end; clean reports present/gputime/draw/syncwait):
  A  0     760 /    766 /    825  3/0/5/3     10.23 presents/10 s
  B  1     825 /    826 /    852  4/0/5/4     4.43 presents/10 s
  A  2     852 /    853 /    879  4/0/5/4     4.34 presents/10 s
  B  3     879 /    880 /    906  5/0/5/4     4.38 presents/10 s
  A  4     906 /    907 /    940  4/0/5/4     5.57 presents/10 s
  B  5     940 /    942 /    971  4/0/4/4     5.03 presents/10 s
  A  6     971 /    973 /   1007  4/0/4/4     5.96 presents/10 s
  B  7    1007 /   1009 /   1038  4/0/5/4     5.05 presents/10 s
  A  8    1038 /   1040 /   1073  4/0/5/4     5.84 presents/10 s
  B  9    1073 /   1074 /   1105  4/0/5/5     5.31 presents/10 s
  A 10    1105 /   1107 /   1141  4/0/5/5     5.84 presents/10 s
  B 11    1141 /   1143 /   1175  4/0/5/5     5.57 presents/10 s
  A 12    1175 /   1177 /   1208  5/0/4/5     5.46 presents/10 s
  B 13    1208 /   1210 /   1242  4/0/4/5     5.61 presents/10 s
  A 14    1242 /   1244 /   1260  1/0/2/2     2.76 presents/10 s
  B 15    1260 /   1262 /   1296  4/0/5/5     5.88 presents/10 s
  A 16    1296 /   1298 /      -  2/0/1/1     unfinished

per phase mean                          A          B        B-A    B/A-1   noise: B vs neighbouring A, mean diff +- SE (SD, n)
presents/10s                         5.75       5.16      -0.59   -10.3%   -0.13 +- 0.62 (SD 1.77, n 8)
frame ms                          1953.82    1959.13      +5.31    +0.3%   -159.89 +- 298.19 (SD 843.41, n 8)
GPU busy ms/frame                  655.93     697.91     +41.99    +6.4%   +16.46 +- 43.79 (SD 123.86, n 8)
GPU idle gaps ms/frame            1762.37    1251.05    -511.32   -29.0%   -626.25 +- 580.26 (SD 1641.21, n 8)
committer us/commit                313.28     321.40      +8.12    +2.6%   +16.21 +- 16.63 (SD 47.04, n 8)
committer % busy                    57.93      63.94      +6.01   +10.4%   +4.58 +- 3.00 (SD 8.48, n 8)
room wait us/draw                   93.94     113.15     +19.22   +20.5%   +18.33 +- 4.01 (SD 11.33, n 8)
drain ms/10s                      1703.96    1733.12     +29.16    +1.7%   -6.47 +- 77.15 (SD 218.23, n 8)
syncwait GPU ms/10s                917.89     815.38    -102.51   -11.2%   -116.54 +- 28.20 (SD 79.77, n 8)
A total (8 phases): alloc 3.5 us/upload, lookup 15.2 us/upload; per phase: small misses 0, small-device hits/misses 87793/357, device misses 162, exact-size hits 400656, rebound 364096, held-read checks skipped 778675, held 0
B total (8 phases): alloc 12.7 us/upload, lookup 36.8 us/upload; per phase: small misses 0, small-device hits/misses 0/0, device misses 29, exact-size hits 381848, rebound 378548, held-read checks skipped 883116, held 0

@oneandonlydean oneandonlydean self-assigned this Oct 22, 2026
@Dextroid17

Copy link
Copy Markdown
Contributor

Linux data point for the test, since @MarcZX10 could not run agc_buffer_pool locally from that checkout: llvmpipe (LLVM 23.1.1, 256 bits) via lavapipe, Mesa 26.2.4, GALLIUM_OVERRIDE_CPU_CAPS=avx, i9-11900K, CachyOS, GCC 16.2.1, CMake 4.4.3, Release, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1; b7fd852e0 merged with main f57ceaad6 (your base is 1408 commits behind it; local merge 4e07dfe34, clean). The target is agc_buffer_pool_tests; it is EXCLUDE_FROM_ALL only without -DBUILD_TESTING=ON, which both puts it in all and registers the ctest entry agc_buffer_pool.

Head. agc_buffer_pool passes 3 of 3 runs.

Negative control. core/libs/prx/libSceAgcDriver/tests/BufferPool.cpp from b7fd852e0 on plain f57ceaad6, rebuilt: fails 3 of 3 with exactly one FAIL line, small host buffer churn evicted a retained small device buffer, the one your body names; the new case's leak check (the small device tier leaked Vulkan objects) and the neighbouring device pool tier retention leaked Vulkan objects pass on both arms.

One scoping note on what this shows: SmallDeviceOwnTier runs against the test's MockDevice, so the pass is the pool's bookkeeping on this compiler and standard library, not a GPU result; no case in agc_buffer_pool_tests opens a Vulkan device (the only device proc in the file is the mock and the binary links no loader), so the lavapipe line above describes the environment the run was given, not anything this test used.

Stacked with #8913 (a62e91004, both merge clean onto main in that order): agc_buffer_pool passes 3 of 3 with both new cases present.

AI-assisted: yes

@Celegans12

Copy link
Copy Markdown
Contributor Author

@Dextroid17, thank you for the native lavapipe runs on all three (#8911, #8912, #8913). The negative controls were the part I couldn't give myself from WSL, and they fail exactly where each body says. Your point that agc_buffer_pool runs against MockDevice is fair: for #8912 and #8913 it's the pool bookkeeping being tested, not anything on the GPU. I'll word it that way when I next touch those bodies.

@LeandroLP23

Copy link
Copy Markdown
Contributor

@Celegans12 sorry for the late reply. The order works for me, with #8748 first. It is approved and mergeable on current main. With git merge-tree, it merges cleanly with the current heads of #8888, #8912 and #8913, so as milescoviello said, the order matters for what each measurement sees, not for conflicts. If something else lands first and #8748 needs a rebase, I'll do it and rerun its regression cases.

AI-assisted: yes (Claude Code)

@mrfelek

mrfelek commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Measured alone against main, as asked in review, on an AMD APU (Radeon 780M, Linux, RADV, Mesa 26.0.8). Base upstream main 853baf3, PR head b7fd852 on top, default settings. Diablo II: Resurrected (Rogue Encampment after loading a save), 60 s settle, 120 s counted, two alternating pairs.

pair main presents/10 s #8912 presents/10 s
1 18 18 18 18 9 9 9 9
2 20 19 18 18 16 15 13 14

The new tier is used: 5951 hits / 656 misses, 652 buffers retained (108 of 165 MiB), and 0 small-pool evictions against 5250 on main.

The PR arm was lower in both pairs, but I can't call it a regression yet: on this machine D2R lands at either ~560 or ~1400 ms per frame in the same scene in either arm, so the noise is about the size of the difference. The title is CPU-bound in the driver (busiest worker at 99-100%, GPU ~170-200 ms per present). Logs and aps5 summaries available if useful.

This comment was written with help from Claude (AI).

@Celegans12

Copy link
Copy Markdown
Contributor Author

@mrfelek thanks for measuring this alone on different hardware. That's exactly the run we couldn't do, and I'm taking it seriously even though you're careful not to call it a regression yet. The counters show the tier really is used in D2R: 5951 hits against 656 misses, and no small-pool evictions where main had 5250. So whatever the presents are doing, it isn't because the change sat idle.

Two questions, if you have time. Do you see the same split between ~560 and ~1400 ms frames on main alone, in about the same proportion? If the PR arm just lands in the slow mode more often, that would show up as exactly this. And would a longer interleaved run be possible, say four or five alternating pairs, or one long run per arm with the mode noted per 10 s interval? Then we could tell a shift between the modes from a slower mode.

For comparison: on Ghost of Yōtei with an RTX 3080, our own per-PR A/B (this switch alone, flipped every 60 s in one run) was in the noise on presents, -0.13 ± 0.62 per 10 s. Per draw it was clearly better: queue-0 room wait went from 113 to 94 µs. That's NVIDIA with separate VRAM, though. On a 780M the retained 108 MiB of "device-local" buffers is system memory, so a real regression on your title would matter, and I'd rather find it now. If the logs and summaries are easy to attach, I'd like to look at the allocation and import-lookup lines around the slow frames.

@LeandroLP23 thanks, #8748 first works for me. I'll rerun our numbers on top of it once it lands.

@mrfelek

mrfelek commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Follow-up to my earlier numbers: three more alternating pairs on the same setup (Radeon 780M, RADV, main 853baf3 vs PR head b7fd852, D2R Rogue Encampment, #8912 first in each pair).

pair #8912 presents/10 s main presents/10 s
3 20 20 20 21 58 60 60 58
4 9 8 8 9 8 8 8 8
5 8 9 8 8 5 4 4 5

Across all five pairs main was faster in three and #8912 in two. D2R on this machine settles into one of four frame-time states (~170, ~560, ~1400, ~2600 ms per frame) in either arm, and that swing is far larger than any difference between the arms. So I withdraw the "looks slower" reading: these runs show no speed effect either way.

What does hold in every run: the small-device tier is used (about 11-15k hits, ~800 misses, 0 evictions, 97-134 MiB retained), and small-pool evictions stay at 0 with the PR against 2,949-4,616 on main. No vkAllocateMemory failures in any run.

This comment was written with help from Claude (AI).

@Celegans12

Copy link
Copy Markdown
Contributor Author

@mrfelek thanks for the three extra pairs, that settles it much better than I could have from here. Four frame-time states swinging far more than the arms differ is a useful thing to know about D2R on an APU, and it explains the first two pairs. The part that holds in every run is the part this PR is about: the small-device tier is used, small-pool evictions stay at 0 against 2,949-4,616 on main, and nothing fails to allocate. I'll keep the speed claim scoped as it is and rerun ours on top of #8748 once it lands.

Celegans12 added a commit to Celegans12/AnyPS5-Ghost-of-Yotei that referenced this pull request Oct 24, 2026
@Celegans12

Copy link
Copy Markdown
Contributor Author

I've finished the second round of world captures I mentioned, this time with 60-present windows (the first round's 16-present windows were too noisy to use). All ten are warm runs of PPSA26344 v1.008 on our own tree (085bd8f, which carries all six of these changes; not main), back to back in one session on the same laptop (RTX 3080 Laptop, driver 610.62, Windows 11), from the same open-world save with the same pad script. Each capture is APS5_TRACE_FRAMES=880:60, about 90 s in the world once the scene settles. The A runs are the tree as it is; each B run adds one off switch. One caveat on the protocol: our run script also sets APS5_PROFILE_DRAW=1 on every run, A and B alike. Run order: pe2-a1, pe2-b9287, pe2-b9289, pe2-a2, pe2-b8911, pe2-b8912k, pe2-a3k, pe2-b8913k, pe2-b10382k, pe2-a4k. For this PR the B run adds APS5_NO_SMALL_DEVICE_TIER=1. That switch is in our tree only; this PR has no switch of its own, so the A/B here is our tree's version of the change.

With the change switched off, the frame p50 was 2,006 ms against 1,867 and 1,605 ms in the A runs on either side (mean 1,736), and GPU busy was 937 against 955 and 906 ms per present (mean 930). The off run is the slowest of all ten, about 16 % above the mean of its A runs. Those two A runs are 15 % apart on their own, though, so I read it as leaning toward the change helping, not as a measured gain.

These are all on one tree, so they don't replace a main capture for the perf label. I'm making main-against-main+this-PR captures on the title menu next and will add those summary lines to the description.

Files are on our media branch (the comment box can't take 35 MB traces):

aps5-summary.jsonl window lines (B, then the two A runs)
{"commit":"085bd8f20325","dirty":false,"env":{"APS5_ASYNC_LANE":"51","APS5_BINDLESS_IMAGES":"1","APS5_BINDLESS_LAZY":"1","APS5_CACHE_SPIRV_BACKING":"1","APS5_COLD_SPIRV":"1","APS5_COLD_SPIRV_FILE":"1","APS5_DEFER_REFRESH_FLUSH":"1","APS5_DUMP_FRAMES":"300","APS5_DUMP_FRAMES_EVERY":"10","APS5_INLINE_SHADER_PREPARE":"1","APS5_MEM_STATS":"10","APS5_NO_ADDRESS_DRAW_REUSE":"1","APS5_NO_DRAW_AHEAD":"1","APS5_NO_PAD_INPUT":"1","APS5_NO_PIPELINE_JOURNAL":"1","APS5_NO_RESULT_MEMO":"1","APS5_NO_SMALL_DEVICE_TIER":"1","APS5_PAD_SCRIPT":"100:cross,106:cross,112:cross,118:cross,124:cross,130:cross,136:cross,142:cross,148:cross,154:cross,160:cross,166:cross,172:cross,178:cross,184:cross,190:cross,196:cross,202:cross,208:cross,214:cross,220:cross,226:cross,232:cross,238:cross,244:cross,250:cross,256:cross,262:cross,268:cross,274:cross,280:cross,286:cross,292:cross,298:cross,304:cross,310:cross,316:cross,322:cross,328:cross,334:cross,340:cross,346:cross,352:cross,358:cross,364:cross,370:cross,376:cross,382:cross,388:cross,394:cross,400:cross,406:cross,412:cross,418:cross,424:cross,430:cross,436:cross,442:cross,448:cross,454:cross,460:cross,466:cross,472:cross,478:cross,484:cross,490:cross,496:cross,502:cross,508:cross,514:cross,520:cross,526:cross,532:cross,538:cross,544:cross,550:cross,556:cross,562:cross,568:cross,574:cross,580:cross","APS5_PAD_SCRIPT_REPEAT":"","APS5_PIPELINE_INDIRECT":"1","APS5_PROFILE_DRAW":"1","APS5_PROFILE_GPU":"0","APS5_RESOURCE_CACHE_ENTRIES":"1024","APS5_SPANNED_WAVE":"1","APS5_STAGING_DIFF":"1","APS5_TEXTURE_OVERFLOW_MIB":"1024","APS5_TRACE_DIR":"<path>","APS5_TRACE_EVENTS":"4194304","APS5_TRACE_FRAMES":"880:60","APS5_TRACE_SUMMARY_EVERY":"10","APS5_UNNORM_WRAP_GUARD":"1"},"gpu":"NVIDIA GeForce RTX 3080 Laptop GPU","driver":"NVIDIA 610.62","os":"Windows 10.0.26200","cpu":"11th Gen Intel(R) Core(TM) i9-11900H @ 2.50GHz","cpu_threads":16,"memory_mib":32469,"title_id":"PPSA26344","title":"Ghost of Y\u00c5\u008dtei","content_version":"01.008.000","resolution":"3840x2160","window":{"spec":"frames 880:60","first_present":880,"ms":122908.802},"presents":60,"frame_ms":{"p50":2006.080,"p90":2374.349,"p99":2697.455,"max":2697.455},"gpu_busy_ms_per_present":937.401,"worker_busy_pct":{"queue worker 0x0":92.508,"queue worker 0x53":59.310,"queue worker 0x52":59.762,"queue worker 0x20":73.381,"queue worker 0x48":3.318,"queue worker 0x30":85.422,"queue worker 0x38":86.599,"queue worker 0x51":8.791,"queue worker 0x50":36.435},"top_phases_ms_per_frame":{"Driver.Packet":10351.328,"Driver.Packet.memory_wait":7255.623,"Driver.Packet.dispatch":1485.759,"DrawPipeline.commit":1284.485,"Driver.Dispatch":1174.559,"Graphics.Draw":1107.170,"Driver.Packet.draw":1087.636,"Driver.Draw":1081.978},"trace_dropped":false,"trace":"aps5-trace-085bd8f20325-5216.json.gz"}
{"commit":"085bd8f20325","dirty":false,"env":{"APS5_ASYNC_LANE":"51","APS5_BINDLESS_IMAGES":"1","APS5_BINDLESS_LAZY":"1","APS5_CACHE_SPIRV_BACKING":"1","APS5_COLD_SPIRV":"1","APS5_COLD_SPIRV_FILE":"1","APS5_DEFER_REFRESH_FLUSH":"1","APS5_DUMP_FRAMES":"300","APS5_DUMP_FRAMES_EVERY":"10","APS5_INLINE_SHADER_PREPARE":"1","APS5_MEM_STATS":"10","APS5_NO_ADDRESS_DRAW_REUSE":"1","APS5_NO_DRAW_AHEAD":"1","APS5_NO_PAD_INPUT":"1","APS5_NO_PIPELINE_JOURNAL":"1","APS5_NO_RESULT_MEMO":"1","APS5_PAD_SCRIPT":"100:cross,106:cross,112:cross,118:cross,124:cross,130:cross,136:cross,142:cross,148:cross,154:cross,160:cross,166:cross,172:cross,178:cross,184:cross,190:cross,196:cross,202:cross,208:cross,214:cross,220:cross,226:cross,232:cross,238:cross,244:cross,250:cross,256:cross,262:cross,268:cross,274:cross,280:cross,286:cross,292:cross,298:cross,304:cross,310:cross,316:cross,322:cross,328:cross,334:cross,340:cross,346:cross,352:cross,358:cross,364:cross,370:cross,376:cross,382:cross,388:cross,394:cross,400:cross,406:cross,412:cross,418:cross,424:cross,430:cross,436:cross,442:cross,448:cross,454:cross,460:cross,466:cross,472:cross,478:cross,484:cross,490:cross,496:cross,502:cross,508:cross,514:cross,520:cross,526:cross,532:cross,538:cross,544:cross,550:cross,556:cross,562:cross,568:cross,574:cross,580:cross","APS5_PAD_SCRIPT_REPEAT":"","APS5_PIPELINE_INDIRECT":"1","APS5_PROFILE_DRAW":"1","APS5_PROFILE_GPU":"0","APS5_RESOURCE_CACHE_ENTRIES":"1024","APS5_SPANNED_WAVE":"1","APS5_STAGING_DIFF":"1","APS5_TEXTURE_OVERFLOW_MIB":"1024","APS5_TRACE_DIR":"<path>","APS5_TRACE_EVENTS":"4194304","APS5_TRACE_FRAMES":"880:60","APS5_TRACE_SUMMARY_EVERY":"10","APS5_UNNORM_WRAP_GUARD":"1"},"gpu":"NVIDIA GeForce RTX 3080 Laptop GPU","driver":"NVIDIA 610.62","os":"Windows 10.0.26200","cpu":"11th Gen Intel(R) Core(TM) i9-11900H @ 2.50GHz","cpu_threads":16,"memory_mib":32469,"title_id":"PPSA26344","title":"Ghost of Y\u00c5\u008dtei","content_version":"01.008.000","resolution":"3840x2160","window":{"spec":"frames 880:60","first_present":880,"ms":116142.666},"presents":60,"frame_ms":{"p50":1867.327,"p90":2241.303,"p99":3623.795,"max":3623.795},"gpu_busy_ms_per_present":954.812,"worker_busy_pct":{"queue worker 0x0":91.749,"queue worker 0x20":73.259,"queue worker 0x52":60.295,"queue worker 0x53":59.844,"queue worker 0x38":86.110,"queue worker 0x48":3.289,"queue worker 0x30":84.485,"queue worker 0x51":7.580,"queue worker 0x50":35.976},"top_phases_ms_per_frame":{"Driver.Packet":9724.737,"Driver.Packet.memory_wait":6819.175,"Driver.Packet.dispatch":1305.988,"DrawPipeline.commit":1198.993,"Graphics.Draw":1073.074,"Driver.Packet.draw":1047.088,"Driver.Draw":1041.814,"Driver.Dispatch":1022.959},"trace_dropped":false,"trace":"aps5-trace-085bd8f20325-1084.json.gz"}
{"commit":"085bd8f20325","dirty":false,"env":{"APS5_ASYNC_LANE":"51","APS5_BINDLESS_IMAGES":"1","APS5_BINDLESS_LAZY":"1","APS5_CACHE_SPIRV_BACKING":"1","APS5_COLD_SPIRV":"1","APS5_COLD_SPIRV_FILE":"1","APS5_DEFER_REFRESH_FLUSH":"1","APS5_DUMP_FRAMES":"300","APS5_DUMP_FRAMES_EVERY":"10","APS5_INLINE_SHADER_PREPARE":"1","APS5_MEM_STATS":"10","APS5_NO_ADDRESS_DRAW_REUSE":"1","APS5_NO_DRAW_AHEAD":"1","APS5_NO_PAD_INPUT":"1","APS5_NO_PIPELINE_JOURNAL":"1","APS5_NO_RESULT_MEMO":"1","APS5_PAD_SCRIPT":"100:cross,106:cross,112:cross,118:cross,124:cross,130:cross,136:cross,142:cross,148:cross,154:cross,160:cross,166:cross,172:cross,178:cross,184:cross,190:cross,196:cross,202:cross,208:cross,214:cross,220:cross,226:cross,232:cross,238:cross,244:cross,250:cross,256:cross,262:cross,268:cross,274:cross,280:cross,286:cross,292:cross,298:cross,304:cross,310:cross,316:cross,322:cross,328:cross,334:cross,340:cross,346:cross,352:cross,358:cross,364:cross,370:cross,376:cross,382:cross,388:cross,394:cross,400:cross,406:cross,412:cross,418:cross,424:cross,430:cross,436:cross,442:cross,448:cross,454:cross,460:cross,466:cross,472:cross,478:cross,484:cross,490:cross,496:cross,502:cross,508:cross,514:cross,520:cross,526:cross,532:cross,538:cross,544:cross,550:cross,556:cross,562:cross,568:cross,574:cross,580:cross","APS5_PAD_SCRIPT_REPEAT":"","APS5_PIPELINE_INDIRECT":"1","APS5_PROFILE_DRAW":"1","APS5_PROFILE_GPU":"0","APS5_RESOURCE_CACHE_ENTRIES":"1024","APS5_SPANNED_WAVE":"1","APS5_STAGING_DIFF":"1","APS5_TEXTURE_OVERFLOW_MIB":"1024","APS5_TRACE_DIR":"<path>","APS5_TRACE_EVENTS":"4194304","APS5_TRACE_FRAMES":"880:60","APS5_TRACE_SUMMARY_EVERY":"10","APS5_UNNORM_WRAP_GUARD":"1"},"gpu":"NVIDIA GeForce RTX 3080 Laptop GPU","driver":"NVIDIA 610.62","os":"Windows 10.0.26200","cpu":"11th Gen Intel(R) Core(TM) i9-11900H @ 2.50GHz","cpu_threads":16,"memory_mib":32469,"title_id":"PPSA26344","title":"Ghost of Y\u00c5\u008dtei","content_version":"01.008.000","resolution":"3840x2160","window":{"spec":"frames 880:60","first_present":880,"ms":97466.171},"presents":60,"frame_ms":{"p50":1605.492,"p90":1758.921,"p99":1913.625,"max":1913.625},"gpu_busy_ms_per_present":906.032,"worker_busy_pct":{"queue worker 0x0":91.049,"queue worker 0x53":58.251,"queue worker 0x20":72.397,"queue worker 0x52":58.648,"queue worker 0x38":85.704,"queue worker 0x48":2.584,"queue worker 0x30":83.804,"queue worker 0x51":7.236,"queue worker 0x50":37.859},"top_phases_ms_per_frame":{"Driver.Packet":8079.757,"Driver.Packet.memory_wait":5587.069,"Driver.Packet.dispatch":1119.145,"DrawPipeline.commit":979.337,"Driver.Dispatch":875.332,"Graphics.Draw":856.851,"Driver.Packet.draw":833.317,"Driver.Draw":828.708},"trace_dropped":false,"trace":"aps5-trace-085bd8f20325-3756.json.gz"}

@github-actions github-actions Bot removed the perf numbers missing perf pull request without a before and after summary line label Oct 24, 2026
@TechLevelZero

TechLevelZero commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head b7fd852 merged onto main 9ab6a28 · tested 2026-11-04 22:15 UTC · 21 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,772 passed · 2 failed (0 new, 1 also fail on main, 1 unstable on main) · 48 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,822 of 3,909 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (969 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.1 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 3 of the last 10 main runs
Failing on main too (1, not counted against this PR)
agc_driver_image_table
Validation IDs already present on main (13 IDs in 548 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (548 tests): agc_driver, agc_driver_address_draw_space_reuse, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, and 540 more
VUID-VkShaderModuleCreateInfo-pCode-08737 (8 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_float16_fma_rounding, agc_driver_float16_unused_nan_payloads, agc_driver_packed_alu, agc_driver_packed_f16_denormal_modes, agc_driver_packed_float16_native
VUID-VkShaderModuleCreateInfo-pCode-08739 (8 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_float16_fma_rounding, agc_driver_float16_unused_nan_payloads, agc_driver_packed_alu, agc_driver_packed_f16_denormal_modes, agc_driver_packed_float16_native
VUID-VkShaderModuleCreateInfo-pCode-08741 (8 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_float16_fma_rounding, agc_driver_float16_unused_nan_payloads, agc_driver_packed_alu, agc_driver_packed_f16_denormal_modes, agc_driver_packed_float16_native
VUID-vkCmdCopyImage-srcImage-01548 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkPipelineShaderStageCreateInfo-module-parameter (1 test): agc_driver_float16_unused_nan_payloads
VUID-VkPipelineShaderStageCreateInfo-pSpecializationInfo-06849 (1 test): agc_driver_float16_unused_nan_payloads
VUID-VkSamplerCreateInfo-addressModeU-01079 (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeU-parameter (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeV-parameter (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeW-parameter (1 test): agc_driver_image_gather_volume
Skipped (48)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_stencil_ref_export: skipped, the device lacks VK_EXT_shader_stencil_export
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_dcc: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_deferred: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or pl
Reproduce
git fetch origin b7fd852e0cba5c2078b742d220a50262d82a5055 9ab6a281ea61c9e62a7eb07eb4cef8f19ede1c5c
git checkout 9ab6a281ea61c9e62a7eb07eb4cef8f19ede1c5c && git merge --no-ff b7fd852e0cba5c2078b742d220a50262d82a5055
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated 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.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the current head b7fd852e0cba5c2078b742d220a50262d82a5055 against live main ba5b5330b37724bca4b99f30faab504653635a04.

The pool change and SmallDeviceOwnTier regression are understandable, and the patch merges cleanly onto current main. git diff --check and tools/check_conventions.py pass on that comparison. The author’s reported focused Windows and WSL test runs are useful; I did not independently run the product tests here. On the current PR head, only conventions/labels ran; the Windows, Linux, write-watch, register-context and relinker jobs are skipped.

I’m requesting two follow-ups before approval:

  1. Performance evidence: the title-menu main/change captures are well matched, but the report says this scene does not exercise the new small-device tier, and the measured change is within main’s run-to-run spread. The open-world result combines #8911, #8912 and #8913 on a non-main tree, so it cannot attribute the result to this PR. Please provide a same-title/scene/machine comparison on current main versus current main plus this PR in a workload that actually exercises the tier, including both summary lines, traces and trace_digest.py output. If this is intended as a correctness-only change, please remove the unsupported performance claim/label and coordinate that scope with maintainers.

  2. Overlap order: #8748 remains open and the PR body acknowledges a shared BufferPool::Take edit, but this PR’s checklist still says Depends on: none and I could not find an author-agreed landing order. Please agree the order with #8748’s author and record the dependency on the second PR, as the contribution rules require.

The current-main merge preview, whitespace check and conventions check are clean; they do not resolve the performance attribution or ordering questions above.

@LeandroLP23

Copy link
Copy Markdown
Contributor

@MarcZX10 on the landing order: Celegans12 and I agreed it in #8912 (comment). #8748 lands first and #8912 follows, so Depends on here can name #8748.

I have rebased #8748 onto main aaccd0ba39 (head 28f80c25a9). I'm rerunning its focused tests and the SuperTuxKart main/change captures you asked for there.

AI-assisted: yes (Claude Code)

@blawesom

Copy link
Copy Markdown
Contributor

Static review plus an independent Linux/AMD run on both trees per the repo AGENTS AGC protocol (CachyOS, kernel 7.2.10, GCC 16.2.1, Ninja 1.13.2, Release, BUILD_TESTING=ON, Ryzen 7 9800X3D). No findings; one sequencing note at the end.

What I checked (static)

  • The change adds a fourth retention tier smallDeviceTier for device-local buffers below classLimit, with budget max(smallBudget, deviceTier.budget / 8) and the per-tier slot cap divisor moving 6 to 8 (now four tiers share maxMemoryAllocationCount, matching the header's "half of it free" intent). tierFor, Take's largest-size sweep, trim, the destructor loop and the profiling line are all extended consistently — no path misses the new tier.
  • The regression class it fixes is real and pinned by SmallDeviceOwnTier: 160 small host-buffer Buffers churned through the pool must not evict the retained 256 KiB device-local buffer (mock.allocations unchanged on reuse, same MemoryOf). Without the split tier, host churn hits the shared tier budget and evicts device-local slots for no reason — exactly the eviction churn the PR body describes.
  • The deleted comments in BufferPool.hpp describe the two-tier layout this change supersedes (base of your diff) — reading the surrounding style that's consistent with what's already done elsewhere in this file.

Focused paired run (VVL env)

tree agc_buffer_pool _small_device_shared _small_device_no_staging _pipeline_store_lazy_buffer_pool total
this head b7fd852e0c Passed (0.32s) Passed Passed Passed 4/4
main bb04a4f1ea Passed (0.32s) Passed Passed Passed 4/4

Note on run history (transparency, same class as #7985's note)

An intermediate pass of mine reported agc_pipeline_store_lazy_buffer_pool Failed on this head. Cause was a stale agc_driver_graphics_tests binary in my worktree from a prior PR checkout (undefined symbol CachedPipeline), not this change — after rebuilding that target the run is 4/4 green. If you see the same fixture fail after switching branches, rebuild agc_driver_graphics_tests first.

Sequencing note

tools/pr_overlap.py reports BufferPool.cpp as contended with #8748 ("leave host-placed device buffers out of idle device reclaim") and #11920. #8748 and #8912 both rework tier selection/retention in this file and will rebase on each other whichever lands first — worth deciding the order explicitly rather than merging both blind.
Both logs: 0 was not found but was requested by env var VK_LAYER (layer found and requested), 0 VUID-. Comparing logs, no new errors from this change.

@milescoviello

Copy link
Copy Markdown
Contributor

On the BufferPool.cpp overlap with my #11920, following up blawesom's sequencing note: git merge-tree of this head b7fd852e0c with #11920's dd7a4d164c conflicts in one hunk, the BufferPool constructor's initializer line. #11920 adds a member there and moves one vkFreeMemory call onto the device function table, and nothing else in the file. That doesn't need an order from this PR: if this lands first, I'll rebase #11920 onto it and rerun its pool tests.

AI-assisted: yes (Claude Code)

@Celegans12

Copy link
Copy Markdown
Contributor Author

I've recorded the agreed #8748-first order in the checklist and clarified the performance limitation in Tested. #8748 is still open, so this branch will follow it as agreed. The combined Yōtei captures don't establish a speed gain for this PR alone, and the neutral menu captures don't exercise the tier; the requested current-main comparison remains outstanding. I haven't rerun the game or claimed a new performance result in this upkeep pass.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* perf Performance change

Projects

None yet

Development

Successfully merging this pull request may close these issues.