Skip to content

fix(agc): reserve only the sampled textures alive from the storage image budget - #7862

Open
parl0v wants to merge 2 commits into
boykopovar:mainfrom
parl0v:fix/storage-budget-sampled-reserve
Open

parl0v wants to merge 2 commits into
boykopovar:mainfrom
parl0v:fix/storage-budget-sampled-reserve

Conversation

@parl0v

@parl0v parl0v commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

What

StorageTextureBudget reserves the sampled textures alive instead of the larger of them and their 2 GiB floor. Everything else stays as #7071 set it: the heap's use outside the caches, the eighth of the budget, the caps and the 512 MiB floor. Whenever the sampled textures alive hold 2 GiB or more, the budget is the same as on main.

The sampled budget already reserves TextureCacheBudget (at least the storage cap) for the storage cache, so one side keeps reserving the other's maximum, as #7071 says one side has to. The storage side no longer does the same in the other direction: as the sampled textures grow it gives the room back at its next read.

Tests: three StorageTextureBudget cases are added (the 8 GB case below, which fails on main; the same heap with the sampled textures grown to 2 GiB; the 10 GB pressure peak from #7071). The existing case that pinned the 2 GiB floor now expects the live sampled bytes, and the storage case in SampledCacheBudget.cpp moves its pressure point from 53 to 55 GiB in use so it stays under the cap on 8-12 GB devices. The debt entry and USAGE.md describe the new reserve.

Why

Minecraft Dungeons II (PPSA16064) gameplay on an RTX 4060 8 GB, Windows 10, main f78675a: the storage image budget sits at its 512 MiB floor in every 10 s window. One budget line: heap 0 has a 7189 MiB budget and uses 4491 MiB, 49 MiB of it sampled textures and 506 MiB cached storage images, so 3936 MiB is in use outside the caches. main reserves 3936 + 2048 (the sampled floor) + 898 (an eighth) MiB, which leaves 306 MiB, so the floor. The sampled side gets the same 306 MiB for itself, because it reserves TextureCacheBudget (2048 MiB) for the storage cache, and sits at its own 2 GiB floor. Each cache keeps 2 GiB for the other out of 2354 MiB of room while the two hold 555 MiB, and 2698 MiB of the reported budget stays free.

One present touches about 0.8-1.0 GB of storage images, so the cache evicts about 3,968 entries per 10 s and re-creates 96.7 % of them within two presents. With the budget forced to 1792 MiB (APS5_TEXTURE_CACHE_MIB=1792) on the same build: 1.74 -> 3.05 FPS, evictions 3,968 -> 6 per 10 s, CPU-path uploads 204 -> 0, and the card stays at about 5.8 GB, inside the 7.2 GB budget. With this change the line above gives 7189 - 3936 - 49 - 898 = 2305 MiB, capped at a quarter of the budget: 1797 MiB. On the last budget line of each of my six gameplay runs (4334-4819 MiB in use, 48-119 MiB sampled) main gives 512 MiB and this change 1797 MiB.

@AlxcBTW found the cause on #6194 on 2026-10-15: the two caches each reserve room for the other, and holding back the sampled cache's 2 GiB floor for a cache of ~33 MiB keeps an 8 GB card at its floor. That proposal targets #6194's growth cap; #7071 then reserved the same floor in main's base formula, as I noted on #6194 on 2026-10-18. This change removes it there.

case (MiB) budget in use sampled cached storage main this PR
PPSA16064, RTX 4060 8 GB, gameplay 7189 4491 49 506 512 (306) 1797 (cap)
same heap, sampled textures grown to 2 GiB 7189 6490 2048 506 512 512
PPSA11386, RTX 3080 10 GB, first pressure peak in #7071 8767 7981 1427 490 512 512

Why the 10 GB stop stays covered. #7071's protection is the term for the heap's use outside the caches, which this change keeps: at 007's level entry it took the storage budget from 2184 to 1795 MiB and to its floor by present 1298. On #7071's lines from the entry on the sampled textures alive held 2009-2404 MiB, so the reserve this change drops (max(0, 2048 MiB - sampled)) is 0-39 MiB there. The one logged line with fewer (1427 MiB, the first pressure peak with APS5_DRAW_PIPELINE=1 APS5_GPU_HEAP=1) is at the 512 MiB floor either way, and is now a unit case. Since #7071, #6174 (release both texture caches when a device allocation is refused, then retry) and #5987 (place a still-refused allocation in host memory) are on main, and depth and render targets now allocate through that path (AllocateImageMemory); when #7071's runs stopped they did not.

Trade-off. The storage budget no longer keeps room ahead of time for sampled textures that do not exist yet. When they grow it gives the room back at its next read (at most a second later) and the cache trims at its next insert, so a burst of sampled textures larger than the free room inside that window can exceed the reported budget. On PPSA16064 the free room at the new budget is 1407 MiB (the eighth, 899 MiB, plus 508 MiB under the cap) where main left 2698 MiB; the sampled textures stayed at 0-119 MiB on every budget line of my runs. If a title shows such a burst, the reserve can follow the sampled cache's recent peak plus a step (SampledCacheHeadroom in @AlxcBTW's #6194 revision) instead of its current bytes; on PPSA16064 that also gives 1797 MiB.

Measured

RTX 4060 8 GB (NVIDIA 617.42), Windows 10 19045, Minecraft Dungeons II (PPSA16064 01.100.100), a saved game and a scripted walk, automatic texture budgets, main 36f60df against this PR at 97aeb51. FPS is the mean of the gameplay 10 s windows of a row; the rows are interleaved over two sessions.

build rows FPS per row mean FPS storage budget, last line of each row
main 36f60df 8 1.87, 1.37, 1.60, 1.23, 1.23, 1.83, 1.63, 1.50 1.53 512 MiB in every row
this PR 97aeb51 8 3.63, 1.70, 2.50, 2.93, 2.07, 1.43, 3.60, 2.00 2.48 (+62 %) 1058-1797 MiB

Welch's t 3.08; a one-sided permutation test over the 12,870 splits of the 16 rows gives p = 0.0026. The rows with this change spread more (1.43-3.63 FPS against 1.23-1.87), and two of its eight rows fall inside main's range.

One traced row on each side, gameplay, APS5_TRACE_SECONDS=85:12: frame p50 669.5 ms (18 presents in the window) on main and 214.5 ms (51 presents) with this change, GPU busy 140.7 and 74.1 ms per present. This one window gains more than the row means do. APS5_LAB_HEAP_CENSUS in both summary lines is a variable of my run script; neither commit reads it.

{"commit":"36f60df9edb3","dirty":false,"env":{"APS5_LAB_HEAP_CENSUS":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_PROFILE_DRAW":"1","APS5_TRACE_SECONDS":"85:12"},"gpu":"NVIDIA GeForce RTX 4060","driver":"NVIDIA 617.42","os":"Windows 10.0.19045","resolution":"3840x2160","window":{"spec":"seconds 85:12","first_present":1651,"ms":12005.055},"presents":18,"frame_ms":{"p50":669.478,"p90":760.423,"p99":808.679,"max":808.679},"gpu_busy_ms_per_present":140.701,"worker_busy_pct":{"queue worker 0x0":99.705,"queue worker 0x20":97.928},"top_phases_ms_per_frame":{"Driver.Packet":1317.635,"Driver.Packet.memory_wait":425.336,"Driver.Packet.dispatch":366.131,"Driver.Dispatch":364.816,"Resources.Complete":358.141,"Graphics.Draw":331.012,"Graphics.Draw.draw_and_resource_release":299.075,"Driver.Packet.draw":257.558},"trace_dropped":false,"trace":"aps5-trace-36f60df9edb3-18888.json.gz"}
{"commit":"97aeb515de30","dirty":false,"env":{"APS5_LAB_HEAP_CENSUS":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_PROFILE_DRAW":"1","APS5_TRACE_SECONDS":"85:12"},"gpu":"NVIDIA GeForce RTX 4060","driver":"NVIDIA 617.42","os":"Windows 10.0.19045","resolution":"3840x2160","window":{"spec":"seconds 85:12","first_present":1679,"ms":12024.254},"presents":51,"frame_ms":{"p50":214.534,"p90":320.135,"p99":400.431,"max":400.431},"gpu_busy_ms_per_present":74.141,"worker_busy_pct":{"queue worker 0x0":99.239,"queue worker 0x20":98.014},"top_phases_ms_per_frame":{"Driver.Packet":464.814,"Driver.Packet.memory_wait":147.959,"Graphics.Draw":143.263,"Graphics.Draw.draw_and_resource_release":126.705,"Driver.Packet.draw":108.416,"Driver.Draw":108.086,"Driver.Packet.dispatch":89.249,"Driver.Dispatch":88.059},"trace_dropped":false,"trace":"aps5-trace-97aeb515de30-30592.json.gz"}

Traces (all four captures in this PR, they open in ui.perfetto.dev): https://github.com/parl0v/AnyPS5/releases/tag/perf-traces-2026-10-19

tools/performance_compare.py

Performance comparison

Capture Commit Trace Presents Window (ms)
baseline 36f60df aps5-trace-36f60df9edb3-18888.json.gz 18 12005.055
candidate 97aeb51 aps5-trace-97aeb515de30-30592.json.gz 51 12024.254
  • Compare captures from the same machine, title and scene; summaries do not record the CPU, title or scene.
  • Changes are candidate minus baseline. A negative frame-time change means a shorter frame interval.
  • A missing top phase is unreported, not zero; its change is unavailable.
  • Recorded configuration differs; these measurements do not isolate the code change.
Configuration differences
Setting Baseline Candidate
window.first_present 1651 1679
Frame and GPU times (ms)
Metric Baseline Candidate Change Change (%)
frame_ms.p50 669.478 214.534 -454.944 -67.955
frame_ms.p90 760.423 320.135 -440.288 -57.900
frame_ms.p99 808.679 400.431 -408.248 -50.483
frame_ms.max 808.679 400.431 -408.248 -50.483
mean_frame_ms 666.947 235.770 -431.178 -64.649
gpu_busy_ms_per_present 140.701 74.141 -66.560 -47.306
Top phases (ms per frame)
Metric Baseline Candidate Change Change (%)
Driver.Dispatch 364.816 88.059 -276.757 -75.862
Driver.Draw n/a 108.086 n/a n/a
Driver.Packet 1317.635 464.814 -852.821 -64.724
Driver.Packet.dispatch 366.131 89.249 -276.882 -75.624
Driver.Packet.draw 257.558 108.416 -149.142 -57.906
Driver.Packet.memory_wait 425.336 147.959 -277.377 -65.214
Graphics.Draw 331.012 143.263 -187.749 -56.720
Graphics.Draw.draw_and_resource_release 299.075 126.705 -172.370 -57.634
Resources.Complete 358.141 n/a n/a n/a

The configuration difference it reports is the window's first present (1651 against 1679): the window is set in seconds, so it starts at a different present.

tools/trace_digest.py, main 36f60df

seconds 85:12 from present 1651: 18 presents, frame p50 669.5 / p90 760.4 / p99 808.7 / max 808.7 ms, GPU busy 140.7 ms per present
NVIDIA GeForce RTX 4060, NVIDIA 617.42, Windows 10.0.19045, 3840x2160, commit 36f60df, dropped: no

{"commit":"36f60df9edb3","dirty":false,"env":{"APS5_LAB_HEAP_CENSUS":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_PROFILE_DRAW":"1","APS5_TRACE_SECONDS":"85:12"},"gpu":"NVIDIA GeForce RTX 4060","driver":"NVIDIA 617.42","os":"Windows 10.0.19045","resolution":"3840x2160","window":{"spec":"seconds 85:12","first_present":1651,"ms":12005.055},"presents":18,"frame_ms":{"p50":669.478,"p90":760.423,"p99":808.679,"max":808.679},"gpu_busy_ms_per_present":140.701,"worker_busy_pct":{"queue worker 0x0":99.705,"queue worker 0x20":97.928},"top_phases_ms_per_frame":{"Driver.Packet":1317.635,"Driver.Packet.memory_wait":425.336,"Driver.Packet.dispatch":366.131,"Driver.Dispatch":364.816,"Resources.Complete":358.141,"Graphics.Draw":331.012,"Graphics.Draw.draw_and_resource_release":299.075,"Driver.Packet.draw":257.558},"trace_dropped":false,"trace":"aps5-trace-36f60df9edb3-18888.json.gz"}

Slowest frames (largest slices inside each, inclusive)

  • present 1651: 808.7 ms at 0 ms: Driver.Packet (queue worker 0x0) 797.0, Driver.Packet (queue worker 0x20) 665.4, Driver.Packet.memory_wait (queue worker 0x20) 439.6, Driver.Packet.draw (queue worker 0x0) 328.9
  • present 1653: 760.4 ms at 1478 ms: Driver.Packet (queue worker 0x20) 758.7, Driver.Packet (queue worker 0x0) 751.0, Driver.Packet.memory_wait (queue worker 0x20) 527.5, Driver.Packet.draw (queue worker 0x0) 325.1
  • present 1656: 757.8 ms at 3712 ms: Driver.Packet (queue worker 0x20) 755.9, Driver.Packet (queue worker 0x0) 747.9, Driver.Packet.memory_wait (queue worker 0x20) 520.8, Driver.Packet.draw (queue worker 0x0) 346.1
  • present 1658: 752.2 ms at 5206 ms: Driver.Packet (queue worker 0x20) 750.4, Driver.Packet (queue worker 0x0) 743.2, Driver.Packet.memory_wait (queue worker 0x20) 521.1, Driver.Packet.draw (queue worker 0x0) 318.3
  • present 1655: 751.2 ms at 2960 ms: Driver.Packet (queue worker 0x20) 748.4, Driver.Packet (queue worker 0x0) 741.9, Driver.Packet.memory_wait (queue worker 0x20) 517.2, Driver.Packet.draw (queue worker 0x0) 294.4

Threads (ms per frame)

thread busy wait untraced in slices < 20 us largest
shader prepare 0.3 (0 %) 0.0 (0 %) 666.7 0.0 Shader.PrepareArtifact 0.3
shader prepare 0.1 (0 %) 0.0 (0 %) 666.8 0.0 Vulkan.GraphicsPipelinePrebuild 0.1
queue worker 0x0 595.8 (89 %) 62.0 (9 %) 9.1 48.4 Driver.Packet 657.8
draw committer 181.1 (27 %) 45.6 (7 %) 440.2 4.6 Graphics.Draw 176.2, Recorder.Submit 7.8
queue worker 0x20 103.8 (16 %) 550.9 (83 %) 12.2 6.9 Driver.Packet 651.3, Driver.EndSubmission 0.4
presenter 1.1 (0 %) 0.2 (0 %) 665.6 0.1 VideoOut.Flip 1.2, Driver.Present 0.1
title 1.1 (0 %) 0.0 (0 %) 665.8 0.0 Shader.ResolveAbi 1.1, Shader.Register 0.0
title 0.0 (0 %) 0.0 (0 %) 666.9 0.0 Shader.Register 0.0
gpu 132.1 (20 %) 0.0 (0 %) 534.8 0.0 batch 132.1

Largest slices (ms per frame, inclusive, all threads): Driver.Packet 1309.1, Driver.Packet.memory_wait 425.2, Driver.Packet.dispatch 365.7, Driver.Dispatch 364.3, Resources.Complete 357.4, Graphics.Draw 330.4, Graphics.Draw.draw_and_resource_release 298.3, Driver.Packet.draw 257.0, Driver.Draw 256.6, Vulkan.Dispatch 250.5

Longest waits

  • Driver.Packet.memory_wait (queue worker 0x20) 256.9 ms at 2107 ms, present 1653
  • Driver.Packet.memory_wait (queue worker 0x20) 253.3 ms at 3579 ms, present 1655
  • Driver.Packet.memory_wait (queue worker 0x20) 245.4 ms at 4357 ms, present 1656
  • Driver.Packet.memory_wait (queue worker 0x20) 238.9 ms at 2849 ms, present 1654
  • Driver.Packet.memory_wait (queue worker 0x20) 231.2 ms at 1367 ms, present 1652
  • Driver.Packet.memory_wait (queue worker 0x20) 227.2 ms at 5852 ms, present 1658
  • Driver.Packet.memory_wait (queue worker 0x20) 207.0 ms at 11386 ms, present 1667
  • Driver.Packet.memory_wait (queue worker 0x20) 206.0 ms at 10147 ms, present 1665
  • Driver.Packet.memory_wait (queue worker 0x20) 199.5 ms at 5135 ms, present 1657
  • Driver.Packet.memory_wait (queue worker 0x20) 195.7 ms at 8912 ms, present 1663

Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)

  • batch 347: 18.7 ms at 1492 ms, present 1653: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 26.6, Driver.Packet.prepare_memory (queue worker 0x0) 19.0, Driver.Packet.dispatch (queue worker 0x0) 7.1, Driver.Dispatch (queue worker 0x0) 7.1
  • batch 677: 18.2 ms at 2976 ms, present 1655: GPU no ranges; recorded by Recorder.Submit (draw committer) 0.3
  • batch 1370: 18.2 ms at 5973 ms, present 1659: GPU no ranges; recorded by Recorder.Sync (queue worker 0x0) 18.3, Recorder.Submit (queue worker 0x0) 0.1
  • batch 510: 18.0 ms at 2251 ms, present 1654: GPU no ranges; recorded by Recorder.Reap (draw committer) 0.5, Recorder.Submit (draw committer) 0.0
  • batch 2706: 18.0 ms at 11474 ms, present 1668: GPU no ranges; recorded by Recorder.Sync (queue worker 0x0) 18.1, Recorder.Submit (queue worker 0x0) 0.1
tools/trace_digest.py, this PR 97aeb51

seconds 85:12 from present 1679: 51 presents, frame p50 214.5 / p90 320.1 / p99 400.4 / max 400.4 ms, GPU busy 74.1 ms per present
NVIDIA GeForce RTX 4060, NVIDIA 617.42, Windows 10.0.19045, 3840x2160, commit 97aeb51, dropped: no

{"commit":"97aeb515de30","dirty":false,"env":{"APS5_LAB_HEAP_CENSUS":"1","APS5_NO_PAD_INPUT":"1","APS5_PAD_INPUT_FILE":"<path>","APS5_PROFILE_DRAW":"1","APS5_TRACE_SECONDS":"85:12"},"gpu":"NVIDIA GeForce RTX 4060","driver":"NVIDIA 617.42","os":"Windows 10.0.19045","resolution":"3840x2160","window":{"spec":"seconds 85:12","first_present":1679,"ms":12024.254},"presents":51,"frame_ms":{"p50":214.534,"p90":320.135,"p99":400.431,"max":400.431},"gpu_busy_ms_per_present":74.141,"worker_busy_pct":{"queue worker 0x0":99.239,"queue worker 0x20":98.014},"top_phases_ms_per_frame":{"Driver.Packet":464.814,"Driver.Packet.memory_wait":147.959,"Graphics.Draw":143.263,"Graphics.Draw.draw_and_resource_release":126.705,"Driver.Packet.draw":108.416,"Driver.Draw":108.086,"Driver.Packet.dispatch":89.249,"Driver.Dispatch":88.059},"trace_dropped":false,"trace":"aps5-trace-97aeb515de30-30592.json.gz"}

Slowest frames (largest slices inside each, inclusive)

  • present 1713: 400.4 ms at 6956 ms: Driver.Packet (queue worker 0x20) 398.0, Driver.Packet (queue worker 0x0) 389.8, Driver.Packet.memory_wait (queue worker 0x20) 282.9, Driver.Packet.draw (queue worker 0x0) 189.0
  • present 1711: 339.1 ms at 6297 ms: Driver.Packet (queue worker 0x20) 336.8, Driver.Packet (queue worker 0x0) 329.9, Driver.Packet.memory_wait (queue worker 0x20) 222.4, Driver.Packet.draw (queue worker 0x0) 160.7
  • present 1714: 336.1 ms at 7356 ms: Driver.Packet (queue worker 0x20) 334.3, Driver.Packet (queue worker 0x0) 327.3, Driver.Packet.memory_wait (queue worker 0x20) 185.7, Driver.Packet.draw (queue worker 0x0) 146.3
  • present 1723: 328.6 ms at 10104 ms: Driver.Packet (queue worker 0x0) 318.2, Driver.Packet (queue worker 0x20) 300.1, Driver.Packet.draw (queue worker 0x0) 171.7, Driver.Draw (queue worker 0x0) 171.3
  • present 1716: 325.6 ms at 7991 ms: Driver.Packet (queue worker 0x20) 323.6, Driver.Packet (queue worker 0x0) 316.0, Driver.Packet.memory_wait (queue worker 0x20) 254.6, Driver.Packet.draw (queue worker 0x0) 135.3

Threads (ms per frame)

thread busy wait untraced in slices < 20 us largest
title 0.0 (0 %) 0.1 (0 %) 235.7 0.0
shader prepare 0.1 (0 %) 0.0 (0 %) 235.7 0.0 Shader.PrepareArtifact 0.1
shader prepare 0.0 (0 %) 0.0 (0 %) 235.7 0.0 Vulkan.GraphicsPipelinePrebuild 0.0
queue worker 0x0 216.4 (92 %) 10.8 (5 %) 8.6 46.8 Driver.Packet 227.1
draw committer 76.7 (33 %) 9.1 (4 %) 150.0 4.4 Graphics.Draw 73.4, Recorder.Submit 3.2
queue worker 0x20 35.6 (15 %) 196.7 (83 %) 3.5 7.2 Driver.Packet 229.5, Driver.EndSubmission 0.2
presenter 0.8 (0 %) 0.2 (0 %) 234.8 0.1 VideoOut.Flip 0.9, Driver.Present 0.0
title 0.3 (0 %) 0.0 (0 %) 235.5 0.0 Shader.ResolveAbi 0.3
title 0.0 (0 %) 0.0 (0 %) 235.8 0.0 Shader.Register 0.0
title 0.0 (0 %) 0.0 (0 %) 235.8 0.0 Shader.ResolveGraphicsStagesAbi 0.0
gpu 74.3 (32 %) 0.0 (0 %) 161.5 0.0 batch 74.3

Largest slices (ms per frame, inclusive, all threads): Driver.Packet 456.6, Driver.Packet.memory_wait 147.9, Graphics.Draw 142.4, Graphics.Draw.draw_and_resource_release 125.4, Driver.Packet.draw 107.9, Driver.Draw 107.5, Driver.Packet.dispatch 88.9, Driver.Dispatch 87.6, batch 74.3, Driver.Packet.device_lock 71.5

Longest waits

  • Driver.Packet.memory_wait (queue worker 0x20) 105.3 ms at 7096 ms, present 1713
  • Driver.Packet.memory_wait (queue worker 0x20) 94.5 ms at 7279 ms, present 1713
  • Driver.Packet.memory_wait (queue worker 0x20) 94.2 ms at 10671 ms, present 1724
  • Driver.Packet.memory_wait (queue worker 0x20) 93.7 ms at 3254 ms, present 1696
  • Driver.Packet.memory_wait (queue worker 0x20) 93.0 ms at 5947 ms, present 1709
  • Driver.Packet.memory_wait (queue worker 0x20) 91.2 ms at 11633 ms, present 1728
  • Driver.Packet.memory_wait (queue worker 0x20) 91.2 ms at 10037 ms, present 1722
  • Driver.Packet.memory_wait (queue worker 0x20) 89.5 ms at 9425 ms, present 1720
  • Driver.Packet.memory_wait (queue worker 0x20) 89.0 ms at 6569 ms, present 1711
  • Driver.Packet.memory_wait (queue worker 0x20) 87.7 ms at 9738 ms, present 1721

Largest GPU batches (GPU ranges inside, and the CPU slices that recorded each, inclusive)

  • batch 4812: 9.9 ms at 11231 ms, present 1727: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 1.7, Driver.Packet.dispatch (queue worker 0x0) 1.5, Driver.Dispatch (queue worker 0x0) 1.5, Vulkan.Dispatch (queue worker 0x0) 0.7
  • batch 4913: 9.4 ms at 11440 ms, present 1728: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 0.6, Driver.Packet.dispatch (queue worker 0x0) 0.5, Driver.Dispatch (queue worker 0x0) 0.4, Vulkan.Dispatch (queue worker 0x0) 0.1
  • batch 2902: 8.1 ms at 6024 ms, present 1710: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 1.3, Driver.Packet.dispatch (queue worker 0x0) 1.1, Driver.Dispatch (queue worker 0x0) 1.1, Vulkan.Dispatch (queue worker 0x0) 0.6
  • batch 4596: 7.7 ms at 10751 ms, present 1725: GPU no ranges; recorded by Driver.Packet (queue worker 0x0) 0.7, Driver.Packet.dispatch (queue worker 0x0) 0.7, Driver.Dispatch (queue worker 0x0) 0.7, Vulkan.Dispatch (queue worker 0x0) 0.4
  • batch 3085: 7.4 ms at 6547 ms, present 1711: GPU no ranges; recorded by Driver.Packet (queue worker 0x20) 1.9, Driver.Packet.dispatch (queue worker 0x20) 1.8, Driver.Dispatch (queue worker 0x20) 1.8, Vulkan.Dispatch (queue worker 0x20) 0.7

RTX 3060 12 GB (NVIDIA 617.42), Windows 10 19045, PPSA16064 title screen, APS5_TRACE_SECONDS=120:12, no other variables, arms alternating, a warm run before the first capture of each side. The storage budget is not at its floor on this card, so nothing changes, as expected:

build frame p50 (ms), three captures GPU busy per present (ms)
main 36f60df 110.655, 110.500, 110.330 91.92, 91.90, 91.85
this PR 97aeb51 110.262, 110.615, 110.470 90.97, 91.87, 91.96
{"commit":"36f60df9edb3","dirty":false,"env":{"APS5_TRACE_SECONDS":"120:12"},"gpu":"NVIDIA GeForce RTX 3060","driver":"NVIDIA 617.42","os":"Windows 10.0.19045","resolution":"3840x2160","window":{"spec":"seconds 120:12","first_present":2209,"ms":12051.908},"presents":105,"frame_ms":{"p50":110.500,"p90":142.805,"p99":147.496,"max":149.005},"gpu_busy_ms_per_present":91.902,"worker_busy_pct":{"queue worker 0x0":99.358,"queue worker 0x20":99.613},"top_phases_ms_per_frame":{"Driver.Packet":228.270,"Driver.Packet.dispatch":82.101,"Driver.Dispatch":80.946,"Driver.Packet.memory_wait":64.269,"Vulkan.Dispatch":33.423,"Vulkan.Dispatch.shader_resources":31.191,"Graphics.Draw":30.365,"Driver.Packet.device_lock":28.979},"trace_dropped":false,"trace":"aps5-trace-36f60df9edb3-8444.json.gz"}
{"commit":"97aeb515de30","dirty":false,"env":{"APS5_TRACE_SECONDS":"120:12"},"gpu":"NVIDIA GeForce RTX 3060","driver":"NVIDIA 617.42","os":"Windows 10.0.19045","resolution":"3840x2160","window":{"spec":"seconds 120:12","first_present":2207,"ms":12066.203},"presents":105,"frame_ms":{"p50":110.615,"p90":143.942,"p99":146.810,"max":147.016},"gpu_busy_ms_per_present":91.871,"worker_busy_pct":{"queue worker 0x0":99.354,"queue worker 0x20":99.481},"top_phases_ms_per_frame":{"Driver.Packet":228.375,"Driver.Packet.dispatch":82.549,"Driver.Dispatch":81.435,"Driver.Packet.memory_wait":65.005,"Vulkan.Dispatch":33.306,"Vulkan.Dispatch.shader_resources":31.029,"Graphics.Draw":30.777,"Graphics.Draw.draw_and_resource_release":29.133},"trace_dropped":false,"trace":"aps5-trace-97aeb515de30-10648.json.gz"}

Tested

  • agc_driver_graphics, agc_driver_sampled_cache_budget, agc_driver_sampled_cache_budget_memory, agc_driver_sampled_cache_budget_without_storage_image_multisample, agc_driver_storage_cache_budget, agc_driver_storage_cache_retained_batch, agc_driver_resource_cache_storage_admission, agc_driver_resource_cache_oversized_storage_admission and agc_driver_resource_cache_storage_clear on the RTX 4060 (NVIDIA 617.42, Windows 10 19045, WinLibs GCC 15.2.0, Release with BUILD_TESTING=ON), ctest -V with VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ANYPS5_NO_SHADER_CACHE=1 ANYPS5_REQUIRE_VULKAN=1 VK_LOADER_DEBUG=error,warn, Vulkan SDK 1.4.363.0: 9 of 9 pass on main 36f60df and 9 of 9 on 97aeb51. Neither log has a was not found but was requested by env var VK_INSTANCE_LAYERS line (the loader reports adding the layer in both), and both have 0 VUID- lines. The storage case of agc_driver_sampled_cache_budget_memory prints 1024 MiB at 55 GiB in use of 64 GiB with this change.
  • With StorageTextureBudget's body put back to main's and this PR's tests: agc_driver_graphics fails on the 5632 MiB case that replaced the floor case ("does not leave 2304 MiB"; main gives 1280 MiB), and with that case removed it fails on the PPSA16064 case (main gives 512 MiB where 1797 MiB is expected). agc_driver_sampled_cache_budget_memory fails with "55 GiB in use of a 64 GiB budget leaves the storage image cache 512 MiB, not the 1024 MiB".
  • b79560d adds @JJenkx's pressure sweep to storageTextureBudgetTests() (2-32 GiB heaps, changing budgets, live sampled bytes and outside usage; he ran it on an RX 6650 XT with RADV). agc_driver_graphics with it passes on the RTX 4060 (NVIDIA 617.42, Windows 10).
  • python3 tools/check_conventions.py --base origin/main passes.
  • In game: the rows above. One of the 10 loads with this change crashed during the save load (FAsyncLoadingThread, read access violation in the game's own code at app_mcd2.exe+0x4d446b), against 0 of 9 on main. MCD2 has crashed in its asynchronous loading on main before (1-2 of 6 loads on 2026-10-18, TAsyncThread / Background Worker).
  • Not checked: PPSA11386, Linux, AMD/RADV, Intel, a 10 GB card.

Related

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 (none added)
  • No notes, investigation .md files or images added to the repository
  • New third-party code is a submodule built from source (none added)
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: NVIDIA GeForce RTX 4060 8 GB, NVIDIA 617.42, Windows 10 (tests and gameplay); NVIDIA GeForce RTX 3060 12 GB, NVIDIA 617.42, Windows 10 (title screen captures only)
  • AI-assisted: yes

…age budget

StorageTextureBudget reserved the larger of the sampled textures alive
and their 2 GiB floor. The sampled budget already reserves
TextureCacheBudget, at least the storage cap, for the storage cache, so
on an 8 GB card the two caches each kept the other's maximum: with
3936 MiB used outside the caches on a 7189 MiB budget and 49 MiB of
sampled textures, the storage budget came out at 306 MiB and sat at its
512 MiB floor while about 2.7 GB of the budget stayed free.

The storage budget now reserves the sampled textures alive and gives
room back as they grow, at its next read. Where they hold 2 GiB or more
the budget is unchanged.
@github-actions github-actions Bot added docs agc libSceAgc* labels Oct 19, 2026
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

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 1395 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 2434 docs/dev/debt/libSceAgcDriver.md
PR 3786 docs/user/USAGE.md
PR 3998 docs/user/USAGE.md
PR 4040 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 4448 docs/dev/debt/libSceAgcDriver.md
PR 4449 docs/dev/debt/libSceAgcDriver.md
PR 4847 docs/user/USAGE.md
PR 5021 core/libs/prx/libSceAgcDriver/tests/execution/SampledCacheBudget.cpp
docs/user/USAGE.md
PR 5058 docs/dev/debt/libSceAgcDriver.md
PR 5372 docs/dev/debt/libSceAgcDriver.md
PR 5441 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5499 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 5500 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5925 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
docs/user/USAGE.md
PR 6146 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6171 docs/user/USAGE.md
PR 6177 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6188 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6192 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6194 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6202 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6218 docs/dev/debt/libSceAgcDriver.md
PR 6223 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6235 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6273 docs/dev/debt/libSceAgcDriver.md
PR 6280 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6292 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6300 docs/dev/debt/libSceAgcDriver.md
PR 6340 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6412 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6481 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6498 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6548 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6549 docs/dev/debt/libSceAgcDriver.md
PR 6572 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6586 docs/user/USAGE.md
PR 6588 docs/dev/debt/libSceAgcDriver.md
PR 6612 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6615 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6636 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6639 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6683 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6718 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6727 docs/dev/debt/libSceAgcDriver.md
PR 6741 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6743 docs/dev/debt/libSceAgcDriver.md
PR 6785 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6788 docs/user/USAGE.md
PR 6801 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6804 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6808 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6820 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6868 docs/dev/debt/libSceAgcDriver.md
PR 6887 docs/dev/debt/libSceAgcDriver.md
PR 6921 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6924 docs/user/USAGE.md
PR 6926 docs/dev/debt/libSceAgcDriver.md
PR 6937 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/execution/SampledCacheBudget.cpp
PR 6946 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
docs/user/USAGE.md
PR 6947 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
docs/user/USAGE.md
PR 6959 docs/dev/debt/libSceAgcDriver.md
PR 6965 docs/dev/debt/libSceAgcDriver.md
PR 6970 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6972 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6973 docs/dev/debt/libSceAgcDriver.md
PR 6977 docs/dev/debt/libSceAgcDriver.md
PR 6982 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 6987 docs/dev/debt/libSceAgcDriver.md
PR 6989 docs/dev/debt/libSceAgcDriver.md
PR 6995 docs/dev/debt/libSceAgcDriver.md
PR 7002 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7085 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7154 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7243 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7256 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7261 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 7307 docs/dev/debt/libSceAgcDriver.md
PR 7310 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7332 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/execution/SampledCacheBudget.cpp
docs/dev/debt/libSceAgcDriver.md
docs/user/USAGE.md
PR 7340 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 7410 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7417 docs/user/USAGE.md
PR 7426 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7446 docs/dev/debt/libSceAgcDriver.md
PR 7449 docs/dev/debt/libSceAgcDriver.md
PR 7450 docs/user/USAGE.md
PR 7480 docs/dev/debt/libSceAgcDriver.md
PR 7484 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 7505 docs/dev/debt/libSceAgcDriver.md
PR 7533 docs/dev/debt/libSceAgcDriver.md
PR 7570 core/libs/prx/libSceAgcDriver/tests/execution/SampledCacheBudget.cpp
PR 7600 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7604 docs/dev/debt/libSceAgcDriver.md
PR 7618 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7627 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
docs/user/USAGE.md
PR 7650 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7659 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7668 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7683 docs/dev/debt/libSceAgcDriver.md
PR 7689 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 7696 docs/dev/debt/libSceAgcDriver.md
PR 7717 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7725 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7729 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7800 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/execution/SampledCacheBudget.cpp
docs/dev/debt/libSceAgcDriver.md
docs/user/USAGE.md
PR 7803 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7831 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7833 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 7844 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7869 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7873 docs/user/USAGE.md
PR 7883 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7893 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7894 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7896 docs/dev/debt/libSceAgcDriver.md
PR 7910 docs/dev/debt/libSceAgcDriver.md
PR 7911 docs/dev/debt/libSceAgcDriver.md
PR 7912 docs/dev/debt/libSceAgcDriver.md
PR 7918 docs/user/USAGE.md
PR 7927 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7942 docs/user/USAGE.md
PR 7948 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7965 docs/dev/debt/libSceAgcDriver.md
PR 7966 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 7978 docs/dev/debt/libSceAgcDriver.md
PR 8014 docs/dev/debt/libSceAgcDriver.md
PR 8016 docs/user/USAGE.md
PR 8026 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 8046 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8047 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/execution/SampledCacheBudget.cpp
PR 8058 docs/user/USAGE.md
PR 8066 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 8069 docs/dev/debt/libSceAgcDriver.md
PR 8070 docs/dev/debt/libSceAgcDriver.md
PR 8078 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 8083 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8099 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8104 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 8105 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 8115 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8117 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
PR 8121 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8123 docs/dev/debt/libSceAgcDriver.md
PR 8145 docs/dev/debt/libSceAgcDriver.md
PR 8162 docs/user/USAGE.md
PR 8180 docs/dev/debt/libSceAgcDriver.md
PR 8206 docs/dev/debt/libSceAgcDriver.md
PR 8212 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 8213 docs/dev/debt/libSceAgcDriver.md
PR 8223 docs/dev/debt/libSceAgcDriver.md
PR 8224 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8246 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 8261 docs/dev/debt/libSceAgcDriver.md
PR 8281 docs/dev/debt/libSceAgcDriver.md
PR 8283 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8297 docs/dev/debt/libSceAgcDriver.md
PR 8319 docs/dev/debt/libSceAgcDriver.md

@JJenkx

JJenkx commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Additional Linux/AMD validation of the production patch from 97aeb515de30 on an RX 6650 XT (RADV/Mesa 26.2.4).

I added a pressure sweep to storageTextureBudgetTests() covering 2/4/6/8/16/32 GiB heaps, changing reported budgets, live sampled bytes and non-cache usage. It checks the floor/caps, that increasing outside usage cannot increase the storage budget, and that a budget above the floor leaves room for live sampled textures plus the one-eighth headroom. All cases pass. This checks the formula at a sampled instant; it does not test the acknowledged burst/refresh-window risk.

After merging upstream main b5bc0c5fdd into that integration (local head cfc1bd106519), the full build and 12 targeted checks passed: five unwind-callback cases, profile-output, draw-input driver selection, the Vulkan draw-input-in-place recorder case, graphics budget tests, AJM, AJM teardown and RTC. Command:

env ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 \
  VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  VK_DRIVER_FILES=/usr/share/vulkan/icd.d/radeon_icd.json \
  XDG_RUNTIME_DIR=/run/user/1000 \
  ctest --test-dir build-full -R '^(agc_profile_output|agc_driver_graphics|agc_draw_inputs_in_place_driver|agc_draw_inputs_in_place|guest_ajm|guest_ajm_teardown|guest_rtc|unwind_callback_abi_.*)$' -V

Independently checked the full log: zero VUID- errors and no missing validation-layer warning. The budget sweep is a CPU test of the production formula; the recorder case exercises Vulkan. The graphics suite reports that guest-arena heap-mirror cases were not tested. The optional f64 execution case is excluded from this build and is not counted as coverage. An initial invocation had an invalid ICD path; the results above are from the corrected run.

Native smoke checks reached the driving cutscene in Crimson Spires (PPSA03568) and the readable opening story in Lodoss (PPSA05347), with automatic texture budgets and ordinary close exiting 0. These runs used an existing correctness integration including #7817, #7627, #6959 and #7755, plus this patch and the test sweep (local head 2575611b4604); they are not isolated PR/main comparisons. No performance claim or new gameplay-unblock claim from these checks.

Optional test addition, inserted before resetting the device-local heap flags in storageTextureBudgetTests():

    for (const auto heapGiB : {2u, 4u, 6u, 8u, 16u, 32u}) {
        memory.memoryHeaps[2].size = heapGiB * GiB;
        for (std::uint64_t budget = GiB; budget <= heapGiB * GiB; budget += 512 * MiB) {
            reported.heapBudget[2] = budget;
            for (std::uint64_t sampled = 0; sampled <= budget; sampled += 256 * MiB) {
                std::uint64_t previous = 0;
                for (std::uint64_t outside = 0; outside <= budget + GiB; outside += 128 * MiB) {
                    reported.heapUsage[2] = outside + sampled + 512 * MiB;
                    const auto limit = StorageTextureBudget(memory, &reported, sampled, 512 * MiB);
                    Require(limit >= 512 * MiB, "pressure sweep lost the storage cache floor");
                    Require(limit <= std::max<std::uint64_t>(512 * MiB, budget / 4u), "pressure sweep exceeded a quarter of the reported budget above the floor");
                    Require(limit <= std::max<std::uint64_t>(2 * GiB, heapGiB * GiB / 4u), "pressure sweep exceeded the heap storage cache cap");
                    if (outside != 0) Require(limit <= previous, "additional non-cache memory usage grew the storage cache budget");
                    if (limit > 512 * MiB) Require(outside + sampled + budget / 8u + limit <= budget, "storage cache budget consumed live sampled memory or pressure headroom");
                    previous = limit;
                }
            }
        }
    }

AI-assisted: yes.

Additional native pressure check (2026-10-19)

Repeated the Crimson Spires opening driving sequence in both run orders, with identical scripted inputs, automatic texture budgets, 3840×2160 rendering and the same trace window. Both runs with this patch and both controls closed normally with exit 0. This is still the prerequisite correctness integration described above, refreshed onto upstream b5bc0c5fdd10; candidate cfc1bd106519 and control 46197e24a26c differ in this patch and its tests/docs. It is not a pure-main benchmark or a new gameplay unblock.

The RX 6650 XT/RADV Mesa 26.2.4 logs exercise the intended low-sampled-memory case: the repeated control reports 101 MiB sampled textures, a 4392 MiB reported device-local budget, and its storage cache budget falling to the 512 MiB floor. The candidate starts with a 4351 MiB reported budget and a 1088 MiB storage cache budget, with no subsequent budget-change report during the run. The first pair showed the same control-floor/candidate-no-change-report pattern. These are budget diagnostics, not measurements proving absence of allocation pressure between reports. The repeated candidate screenshot reaches the same opening dialogue with readable text; animation progress differs between the timed captures.

No isolated-main speed claim from these integration runs. AI-assisted: yes.

Isolated Lodoss comparison (2026-10-19): pinned pure main b106f6f8c04c versus only this PR cherry-picked as 71f17c4d7976, with no other prerequisite patches. PPSA05347 stationary initial forest, same RX 6650 XT/RADV machine/session, 3840×2160 rendering and 1536×864 window. Independent per-source shader/Mesa caches were warmed by running the scene once and restarting. Only APS5_TRACE_SECONDS=200:10 was enabled; game output stayed on a real PTY without redirection. The 190s/210s captures show the same character position, camera and tutorial overlay.

Main: 25.42 FPS; this PR: 24.56 FPS. This single pair demonstrates no Lodoss gain and is insufficient to establish a regression. Both sides passed the same 12 focused tests with the required Vulkan validation layer and zero VUID- errors. Both measured native runs exited 0 and restored the kernel setting; shutdown still reports storage images destroyed with GPU results pending. Upstream advanced after the pinned base.

Summary lines:

{"commit":"b106f6f8c04c","dirty":false,"env":{"APS5_DRAW_CACHE_ENTRIES":"4096","APS5_HOST_IMPORT_MIB":"16384","APS5_PAD_INPUT_FILE":"<path>","APS5_PIPELINED_DRAWS":"1","APS5_SPIRV_OPT":"full","APS5_TRACE_SECONDS":"200:10"},"gpu":"AMD Radeon RX 6650 XT (RADV NAVI23)","driver":"radv Mesa 26.2.4-arch1.1","os":"Linux 7.2.9-arch1-1","resolution":"3840x2160","window":{"spec":"seconds 200:10","first_present":9594,"ms":10030.603},"presents":255,"frame_ms":{"p50":36.975,"p90":48.536,"p99":65.373,"max":70.653},"gpu_busy_ms_per_present":2.244,"worker_busy_pct":{"queue worker 0x0":34.909},"top_phases_ms_per_frame":{"Driver.Packet":13.729,"Driver.Packet.device_lock":9.528,"Graphics.Draw":6.89,"Graphics.Draw.validate":5.182,"Driver.Packet.prepare_memory":3.438,"Recorder.Sync":2.394,"GpuMutex.wait.other":1.825,"GpuMutex.wait.label":1.8},"trace_dropped":false,"trace":"aps5-trace-b106f6f8c04c-3825443.json.gz"}
{"commit":"71f17c4d7976","dirty":false,"env":{"APS5_DRAW_CACHE_ENTRIES":"4096","APS5_HOST_IMPORT_MIB":"16384","APS5_PAD_INPUT_FILE":"<path>","APS5_PIPELINED_DRAWS":"1","APS5_SPIRV_OPT":"full","APS5_TRACE_SECONDS":"200:10"},"gpu":"AMD Radeon RX 6650 XT (RADV NAVI23)","driver":"radv Mesa 26.2.4-arch1.1","os":"Linux 7.2.9-arch1-1","resolution":"3840x2160","window":{"spec":"seconds 200:10","first_present":9667,"ms":10017.988},"presents":246,"frame_ms":{"p50":39.986,"p90":49.004,"p99":63.075,"max":65.082},"gpu_busy_ms_per_present":2.267,"worker_busy_pct":{"queue worker 0x0":41.096},"top_phases_ms_per_frame":{"Driver.Packet":16.732,"Driver.Packet.device_lock":11.52,"Graphics.Draw":8.437,"Graphics.Draw.validate":6.67,"Driver.Packet.prepare_memory":4.355,"Recorder.Sync":2.816,"GpuMutex.wait.label":2.762,"GpuMutex.wait.other":2.491},"trace_dropped":false,"trace":"aps5-trace-71f17c4d7976-4063648.json.gz"}

Artifacts: report, main trace, candidate trace, main tests, candidate tests. AI-assisted: yes.

@parl0v

parl0v commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the RADV run and the sweep. I'll add the sweep to this PR with you as co-author and post the RTX 4060 results with the validation layer once it's in.

Checks the floor and caps, that more memory used outside the texture
caches never raises the budget, and that a budget above the floor
leaves room for the live sampled textures plus an eighth of the budget,
on 2-32 GiB heaps.

Co-authored-by: JJenkx <31495916+JJenkx@users.noreply.github.com>

@fogydes fogydes 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.

Verified the diff, test fixtures, and documentation in PR #7862:

  • Mathematical Rationale: In StorageTextureBudget (core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp), replacing std::max<std::uint64_t>(2048ull << 20u, sampledBytes) with sampledBytes eliminates double-reservation of the 2 GiB floor against both caches. On 8 GiB cards, this prevents the storage image cache from collapsing to its 512 MiB floor when sampled texture usage is low, while continuing to dynamically yield headroom as sampled textures grow.
  • Unit Testing: Unit test assertions in tests/Graphics.cpp (storageTextureBudgetTests) and tests/execution/SampledCacheBudget.cpp accurately verify the revised budget calculations across varied usage levels, including the PPSA16064 8 GiB heap profile.
  • Documentation & Conventions: Both docs/dev/debt/libSceAgcDriver.md and docs/user/USAGE.md accurately document the updated cache budget dynamics. No code comments introduced; tools/check_conventions.py passes with 0 findings.

@parl0v

parl0v commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Added the sweep as b79560d with @JJenkx as co-author. agc_driver_graphics passes with it on the RTX 4060 (NVIDIA 617.42, Windows 10 19045).

@whamre

whamre commented Oct 19, 2026

Copy link
Copy Markdown

Independent data point from LEGO Builder's Journey (PPSA05496): RTX 2080 SUPER 8 GB, Windows 11, NVIDIA 616.92. It's the same storage-image thrash. In this title the quarter-budget cap binds as well, not only the 2 GiB sampled reserve, so this PR alone doesn't fix it here.

First scene, same build and the same shader cache, with only the budget changed (5 s APS5_TRACE_SUMMARY_EVERY rows):

storage budget frame p50 GPU busy / present
1804 MiB (main's value here, APS5_TEXTURE_CACHE_MIB=1804) ~630 ms ~270 ms
2048 MiB 441 ms 237 ms
2560 MiB 176 ms 102 ms
3609 MiB (formula below) ~145 ms ~98 ms

At the old value, [gputime] showed 4.7 GB of storage-upload and 2.3 GB of storage-writeback + shadow-publish per present. At 2.5 GiB and above they almost disappear (0.6 MB write-back per present). The title uses only 16 MiB of sampled textures, so on this card the free-memory term with your change comes to about 4.1 GiB. The binding limit is then min(TextureCacheBudget, budget / 4) = 1804 MiB, just below this scene's working set (somewhere between 2.0 and 2.5 GiB). Your PPSA16064 numbers on the 4060 land on exactly that cap (1797 MiB), so it may bind there too.

What I tried locally: keep your change (reserve what the sampled textures use, but with a 512 MiB floor), and replace the cap with min(budget, heap) / 2. That gives 3609 MiB here, and the existing agc_driver_graphics, agc_driver_sampled_cache_budget (incl. _memory) and agc_driver_storage_cache_budget tests pass with their expectations updated. Since this PR touches the same two lines, I'd rather not open a competing one. Happy to send the cap change as a follow-up once this merges (Depends on: #7862), or you can fold it in, whichever you prefer.

AI-assisted: yes. The numbers are from my machine.

@SleglJan

Copy link
Copy Markdown
Contributor

@parl0v a 007 data point for the 10 GB case from #7071, which this PR discusses. Four gameplay runs from the same save, two without this change and two with it.

  • In the run with budget logging, this change's reserve and main's gave the same storage image budget at all 790 evaluations: in the menus, through the level load and in gameplay.
  • Three of the four runs, one of them with this change, stalled at the same view while the texture-cache release calls freed 0 MiB.
  • The run without a stall (run 3, with this change) entered the level with less memory in use outside the two caches: 2883–3137 MiB on its two logged lines, against 3502–4154 MiB in the three that stalled.

Setup:

Present numbers are the title's. A log line's present is bounded by the [present] lines around it, 1 to 10 presents apart in gameplay. "Level entry" is from the save resume to 30 presents into gameplay. Memory in MiB.

run 1 run 2 run 3 run 4
this PR no no yes yes, with budget logging
save resumed at, gameplay from 1172, 1220 1316, 1331 (set by hand: the harness's rule didn't fire, and 1331 is the same 10 s step after the resume where run 3's fired) 1335, 1349 1330, 1363
in use outside the two caches at level entry (usage − sampled − cached storage) 3502–4122 3804–4154 2883–3137 3748–4139
storage budget, first line certainly after the resume 1548 1643 2254 1333
storage budget first logged at the 512 floor 1429, in the stall 1383 not logged (2237–2254 to the end) 1403
stall at the view release calls at 1416–1421 (5 presents in that window, 8 before it), recovered; again from 1429, then no present after 1434 for the 220 s until the harness stopped the run from 1557; presents 1560 to 1563 came 691–1071 s apart (about 62 min), then none until the 5400 s limit none, to 1804 1571–1573, then recovered and went on to 1632
sampled textures alive before the stall, logged peak 2083, 8374 3066, 9394 – 2942, 10267
release calls that logged 0 MiB freed 25,404 of 25,414 628,925 of 628,981 0 9,325 of 9,329
peak memory.used (nvidia-smi, with about 640 of desktop) 9906 9906 8631 9906

All four runs print the [gpu] sampled texture cache budget line only on a 10 % change at least 10 s after the last one. The table's rows from "in use outside the two caches" to "sampled textures alive" come from those lines, except run 4's sampled values (2942, 10267), which come from its per-evaluation lines. So they are samples, runs 1 and 2's logged peaks are lower bounds, and "not logged" for run 3 means no line showed it. Run 3 has two such lines at level entry. Over the same presents, run 4's per-evaluation lines give 3704–4150 MiB outside the caches and a storage budget of 1023–1523 MiB.

Run 4's logging:

  • 790 evaluations, with no difference between the two results. They cover the menus, the level load and gameplay.
  • Gaps longer than 20 s: one of 112 s at boot, while the title presented nothing, and three of 20–25 s in gameplay (presents 1477–1494).
  • 246 evaluations had less than 2 GiB of sampled textures alive, the only case where the two reserves differ.
    • In all 246, the room left under either reserve was more than a quarter of the heap budget, so that cap set both results (2249–2289 MiB).
    • All 246 are before gameplay: 234 in the title screens and menus, 12 in the load.
  • The sampled textures crossed 2 GiB during the load, from 1633 to 2495 MiB over three evaluations (presents 1162–1329). From the end of the load on, they stayed above 2 GiB at every evaluation, so the reserve this change drops was 0 there.
  • As in whamre's PPSA05496 case, the quarter-budget cap binds while the sampled textures are below 2 GiB; here that is only before gameplay.

The stalls:

  • In the three runs that stalled, the stall starts as the sampled textures alive grow within a few presents: in run 4, by 7,325 MiB within presents 1571–1573. That growth started from 2942 MiB, above the 2 GiB where the two reserves differ.
  • During that growth the sampled count exceeds the heap's reported usage, so "outside the caches" goes negative (to −1844 MiB in run 4).
  • Over the same presents nearly all release calls log 0 MiB freed. In at least 25,011, 616,519 and 9,232 of them (some lines are cut by other output) the sampled cache was skipped as busy and every cached storage image had pending results, as in run 4's most frequent line (3,158 times): [gpu] texture caches released 0 MiB of the 256 MiB asked for: 0 sampled entries (cache busy, skipped), 0 storage images, 123 with pending results kept.
  • I haven't found what holds the difference in memory outside the caches at level entry.

AI-assisted: yes (Claude Code)

@adriadam10 adriadam10 self-assigned this Oct 19, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 20, 2026

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* docs needs rebase Conflicts with main, rebase needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants