Repository navigation
perf(agc): refuse a staging shadow that would take the last eighth of the device-local budget - #14815
Conversation
… the device-local budget
MarcZX10
left a comment
There was a problem hiding this comment.
Reviewed — changes requested
The headroom gate can refuse reusable staging buffers and does not necessarily inspect the heap selected for a new buffer.
What I ran
Reviewed head a37af8b9107e47a6d3236942a0a98a581c1c567a against base f2d874c1fdccf781c0fa5d86619bb5dd8894b958; the stack preview was clean. Environment: Linux x86_64, Python 3.11.2, GCC 12.2.0. I did not build or compile.
git diff --check f2d874c1fdccf781c0fa5d86619bb5dd8894b958...a37af8b9107e47a6d3236942a0a98a581c1c567a
python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 14815
python3 tools/pr_test_quality.py --root /marc/AnyPS5 --json core/libs/prx/libSceAgcDriver/tests/DeviceMemoryFallback.cpp
python3 tools/pr_abi_audit.py --repo /marc/AnyPS5 --base f2d874c1fdccf781c0fa5d86619bb5dd8894b958 --head a37af8b9107e47a6d3236942a0a98a581c1c567a --json
GITHUB_REPOSITORY=boykopovar/AnyPS5 python3 tools/pr_labels.py --pr 14815 --dry-run
python3 tools/pr_stack_check.py 14815 --repo boykopovar/AnyPS5Results
| Check | Result | Note |
|---|---|---|
| Patch whitespace | passed | No diff errors. |
| Conventions and template | passed | Local check passed. |
| Test-quality heuristic | passed after inspection | Its registration warning is a false positive; the test is in the included CMake fragment. |
| ABI audit | not applicable | No changed export declaration or alias. |
| Performance report | failed | The title requires perf evidence; summary lines and traces are absent. |
| Build and C++ tests | not run | No build was performed. |
| Hosted CI | not run to completion | Required jobs were queued; an earlier conventions run was cancelled. |
| Merge preview | passed | Clean against the PR base. |
Precheck output and full table
/tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-14815-a37af8b9107e/report.md
/tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-14815-a37af8b9107e/report.json
Conclusion: checks failed
| Check | Status | Command / reason |
|---|---|---|
| build_and_compile | not_applicable | The precheck deliberately does not configure, build, or compile AnyPS5. |
| diff_check | passed | git diff --check f2d874c1fdccf781c0fa5d86619bb5dd8894b958...a37af8b9107e47a6d3236942a0a98a581c1c567a |
| conventions_and_pr_template | passed | /usr/bin/python3 tools/check_conventions.py --base f2d874c1fdccf781c0fa5d86619bb5dd8894b958 --head a37af8b9107e47a6d3236942a0a98a581c1c567a --title perf(agc): refuse a staging shadow that would take the last eighth of the device-local budget --body /tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-14815-a37af8b9107e/pr-body.md |
| changed_export_abi_audit | failed | /usr/bin/python3 tools/pr_abi_audit.py --base f2d874c1fdccf781c0fa5d86619bb5dd8894b958 --head a37af8b9107e47a6d3236942a0a98a581c1c567a --json |
| python_syntax | not_applicable | No changed Python implementation files. |
| related_python_unit_tests | not_applicable | No changed tools/ module has an existing sibling tools/tests/test_<module>.py suite. |
| python_test_registration | not_applicable | No changed test_*.py files. |
| live_dependency_and_mergeability_check | passed | /usr/bin/python3 /marc/AnyPS5/tools/pr_stack_check.py 14815 --repo boykopovar/AnyPS5 --output-dir /tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-14815-a37af8b9107e/stack |
| build_dependent_cpp_tests | not_applicable | Not run: C++ tests need exact-head binaries, which require a build; the precheck does not build. |
| test_quality_heuristics | failed | /usr/bin/python3 /tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-14815-a37af8b9107e/head-a37af8b9107e-25a7f1e3/tools/pr_test_quality.py --root /tmp/anyps5-pr-precheck/boykopovar-AnyPS5-pr-14815-a37af8b9107e/head-a37af8b9107e-25a7f1e3 --json core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp core/libs/prx/libSceAgcDriver/tests/DeviceMemoryFallback.cpp |
| negative_controls | not_applicable | No changed Python test module in tools/ can be run as a no-build base-source control. |
The precheck's ABI and test-quality helper launches failed because those local scripts are not part of the isolated PR checkout. I ran both helpers from the workspace; the ABI check was not applicable, and manual inspection confirmed the test-quality warning is a false positive because agc_device_memory_fallback_tests.cmake registers this source.
Unedited helper output:
{
"findings": [
{
"path": "core/libs/prx/libSceAgcDriver/tests/DeviceMemoryFallback.cpp",
"line": 1,
"kind": "possible-unregistered-cpp-test",
"message": "Changed C++ test source path was not found literally in CMake files; inspect registration, generated source lists, and workflow coverage."
}
],
"negative_control_candidates": [],
"limits": [
"Heuristics can miss weak assertions and can flag valid tests; inspect every finding.",
"A test that passes against base is not by itself proof that the PR test is inadequate.",
"Negative controls are only candidates until the test actually runs against base and fails by assertion rather than import/setup error."
]
}Unedited label dry-run output:
would change #14815: +perf +perf numbers missing +unblocks title comment
Unedited relevant command output:
git diff --check produced no output.
Conventions:
All conventions checks passed.
Stack check:
/tmp/anyps5-pr-stack/boykopovar-AnyPS5-pr-14815-a37af8b9107e/stack-report.md
/tmp/anyps5-pr-stack/boykopovar-AnyPS5-pr-14815-a37af8b9107e/stack-report.json
Negative control
Not run. The C++ tests require a built binary; no build was performed.
Findings
Blocking
stagingBufferchecksDeviceLocalHeadroomand returnsnullptrbefore constructing aBuffer. TheBufferconstructor first asksBufferPool::Takefor a matching cached allocation. At the 7/8 threshold, the new early return therefore skips a possible cache hit even though reusing that allocation would add no heap usage; the retained allocation remains in the pool while this region falls back to the unstaged path. Please preserve cached-buffer reuse and apply the headroom refusal only when a fresh device allocation is needed, with a test for a reusable entry at the threshold.DeviceLocalHeadroomuses the first device-local memory type, butBufferchooses a type after Vulkan returns this buffer'smemoryTypeBits. If the selected type belongs to another heap, the check reads the wrong heap's budget and can either refuse useful staging or permit allocation past the selected heap's threshold. Please base the check on the actual compatible type/heap and cover differing memory-type masks in the budget test.- The title matches the repository's
perf(...)rule. The description has no two valid, distinct-commitaps5-summary.jsonllines, raw traces ortrace_digest.pyoutput; the label dry run also reportsperf numbers missing. Please add the matched baseline and candidate captures required by the performance report form.
Non-blocking
- None.
Sources
- Headroom check and selected type logic at this head.
BufferMemoryTypeselects from the buffer'smemoryTypeBits;DeviceLocalHeadroomchecks the first device-local type. - Staging guard at this head returns before constructing the buffer.
- Buffer construction and pool lookup at this head tries a cache hit before requesting new memory; pool hit behavior reuses retained allocations.
- Performance requirements at this head require two summary lines for
perftitles; the performance form asks for the traces and digest.
Not verified
- No build, CTest run, negative control, independent GPU run, or Linux/Windows/console behavior check was performed. The live hosted jobs were still queued or cancelled.
To get this to a good state
Preserve pool reuse, check the actual selected heap, and provide the matched performance artifacts. I will rerun the same no-build checks on the updated head.
|
Thanks for the measurements; the breakdown by category makes the case clearly, and falling back to the existing unstaged path keeps the change small. Two requests at
Reviewed at |
|
This pull request is labeled |
…heap it uses The headroom check ran before the Buffer was made, so a shadow the buffer pool could serve without allocating was refused too, and it read the heap of the first device-local memory type rather than the one the buffer's memoryTypeBits select. Buffer::WithinDeviceLocalHeadroom makes the shadow: a pooled allocation is reused as before, and a new one is refused when it would end within the last eighth of the reported budget of the heap of the memory type chosen for it. The eighth gets a debt entry.
|
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.
|
✅ AMD GPU test: passedPR head
Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (9, not counted against this PR)Validation IDs already present on main (5 IDs in 484 tests)Skipped (55)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
✅ NVIDIA GPU test: passedPR head
Unstable on main (2, 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)Validation IDs already present on main (5 IDs in 484 tests)Skipped (46)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. |
|
Thanks to both of you. The two code points were the same underlying mistake (the check ran before the
Validation (RX 9070 XT, RADV Mesa 26.2.6, Linux), Could you take another look at b491e43? AI-assisted: yes (Claude Code) |
✅ NVIDIA GPU test: passedPR head
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)Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)Validation IDs already present on main (4 IDs in 510 tests)Skipped (44)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. |
✅ AMD GPU test: passedPR head
Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (9, not counted against this PR)Validation IDs already present on main (5 IDs in 484 tests)Skipped (55)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
Windows re-check of
|
| Run, Windows 11, WinLibs GCC 15.2.0, Release, RX 9070 XT | ctest -R '^agc_(buffer_pool.*|device_memory_fallback|memory_accounting.*)$' |
|---|---|
Head merged onto current main |
7/7 pass |
Same tree with the check disabled (if (false && headroom && …) in Resources.cpp) |
6/7; agc_buffer_pool fails: "a staging buffer that needs a new allocation within the last eighth of its heap's budget was not refused", "the second device-local heap is full, the buffer's mask allows only the second: made", "a refused staging buffer left its VkBuffer alive" |
Blocker 3 (performance report)
The description now has four summary lines, the trace files and trace_digest.py output. One question, @hugodearaujo: the change captures are from e5b4c1a76648 and the base captures from a9b16f94d161. Neither commit is on GitHub (gh api repos/boykopovar/AnyPS5/commits/e5b4c1a76648 returns 422). From GitHub alone I can't tell whether e5b4c1a76648 contains b491e43a20's rework or the earlier gate. Could you say which, or point it at a pushed commit?
I didn't run the validation layer or PRAGMATA.
|
@BunnyFl thanks for the re-check. Both capture commits are now pushed to my fork, on the branch
So the change captures include AI-assisted: yes (Claude Code) |
BunnyFl
left a comment
There was a problem hiding this comment.
Thanks @hugodearaujo, that settles my question about the captures.
I fetched pr14815-perf-captures. a9b16f94d1 is exactly two commits below e5b4c1a766, and between them the four production files (GuestBufferMemory.cpp, Resources.cpp, Resources.hpp, Context.hpp) change in exactly the same lines as this PR's own diff at b491e43a20 (0 differing +/- lines). So the change captures ran this head's rework, including the check in Buffer's constructor.
That and my Windows re-check of b491e43a20 cover what I raised. On Windows 11 with an RX 9070 XT, the 7 agc_buffer_pool*/agc_device_memory_fallback/agc_memory_accounting* tests pass merged onto main. With the check disabled, agc_buffer_pool fails on the threshold, chosen-heap and VkBuffer cases. The PR head is still b491e43a20 today.
I didn't run the validation layer or PRAGMATA, and I didn't re-derive the trace_digest.py numbers. @MarcZX10's change request from a37af8b910 is still open on his side.
What
A staged buffer region (a written element, a swept range up to 256 MiB on a discrete GPU, an atomic element) gets a device-local shadow from
stagingBuffer(GuestBufferMemory.cpp). Each shadow is its ownVkDeviceMemory, it lives as long as the cached build that holds it, and its allocation never looks at the device-local budget:BufferMemoryTypeonly checks the budget for host-coherent buffers, so a device-local shadow is allocated until the driver refuses it. The other big device-local consumers adapt to the budget (the storage image cache keepsbudget / 8free in its reserve, the sampled texture cache has its own budget), so when a title fills the heap it is the shadows that keep growing into that last eighth and past the budget.Context::DeviceLocalHeadroom(type, bytes)(new): whether an allocation ofbytesfrom memory typetypestill ends at or below seven eighths of the budgetVK_EXT_memory_budgetreports for that type's heap. A type on a heap withoutVK_MEMORY_HEAP_DEVICE_LOCAL_BIT, an unknown type, a device without the extension and a heap without a reported budget all answer yes, so those cases behave as before.Buffer::WithinDeviceLocalHeadroom(context, bytes, usage)(new) makes a device-localBufferthrough the normal constructor path. A buffer the pool already retains (BufferPool::Take) is reused as before, without the check, since it adds nothing to the heap. Only on a pool miss, afterBufferMemoryType(requirements.memoryTypeBits, ...)has picked the memory type, is the new allocation checked against the heap of that type; without headroom theVkBufferjust created is destroyed and the function returnsnullptr.stagingBuffermakes its shadows through it. A refused shadow is treated exactly like a shadow the device cannot provide:nullptr, counted instagingRefused(the[buffers] stagingline ofAPS5_PROFILE_DRAW), and the region takes the path it takes without staging (Region::unstaged: in place when aligned, otherwise the host copy). The first four refusals print one[buffers] staging shadow of N bytes refused: ...line each.docs/dev/debt/libSceAgcDriver.mdgets a "Staging shadow headroom" entry: the eighth comes from one title on one GPU, budget readings are estimates, and the check reserves nothing against concurrent allocations.Why (PRAGMATA, PPSA02530)
On an RX 9070 XT (16 GiB, RADV, Linux), the title is stopped by device memory every time the player walks from the "Basic Movement" tutorial save into the forklift room: the device-local heap reaches 15.3 to 16.1 GiB, and free GTT drops next (1.3 GiB of VRAM and 3.0 GiB of GTT free at the stop; our local guard stops the process there, before the desktop compositor starts failing command submission, which an earlier run on this machine caused). With
APS5_TRACE_MEMORY_BUDGET=1(#10194) plus a local diagnostic that names genericBufferallocations by their caller, the device-local heap at the stop was:stagingBuffer<GuestBufferMemory::UploadPrepare)Between the lobby and the forklift room the shadows grew by 1.3 GiB active (3132 -> 4415 MiB) while the storage cache stayed at its budget, and the heap went from 13.3 to 15.3 GiB.
Same build (main 8f55414 + the PRs PRAGMATA needs to reach gameplay + #14367 + #10194, plus that diagnostic), same save, same scripted route (Continue, then the left stick held forward), one run each, with the first version of this change (a37af8b):
APS5_NO_WRITTEN_SHADOW=1The storage image cache takes part of the room the shadows leave (5.6 -> 6.4-6.8 GiB), which is its budget doing its job.
Performance
Captured as the Performance report form describes: PRAGMATA (PPSA02530), the "Basic Movement" save, Continue, then the left stick held forward by script; window
APS5_TRACE_SECONDS=410:20, which falls in the Delphi lobby on the way to door A2 (screenshots match between the arms), before the point where the base is stopped on entering the forklift room. No otherAPS5_TRACE_*orAPS5_PROFILE_*switch (the summary lines show onlyAPS5_TRACE_SECONDSand the input file of our runner, which also captures the log to a file); warm shader and pipeline caches (the same cache copied into both run directories); RX 9070 XT, 3840x2160, same session. PRAGMATA does not reach gameplay onmainalone, so the base is our test stack:main8f55414 + the PRs PRAGMATA needs to reach gameplay + #14367 + #10194 (commita9b16f94d161in the lines), and the change is that stack + a37af8b and b491e43 cherry-picked (e5b4c1a76648). Two pairs, in the order base, change, change, base:a9b16f94d161e5b4c1a76648e5b4c1a76648a9b16f94d161The run-to-run spread is larger than any difference between the arms: capture 2 ran no refused shadow inside its window, so it ran the same code as the base there, and is still the slowest; capture 3, with the gate refusing shadows, is within the two base captures. In every capture the largest phase after
Driver.PacketisDriver.Packet.memory_wait.{"commit":"a9b16f94d161","dirty":null,"env":{"APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_SECONDS":"410:20"},"gpu":"AMD Radeon RX 9070 XT (RADV GFX1201)","driver":"radv Mesa 26.2.6-arch1.1","os":"Linux 7.2.11-arch1-1","cpu":"AMD Ryzen 7 9800X3D 8-Core Processor","cpu_threads":8,"memory_mib":31192,"title_id":"PPSA02530","title":"PRAGMATA","content_version":"01.200.000","resolution":"3840x2160","window":{"spec":"seconds 410:20","first_present":2289,"ms":21384.855},"presents":13,"frame_ms":{"p50":1596.384,"p90":1765.323,"p99":1914.261,"max":1914.261},"gpu_busy_ms_per_present":273.070,"worker_busy_pct":{"queue worker 0x38":92.947,"queue worker 0x20":69.872,"queue worker 0x0":99.465,"queue worker 0x28":96.512},"top_phases_ms_per_frame":{"Driver.Packet":5900.144,"Driver.Packet.memory_wait":3544.659,"Driver.Packet.dispatch":1335.178,"Driver.Dispatch":1239.117,"Resources.Complete":925.882,"Driver.Packet.draw":924.118,"Driver.Draw":923.648,"Graphics.Draw":753.789},"trace_dropped":false,"trace":"aps5-trace-a9b16f94d161-2684461.json.gz"} {"commit":"e5b4c1a76648","dirty":null,"env":{"APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_SECONDS":"410:20"},"gpu":"AMD Radeon RX 9070 XT (RADV GFX1201)","driver":"radv Mesa 26.2.6-arch1.1","os":"Linux 7.2.11-arch1-1","cpu":"AMD Ryzen 7 9800X3D 8-Core Processor","cpu_threads":8,"memory_mib":31192,"title_id":"PPSA02530","title":"PRAGMATA","content_version":"01.200.000","resolution":"3840x2160","window":{"spec":"seconds 410:20","first_present":2425,"ms":20649.170},"presents":10,"frame_ms":{"p50":2109.147,"p90":2828.118,"p99":2828.118,"max":2828.118},"gpu_busy_ms_per_present":314.273,"worker_busy_pct":{"queue worker 0x38":95.928,"queue worker 0x20":78.151,"queue worker 0x0":99.634,"queue worker 0x28":97.109},"top_phases_ms_per_frame":{"Driver.Packet":7655.591,"Driver.Packet.memory_wait":4592.974,"Driver.Packet.dispatch":1973.793,"Driver.Dispatch":1893.469,"Resources.Complete":1217.791,"Vulkan.Dispatch":969.457,"Driver.Packet.draw":965.215,"Driver.Draw":964.749},"trace_dropped":false,"trace":"aps5-trace-e5b4c1a76648-2745028.json.gz"} {"commit":"e5b4c1a76648","dirty":null,"env":{"APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_SECONDS":"410:20"},"gpu":"AMD Radeon RX 9070 XT (RADV GFX1201)","driver":"radv Mesa 26.2.6-arch1.1","os":"Linux 7.2.11-arch1-1","cpu":"AMD Ryzen 7 9800X3D 8-Core Processor","cpu_threads":8,"memory_mib":31192,"title_id":"PPSA02530","title":"PRAGMATA","content_version":"01.200.000","resolution":"3840x2160","window":{"spec":"seconds 410:20","first_present":2292,"ms":21376.039},"presents":13,"frame_ms":{"p50":1612.930,"p90":1918.206,"p99":1923.364,"max":1923.364},"gpu_busy_ms_per_present":270.926,"worker_busy_pct":{"queue worker 0x38":94.585,"queue worker 0x20":72.484,"queue worker 0x0":99.506,"queue worker 0x28":96.598},"top_phases_ms_per_frame":{"Driver.Packet":5969.986,"Driver.Packet.memory_wait":3613.671,"Driver.Packet.dispatch":1393.410,"Driver.Dispatch":1261.634,"Resources.Complete":907.912,"Driver.Packet.draw":850.179,"Driver.Draw":849.714,"Graphics.Draw":738.702},"trace_dropped":false,"trace":"aps5-trace-e5b4c1a76648-2978920.json.gz"} {"commit":"a9b16f94d161","dirty":null,"env":{"APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_SECONDS":"410:20"},"gpu":"AMD Radeon RX 9070 XT (RADV GFX1201)","driver":"radv Mesa 26.2.6-arch1.1","os":"Linux 7.2.11-arch1-1","cpu":"AMD Ryzen 7 9800X3D 8-Core Processor","cpu_threads":8,"memory_mib":31192,"title_id":"PPSA02530","title":"PRAGMATA","content_version":"01.200.000","resolution":"3840x2160","window":{"spec":"seconds 410:20","first_present":2270,"ms":21051.273},"presents":12,"frame_ms":{"p50":1760.807,"p90":2023.421,"p99":2109.706,"max":2109.706},"gpu_busy_ms_per_present":296.581,"worker_busy_pct":{"queue worker 0x20":74.765,"queue worker 0x38":92.637,"queue worker 0x0":99.711,"queue worker 0x28":96.847},"top_phases_ms_per_frame":{"Driver.Packet":6383.273,"Driver.Packet.memory_wait":3929.957,"Driver.Packet.dispatch":1399.155,"Driver.Dispatch":1303.774,"Resources.Complete":1000.844,"Driver.Packet.draw":927.537,"Driver.Draw":927.094,"Graphics.Draw":794.631},"trace_dropped":false,"trace":"aps5-trace-a9b16f94d161-3039591.json.gz"}Trace files: 1, base (
aps5-trace-a9b16f94d161-2684461), 2, change (aps5-trace-e5b4c1a76648-2745028), 3, change (aps5-trace-e5b4c1a76648-2978920), 4, base (aps5-trace-a9b16f94d161-3039591).python3 tools/trace_digest.py <trace>:Full digest, capture 3 (change)
seconds 410:20 from present 2292: 13 presents, frame p50 1612.9 / p90 1918.2 / p99 1923.4 / max 1923.4 ms, GPU busy 270.9 ms per present
AMD Radeon RX 9070 XT (RADV GFX1201), radv Mesa 26.2.6-arch1.1, Linux 7.2.11-arch1-1, 3840x2160, commit e5b4c1a, dropped: no
{"commit":"e5b4c1a76648","dirty":null,"env":{"APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_SECONDS":"410:20"},"gpu":"AMD Radeon RX 9070 XT (RADV GFX1201)","driver":"radv Mesa 26.2.6-arch1.1","os":"Linux 7.2.11-arch1-1","cpu":"AMD Ryzen 7 9800X3D 8-Core Processor","cpu_threads":8,"memory_mib":31192,"title_id":"PPSA02530","title":"PRAGMATA","content_version":"01.200.000","resolution":"3840x2160","window":{"spec":"seconds 410:20","first_present":2292,"ms":21376.039},"presents":13,"frame_ms":{"p50":1612.93,"p90":1918.206,"p99":1923.364,"max":1923.364},"gpu_busy_ms_per_present":270.926,"worker_busy_pct":{"queue worker 0x38":94.585,"queue worker 0x20":72.484,"queue worker 0x0":99.506,"queue worker 0x28":96.598},"top_phases_ms_per_frame":{"Driver.Packet":5969.986,"Driver.Packet.memory_wait":3613.671,"Driver.Packet.dispatch":1393.41,"Driver.Dispatch":1261.634,"Resources.Complete":907.912,"Driver.Packet.draw":850.179,"Driver.Draw":849.714,"Graphics.Draw":738.702},"trace_dropped":false,"trace":"aps5-trace-e5b4c1a76648-2978920.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
Largest slices (ms per frame, inclusive, all threads): Driver.Packet 5942.8, Driver.Packet.memory_wait 3613.6, Driver.Packet.dispatch 1377.6, Driver.Dispatch 1234.5, Resources.Complete 905.4, Driver.Packet.draw 846.7, Driver.Draw 846.3, Graphics.Draw 732.2, Graphics.Draw.draw_and_resource_release 720.6, Vulkan.Dispatch 658.9
Longest waits
Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)
Full digest, capture 4 (base)
seconds 410:20 from present 2270: 12 presents, frame p50 1760.8 / p90 2023.4 / p99 2109.7 / max 2109.7 ms, GPU busy 296.6 ms per present
AMD Radeon RX 9070 XT (RADV GFX1201), radv Mesa 26.2.6-arch1.1, Linux 7.2.11-arch1-1, 3840x2160, commit a9b16f9, dropped: no
{"commit":"a9b16f94d161","dirty":null,"env":{"APS5_PAD_INPUT_FILE":"<path>","APS5_TRACE_SECONDS":"410:20"},"gpu":"AMD Radeon RX 9070 XT (RADV GFX1201)","driver":"radv Mesa 26.2.6-arch1.1","os":"Linux 7.2.11-arch1-1","cpu":"AMD Ryzen 7 9800X3D 8-Core Processor","cpu_threads":8,"memory_mib":31192,"title_id":"PPSA02530","title":"PRAGMATA","content_version":"01.200.000","resolution":"3840x2160","window":{"spec":"seconds 410:20","first_present":2270,"ms":21051.273},"presents":12,"frame_ms":{"p50":1760.807,"p90":2023.421,"p99":2109.706,"max":2109.706},"gpu_busy_ms_per_present":296.581,"worker_busy_pct":{"queue worker 0x20":74.765,"queue worker 0x38":92.637,"queue worker 0x0":99.711,"queue worker 0x28":96.847},"top_phases_ms_per_frame":{"Driver.Packet":6383.273,"Driver.Packet.memory_wait":3929.957,"Driver.Packet.dispatch":1399.155,"Driver.Dispatch":1303.774,"Resources.Complete":1000.844,"Driver.Packet.draw":927.537,"Driver.Draw":927.094,"Graphics.Draw":794.631},"trace_dropped":false,"trace":"aps5-trace-a9b16f94d161-3039591.json.gz"}Slowest frames (largest slices inside each, inclusive)
Threads (ms per frame)
Largest slices (ms per frame, inclusive, all threads): Driver.Packet 6354.3, Driver.Packet.memory_wait 3929.9, Driver.Packet.dispatch 1381.8, Driver.Dispatch 1276.4, Resources.Complete 997.3, Driver.Packet.draw 924.0, Driver.Draw 923.6, Graphics.Draw 787.1, Graphics.Draw.draw_and_resource_release 776.0, Vulkan.Dispatch 695.2
Longest waits
Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)
Frame rate through the forklift room, same builds and route,
APS5_TRACE_SUMMARY_EVERY=10(separate runs, not the captures above), presents per 10 s counted from the end of the loading screen:The change's process VRAM peaked at 13561 MiB over its 900 s run.
Tested
On
mainf2d874c1fdand this head (b491e43), same machine (RX 9070 XT, RADV Mesa 26.2.6, Linux),ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation:ctest -V -R 'agc_device_memory_fallback|agc_adaptive_staging|agc_buffer_pool|staging': 7/7 pass on both (agc_adaptive_staging,agc_buffer_pooland its two small-device modes,agc_device_memory_fallback,agc_bda_staging,agc_read_written_staging), noVUID-line and nowas not found but was requestedline in either log.agc_device_memory_fallback(mock budget):HeadroomTestschecks room below seven eighths, an allocation ending exactly at seven eighths, one crossing it, usage already past it, no reported budget, a host memory type, an unknown type, noVK_EXT_memory_budgetand no device-local heap.SecondHeapHeadroomTestsadds a second device-local heap and type and picks the type from the mask asBufferdoes: with a mask that excludes the first device-local type, pressure on the first heap leaves room and pressure on the second refuses; with a mask that allows both, only the first heap counts.agc_buffer_pool(mock device),StagingHeadroom: a 24 MiB shadow made with room and released to the pool is reused at 1900 of 2048 MiB (past seven eighths) with no new allocation; a 40 MiB shadow at the same usage is refused with no allocation and noVkBufferleft alive; withmemoryTypeBitsexcluding the first device-local type, a full first heap does not refuse a shadow that goes to the second heap, and a full second heap does.agc_device_memory_fallbackfails the two mask-excludes-first cases (and the host-type and no-device-local-heap cases) andagc_buffer_poolfails both second-heap cases; with the check moved back before the pool lookup,agc_buffer_poolfailsa retained staging buffer was refused within the last eighth of the budget, although reusing it allocates nothing.python tools/check_conventions.py --base origin/mainpasses.Overlap: no open PR changes
stagingBufferor the shadow size limits (checked the diffs of the 11 open PRs that touchGuestBufferMemory.cpp). #4472 drops the buffer pool's retained device memory over the budget; it is complementary (the 1.3 GiB of retained shadow memory above is that pool's) and does not act here, because the heap stays under the reported budget until the very end.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)