Repository navigation
fix(agc): place device memory in host memory when the driver refuses a device-local allocation - #5987
Conversation
CnRJay
left a comment
There was a problem hiding this comment.
Falling back on VK_ERROR_OUT_OF_DEVICE_MEMORY instead of stopping is reasonable for buffers. They are pooled and short-lived, and a host-placed one leaves the pool on release, so the cost goes away with it.
For images it is different, because a host-placed image keeps its placement until it is destroyed. If a refusal hits during a short spike, a long-lived render target or depth target lands in system memory and stays there for the rest of the session. Every draw that writes or tests against it then goes over PCIe, at a fraction of VRAM bandwidth. On a 4 GB laptop part that is a large, permanent frame-time loss that no log line ties to the spike. Render targets, depth targets, multisampled targets and attachment proxies are the worst candidates for host memory. Sampled textures and block/depth copies are much more tolerable.
Some suggestions:
- Reclaim before falling back for images too. The buffer path reclaims the pool and retries device-local first, but the image paths go straight to host memory. Evicting from the sampled and storage image caches (and the buffer pool) and retrying device-local once would avoid most of these placements, since the caches are usually what fills the heap.
- Keep attachments device-local. For render, depth and multisampled targets, it may be better to keep the current failure, or to fall back only after a reclaim fails, and log that as the hard limit.
- Show the cost. The PR says about 2 GB ended up in host memory in Beast of Reincarnation but gives no frame times. The summary lines for a stretch of that run with and without placements (or with the cache budgets reduced so
maindoesn't hit the refusal) would show what the title pays.
This is mostly a Linux/NVIDIA problem: on Windows, WDDM demotes allocations itself instead of refusing them. A sentence in the description saying so would help reviewers on the other platform.
|
#6174 touches the same allocation sites (it releases the texture caches before the pool's retry). I will put it behind this one and route its texture and storage image allocations through |
|
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.
|
|
Point 4 (textures and storage textures going through the pool reclaim and a device-local retry first) is what #6174 does: A possible order: #6174 first (it merges cleanly with current |
09c712d to
4afec57
Compare
|
Thanks @adriadam10 for the review, and @CnRJay, @ee6aba54, @SleglJan and @parl0v for the runs, data and corrections on this PR; they made the remaining work much clearer. Sorry for the delay in getting back to this. Rebased onto
AI-assisted: yes |
4afec57 to
b3187ec
Compare
|
Rebased onto main 2626e5e, which carries #6174 (head b3187ec). Host placement is now the last step of
A refusal that the reclaim can satisfy never reaches host placement, which covers the textures and storage textures point from @adriadam10's review. The new commit also routes every image allocation through one helper, Tests, Release build, Linux 7.2.9:
New
It fails when the cache release is skipped before host placement. Correction to my earlier reply: the 007 First Light data came from @SleglJan, not @CnRJay. Sorry about the mix-up. AI-assisted: yes |
b3187ec to
9f98c7c
Compare
|
Rebased onto main 9f25d0b (head 9f98c7c). A commit merged since the last rebase added a repacked copy in All 700 Beast of Reincarnation ran 900 s on the previous head 8bb3e07 with no stop and no fault, at the same frame times as @adriadam10, the four points from your review should be covered now; could you take another look? AI-assisted: yes |
|
@adriadam10 when you have a moment, could you start the full build for this head (9f98c7c)? Only collaborators can run it: Locally all 700 Thanks again to @SleglJan, @parl0v, @CnRJay and @ee6aba54 for the runs, data and reviews that shaped this PR. |
…a device-local allocation The budget check in DeviceMemoryType trusts VK_EXT_memory_budget, which lags behind the driver: with the device-local heap nearly full the budget can still report room, the allocation fails with VK_ERROR_OUT_OF_DEVICE_MEMORY, and the title stops. Device buffers (after the pool's reclaim and retry), textures and storage textures now retry in a host memory type allowed by their requirements.
…refused device memory Depth targets, depth plane copies, multisampled and render targets, block and depth copies and attachment proxies allocated device-local memory without the fallback, so a full device-local heap still stopped the title at them.
…t memory Render, depth and multisampled targets and attachment proxies that the driver refuses device memory now go through the buffer pool's recovery: the pool releases its idle allocations and the attachment is retried in device-local memory. Only a second refusal places it in host memory, and that placement is logged with the bytes reclaimed.
… memory Textures, storage textures, block, depth and depth plane copies and attachments now share one allocation path: the pool trims its idle tiers, releases the texture caches, retries device-local memory and places the image in host memory only when that retry is refused too. Block and depth plane copies no longer fall back on their first refusal.
…fused reclaim Repacked copies and depth stencil copies allocated device-local memory directly and stopped the title on a refusal; they now take the same allocation path as the other images. Device buffers choose their host fallback type the same way, the host placement line carries the cumulative MiB, and the debt file records that a placed image stays in host memory for its lifetime.
9f98c7c to
b8692a7
Compare
What
DeviceMemoryTypeplaces a device-local allocation in host memory only whenVK_EXT_memory_budgetreports the heap as full. The report lags the driver. On a 4 GB RTX 3050 Laptop the heap was at 3684 of a 3685 MiB budget and the budget still showed room. The NVIDIA driver refused the allocation withVK_ERROR_OUT_OF_DEVICE_MEMORY, and the title stopped:With device buffers covered, the next run stopped the same way at
vkAllocateMemory depth target.This is mostly a Linux/NVIDIA problem. On Windows, WDDM demotes allocations to system memory itself instead of refusing them, so these paths should rarely run there. I have not tested Windows.
Host placement is the last step of
BufferPool::AllocateMemory, after the recovery that is already onmain:A refusal the reclaim can satisfy never reaches host placement.
Changes:
fix: a device buffer still refused after steps 1 and 2 is allocated in host memory. It is marked host-placed, so the device pool does not keep it after release.
fix: every image allocation goes through the same path (
AllocateImageMemory). This covers textures, storage textures, block, repacked, depth, depth plane and stencil copies, render, depth and multisampled targets, and attachment proxies. Several of these sites calledvkAllocateMemorydirectly and stopped the title on the first refusal: the render, depth and multisampled targets, the attachment proxies, and the block, repacked, depth plane and stencil copies. The 1x1 denormal clear probe is left as it is.Only
VK_ERROR_OUT_OF_DEVICE_MEMORYfrom a device-local type falls back. Other results, and requirements that allow no host type, fail as before.Each placement is logged with the bytes trimmed and released and the cumulative MiB, rate-limited to powers of two like the existing budget placement.
docs:
docs/dev/debt/libSceAgcDriver.mdrecords that a placed image keeps its placement until it is destroyed and is never moved back, so it is slower (PCIe) for its lifetime.test:
agc_buffer_pool_testscovers these cases:RefusedDevicePlacement: a device buffer refused within the reported budget must land in host memory and leave the pool on release; a context with no host type must still fail.AttachmentReclaim: an attachment refused once must reclaim the pool and stay device-local; refused twice, it must land in host memory.ImageCacheReclaim: a texture refused once must release the texture caches and stay device-local; refused again, it must land in host memory.Each fails without its fix.
agc_device_memory_fallback_testscovers the choice of fallback type: a refused device-local type, a refused host type, the type mask, and an unknown type.Overlaps #4472 in
Context.hppandtests/BufferPool.cpp. That PR stops pooling released device buffers over the budget; this one handles a refusal the budget did not predict. Whichever merges second needs a manual merge there.pr_overlapalso reports line overlaps with #7119 (tests/BufferPool.cpp) and #6764 and #7256 (DepthSurface.cpp). #6553 is stacked on this PR.Tested
Rebased onto
main9f25d0b (head 9f98c7c). Linux (CachyOS, kernel 7.2.9), Release build:agc_buffer_pool,_small_device_shared,_small_device_no_stagingagc_device_memory_fallbackagc_driver_graphicsagc_driver_covered_centroid_stateagc_driver_tessellationagc_driver_depth_storage_reuseagc_*ctestsBeast of Reincarnation (PPSA29343), RTX 3050 Laptop 4 GB, 900 s per run with the same scripted pad input. These runs used head 8bb3e07, which lacks only the repacked/stencil copy routing, the MiB counter and the
DeviceBuffercleanup:main(bd58561), two warm runs: FPS / p50Both runs reached 900 s with no stop, no fault and no device lost. Peak VRAM was 3480 MiB, the same as
main. The driver refused no allocation in these runs, so the host placement path did not run there: they show the rebase costs nothing, not what a placement costs. The run with a cold shader cache (2617 misses) is left out of the table.Earlier data, before #6174 merged (head 4afec57 and older):
mainstopped at the device buffer refusal after about 15 minutes. With only the buffer fallback, the next run stopped at the depth target refusal. With this PR the runs stayed up for the full 15–24 minutes measured.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)