Skip to content

fix(agc): place the presentation source in host memory when device memory runs out - #10807

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
milescoviello:fix/presentation-source-host-fallback
Oct 26, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
milescoviello:fix/presentation-source-host-fallback

Conversation

@milescoviello

@milescoviello milescoviello commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

What

Forspoken (PPSA03984) on main stopped once at 118 s with:

[gpu] driver failure: AGC graphics: vkAllocateMemory presentation source: Vulkan result -2
[videoout] presentation failed: AGC graphics: vkAllocateMemory presentation source: Vulkan result -2
terminate called after throwing an instance of 'std::runtime_error'

That run was on Linux, RTX 4070 Ti (12 GB), NVIDIA 615.71.09, with about 2.3 GB of the card held by other processes on the machine. The title fills the device-local heap (on that card it already reports device-local heap 0 uses 9164 of its 9164 MiB budget for textures), so when the presentation source image was recreated, its vkAllocateMemory returned VK_ERROR_OUT_OF_DEVICE_MEMORY. PresentationScaler::EnsureSourceImage asked for device-local memory only and threw, and the presentation failure terminated the title. Any card whose device-local heap the title fills can hit this.

How

  • PresentationScaler::EnsureSourceImage picks the source's memory type with Context::DeviceMemoryType, as textures do: a device-local type while the heap's budget has room, else a host type. It takes the memory from the buffer pool's image path (AllocateImageMemory). On a refused device-local allocation that path reclaims cached pool memory first, then falls back to host memory through RefusedDeviceMemoryFallback.
  • release() gives the memory back with ReleaseImageMemory instead of vkFreeMemory. The scaler keeps the allocation's size and memory type for that.
  • Nothing else changes: the black image, the blits and the uploads are as before.

Overlap (tools/pr_overlap.py and git merge-tree of each head with this one):

Tested

Linux (Ubuntu 26.04.1, GCC 15.2, Release, RTX 4070 Ti, NVIDIA 615.71.09) unless noted.

  • Red/green: agc_driver_presentation_pass gains a case that gives the scaler a context whose vkAllocateMemory refuses every device-local memory type. It requires the source image to be created, at least one device-local refusal, and the image's memory to come from a host heap. With the test alone on main (7d24e61d28) it fails 3 of 3 with the title's message, AGC graphics: vkAllocateMemory presentation source: Vulkan result -2. With this change it passes 3 of 3, and the pool logs vkAllocateMemory presentation source: memory type 1 is out of device memory for 16384 bytes ...; placed in host memory type 0. The case skips on a device with no host memory type for the image.
  • Focused tests: the 8 tests matching presentation|buffer_pool|^agc_driver_graphics$|display pass 3 of 3. On the Windows 11 VM (GT 1030, driver 582.78, WinLibs GCC 15.2.0 r7) the same 8 pass 3 of 3 on the merge base and on this head. The new case runs there too (memory type 7 refused, placed in host memory type 1).
  • Khronos validation 1.4.363 over the same 8, each arm run as VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 ctest -R "$R" -V -j4: 8 of 8 on main bd8e134 and on this head, no layer-missing line, no VUID- message on either.
  • Forspoken (PPSA03984 01.020.000), warm, 16 GB cap, 250 s runs:
    • main 2e22b67 plus a local diagnostic that only records VK_EXT_device_address_binding_report events: 2 of 15 runs stopped as quoted above (at 118 s and 115 s).
    • This PR: 6 runs. 4 ran to the limit; 2 were stopped by my 16 GB host-memory cap at 218 s and 86 s (the second while a backup of the VM was writing). None stopped on the presentation source. The fallback also didn't trigger in these runs (no presentation source line), so they show nothing regressed, not the fallback in the title.

Checklist

🤖 Generated with Claude Code

…mory runs out

The presentation scaler allocated its source image from device-local
memory with vkAllocateMemory and threw on any failure. With the
device-local heap full (Forspoken fills it on a 12 GB card next to
the host's other GPU users), the next resize of the source failed
with VK_ERROR_OUT_OF_DEVICE_MEMORY, the presentation failed and the
title terminated. The source now picks its memory type through
DeviceMemoryType, like textures, and takes its memory from the buffer
pool's image path, which reclaims cached memory and then falls back
to host memory when device-local memory is refused; it gives the
memory back through ReleaseImageMemory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I reviewed the fallback path and test. The host-memory fallback is exercised by injecting a device-local allocation refusal, and the test verifies both the refusal and resulting heap class. I did not find an additional correctness blocker in the changed ownership path: the source allocation records its size/type and returns through ReleaseImageMemory. The hosted workflow has conventions/intake/report green; platform product jobs are skipped. No separate local Vulkan rerun was available here.

@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 9508 core/libs/prx/libSceAgcDriver/tests/PresentationPass.cpp L584

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 7642 core/libs/prx/libSceAgcDriver/tests/PresentationPass.cpp
PR 10193 core/libs/prx/libSceAgcDriver/tests/PresentationPass.cpp

@milescoviello

Copy link
Copy Markdown
Contributor Author

@MarcZX10 thank you for the review. The Vulkan runs are the ones under Tested: the new case fails 3 of 3 on main with the title's exact message and passes 3 of 3 here, the 8 tests run clean under VK_LAYER_KHRONOS_validation on both sides (no new VUID), and the Windows VM (GT 1030) passes 8 of 8, 3 times, where the new case refuses memory type 7 and the source lands in host memory type 1.

AI-assisted: yes (Claude Code)

@MarcZX10

Copy link
Copy Markdown
Contributor

One overlap follow-up after manually checking the bot’s full report: #7642 and #10193 are also open and both add the same GpuHeapEnabled() / OpenVulkanTestDevice() block after residentPresentTests(device->GetContext()) in PresentationPass.cpp::main. The description lists #9508, #10043 and #10507 but omits these two. The edits can be retained together with this PR’s new fallback test, but please add both to the overlap note so the merge-time resolution is explicit.

@milescoviello

Copy link
Copy Markdown
Contributor Author

@MarcZX10 thanks for checking the full report. Confirmed: #7642 and #10193 both add that GpuHeapEnabled() / OpenVulkanTestDevice() block right after residentPresentTests(device->GetContext()), which is where this PR adds sourceImageOutsideDeviceMemoryTests(device->GetContext()). The tool missed #10193 because it conflicts with main. I've added both to the overlap note: keeping this PR's call and their block resolves either order.

AI-assisted: yes (Claude Code)

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

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for confirming that #7642 and #10193 add the same GpuHeapEnabled() / OpenVulkanTestDevice() fixture block. Both PRs are still open, while this PR checklist remains Depends on: none. CONTRIBUTING.md requires the authors to agree which overlapping PR goes first and declare it with Depends on. Please coordinate with #7642 and #10193 and update the checklist (and have the other affected checklist(s) reflect the agreed order). I have not modified or rebased any branch. The hosted policy checks currently pass; product jobs are skipped.

@milescoviello

Copy link
Copy Markdown
Contributor Author

@MarcZX10 I've asked both authors, on #7642 and #10193. I proposed this PR goes first: #7642 is a draft with changes requested, #10193 depends on #7642 and needs a rebase, and it's a merge order with no code dependency either way. The checklist now records that proposal and that they haven't answered yet. I'll update it, and rebase if they'd rather go first, once they reply.

AI-assisted: yes (Claude Code)

@milescoviello

Copy link
Copy Markdown
Contributor Author

Thanks for merging, @oneandonlydean, and @MarcZX10 for the review.

AI-assisted: yes (Claude Code)

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

Labels

agc libSceAgc*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants