Skip to content

fix(agc): keep heap mirror slabs within device memory - #15295

Open
Prophet069 wants to merge 4 commits into
boykopovar:mainfrom
Prophet069:fix/agc-heap-mirror-device-memory
Open

Prophet069 wants to merge 4 commits into
boykopovar:mainfrom
Prophet069:fix/agc-heap-mirror-device-memory

Conversation

@Prophet069

Copy link
Copy Markdown
Contributor

What

A heap mirror slab refused by a full device heap stopped the title. The slab now falls back to host memory, as buffers and images already do, and a refusal lowers the heap mirror budget, evicts down to it and retries; a failed bind gives back the blocks it took.

Slabs are refused before the device heap is exhausted: when all live slabs would pass APS5_HEAP_MIRROR_MIB or when the device heap would keep less than 256 MiB of its reported budget, so the shrink and sweep path runs before Windows starts paging device memory. A budget lowered by refusals is a soft target that a mirror may pass up to the configured budget when nothing can be evicted, and the headroom refusal is not repeated within two seconds on the same thread.

Blocks are taken from the fullest slab, so partly used slabs drain and are freed instead of every slab staying partly used after evictions.

Nonzero scans of a heap remember each 64 KiB block with its write generation and read only blocks written since, instead of reading the whole multi-GiB heap again on every rebuild.

Tested

NVIDIA GeForce RTX 3080 Laptop GPU, driver 616.64, Windows 10 Pro 22H2 (19045.6466).
Focused tests on main (9090972) and on this branch with VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, ANYPS5_NO_SHADER_CACHE=1, ANYPS5_REQUIRE_VULKAN=1, ctest -V -R "heap_mirror|^agc_driver_graphics$|^agc_driver_key_miss$|^agc_driver_bda_device$|^agc_driver_recorder$|native_multisample_depth|depth_storage|^agc_host_import_retire$|^agc_host_import_sparse$":

  • main: 17 of 20 selected tests passed (agc_host_import_sparse skips on this machine; the three agc_driver_depth_storage_* tests were not built in this run); validation layer loaded in both logs; VUID- errors: none.
  • this branch: 17 of 20 selected tests passed (agc_host_import_sparse skips on this machine; the three agc_driver_depth_storage_* tests were not built in this run), the same as main; VUID- errors: none.
  • Death Stranding 2 (PPSA02015), local integration branch with the GPU heap path pull requests: before this series the title stopped on a heap mirror slab allocation (device heap 8461/7235 MiB); with it the slab moves to host memory or the budget shrinks, and the title reached its main menu and game world.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: PPSA02015 Death Stranding 2: On the Beach (PPSA02015) #5424
  • GPUs tested: NVIDIA GeForce RTX 3080 Laptop GPU, driver 616.64, Windows 10 Pro 22H2 (19045.6466)
  • AI-assisted: yes

🤖 Generated with Claude Code

A heap mirror slab refused by a full device heap stopped the title. The slab
now falls back to host memory, as buffers and images already do, and a
refusal lowers the heap mirror budget, evicts down to it and retries; a
failed bind gives back the blocks it took.

A slab is refused before the device heap is exhausted: when all live slabs
would pass APS5_HEAP_MIRROR_MIB or when the device heap would keep less than
256 MiB of its reported budget, so the shrink and sweep path runs before
Windows pages device memory. A budget lowered by refusals is a soft target
a mirror may pass up to the configured budget when nothing can be evicted,
and the headroom refusal is not repeated within two seconds on one thread.

Blocks are taken from the fullest slab, so partly used slabs drain and are
freed. Nonzero scans remember each 64 KiB block with its write generation
and read only blocks written since.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Nov 2, 2026

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

Reviewed — changes requested

The merged tree builds and the heap mirror and resident tests pass. However, a new pre-allocation refusal can now stop a title on a nearly full device heap when the heap mirrors hold 256 MiB or less, where main would have tried the allocation. None of the new paths has a test.

What I ran

Head 1743177ae4af40502f26cd384d2376d555074681, main 5cb015caaaaf83f8ee431414ee6a457b5b307d49, merged tree 7895dfbc86c6627273ff29b2d3bbc7eed5af335f.

git diff --check 5cb015caaaaf83f8ee431414ee6a457b5b307d49...1743177ae4af40502f26cd384d2376d555074681
python3 tools/check_conventions.py --base 5cb015caaaaf83f8ee431414ee6a457b5b307d49
ctest --test-dir build --output-on-failure -R "^agc_driver_image_sample_lod_clamp_shader_lane$|^unwrap_self_UnwrapTest_test_new_output_inherits"
# the PR's 4 production files restored from main:
ninja -C build -j32 -l32 agc_driver_image_sample_lod_clamp_tests
ctest --test-dir build --output-on-failure -R "^agc_driver_image_sample_lod_clamp_shader_lane$"
# merged files restored, git status --porcelain empty:
ninja -C build -j32 -l32
ctest --test-dir build -j8 --test-load 32 --output-on-failure -R "heap_mirror|resident|host_import|partially_resident"

Results

Check Result
git diff --check clean
check_conventions.py All conventions checks passed
Configure, build, libs (script) ok
Script test run 2178 ran; 2 not on the baseline's failure list, neither caused by the PR (see below); 6 known failures that also fail on main
agc_driver_image_sample_lod_clamp_shader_lane fails with the PR, and still fails after the PR's production files are restored from main 5cb015ca. The baseline (096c4557) is older, so this failure comes from main, not this PR.
unwrap_self_..._destination_directory_group fails at os.chown on a temporary directory (EINVAL). This is a tools/ Python test the PR doesn't touch, and the failure comes from the sandbox.
Heap mirror, resident and host-import tests (27) 26 passed, 1 skipped (agc_host_import_sparse, an expected skip)
Validation layer (script, 759 tests) layer loaded in 758, missing in none; new VUID- lines: 0
lod_clamp failure, same output with the PR and on main
AGC graphics: image_sample_lz through the S# that holds the LOD at 0 with T# MIN_LOD 768 over BASE_LEVEL 0 with a second S# of words 1 0 and 2 5242880, and a second T# of MIN_LOD 0 and LAST_LEVEL 0: thread 0 component 1 is 0.000000, expected 0.062500

Negative control

There is none, because the PR adds and changes no tests. I used the restore-from-main procedure only to clear the PR of the lod_clamp failure (see Results).

Findings

Blocking

  1. The headroom refusal stops the title when the heap mirrors hold 256 MiB or less. In core/libs/prx/libSceAgcDriver/Graphics/src/ResidentMirror.cpp:153, Take() calls refuseSlab() before any vkAllocateMemory. At ResidentMirror.cpp:188, refuseSlab() throws when the device heap would keep less than 256 MiB of its reported budget. The only recovery is shrinkHeapMirrorBudget, and it returns false when held <= step (256 MiB) (core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp:2823). Both callers then rethrow the error wrapped in heapMirrorError:

    • growResident at GuestBufferMemory.cpp:2852-2866;
    • acquireMirror at GuestBufferMemory.cpp:3396-3407.

    So when the device heap is close to its budget for reasons other than the heap mirrors (textures, render targets), the first slab of a small mirror is refused with nothing to evict, and the title stops. On main the same slab would go to vkAllocateMemory. With this PR it would even fall back to host memory, but that fallback is never reached. This is most likely on GPUs with little VRAM, which this PR is meant to help.

    The APS5_HEAP_MIRROR_MIB refusal at ResidentMirror.cpp:184 hits the same dead end. It counts whole 16 MiB slabs, fragmentation included, while state.heapBytes counts only accounted blocks. A small configured budget can therefore refuse a slab while held <= 256 MiB.

    Suggested fix: treat a refusal as a request to shrink and sweep, not as a final error. When shrinkHeapMirrorBudget can't lower anything, or the retries run out, go on to the real allocation and its host-memory fallback. Add a test that sets a heap budget the refusal triggers on (or a test hook for ReportedHeap) with fewer than 256 MiB of mirrors, and checks that the mirror is still created.

  2. None of the new behaviour has a test. The PR changes several things:

    • slab refusal at the configured budget and at device headroom;
    • budget shrink and retry;
    • the host-memory fallback for slabs;
    • returning blocks when Bind fails partway;
    • taking blocks from the fullest slab;
    • the generation-keyed nonzero-scan cache.

    It adds no test for any of them. All 27 existing heap mirror, resident and host-import tests pass with and without the change, so none of them covers these paths. Finding 1 shows a regression these paths can hide. At least the refusal/shrink/retry path and the Bind give-back need a test that fails on main (SetHeapMirrorBudget already exists for that). A scan-cache test should write a block after a first scan and check that the next scan sees it.

Non-blocking

  1. The scan cache trusts UnchangedSince even for content the write watch may not record. At ResidentMirror.cpp:401, a cached "zero" result is reused while GuestMemory::UnchangedSince holds. On Windows, possiblyNonzero returns the whole range as readable, so the result there depends entirely on the write watch. docs/dev/debt/libc.md describes direct-memory pages that are kept in a private reservation while unmapped and mapped again later, and pages mapped at a second address that become views tracked by write protection. Content can then appear at a guest address without a CPU write there. If no stamp covers that case, the mirror leaves a nonzero block unbacked, and the GPU reads zeros. Please confirm that guest maps and alias writes stamp the tracker, or use CollectCommittedWrites/UnchangedCommittedSince (which also check refusals). If neither covers it, record the gap in docs/dev/debt/libSceAgcDriver.md.
  2. The scan cache can use a lot of host memory. At ResidentMirror.cpp:382-383, the map grows to 2^22 entries before it is cleared. That is a few hundred MiB of unordered_map nodes and buckets for a process-lifetime cache, and the whole cache is then dropped at once. A per-heap bitmap of generations, or a smaller limit, would bound this better.
  3. The lowered budget never recovers. heapMirrorMemoryLimit (GuestBufferMemory.cpp:2714) is only ever set from the current heapBytes minus 256 MiB, never back toward the configured budget. A single transient headroom refusal, for example during a loading spike, caps the heap mirrors for the rest of the run. This is mitigated by the "soft target", but it is still surprising.
  4. Refusals are recognised by their error text. shrinkHeapMirrorBudget matches "of a heap mirror slab" in error.what() (GuestBufferMemory.cpp:2816). A dedicated exception type would be more robust to message edits.
  5. New tuning constants aren't recorded as technical debt. The 256 MiB reserve, the 2 s per-thread throttle, the 8 retries, 256 MiB shrink step and the 2^22-entry cache limit are unmeasured choices. docs/dev/debt/libSceAgcDriver.md already lists similar choices for the heap mirror reference storage ("unmeasured choices"); these belong there too.

Not verified

  • I couldn't reproduce Finding 1 at runtime: lavapipe reports a 64 GiB budget, so the headroom refusal never triggers here. The finding comes from reading the code paths cited above.
  • The host-memory fallback for slabs, and whether sparse binding of host-visible memory works on the drivers the PR targets (NVIDIA on Windows).
  • The Death Stranding 2 (PPSA02015) result and the Windows/NVIDIA test run in the description. No title was run.
  • Whether guest remaps and alias writes stamp the write tracker (Non-blocking 1).

To get this to a good state

  • Make a slab refusal that can't shrink anything go on to the real allocation (with its host fallback) instead of throwing.
  • Add tests that fail on main for the refusal/shrink/retry path, the Bind give-back on failure, and the scan cache seeing a later write.
  • Confirm or document how the scan cache stays sound for remapped or aliased guest memory.
  • Record the new constants in the debt file.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 1743177 merged onto main 9090972 · tested 2026-11-03 00:32 UTC · 34 min

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

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Hi Prophet069,

Thanks for the heap mirror slab work. Lowering the budget on a refused slab, evicting and retrying is a sensible way to keep a full device heap from stopping the title, and the NVIDIA run at this head shows no new failures.

Two requests before it can go in:

  1. None of the new paths has a test. Please add focused cases in core/libs/prx/libSceAgcDriver/tests/execution/ResidentHeapMirror.cpp, which already uses the test budget hook. One should force a slab refusal through the APS5_HEAP_MIRROR_MIB limit (that reason is deterministic and needs no full device heap) and check that the budget is lowered, mirrors are evicted and the retry succeeds or fails cleanly. Another should cover the bind give-back. A third is for the nonzero-block cache in GuestNonzeroBlocks: write into a block that an earlier scan cached as zero, then check that the next scan reports it. That cache is the part that could silently upload stale zeros if a write path ever bypasses the write tracking.
  2. shrinkHeapMirrorBudget recognises a refused slab by searching the exception text for "of a heap mirror slab", so rewording either message would silently turn the retry off. A small dedicated exception type, thrown from refuseSlab and from the allocation failure, would keep the two in step.

@github-actions github-actions Bot added the agc libSceAgc* label Nov 3, 2026
@oneandonlydean oneandonlydean self-assigned this Nov 3, 2026
…ost fallback

A slab refused for headroom or for APS5_HEAP_MIRROR_MIB while the heap mirrors
held 256 MiB or less stopped the title: the budget shrink has nothing to evict
there. Refuse a slab only while more than that is held, so the allocation and
its host-memory fallback run otherwise. Refusals and failed slab allocations
throw HeapMirrorSlabRefused instead of being matched by their text, a budget
lowered by a refusal recovers after 30 s, and the headroom check uses
HeapReport::Fits. A test reports the device heap full and checks that small
mirrors are still made.

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

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Addressed in 89e3292:

  • Blocking 1: a slab is refused (for headroom or APS5_HEAP_MIRROR_MIB) only while the live slabs hold more than 256 MiB, the step the budget shrink can evict. Below that the real allocation and its host-memory fallback run.
  • Tests: FullDeviceHeapTests in tests/execution/ResidentHeapMirror.cpp reports the device heap full through a test hook (SetDeviceHeapFullForTests) and builds a heap mirror that needs a new slab while the mirrors hold 15 MiB. Negative control: with the early return removed it fails with vkAllocateMemory of a heap mirror slab refused: its device heap would keep less than 256 MiB of its reported budget; with the change it passes.
  • Refusals and failed slab allocations now throw HeapMirrorSlabRefused; shrinkHeapMirrorBudget checks the type instead of the message text.
  • A budget lowered by a refusal recovers after 30 s.
  • The headroom check uses HeapReport::Fits.

Re-tested on Windows 10, NVIDIA RTX 3080 Laptop (616.64) with the validation layer: the 9 heap_mirror tests pass, no VUID-; check_conventions.py passes.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 89e3292 merged onto main f6232fc · tested 2026-11-03 05:50 UTC · 32 min

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

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

XairRrR
XairRrR previously requested changes Nov 3, 2026

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

Re-checked — changes still requested

Commit 89e3292072 now lets a refusal fall through to the real allocation while the live slabs hold 256 MiB or less. It replaces text matching with HeapMirrorSlabRefused, lets a lowered budget recover after 30 s, and adds one full-device-heap test, whose negative control fails as it should. Three paths still have no test: the shrink/evict/retry path, the Bind give-back and the scan cache. A refusal can also still stop the title when fragmented slabs keep the slab count above the gate while the accounted heap mirror bytes are below the shrink step.

What changed since my last review

  • New commit 89e3292072 ("let a small heap mirror reach the slab allocation and its host fallback"). The first commit 1743177ae4 is unchanged.
    • refuseSlab() returns early while the live slabs hold 256 MiB or less.
    • Refusals and slab allocation failures throw HeapMirrorSlabRefused.
    • heapMirrorBudget() restores a lowered limit after 30 s.
    • The headroom check uses the new Context::HeapReport::Fits.
    • New test hook SetDeviceHeapFullForTests and test FullDeviceHeapTests in tests/execution/ResidentHeapMirror.cpp.
  • The author replied in the discussion with a negative control and a Windows/RTX 3080 Laptop re-run. I accept that run as author-reported.

What I ran

Head 89e3292072015ff5710305f6fd2e097545a0bb50, main d1ae628bc2f62459cce1e9872ba5632c938c71f2, merged tree e656cfa05a1d3ab6e971f1f891ce43a02603f091.

git diff --check d1ae628bc2f62459cce1e9872ba5632c938c71f2...89e3292072015ff5710305f6fd2e097545a0bb50
python3 tools/check_conventions.py --base d1ae628bc2f62459cce1e9872ba5632c938c71f2
ctest --test-dir build -j8 --test-load 32 --output-on-failure -R "heap_mirror|resident"
ctest --test-dir build -V -R "^agc_driver_resident_heap_mirror$"
# negative control: in ResidentMirror.cpp:188, `if (live <= Reserve) return;` replaced by `(void)live;`
ninja -C build -j32 -l32 agc_driver_resident_heap_mirror_tests
ctest --test-dir build --output-on-failure -R "^agc_driver_resident_heap_mirror$"
build/tests/agc_driver_resident_heap_mirror_tests
# merged file restored from e656cfa05a, git status --porcelain empty
ninja -C build -j32 -l32 agc_driver_resident_heap_mirror_tests
ctest --test-dir build -R "^agc_driver_resident_heap_mirror$"
Check Result
git diff --check clean
check_conventions.py All conventions checks passed
Configure, build, libs (script) ok
Script test run 2142 ran, 0 new failures, 0 timeouts; 7 known failures that also fail on main (baseline = main d1ae628b)
Validation layer (script, 767 tests) layer loaded in 766, missing in none; new VUID- lines: 0
Heap mirror and resident tests (16) all passed; agc_driver_resident_heap_mirror 2.1 s. The sparse branch runs on lavapipe (no skip message), so FullDeviceHeapTests is exercised.

Negative control

The new test calls SetDeviceHeapFullForTests, which main does not have, so it cannot be built against main's production files. Instead I removed only the new early return in refuseSlab (ResidentMirror.cpp:188). The test then fails with the same message the author reported:

output
AGC graphics: heap mirror of 0x7f2aa9ff0000+0x2000000: vkAllocateMemory of a heap mirror slab refused: its device heap would keep less than 256 MiB of its reported budget; heap mirrors hold 15 MiB of 24576 MiB (APS5_HEAP_MIRROR_MIB)

With the merged file restored, the test passes and the worktree matches the merged tree.

Previous findings

  • Blocking 1 (headroom refusal stops the title with ≤ 256 MiB of mirrors): resolved for the case I described. A refusal is now skipped while the live slabs hold 256 MiB or less, and the negative control above shows the test covers it. A related gap remains; see new Blocking 1.
  • Blocking 2 (no tests for the new paths): still open. One test was added, for the small-mirror headroom case. Still untested: the refusal → shrinkHeapMirrorBudget → sweep → retry path, the Bind give-back when Take() or submit fails partway, the host-memory fallback for slabs, and the generation-keyed cache in GuestNonzeroBlocks. oneandonlydean's review asked for the same three cases. The APS5_HEAP_MIRROR_MIB refusal can be forced deterministically with SetHeapMirrorBudget: build more than 256 MiB of slabs (or lower the gate for tests), then check the "budget lowered" path evicts and the retry succeeds.
  • Non-blocking 1 (scan cache trusts UnchangedSince for remapped or aliased content): still open. The code is unchanged (ResidentMirror.cpp:409), with no reply and no debt entry.
  • Non-blocking 2 (scan cache up to 2^22 map entries): still open (ResidentMirror.cpp:419).
  • Non-blocking 3 (lowered budget never recovers): resolved. heapMirrorBudget() restores the limit 30 s after the last lowering (GuestBufferMemory.cpp:2734).
  • Non-blocking 4 (refusal matched by error text): resolved. shrinkHeapMirrorBudget uses dynamic_cast<const HeapMirrorSlabRefused*> (GuestBufferMemory.cpp:2834).
  • Non-blocking 5 (tuning constants not in debt): still open. The PR now also adds a 30 s recovery; no file under docs/dev/debt/ changes.

New findings

Blocking

  1. The refusal gate and the shrink gate count different things, so a refusal can still stop the title.

    • refuseSlab() skips the refusal on live slab memory (Slabs().slabs * 16 MiB, ResidentMirror.cpp:187-188).
    • shrinkHeapMirrorBudget() gives up on accounted heap mirror bytes (state.heapBytes <= 256 MiB, GuestBufferMemory.cpp:2841).

    A slab is freed only when every block in it is free (ResidentMirror.cpp:175). After evictions, surviving mirrors keep blocks scattered across many slabs, so the live slabs can stay well above 256 MiB while heapBytes is far below it. That is exactly the state the shrink-and-sweep path leaves behind.

    What happens then on a device heap near its budget:

    • The headroom refusal fires.
    • shrinkHeapMirrorBudget returns false.
    • Both callers (GuestBufferMemory.cpp:2872 and :3416) rethrow without a retry, so the title stops.

    main would have attempted the allocation, and with this PR that allocation even has a host fallback. The 2 s throttle (ResidentMirror.cpp:193-198) does not help, because the first refusal is already final. The APS5_HEAP_MIRROR_MIB refusal has the same mismatch.

    Suggested fix: when shrinkHeapMirrorBudget returns false for a HeapMirrorSlabRefused that came from refuseSlab (not from a failed allocation), retry once with the refusal bypassed, so the real allocation and its fallback run. Alternatively, gate refuseSlab and the shrink on the same counter. Add a test that fragments slabs past the gate (or lowers the gate through a hook) with a full device heap, and checks the mirror is still made.

Non-blocking

  1. The shrank report prints whenever the last slab is freed. count % 32 == 0 is true for count == 0 (ResidentMirror.cpp:181), so each mirror whose release empties the slab list logs a line. The test log shows several [heap-mirror] shrank: slabs 0 (0 MiB) ... lines in a row. Suggested fix: use count != 0 && count % 32 == 0.
  2. The recovery returns the budget to its full value in one step. After 30 s, the limit goes straight back to the configured budget (GuestBufferMemory.cpp:2734). Under sustained pressure, this can cycle every 30 s: the mirrors grow, a refusal fires, the budget shrinks and the sweep runs again. Raising the limit in steps, or only while the heap reports headroom, would avoid the cycle. This is not shown on hardware; it comes from reading the code.

Not verified

  • The new Blocking 1 at runtime. Lavapipe reports a large heap budget, and the test hook only covers the small-mirror case; the finding comes from reading the cited code.
  • The host-memory fallback for slabs, and sparse binding of host-visible memory on NVIDIA/Windows.
  • The Windows/RTX 3080 Laptop re-run and the PPSA02015 result (author-reported; no title was run).
  • Whether guest remaps and alias writes stamp the write tracker (previous Non-blocking 1).

To get this to a good state

  • Make a refusal that shrinkHeapMirrorBudget cannot act on go on to the real allocation, whatever the slab fragmentation.
  • Add tests for the refusal → shrink → sweep → retry path, the Bind give-back, and the scan cache seeing a write made after an earlier scan cached the block as zero.
  • Confirm or record in docs/dev/debt/libSceAgcDriver.md how the scan cache stays sound for remapped or aliased guest memory, and record the new constants (256 MiB reserve and step, 2 s throttle, 8 retries, 30 s recovery, 2^22-entry cache limit).

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head 89e3292 merged onto main 7feb37e · tested 2026-11-03 07:06 UTC · 33 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,666 passed · 9 failed (0 new, 8 also fail on main, 1 unstable on main) · 58 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,733 of 3,743 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (934 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.3 s, ⚠️ agc_driver_image_sample_unnormalized 49.1 s, ⚠️ public_headers 43.7 s, ⚠️ agc_driver_lazy_resolved_abi 37.8 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 5 of the last 7 main runs
Failing on main too (8, not counted against this PR)
agc_driver_image_table
agc_driver_native_depth_bda_clear
agc_driver_native_multisample_depth
agc_idle_publish_wait
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Validation IDs already present on main (4 IDs in 525 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (525 tests): agc_driver, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, agc_driver_bda_span_reads, and 517 more
VUID-vkCmdCopyImage-srcImage-01548 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth, agc_driver_stencil_null_feedback
VUID-vkCmdCopyImage-srcImage-01551 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth, agc_driver_stencil_null_feedback
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (1 test): agc_driver_srgb8_color_target
Skipped (58)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_float16_unused_nan_payloads: skipped, the device has no native f16 conversions with RTE rounding and kept f16 denormals
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not co
Reproduce
git fetch origin 89e3292072015ff5710305f6fd2e097545a0bb50 7feb37ee3e30e10139b483fb4df689d5b5b69ab8
git checkout 7feb37ee3e30e10139b483fb4df689d5b5b69ab8 && git merge --no-ff 89e3292072015ff5710305f6fd2e097545a0bb50
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@sktharunbalaji13-cmd

Copy link
Copy Markdown
Contributor

This PR's failure mode, seen in a real run: Ghost of Tsushima DC (PPSA02225) on a 16 GB RAM / 8 GB VRAM Windows laptop (RTX 4060 Laptop), main b11c7bf + #12840 + #12792, aborted at 135 s with

heap mirror of 0x1200000000+0x125800000: vkAllocateMemory of a heap mirror slab: allocation=16777216 memoryType=1 reclaimed=0 released=4194304 initialResult=-2 attempts=2; heap 0 7941/7188 MiB; heap 1 4185/7180 MiB: Vulkan result -2; heap mirrors hold 1051 MiB of 24576 MiB (APS5_HEAP_MIRROR_MIB)

The second part of this PR, nonzero scans that read only blocks written since the last scan, also matches what we profiled there. Before each scan on Windows, possiblyNonzero returns the whole range (no pagemap equivalent), so GuestBytesZero reads multi-GB heaps. With RAM full, that pages them in from disk.

Context and screenshots: #5166 (comment). I'll test this PR (together with #15296) on the same machine and report back.

@sktharunbalaji13-cmd

Copy link
Copy Markdown
Contributor

Results from Ghost of Tsushima DC (PPSA02225) on the 16 GB RAM / 8 GB VRAM laptop (RTX 4060 Laptop, driver 617.42).

This PR was applied onto main b11c7bf + #12840 + #12792, the tree that reached the logo (it applied cleanly except the HeapReport hunk, which main already has). Every run uses the same relinked executable and environment (APS5_SKIP_REFUSED_DRAWS=1 plus an AudioOut2 trace), 150 s, with a screenshot every 2 s. The conditions were the same for all of them: Claude open, Defender real-time protection on.

main + #12840 + #12792, run 1 same, run 2 + #15295 main + #15296 (no #12840)
free RAM at launch 8.1 GB 10.0 GB 10.9 GB 10.8 GB
first freeze after the intro soundtrack starts 18.1 s 14.0 s 11.9 s 12.0 s
peak process private memory 25.6 GB 24.6 GB 25.1 GB 21.8 GB
lowest free RAM 3 MB 54 MB 9 MB 99 MB
VRAM wall no abort in 150 s abort at ~120 s: vkAllocateMemory of a heap mirror slab ... -2; heap 0 7852/7188 MiB no abort; device memory refused a heap mirror slab; heap mirror budget lowered x9 no abort; the same message x10
intro reached card 36-38 s card 28-34 s card 26-34 s card 26-34 s
  • The abort this PR targets is fixed. On the base tree the heap mirror slab allocation aborted the title in 2 of the last 3 runs (at ~120 s here, at 135 s in the run linked on Ghost of Tsushima Director's Cut (PPSA02225, Windows): black frames despite continuing draw and presentation activity #5166). With this PR the device heap filling up lowers the mirror budget 9 times and the run continues.
  • Peak private memory is unchanged (25.1 GB, against 24.6-25.6 GB). The freeze is ~12 s, against the base's 14-18 s, close to run-to-run noise. In these runs the intro reached the full-brightness card but no logo frame was captured.

Logs are available on request.

oneandonlydean and others added 2 commits November 6, 2026 10:26
…e budget cannot shrink

The slab refusal gate counts live slab memory, while the budget shrink
counts the bytes of listed heap mirrors. Mirrors that are unlisted but
still held by recorded work keep their slab blocks live after their
bytes leave the count, so a refusal can arrive with nothing left to
shrink, and both callers then failed the mirror. When the shrink cannot
act, retry once with the slab refusal waived, so the allocation runs as
it did before the refusal existed. Add a test that lowers the refusal
gate with a full device heap.

Co-authored-by: Prophet069 <291514246+Prophet069@users.noreply.github.com>
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Hi @Prophet069, we pushed c75bd0a393 to your branch: when shrinkHeapMirrorBudget cannot act on a slab refusal, the mirror now retries once with the refusal waived, so the allocation runs as before; FullDeviceHeapPastReserveTests covers it. You are credited as co-author.

@oneandonlydean
oneandonlydean dismissed XairRrR’s stale review November 5, 2026 22:37

Addressed in c75bd0a. The fragmented-slab stop as stated does not occur: Take hands out free blocks before refuseSlab runs, so fragmentation alone never reaches a refusal. The same gate mismatch was reachable another way (mirrors unlisted while recorded work still holds their slab blocks), and c75bd0a fixes it: when shrinkHeapMirrorBudget cannot act on a refusal, the mirror retries once with the refusal waived, so the allocation runs as before. FullDeviceHeapPastReserveTests covers it and fails without the change; heap_mirror and resident tests pass 16/16 locally. The other points ask for more test coverage of working paths and name no defect.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 21e7f2f merged onto main 55064c8 · tested 2026-11-05 22:38 UTC · 19 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,884 passed · 5 failed (0 new, 3 also fail on main, 2 unstable on main) · 51 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,940 of 4,105 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (969 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.1 s
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 1 of the last 10 main runs
agc_submit_progress_no_timeline: failed in 1 of the last 10 main runs
Failing on main too (3, not counted against this PR)
agc_driver_image_table
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (2, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
agc_driver_submit_progress: Failed in the parallel run; also passed only on retry in 23 other runs in the last 7 days
agc_submit_progress: Failed in the parallel run
Validation IDs already present on main (9 IDs in 585 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (585 tests): agc_driver, agc_driver_address_draw_space_reuse, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, and 577 more
VUID-VkShaderModuleCreateInfo-pCode-08737 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-VkShaderModuleCreateInfo-pCode-08739 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-VkShaderModuleCreateInfo-pCode-08741 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-vkCmdCopyImage-srcImage-01548 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (1 test): agc_driver_prepared_exports
VUID-VkPipelineShaderStageCreateInfo-module-parameter (1 test): agc_driver_float16_unused_nan_payloads
VUID-VkPipelineShaderStageCreateInfo-pSpecializationInfo-06849 (1 test): agc_driver_float16_unused_nan_payloads
Skipped (51)
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_net_ctl_disconnected: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_device_capability: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_disabled: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_four_k: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_height_only: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_limit: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_malformed: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_width_only: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_dcc: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_deferred: skipped, no
Reproduce
git fetch origin 21e7f2f0f5e0b4e092b9aee7205cfa43bc202437 55064c81fb67060082e3e9f6f4262ba348d95a0a
git checkout 55064c81fb67060082e3e9f6f4262ba348d95a0a && git merge --no-ff 21e7f2f0f5e0b4e092b9aee7205cfa43bc202437
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Nov 5, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

❌ NVIDIA GPU test: failed

PR head c75bd0a merged onto main e9247a9 · tested 2026-11-05 23:16 UTC · 23 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ❌ 2,883 passed · 6 failed (1 new: 1 not compared, 3 also fail on main, 2 unstable on main) · 51 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,940 of 4,105 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (969 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.1 s

Failing tests

This PR changes test files, so each failing test was also built and run on main with only the PR's test changes applied. ✅ there means the failure comes from the PR's other changes; ❌ means the test fails on main too, so this run does not show the PR caused it.

Test With this PR Same test on main e9247a9 (main + this PR's test changes only)
agc_submit_progress ❌ failed not compared: the PR's tests do not build without its other changes
agc_submit_progress: Failed (also failed when re-run alone), assertion and diagnostics
ctest: Failed

[write-watch] host imports resolve write protection: no (0 of 4 scratch pages written after a GPU read, 0 after a GPU write, 4 after the import); imported ranges stay watched
[write-watch] host imports resolve write protection: no (0 of 4 scratch pages written after a GPU read, 0 after a GPU write, 4 after the import); imported ranges stay watched
submission lost its real GPU completion store
[Shaders/ShaderRegistry.cpp:901 ReportPreparedAtUse] Compute shader 0x5a529f21d100 was not registered; preparing its artifact at dispatch
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 1 of the last 10 main runs
agc_submit_progress_no_timeline: failed in 1 of the last 10 main runs
Failing on main too (3, not counted against this PR)
agc_driver_image_table
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
agc_driver_submit_progress: Failed in the parallel run; also passed only on retry in 28 other runs in the last 7 days
Validation IDs already present on main (9 IDs in 585 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (585 tests): agc_driver, agc_driver_address_draw_space_reuse, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, and 577 more
VUID-VkShaderModuleCreateInfo-pCode-08737 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-VkShaderModuleCreateInfo-pCode-08739 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-VkShaderModuleCreateInfo-pCode-08741 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-vkCmdCopyImage-srcImage-01548 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (1 test): agc_driver_prepared_exports
VUID-VkPipelineShaderStageCreateInfo-module-parameter (1 test): agc_driver_float16_unused_nan_payloads
VUID-VkPipelineShaderStageCreateInfo-pSpecializationInfo-06849 (1 test): agc_driver_float16_unused_nan_payloads
Skipped (51)
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_net_ctl_disconnected: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_device_capability: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_disabled: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_four_k: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_height_only: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_limit: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_malformed: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_width_only: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_dcc: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_deferred: skipped, no
Reproduce
git fetch origin c75bd0a3931cc136c47bc31113af33c7368e91b6 e9247a966ff6cce55816ffe1d73a2679074348a4
git checkout e9247a966ff6cce55816ffe1d73a2679074348a4 && git merge --no-ff c75bd0a3931cc136c47bc31113af33c7368e91b6
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1
# the failing tests, on this merge and on main:
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc_submit_progress)$' --parallel 1 -V

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@github-actions github-actions Bot added the review queue Next in the maintainer review queue: checks pass, no open change request, no conflict label Nov 6, 2026

@Superseabas Superseabas left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Windows run of the current head, c75bd0a393, on an RTX 5070 Ti and on the integrated Radeon of a Ryzen 7 7700X, the first AMD result on Windows here. Nothing regresses, and the new FullDeviceHeapPastReserveTests catches the loss of the retry. One non-blocking note on what it doesn't cover.

Head merged onto main ed41d86d32 (clean merge, local only). Windows 11 Home 10.0.26200, WinLibs GCC 16.2.0 (x86_64-ucrt-posix-seh, r3), Release. ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, no missing-layer line, ANYPS5_GPU selecting the device, confirmed by Selected GPU:

  • NVIDIA GeForce RTX 5070 Ti (10de:2c05), 621.01
  • AMD Radeon of a Ryzen 7 7700X (1002:164e, integrated), AMD 26.10.2 (32.0.21045.20006)

Focused set. The 17 tests matching heap_mirror|resident (agc_driver_resident_heap_mirror, the agc_heap_mirror_* and agc_sparse_heap_mirror_* entries, agc_driver_partially_resident_*, agc_resident_* and the others) pass 3 of 3 on both GPUs, on main and on this PR. No VUID- lines. Both devices take the sparse path: agc_driver_resident_heap_mirror prints no skip line, and the [heap-mirror] refused line shows the full-heap cases ran.

Whole AGC set. ctest -R '^agc' -j 8 --timeout 300, 1,388 tests: 1,386 pass on the RTX and 1,385 on the Radeon, the same as main on this machine. Every failure is tracked: agc_driver_direct_write_completion (#15564), agc_driver_quad_pixel_mask on the RTX (#14341), and on the Radeon agc_driver_scratch_pool (#15566) and agc_driver_sample_order_grid_reference (#9503).

Red checks on agc_driver_resident_heap_mirror, 3 runs per GPU, each a local edit of the merged tree:

Edit Result
none passes
both if (!waive && dynamic_cast<const HeapMirrorSlabRefused*>(&error) != nullptr) { waive = true; continue; } retries removed (growResident, acquireMirror) fails: vkAllocateMemory of a heap mirror slab refused: its device heap would keep less than 256 MiB of its reported budget
only if (slabRefusalWaived) return; removed from refuseSlab passes
that line removed, and the headroom throttle (now - lastHeadroomRefusal > std::chrono::seconds(2)) disabled fails, the same message
the throttle disabled, the line kept passes

Non-blocking. The test covers the retry, but not the waive itself. Its refusal is the headroom one, and lastHeadroomRefusal was set by the refusal a moment earlier on the same thread, so the throttle already lets the retry's allocation through, with or without slabRefusalWaived. The waive matters for the other reason, the heap mirrors' slabs would pass APS5_HEAP_MIRROR_MIB, which has no throttle. A case refused through the budget hook, with too little held for shrinkHeapMirrorBudget to act, would cover it.

check_conventions.py --base main passes.

AI-assisted: yes (Claude Code)

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* review queue Next in the maintainer review queue: checks pass, no open change request, no conflict unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants