Skip to content

fix(agc): save the guest bytes of results that become pending over a surface that is no longer fully write-watched - #7048

Closed
AlxcBTW wants to merge 22 commits into
boykopovar:mainfrom
AlxcBTW:pr/storage-pending-save
Closed

AlxcBTW wants to merge 22 commits into
boykopovar:mainfrom
AlxcBTW:pr/storage-pending-save

Conversation

@AlxcBTW

@AlxcBTW AlxcBTW commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

What

StorageTexture::markLayersPending saved the guest bytes of a unit that becomes pending only when the unit had no write generation. A unit last stored or uploaded while its memory was watched keeps a generation. When a block of the surface leaves the write watch while that unit is idle (the flush for the very access that caused the unwatch has just stored it, so the unwatch hook finds nothing pending) and the unit becomes pending again afterwards, it has neither the watch nor saved bytes to tell the CPU's writes from its own results. Its write-back then refuses: "storing storage image results pending over memory outside the write watch without the guest bytes saved before them is not implemented". One such unit makes the whole stored span untracked (ChangedBlocks is all-or-nothing), so every unit of that write-back needs its bytes. The colour mirror of a depth plane is marked pending after every clear dispatch without a lookup (ShaderResources::MarkGpuWrites), which is how an idle unit gets here.

  1. fix(agc): save the guest bytes of results that become pending ...: a unit without saved bytes and without a save on its way takes its guest bytes when it becomes pending over a surface that is not fully watched, whatever its generation. The blocks the CPU or the driver wrote since the unit's generation are still told from the stamps while the unit's own blocks are watched, and keep their bytes at the store, exactly as when the unwatch hook saves a pending unit (keepWrittenSince, shared with saveBeforeUnwatch). Their writes are collected unit by unit, because a collect over a range that holds an unwatched block does nothing. A unit whose own block is unwatched gets its bytes alone.

  2. fix(agc): retile into the unit shadow only when the whole stored span is write-watched: with 1. the write-back goes through, and a second loss shows. writeBackWindows routes each wholly watched kept range into the unit shadow's slab and marks the pieces shadowed at the generation of a collect over the whole stored span. Over a span with an unwatched block that collect returns 0, MarkShadowed ignores generation 0, and the pieces stay in the slab without a record and are never published. Over such a span every piece now goes to the import, since the span already decides the saved bytes of the store.

  3. test(agc): store results that become pending after ...: agc_storage_pending_after_unwatch.

  4. Review follow-up, fix(agc): refuse pending results over an unwatched unit whose bytes may differ from its upload (695f8b9): the capture in 1. took the current guest bytes as the baseline whatever had happened since the unit's upload. If a block had left the watch and the CPU wrote it after the upload, that write cannot be seen: the image holds the upload's content for texels the GPU does not write, and storing the unit would silently overwrite the CPU's bytes there. The capture now trusts the current guest bytes only while the unit is still write-watched or still equals the bytes it was uploaded from (StorageTexture::guestMatchesUpload against the upload snapshot). Otherwise the unit keeps the previous refusal. That covers surfaces above the 1 MiB snapshot limit, and an earlier write-back that made the snapshot stale. Units with a stamped CPU write still go through the kept-block path. The same staleness exists on the older capture for units without a generation, which this PR does not change; I can follow up on it separately.

  5. Review follow-up, fix(agc): compare an unwatched unit of a large surface with a hash of its upload bytes (5cf43e5): the upload snapshot behind 4. exists only for surfaces up to 1 MiB, so on a larger surface (the 32 MiB shadow-atlas mirror that motivated this PR) an unwatched unit went back to the old refusal. Each unit of a larger surface now keeps a 128-bit hash of its upload bytes (ShaderRecompiler::HashBytes with two seeds over 64 KiB chunks, 16 bytes per unit). The hash is taken where the snapshot is, and dropped wherever the GPU content stops being the upload's (alias borrow, fast clear, a write-back of the unit). An unwatched unit whose current bytes hash to the same value takes them as the baseline. A different or missing hash keeps the refusal. Exactness assumes no collision of the 128-bit non-cryptographic hash between two 64 KiB contents of guest memory. The next commit (2dd5117) hashes through a local buffer instead of a thread_local vector, so the library registers no thread_local destructor.

  6. Review follow-up, the upload-path hash: 5cf43e5 and 2dd5117 hashed every settled upload above 1 MiB, which cost 45 to 80 % of the presents on Astro Bot, PPSA02100 and The Pathless. The hash is now taken in the unwatch hook, while the range is still watched, only for an idle unit with a generation, no pending results, no saved bytes and no CPU write since that generation; upload() and refresh hash nothing. This is fix(agc): hash an idle unit when its range leaves the write watch instead of at every upload #10026 by winstrex (ec6ceece9, f27fcde50, d73e64951, 43a9245e8, cfbe00cb4, 660cef6fe), merged into this branch unchanged so the two land together. It also covers both ImportWatched exits that skip the hook, names the cause when a unit has no baseline, and drops the unconditional Require(refused, ...) for the large UnwatchedPartialWrite case in tests/Recorder.cpp.

  7. Conflict with main in Texture.cpp (markLayersPending and saveBeforeUnwatch): merge 730c72177 of main f8f5b24. keepWrittenSince takes the generation of each 64 KiB block from unitBlockGeneration (main's per-block completion stamps), so a block main stamped at a completion is compared with that stamp and not with the unit's generation, exactly as saveBeforeUnwatch does on main now.

After the merge of main (base 0c6a5bd, merge commit ad54ca800) the build broke on a second GenerationBaselineBytes constant in Texture.cpp that the merge left next to the one main now has; da9f0ead4 (fix(agc): drop the duplicate generation baseline constant left by the merge) removes the two duplicate lines and nothing else.

Both fixes only change what happens for a surface that is not fully write-watched; a fully watched surface takes the old path in both places.

Why it is correct: the refusal exists because the write-back needs a baseline to tell the CPU's writes from the GPU's results. The unit now has that baseline from the moment it can no longer get one from the watch, taken at the same point and with the same merge of stamped blocks that the unwatch hook uses for a unit that is pending when the block leaves.

Tested

  • Linux (Ubuntu 24.04, Mesa 25.2.8 lavapipe), fork CI at fe1fa2f: 516 of 516 agc_storage_*, agc_unit_shadow*, agc_driver_*, agc_recorder*, agc_import_watch*, agc_pending* and related tests pass, the new one included. Windows Server runner, Mesa 26.2.4 lavapipe for Windows, fork CI: the 7 storage write-back tests pass, the new one included (it ran there; the surface stayed watched).
  • New test agc_storage_pending_after_unwatch (tests/Recorder.cpp, also called from the full agc_driver_recorder_tests sequence). An image over watched memory is uploaded (units with a generation, no saved bytes), the last of its 4 blocks leaves the write watch while nothing is pending, the image is cleared on the GPU and marked written again without a lookup, then the store runs three ways: with no CPU write, with a CPU write to block 2 after the results were pending again, and with that write before. The store must succeed, keep the CPU's page and hold the results in every other block, the unwatched one included. Without 1. it fails with the refusal above; with 1. alone it fails on the second defect (units 0 and 1 not stored). Where the image's host import takes the surface out of the write watch (a Windows runner) the test reports that it was not run.
  • Fork CI at the head 2dd5117 (Linux and Windows lavapipe): the same sets pass. The new test now runs a 256x256 and a 1024x1024 (4 MiB, above the snapshot limit) surface, with two more variants each, a CPU write into the unwatched unit followed by a full-coverage clear and by a partial (16x16) GPU write; each must either refuse with the CPU's bytes intact or store exactly the CPU's dword or the GPU result per texel.
  • Windows 11, NVIDIA, Minecraft Dungeons II (PPSA16064): this refusal ended 7 of our lab rows between 15.10. and 17.10. through std::terminate on the driver failure (in the three I checked, after the world had loaded), among them one on plain main (636b1b9) and one on main of 17.10. with a lab switch on. In one row it came from 29 of the 512 units of the 4096x4096 R16 colour mirror of a D16 shadow atlas, each with one write generation and no saved bytes. On our lab stacks that carry the first two commits, 27 rows reached gameplay without it; game rows with the follow-up commit are still to come. Without the fix the rate is a few percent of rows, so these rows are consistent with the fix but do not prove it by themselves; the test reproduces the flow deterministically.
  • After the merge of main, fork CI on Linux (Ubuntu 24.04, Mesa lavapipe), at the head da9f0ead4: the run at the merge commit ad54ca800 (run 39677534763, regex agc_|Texture|Storage|Recorder) did not build (the duplicate GenerationBaselineBytes, fixed by da9f0ead4). The run at da9f0ead4 (run 39678944715, same regex, plus the CI scaffolding commit): 876 of 877 passed, among them agc_storage_pending_after_unwatch; agc_driver_sgpr_pattern_fill_keys failed with "the dispatch completed 17 times without a pending write over the keys". That test (tests/execution/SgprPatternFillKeys.cpp) is not touched by this PR; it submits a compute dispatch over a DCC key buffer and polls for the recorder's pending write note, and fails when the dispatch has retired 16 times without the poll seeing it, which is a timing condition of the test. A rerun of da9f0ead4 with sgpr_pattern_fill_keys|storage_pending (run 39683919241): 2 of 2 passed. The test did not fail in any other run I have: it passed in 2 runs of main (a6dcea9; runs 39688450938 and 39693413422) and in 9 runs of perf(agc): answer Refresh's registry and host-import lookups from an index and a cache #7514's and perf(agc): clear the mirror of a depth plane instead of dispatching the UAV clear over it #7449's heads (39677504672, 39683281454, 39677622121, 39688330136, 39688369669, 39688423114, 39693268189, 39693318038, 39693362034). So one failure in 13 runs that held the test, none of the 2 on main; it is not shown to be flaky on main and not shown to depend on this PR.
  • Fork CI (fix(agc): save the guest bytes of results that become pending over a surface that is no longer fully write-watched (upstream #7048) anyplaynet/AnyPS5#48, the same diff on our main), Windows job (full build, all tests registered with ctest): passed, https://github.com/anyplaynet/AnyPS5/actions/runs/40720659323
  • python3 tools/check_conventions.py --base upstream/main passes on 730c72177.

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: none (PPSA16064 gameplay abort at this refusal; no Game bring-up discussion yet)
  • GPUs tested: Mesa 25.2.8 lavapipe, Ubuntu 24.04, and Mesa 26.2.4 lavapipe for Windows (GPU tests); NVIDIA GeForce RTX 5070 Laptop GPU, driver 617.42, Windows 11 26200 (game rows only, GPU tests not run there)
  • AI-assisted: yes

Neighbours: #6490 (parl0v) also changes writeBackWindows and #6960 adds a line next to the changed one in markLayersPending; neither implements this, and #6308 runs the unwatch hook for a different exit (an import that leaves the watch). Whichever lands second rebases.

…surface that is no longer fully write-watched

markLayersPending saved the guest bytes of a newly pending unit only when it had no write generation. A unit that was
last stored or uploaded while its memory was watched keeps a generation, so when a block of the surface left the write
watch while the unit was idle (a flush for the access that caused the unwatch had just stored it, so the unwatch hook
found it not pending) and the unit became pending again afterwards, it had neither the watch nor saved bytes to tell
the CPU's writes from its results. Its write-back then refused: storing storage image results pending over memory
outside the write watch without the guest bytes saved before them is not implemented. One such unit makes the whole
span untracked, so every unit of the write-back needs its bytes.

A unit without saved bytes and without a save on its way now takes its guest bytes when it becomes pending over a surface
that is not fully watched, whatever its generation. The blocks the CPU or the driver wrote since the unit's generation
are still told from the stamps while the unit's own blocks are watched and keep their bytes at the store, exactly as
when the hook saves a pending unit (keepWrittenSince, shared with saveBeforeUnwatch); their writes are collected unit by
unit, since a collect over a range holding an unwatched block does nothing. A unit whose own block is unwatched gets its
bytes alone.
… is write-watched

A write-back routes each kept range that is wholly watched into the unit shadow's slab, and marks the pieces shadowed at
the generation its collect over the whole stored span returns. When a block of the span has left the watch that collect
returns 0, MarkShadowed ignores a generation of 0, and the pieces stay in the slab without a record: never published,
their results lost. Over a span with an unwatched block every piece now goes to the import, as the saved bytes of the
store already are decided by the span.
…urface left the write watch

A storage image over write-watched memory is uploaded, so its units have a write generation and no saved guest bytes.
A block of the surface then leaves the write watch while nothing is pending, which the unwatch hook does not see, and
the image is marked written again without a lookup (as the colour mirror of a depth plane is after each dispatch). The
write-back cannot tell the CPU's writes from the results without saved bytes and refuses.

agc_storage_pending_after_unwatch runs that flow with no CPU write, with the CPU writing a block of the surface after
the results became pending again and with it writing before; the store has to succeed, keep the CPU's block and store
the rest, the block that left the watch included. Where the image's host import takes the surface out of the write
watch (a Windows runner), the test reports that it was not run.
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 862 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 1896 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 2434 docs/dev/debt/libSceAgcDriver.md
PR 4448 core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 4449 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5372 docs/dev/debt/libSceAgcDriver.md
PR 5609 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6108 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 6146 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6182 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6185 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6186 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6194 docs/dev/debt/libSceAgcDriver.md
PR 6197 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6223 docs/dev/debt/libSceAgcDriver.md
PR 6273 core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6280 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6501 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6548 docs/dev/debt/libSceAgcDriver.md
PR 6549 docs/dev/debt/libSceAgcDriver.md
PR 6615 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 6743 docs/dev/debt/libSceAgcDriver.md
PR 6804 docs/dev/debt/libSceAgcDriver.md
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6905 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6921 core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 6926 core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6959 docs/dev/debt/libSceAgcDriver.md
PR 6970 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6973 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6977 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6987 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6989 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6995 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7002 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7085 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7223 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7256 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7275 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7446 docs/dev/debt/libSceAgcDriver.md
PR 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7508 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7668 docs/dev/debt/libSceAgcDriver.md
PR 7689 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7717 docs/dev/debt/libSceAgcDriver.md
PR 7893 docs/dev/debt/libSceAgcDriver.md
PR 7978 docs/dev/debt/libSceAgcDriver.md
PR 8014 docs/dev/debt/libSceAgcDriver.md
PR 8089 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 8395 docs/dev/debt/libSceAgcDriver.md
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 8542 docs/dev/debt/libSceAgcDriver.md
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8707 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8874 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8887 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 9020 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9248 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9289 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 9318 docs/dev/debt/libSceAgcDriver.md
PR 9335 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9356 docs/dev/debt/libSceAgcDriver.md
PR 9387 docs/dev/debt/libSceAgcDriver.md
PR 9432 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9445 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 9508 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9533 docs/dev/debt/libSceAgcDriver.md
PR 9592 docs/dev/debt/libSceAgcDriver.md
PR 9671 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9672 docs/dev/debt/libSceAgcDriver.md
PR 9673 docs/dev/debt/libSceAgcDriver.md
PR 9674 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9675 docs/dev/debt/libSceAgcDriver.md
PR 9700 docs/dev/debt/libSceAgcDriver.md
PR 9712 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 9717 docs/dev/debt/libSceAgcDriver.md
PR 9742 docs/dev/debt/libSceAgcDriver.md
PR 9748 docs/dev/debt/libSceAgcDriver.md
PR 9852 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9858 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 9875 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 9884 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 9891 docs/dev/debt/libSceAgcDriver.md
PR 9892 docs/dev/debt/libSceAgcDriver.md
PR 9893 docs/dev/debt/libSceAgcDriver.md
PR 9895 docs/dev/debt/libSceAgcDriver.md
PR 9898 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9915 docs/dev/debt/libSceAgcDriver.md
PR 9950 docs/dev/debt/libSceAgcDriver.md
PR 9999 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10026 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp
core/libs/prx/libSceAgcDriver/tests/execution/LinearTargetCpuWrites.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10043 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10049 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10064 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10069 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10159 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10193 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10233 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 10249 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 10256 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10304 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10333 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10351 docs/dev/debt/libSceAgcDriver.md
PR 10359 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 10435 docs/dev/debt/libSceAgcDriver.md
PR 10442 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10443 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10469 docs/dev/debt/libSceAgcDriver.md
PR 10472 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10492 docs/dev/debt/libSceAgcDriver.md
PR 10497 docs/dev/debt/libSceAgcDriver.md
PR 10656 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 10743 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10757 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10760 docs/dev/debt/libSceAgcDriver.md
PR 10773 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10779 docs/dev/debt/libSceAgcDriver.md
PR 10859 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 10867 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10906 docs/dev/debt/libSceAgcDriver.md
PR 10920 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10940 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 11104 docs/dev/debt/libSceAgcDriver.md
PR 11126 docs/dev/debt/libSceAgcDriver.md
PR 11280 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 11346 docs/dev/debt/libSceAgcDriver.md
PR 11351 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 11444 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 11485 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 11551 docs/dev/debt/libSceAgcDriver.md
PR 11554 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 11557 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 11587 docs/dev/debt/libSceAgcDriver.md
PR 11825 docs/dev/debt/libSceAgcDriver.md
PR 11847 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 11886 docs/dev/debt/libSceAgcDriver.md
PR 11995 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 11998 docs/dev/debt/libSceAgcDriver.md
PR 12070 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 12120 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 12158 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 12246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 12323 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 12324 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 12348 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 12452 core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp
docs/dev/debt/libSceAgcDriver.md
PR 12565 docs/dev/debt/libSceAgcDriver.md
PR 12567 docs/dev/debt/libSceAgcDriver.md
PR 12568 docs/dev/debt/libSceAgcDriver.md
PR 12617 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 12651 docs/dev/debt/libSceAgcDriver.md
PR 12662 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 12745 core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 12746 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 12764 docs/dev/debt/libSceAgcDriver.md
PR 12792 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 12813 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 12828 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 12840 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 12855 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 12904 docs/dev/debt/libSceAgcDriver.md
PR 12921 core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp
PR 12980 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 12994 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 13021 core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 13023 core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 13044 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 13131 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 13146 docs/dev/debt/libSceAgcDriver.md
PR 13147 docs/dev/debt/libSceAgcDriver.md
PR 13148 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 13151 docs/dev/debt/libSceAgcDriver.md
PR 13169 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 13248 docs/dev/debt/libSceAgcDriver.md
PR 13251 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 13262 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 13263 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 13474 docs/dev/debt/libSceAgcDriver.md
PR 13599 docs/dev/debt/libSceAgcDriver.md
PR 13646 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 13707 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 13793 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 13796 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 13809 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 13858 docs/dev/debt/libSceAgcDriver.md
PR 13863 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 13920 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 13928 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 13929 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 13942 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 13963 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 13991 docs/dev/debt/libSceAgcDriver.md
PR 13994 docs/dev/debt/libSceAgcDriver.md
PR 14005 docs/dev/debt/libSceAgcDriver.md
PR 14018 docs/dev/debt/libSceAgcDriver.md
PR 14051 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 14085 docs/dev/debt/libSceAgcDriver.md
PR 14212 docs/dev/debt/libSceAgcDriver.md
PR 14220 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 14243 docs/dev/debt/libSceAgcDriver.md
PR 14248 docs/dev/debt/libSceAgcDriver.md
PR 14261 core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 14262 core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 14265 docs/dev/debt/libSceAgcDriver.md
PR 14296 docs/dev/debt/libSceAgcDriver.md
PR 14302 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 14311 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 14330 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 14344 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
docs/dev/debt/libSceAgcDriver.md
PR 14473 core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 14561 docs/dev/debt/libSceAgcDriver.md
PR 14621 docs/dev/debt/libSceAgcDriver.md
PR 14628 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 14799 core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 14815 docs/dev/debt/libSceAgcDriver.md
PR 14868 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 14899 core/libs/prx/libSceAgcDriver/Execution/include/GuestMemory.hpp
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
core/libs/prx/libSceAgcDriver/tests/WriteTracking.cpp
PR 14909 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 14914 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 14943 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 14944 docs/dev/debt/libSceAgcDriver.md
PR 14950 docs/dev/debt/libSceAgcDriver.md
PR 14963 docs/dev/debt/libSceAgcDriver.md
PR 14964 docs/dev/debt/libSceAgcDriver.md
PR 14976 docs/dev/debt/libSceAgcDriver.md
PR 14982 docs/dev/debt/libSceAgcDriver.md

@github-actions github-actions Bot added build agc libSceAgc* labels Oct 17, 2026
@LeandroLP23

Copy link
Copy Markdown
Contributor

Hardware run on Windows, where the description's runner reported the new test as not run: here it runs, since on this AMD driver host imports of write-watched memory keep the watch ([write-watch] host imports of write-watched memory keep the write watch: yes).

Windows 11 (26300), Radeon 860M (RDNA 3.5, driver 32.0.13058.8001), WinLibs GCC 15.2.0, Release. main adb0e7a8c with this head merged cleanly.

AI-assisted: yes (Claude Code).

@johnmaia

Copy link
Copy Markdown
Contributor

Windows run on real GPUs: i7-13700K, RTX 4080 (617.42) and Intel UHD 770 (101.6129), Windows 11, WinLibs GCC 15.2.0, Release, ANYPS5_REQUIRE_VULKAN=1. Tested this PR merged onto main adb0e7a.

ctest -j4 -R "agc_storage|unit_shadow|recorder|import_watch|agc_pending|storage_write_back|storage_cache" (19 tests):

  • RTX 4080: 19/19 pass.
  • UHD 770: 18/19 pass. agc_import_watch fails ("the Windows import probe failed at the settle queue (0)"), and it fails the same way on plain main adb0e7a, so it doesn't come from this PR.

agc_storage_pending_after_unwatch passes on both GPUs and runs its three stores there ("host imports are compared"). It doesn't report "not run" the way it does on the lavapipe runner, so the unwatched-block path is exercised on NVIDIA and Intel.

AI-assisted: yes (Claude Code)

…ay differ from its upload

A unit uploaded while watched has no saved bytes. Once its block left the
write watch the CPU's writes there are unstamped, and capturing the current
bytes as the baseline when the results turn pending without a lookup stored
the stale upload content over them for every texel the GPU did not write.
Capture only when the unit is still watched or equals the bytes it was
uploaded from; otherwise keep the refusal for that unit.
@johnmaia

Copy link
Copy Markdown
Contributor

In-game regression check on Linux, RX 7600 (RADV NAVI33, Mesa 25.2.8), Ubuntu 24.04, GCC 13.3 Release: Epic Mickey: Rebrushed (PPSA15657, UE 4.27), head fe1fa2f, six short runs to the first level, no environment switches.

All six reached the level and played to the end of the run: no driver throw, device loss or title fatal error.

This title no longer reaches the throw this PR fixes. It hit it on Windows (RTX 4080) until #5671, and it never did on Linux. So this run only shows that nothing regressed in a UE4 title that keeps storage images pending over its own memory.

AI-assisted: yes (Claude Code)

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@AlxcBTW thanks for 695f8b9. Refusing pending results over an unwatched unit closes a real silent-corruption path (stale upload bytes over unwatched CPU writes), and it fails closed, which is the right direction.

One gap: generationBaseline is only kept for surfaces up to 1 MiB (Texture.cpp around lines 2191 and 2207). For a larger surface, an unwatched unit with no saved bytes now goes back to the original Require(!unsavedOutsideWatch) terminate, even when the CPU never wrote there. That's the PPSA16064 failure this PR set out to fix. The test fixture is 256 KiB, so agc_storage_pending_after_unwatch can't show it either way.

So before this merges, could you do a PPSA16064 run on 695f8b9 and post whether the rows that used to end at unsavedOutsideWatch still terminate? If they do, the PR needs to handle >1 MiB surfaces as well (keep a baseline for them, or refuse rather than terminate there). Full CI is running on the new head.

@AlxcBTW

AlxcBTW commented Oct 17, 2026

Copy link
Copy Markdown
Contributor Author

@oneandonlydean good catch, thanks. The shadow-atlas mirror that motivated this is 32 MiB, so it is exactly the case above the snapshot limit. Two things are underway:

  • A PPSA16064 run with counters that separate "captured, unit still watched", "captured, matches upload", "refused, changed since upload" and "refused, no baseline", plus whether the unsavedOutsideWatch terminate still fires. One arm runs 695f8b9 as it is, the other adds the change below.
  • For surfaces above 1 MiB: a small per-unit content hash taken where the unit is uploaded or stored, so an unwatched unit the CPU did not touch can still take its bytes as the baseline without keeping a full copy. A unit without a matching hash keeps the refusal, so it still fails closed. There will be a test variant with a 4 MiB surface (one untouched unwatched unit must store, one written by the CPU must refuse with the CPU's bytes kept).

I'll post the rows and the commit here when both are done.

… its upload bytes

Surfaces above 1 MiB keep no byte snapshot of their upload, so an unwatched
unit with no saved bytes fell back to the unsaved-outside-watch refusal even
when the CPU never wrote there. Keep a 128-bit hash of each unit's bytes taken
when the unit was uploaded and compare it at markLayersPending: equal means the
current bytes are the upload and are saved as the baseline, a different hash or
none keeps the refusal. Units whose content came from elsewhere (borrowed from an
alias, cleared, stored by a write-back) drop their hash.

The pending-after-unwatch test also runs a 1024x1024 surface: an unwatched unit
the CPU did not write is stored, one it wrote is refused with its bytes intact.
@AlxcBTW

AlxcBTW commented Oct 17, 2026

Copy link
Copy Markdown
Contributor Author

@oneandonlydean the PPSA16064 rows you asked for. Six in-world gameplay walks (~180 s each, RTX 5070 Laptop, Windows 11), on a lab stack (our best10 = current main plus our other lab changes) carrying this PR, with a counter at the capture in markLayersPending and at the old Require(!unsavedOutsideWatch):

  • arm a: the PR up to 695f8b9 (no hash for large surfaces)
  • arm b: plus 5cf43e5 / 2dd5117 (per-unit upload hash above 1 MiB)
row arm stop refusal lines captured, unit still watched captured, snapshot/hash matched refused, changed refused, no baseline Require hits
1 a walk completed 0 9,913,621 0 0 0 0
2 a walk completed 0 11,503,628 0 0 0 0
3 a walk completed 0 13,677,820 0 0 0 0
1 b walk completed 0 13,328,220 0 0 0 0
2 b walk completed 0 9,557,957 0 0 0 0
3 b walk completed 0 9,922,661 0 0 0 0

So on this title every unit that needed a baseline was still watched when it became pending. 695f8b9 alone does not bring the terminate back here, and the large-surface branch you pointed at is never reached. The hash commit is still the right behaviour for a title where it is reached; on this one it changes nothing. In every row 6-9k write-backs went over a span with an unwatched block and needed no saved bytes. Without this PR that is the situation where the refusal used to end ~5 % of our rows (7 rows over 15.-17.10.). Counter branch: fork lab/pending-save-game.

@huseinnashr

Copy link
Copy Markdown

Astro Bot (PPSA21564, content 01.018.000) on Windows / RTX 5060 Ti: reaches the intro cutscene with #7048, 3.5–20 fps

With main plus PR #7048 the title no longer aborts. It goes through the logos, title, save-slot selection, loading, world map and into the intro cutscene, rendering correctly. Frame rate is the problem: 14–20 fps on the logos and title, 6–11 fps in the world map and intro, and 3.5 fps in the crowd cutscene.

Environment

Title Astro Bot, PPSA21564 (US), content version 01.018.000, retail dump (decrypted)
OS Windows 11 Pro for Workstations, build 26200
CPU Intel Core i7-14700
GPU NVIDIA GeForce RTX 5060 Ti 16 GB, driver 610.88 (the Intel UHD 770 is also present; ANYPS5_GPU=NVIDIA selected the 5060 Ti)
RAM 64 GB

Build

Input preparation

  • eboot.bin was a decrypted SELF (no segment flagged encrypted or compressed). I unwrapped it to a plain ELF with a small script that copies the blocked segments to their program-header offsets.
  • The two files in sce_module/ (libc.prx, libSceNpCppWebApi.prx) were SELFs as well and needed the same unwrap, or they get skipped.
  • Resulting ELF: 246.9 MiB. With the modules that is 255.7 MiB, right under the web converter's 256 MiB cap.

What happens

v0.1.2 release build (relinker + prx-windows v0.1.2): the engine starts, picks the 5060 Ti, creates the swapchain, then aborts about 8 s after launch:

[gpu] color target 0x53aa00000 stays non-resident: AGC graphics: storing storage image results pending over memory outside the write watch without the guest bytes saved before them is not implemented
terminate called after throwing an instance of 'std::runtime_error'
FATAL: abort() (SIGABRT) on thread ...

It reproduces on every run. APS5_STORE_AT_FLIP=1 and APS5_STORE_AT_LABEL=1 do not avoid it. This looks like #7017 / #6301.

main + #7048: no abort in several runs, including a 300 s capture. Levels loaded in order: ps_logo, title_controller_ship, worldmap, intro_next. Save slot 1 selection works and the intro cutscene renders correctly (screenshot attached).

Performance (shader cache warm after the first pass)

Stage Presents per 10 s GPU busy per frame
Logos and title 139–199 11–34 ms
World map and intro 61–112 60–130 ms
Crowd cutscene (title-bar FPS counter) about 3.5 fps n/a

First run: about 1,750 shader-cache misses and a shader cache of about 1.3 GB. Later windows still show new misses when new content appears.

Excerpts from a window in the world map / intro (APS5_PROFILE_DRAW=1):

[present] 90 presents over 10 s (0 dropped ...); inflight_wait p50 44.87 ms, p90 48.55 ms; per frame: GPU busy 92.5 ms, idle gaps 19.6 ms; per flip: 69.5 batches recorded
[recorder] 53057 syncs waited 36.4 s for the GPU in total (... pending write 7467/35.0s ...)
[gpu] 117000 dispatches (99555 dispatch cache hits, 0 evictions, 17445 recompile cache hits) ... device 125.2 s

Host observations while it ran at 3.5 fps: GPU utilization 89–99% at P0 and 2782 MHz, but only about 57 W (the card is rated around 180 W); about 1.9 CPU threads busy of 28; disk about 0% busy (NVMe); 28 GiB private memory, 22 GiB resident. So the GPU looks busy but is mostly stalled on serialized work. Nearly all of the sync wait is on pending GPU writes.

Not tested

Side note

The web converter fails with std::bad_alloc on this input (255.7 MiB executable plus modules), so the native relinker was needed.

AI-assisted: yes (Claude Code did the builds, runs and log analysis; I ran the game and checked the screen).

@oneandonlydean oneandonlydean self-assigned this Oct 17, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Thanks for this. We ran it against two Astro Bot game runs on an RTX 4090 (head 2dd5117, merged onto main 52798f7, each 450 s against two main runs on the same build). Rendering and checkpoints are fine and Dreaming Sarah passes. But Astro Bot is clearly slower, and both runs show the same pattern:

phase this PR (run 1 / run 2) main (two runs)
intro FPS 12.55 / 11.15 21.75 / 20.30
title FPS 12.01 / 12.81 29.19 / 29.62
menu FPS 15.87 / 16.00 32.71 / 33.03
level-phase median frame time 54.2 / 54.9 ms 39.1 / 39.8 ms

CPU load and clocks match main and GPU load is lower, so the GPU is waiting on the CPU. That fits the one cost the PR adds: every settled re-upload of a surface larger than 1 MiB now does an extra full read and hash pass over its guest bytes (the generation baseline is only kept for surfaces of 1 MiB or less). Full-screen targets are well over 1 MiB, so that pass runs on most frames.

Could you avoid the extra pass for large surfaces? For example, compare only the blocks that hold an unsaved unit, or keep a per-block baseline above 1 MiB, rather than reading and hashing the whole surface. We'll run Astro again on the new head.

@frankea

frankea commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

NHL 26 (PPSA26785, 01.060.000) on NVIDIA Ada, where this refusal was the title's next stop after #7346 (my #7346 comment: frameIndex 423, TextureCompositeDraw, four 0x870000-byte storage images dropped as "memory no longer registered" right before it).

Setup: RTX 4090 (NVIDIA 616.92, Vulkan 1.4.341), Windows 11 26200, Ryzen 7 5800X, MinGW-w64 GCC 15.2.0 r7, Release libs; tree main 376c040 (2026-10-18) + this PR's head 2dd5117, clean merge; #7346 is in that main. Same relinked app.exe as my earlier runs, ANYPS5_REQUIRE_VULKAN=1, no controller, no input, three runs with a 600 s cap each, captures of the window every 10 to 30 s on the last run. The shader cache was cold for this tree (new source version), so run 1 spent its 600 s compiling; runs 2 and 3 are the result.

run shader cache ends storage-image refusal screen FPS (window counter)
1 cold, 18,476 shaders compiled in the 600 s killed at the cap no driver failure of any kind, but it never reached the frames in question not captured -
2 18,663 hits, 8,688 compiled killed at the cap, no stop gone: the storage images drop out of the watch as before (0x11c7680000+0x650000, 0x11c7480000+0x480000, 0x11c7ef0000+0x280000, 0x11c7ef0000+0x110000) and the write-back goes through; no throw, no device loss for the remaining 5 min not captured; same log signature as run 3 -
3 warm, 27,351 hits, 0 misses killed at the cap, no stop gone: same drops as the #7346 run that aborted (0x11c3de0000, 0x11c2d00000, 0x11c67c0000, all +0x870000, plus 0x11c7680000+0x650000), no refusal black until about 3 min 40 s, first frames at 4:08, the legal screen at 4:38, the title screen ("START" prompt, animated background) from 5:08 to the cap, the first time this title gets there on this box 17.5 on the first frames, 7.2 on the legal screen, 12.3 to 12.4 on the title screen (frame 714 at 5:08, frame 4078 at 9:40)

So on this GPU the PR does for NHL 26 what it did for PPSA16064: the refusal that ended every warm run on main + #7346 at frameIndex 423 no longer fires, and the title runs on through the legal screen to its title screen and sits there until the cap. The sce::Json::Parser::parse: null text abort I saw once after the legal screen on #7346 did not occur in either 600 s run here; I do not know why (the file-open sequences of the runs are identical, that run just died 36 s after the legal screen), so I am not counting it either way.

Two notes for the cost discussion above:

  • The title's own "GPU stopped working" breadcrumb reports (the 1 s WAIT_REG_MEM timeouts the engine recovers from) are still there: six in run 2 (frames 113 to 162: DeferredGpuProbes, hdriSky, sssConvHorizontal, planarShadowDL, TextureCompositeDraw, TAAPostSharpen), one in run 3 (frame 165, Depth_writeHalfResZ_3d, pipeline ShaderProgram_ProceduralSky), all before the first visible frame, none after. Same as on fix(agc): read a 32-bit depth-bits T# over guest memory through a float view #7346, not this PR's doing.
  • Once the title screen is up, stdout logs a steady ~215 StorageTexture upload submit/done pairs per second (Texture.cpp:2423) for 1176x664 storage images (format 50 / VK 64 at 0x11c04d0000, and in run 2 also format 69 / VK 95 at 0x11c1f70000; about 3 MiB each, so above the 1 MiB snapshot limit), roughly 17 uploads per 12-FPS frame, for the whole 4 to 5 min on that screen (66,690 and 74,844 such pairs in runs 2 and 3; the fix(agc): read a 32-bit depth-bits T# over guest memory through a float view #7346 runs had 1,303 and 1,721 in total, but they never reached this screen). I have no main run of this screen to compare against, so I cannot tell the title's animated background from a cost this PR adds; GPU use stayed around 2.6 GiB. Mentioning it only because of the large-surface re-upload cost @oneandonlydean measured on Astro Bot.

Logs in the details blocks (stderr trimmed to the driver lines; per-shader registration notes, missing-file opens and the 10 s counters removed), full logs on request.

run 3, warm cache, title screen until the cap: stderr (14 of 673 lines kept)
[time] scheduler tick raised to 0.5 ms (was 0.50 ms, now 0.50 ms, timed waits fine)
[write-watch] host imports of write-watched memory keep the write watch: yes (4096 of 4096 scratch pages reported written at the import, 0 by its 4-byte settle read, 0 after it, 0 after a full GPU read, 0 after a second, 0 after a GPU write, 1 after a CPU store); direct host imports of private memory are settled and stay watched, separate shared aliases retain guest write tracking
[pipeline-cache] loaded 216477.2 KiB from C:\src\nhl26\run\shader_cache\vk-pipelines-10de-2684-9a170000-234d00caf7fd8b6cadcf90d3a502d334.bin
[shader-disk-cache] C:\src\nhl26\run\shader_cache\e1a9668d4734dbb5 (source version e1a9668d4734dbb5, format 38)
[pipeline-journal] loaded 162 pipelines and 179 modules (23.8 MiB) from C:\src\nhl26\run\shader_cache\e1a9668d4734dbb5\pipelines.journal in 29 ms
[mouse] video driver windows: guest 1920x1080 presented at 2446x1376, delta scale 0.7850 x 0.7849
[memory] configured flexible memory: 0x22600000 bytes (550 MiB)
[gpu] sampled texture cache budget 13759 MiB: device-local heap 0 has a VK_EXT_memory_budget budget of 23370 MiB and uses 663 MiB, 0 MiB of it sampled textures (0 MiB in the cache) and 8 MiB cached storage images; storage image cache budget 5842 MiB
[gpu] layer/viewport index vertex exports are ignored (PA_CL_VS_OUT_CNTL=0x01240000)
[SAVEDATA:native] mount3 user=268435456 dir=Profile0 mode=0x1 blocks=256 -> 0x809f0008 point= status=0
[gpu] storage image 0x11c3de0000+0x870000: memory no longer registered; 1 pending ranges dropped
[gpu] storage image 0x11c2d00000+0x870000: memory no longer registered; 1 pending ranges dropped
[gpu] storage image 0x11c67c0000+0x870000: memory no longer registered; 1 pending ranges dropped
[gpu] storage image 0x11c7680000+0x650000: memory no longer registered; 1 pending ranges dropped
run 2, partly cold cache, no stop: stderr (14 of 674 lines kept)
[time] scheduler tick raised to 0.5 ms (was 0.50 ms, now 0.50 ms, timed waits fine)
[write-watch] host imports of write-watched memory keep the write watch: yes (4096 of 4096 scratch pages reported written at the import, 0 by its 4-byte settle read, 0 after it, 0 after a full GPU read, 0 after a second, 0 after a GPU write, 1 after a CPU store); direct host imports of private memory are settled and stay watched, separate shared aliases retain guest write tracking
[pipeline-cache] loaded 181660.6 KiB from C:\src\nhl26\run\shader_cache\vk-pipelines-10de-2684-9a170000-234d00caf7fd8b6cadcf90d3a502d334.bin
[shader-disk-cache] C:\src\nhl26\run\shader_cache\e1a9668d4734dbb5 (source version e1a9668d4734dbb5, format 38)
[mouse] video driver windows: guest 1920x1080 presented at 2446x1376, delta scale 0.7850 x 0.7849
[memory] configured flexible memory: 0x22600000 bytes (550 MiB)
[gpu] sampled texture cache budget 14108 MiB: device-local heap 0 has a VK_EXT_memory_budget budget of 23370 MiB and uses 314 MiB, 0 MiB of it sampled textures (0 MiB in the cache) and 8 MiB cached storage images; storage image cache budget 5842 MiB
[gpu] layer/viewport index vertex exports are ignored (PA_CL_VS_OUT_CNTL=0x01240000)
[SAVEDATA:native] mount3 user=268435456 dir=Profile0 mode=0x1 blocks=256 -> 0x809f0008 point= status=0
[gpu] sampled texture cache budget 12596 MiB (was 14108 MiB): device-local heap 0 has a VK_EXT_memory_budget budget of 23370 MiB and uses 2604 MiB, 345 MiB of it sampled textures (345 MiB in the cache) and 441 MiB cached storage images; storage image cache budget 5842 MiB (was 5842 MiB)
[gpu] storage image 0x11c7680000+0x650000: memory no longer registered; 1 pending ranges dropped
[gpu] storage image 0x11c7480000+0x480000: memory no longer registered; 1 pending ranges dropped
[gpu] storage image 0x11c7ef0000+0x280000: memory no longer registered; 1 pending ranges dropped
[gpu] storage image 0x11c7ef0000+0x110000: memory no longer registered; 1 pending ranges dropped
run 3, the title's one breadcrumb report (stdout)
Incomplete breadcrumb found on device 0, processQueue 0, frameIndex 165, dataIndex 0, hardware queue Main: (IFrameData*)0x1050056490
 ------- First incomplete breadcrumb - GPU Stopped somewhere near here -------
		Breadcrumb Stack: / Depth_writeHalfResZ_3d
	... the last begun renderPass was:  (fb::OpcodeBeginRenderPass*)0x11498499a8 -- renderPass name: 'Depth_writeHalfResZ_3d'
	... the last bound pipeline was:    (fb::OpcodePipeline*)0x1149849038 -- pipeline name: 'ShaderProgram_ProceduralSky'

AI-assisted: yes (Claude Code).

@AlxcBTW

AlxcBTW commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

@oneandonlydean the open points, all in 730c72177:

  • Conflict with main in Texture.cpp (25.10.): merged main f8f5b24. markLayersPending and saveBeforeUnwatch now use main's per-block generation: keepWrittenSince takes each block's generation from unitBlockGeneration, so a block stamped at a completion is compared with that stamp, as saveBeforeUnwatch does on main. The pending-layer rules of main (CommitTrims fast path, completionKept reset) are kept as they are; this PR only adds the baseline conditions.
  • Upload-hash cost (22.10. and your review of the stacked change): the hash is gone from the upload and refresh path. I took fix(agc): hash an idle unit when its range leaves the write watch instead of at every upload #10026's unwatch-hook version into this branch unchanged, with its authorship (ec6ceece9, f27fcde50, d73e64951, 43a9245e8, cfbe00cb4, 660cef6fe), so the two land together and the intermediate slow state never reaches main. This also answers the request not to merge fix(agc): save the guest bytes of results that become pending over a surface that is no longer fully write-watched #7048 alone. @winstrex this makes fix(agc): hash an idle unit when its range leaves the write watch instead of at every upload #10026 equal to this head; close it or rebase it onto this one, whichever you prefer.
  • Test change (26.10.): the unconditional Require(refused, ...) for the large UnwatchedPartialWrite case is gone (660cef6fe); the checks after it (CPU bytes intact after a refusal, GPU patch merged with the CPU rest on a store, size and path checks) stay. The production refusal stays for a unit without a baseline.
  • parl0v's Minecraft Dungeons II measurement (-19 % on the means, 4 rows each): that run was da9f0ead4, which hashed on every settled upload above 1 MiB (the shadow-atlas mirror is 32 MiB there), the cost described above. The head now takes no hash outside the unwatch hook. That title's load crash is in game code and is not this PR's; a re-measure on this head against main is welcome, I did not run one.

Evidence: no local build. Our fork's CI built and tested the same diff on our main (anyplaynet#48): the Windows job passed, https://github.com/anyplaynet/AnyPS5/actions/runs/40720659323 (full build, every registered ctest). python3 tools/check_conventions.py --base upstream/main passes. The upstream windows and linux jobs need a collaborator to start them on 730c72177.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@AlxcBTW The current composition includes #10026’s lazy unwatch hashes and its byte-based tests; the earlier eager-upload hash path is gone. The existing performance hold still needs matched main/current-head measurements before landing: PPSA21564 intro/title/menu, with the previously routed PPSA10872 logo and PPSA01825 menu evidence, plus agc_storage_pending_after_unwatch at both sizes and full CI. Please bind the runs to 730c721 and record the same machine, scene, duration, settings and warm-cache conditions for both arms. The old eager-head slowdown reports do not establish this head’s performance. #7048 and #10026 currently produce the same merged tree, so please coordinate one landing composition.

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

Re-reviewed exact head 730c721773cd3d12422b715c725a2678122e85f7. The current-main merge preview is clean, but the landing/performance blockers remain on this composition:

  • This head and open #10026 produce the same current-main merged tree. Please record which PR is the landing vehicle and how the duplicate branch will be handled, with the matching checklist/order updated; the current checklist still says Depends on: none and does not identify #10026.
  • The material slowdown previously measured for #7048 alone is not cleared by the new code composition. The latest review asks for matched current-head/main measurements on PPSA21564 intro/title/menu, the routed PPSA10872 logo and PPSA01825 menu, plus agc_storage_pending_after_unwatch at both sizes, with full CI. The old eager-head slowdown and fork CI report do not establish performance for head 730c721773.

I do not have a new source-level correctness finding. The upstream Windows workflow passes, while product Linux and GPU jobs are skipped; I did not run a local GPU build or capture. Please post exact-head results and clarify the single landing composition before requesting another review.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 29, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 730c721 merged onto main ba5b533 · tested 2026-10-29 15:03 UTC · 25 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,288 passed · 5 failed (0 new, 5 also fail on main) · 39 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,332 of 2,891 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 (895 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_bda_device 64.8 s
Failing on main too (5, not counted against this PR)
agc_compute_pipeline_inspection
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Validated but not compared (1, not in the main baseline's validation pass)
agc_storage_pending_after_unwatch
Validation IDs already present on main (4 IDs in 436 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (436 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_array_layer_target, agc_driver_array_layer_target_cpu, agc_driver_bda_anchor_reads, and 428 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
Skipped (39)
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_msaa_dcc_compared_import_2: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_4: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_8: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
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_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_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_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_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_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_fresh: 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_labels: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SD
Reproduce
git fetch origin 730c721773cd3d12422b715c725a2678122e85f7 ba5b5330b37724bca4b99f30faab504653635a04
git checkout ba5b5330b37724bca4b99f30faab504653635a04 && git merge --no-ff 730c721773cd3d12422b715c725a2678122e85f7
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.

@XatiX

XatiX commented Oct 29, 2026

Copy link
Copy Markdown

Follow-up to my earlier comment: with this PR's current head 730c721, Mewgenics (PPSA37318 01.121.220) deadlocks before it reaches the battle, so I couldn't test the refusal this PR fixes. Windows 11, RTX 5080, Release build. The mouse calls come from #12958's libSceMouse (they only touch libSceMouse and MouseInput.cpp). Each game was relinked with the relinker from the same build.

Build Result
main bb04a4f1ea + #12958 Menus OK; the first battle aborts with the refusal this PR addresses
main bb04a4f1ea + #12958 + this PR Freezes on the first title-menu hover, 2 of 2 runs
Same, with #11694 reverted Freezes on the first title-menu hover
This PR's head as-is (main ba5b533) + #12958's libSceMouse files Menu hovers work, then it freezes while loading the save (the catnames_*.txt reads)

On 2026-10-26 a local merge of the older head da9f0ead played the battle tutorial with no errors and no freeze.

The merge onto bb04a4f1ea had one conflict, the destination line in writeBackWindows against #11694. I kept both conditions there (guestBytes > SmallStoreBytes && spanWatched), and the #11694-reverted run removes that line from the question.

In each freeze the audio keeps playing but presents stop, nothing is logged, and APS5_STALL_WATCHDOG_SECS=15 never fires. gdb thread dumps of the frozen process (Release build, so the frames are partial):

Own base (730c721 + libSceMouse):

main thread:        Mutex::lockContended < GuestMemory::GpuMutexType::enter < GuestMemory::flushHook
queue worker 0x0:   Mutex::lockContended < GuestMemory::collectWrites(unsigned long long, unsigned long long, bool)

On bb04a4f1ea:

main thread:        Mutex::lockContended < GuestMemory::GpuMutexType::enter
presenter:          Mutex::lockContended < GuestMemory::GpuMutexType::enter < flushHook (commitLabel lambda on the stack)
queue worker 0x0:   DrawPipeline::Enqueue < Driver::finishDraw
one more thread:    Mutex::lockContended (caller not resolved)

It looks like a lock-order inversion. The main thread waits for the GPU mutex inside the write-watch flush hook, while the queue worker waits for a lock inside collectWrites. I couldn't see which thread holds which lock. Since da9f0ead the branch has taken #10026's unwatch-baseline work, including 43a9245e89 ("run an import's unwatch hooks after every window walk") and ec6ceece96. Running unwatch hooks from inside the window walk would fit the worker's stack, but that's a guess. I haven't bisected those commits.

The full dumps are available if they'd help.

AI-assisted: yes (Claude Code)

@EagleBearerYS

Copy link
Copy Markdown
Contributor

Thanks for the table and the thread stacks, @XatiX. They're enough to work from.

They fit a lock-order cycle between the write-watch tracker lock and the GPU mutex:

  • Queue worker 0x0 waits for a lock inside collectWrites.
  • The main thread waits for the GPU mutex inside flushHook, a hook that runs from the write-watch code.

The unwatch hook (StorageTexture::SaveBeforeUnwatch) also takes the GPU mutex. On main it does so only when a pending storage result overlaps the range that leaves the watch. #10026's ec6ceec makes it also take the mutex whenever a live storage image larger than 1 MiB overlaps that range, so it takes it much more often. That fits your guess about the da9f0ead..730c721 range.

We're mapping the lock order from the code and writing a test that reproduces the cycle without the game. If it confirms, we'll offer the fix as commits that @AlxcBTW and @winstrex can take, and ask you then for a Mewgenics (PPSA37318) rerun.

@winstrex

Copy link
Copy Markdown
Contributor

@XatiX @EagleBearerYS I reproduced the deadlock with a test that fails 3 of 3 without the game. It comes from our #10026 commits: ec6ceec makes the unwatch hook take the GPU mutex whenever a live storage image larger than 1 MiB overlaps the range, and f27fcde extends that to the unproven case.

The lock that closes the cycle is Imports().mutex. A map-time import runs on its own thread without the GPU mutex: ImportMappedRanges holds Imports().mutex and reaches the hook inside ImportWatched, which then waits for the GPU mutex. Meanwhile a thread that holds the GPU mutex waits for Imports().mutex in HostImportFor. ImportMappedRanges has a guard for that path, but it only checks for pending results overlapping the range (AnyPendingOverlaps), and it wasn't updated when the hook started taking the GPU mutex for large live images.

The fix and the test are on their way as commits for @AlxcBTW to take, so there's no need to duplicate the work.

@XatiX

XatiX commented Oct 29, 2026

Copy link
Copy Markdown

@winstrex @EagleBearerYS thanks. A RelWithDebInfo build of main bb04a4f1ea + this PR confirms the same cycle on Mewgenics. I read the GPU mutex's owner field from the frozen process:

  • Main thread: holds Imports().mutex and waits for the GPU mutex, in VulkanDevice::ImportGuestMemory > ImportMappedRanges > importAllocation > importRunAt > importPages > GuestMemory::ImportWatched > the unwatch hook > StorageTexture::SaveBeforeUnwatch.
  • Draw committer: holds the GPU mutex (its owner is this thread's TEB, depth 1) and waits for Imports().mutex, in DrawPipeline::run > commitDraw > prepareDraw > ShaderResources::revalidateFull > HostImportsUnchanged.

Before your commits arrived I'd tried a narrower stopgap. It's at XatiX/AnyPS5 fix/agc-unwatch-hash-lock-order, as this head merged onto main bb04a4f1ea plus two commits:

  • f9dc743d6c: the hash pass in SaveBeforeUnwatch takes the GPU mutex with try_to_lock and hashes nothing when another thread holds it. Those units then refuse at their store, as with no hook. The pending-unit save keeps its blocking lock. Your guard approach is better, because it keeps the hash instead of dropping it, so please don't take this one.
  • c41c1e3282: a variant of agc_storage_pending_after_unwatch (1024x1024 only) where the last block leaves the watch on a second thread while the test thread holds the GPU mutex. It requires the hook to return within 10 s and the store to refuse for want of a hash. Without the try_to_lock it fails after 10 s. It might also be useful against your fix.

With the stopgap, Mewgenics plays through the title menu, the save load, the battle tutorial (tutorialbasic.lvl) and on to levels/alley/easy/ch1-26.lvl, with no freeze and no storage refusal. Before this PR, the battle ended on that refusal. Focused ctest (agc_storage|agc_unit_shadow|agc_import_watch|agc_pending|agc_write_tracking), RTX 5080, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation (SDK 1.4.363.0, the layer loaded in every test): 23 of 23 pass on the branch and 22 of 22 on main, with no VUID- lines in either log.

I'll rerun Mewgenics on your fix once it's on this PR.

AI-assisted: yes (Claude Code)

@winstrex

Copy link
Copy Markdown
Contributor

@XatiX @EagleBearerYS @AlxcBTW the fix is 53c3cd4ee on #10026's branch.

Cause, as XatiX's read of the GPU mutex owner shows: the map-time import (ImportMappedRanges without the GPU mutex) holds Imports().mutex and runs the unwatch hook inside ImportWatched. Since ec6ceec the hook takes the GPU mutex for live storage images larger than 1 MiB, not only for pending results, but the guard on that path still checked only AnyPendingOverlaps. A GPU mutex owner then waits for Imports().mutex, in XatiX's dump the draw committer in HostImportsUnchanged.

Fix: one predicate, StorageTexture::UnwatchHookLocksGpu, now decides both when the hook takes the GPU mutex and when the map-time import without it is deferred. The map observer then redoes the deferred import with the GPU mutex taken first, so the hash is kept. The window walks and the single suspension from 43a9245 don't change.

Test: agc_unwatch_hook_lock_order maps 512 KiB inside a live 4 MiB storage image and imports it on a thread without the GPU mutex, while the test thread holds the mutex and calls HostImportFor. A watchdog fails it after 10 s. It fails 3 of 3 without the fix and passes with it. The storage, import and write-watch set passes with the validation layer (74 tests, APS5_DRAW_PIPELINE unset and set, no VUID- lines), on Windows 11 with an RTX 5070 Ti (621.01).

Cost: a temporary counter (not in the commit) counted the map-time imports the new guard defers. The Pathless (PPSA01825) up to its menu made 2,329 map imports and Hades (PPSA03355) in gameplay 3; the guard deferred none of them. Both ran without a freeze on #7048's head with the fix, as The Pathless also did without it.

@AlxcBTW could you take 53c3cd4 into #7048? On this head it conflicts in one line of ImportMappedRanges: keep your replacing || and replace AnyPendingOverlaps(span) with UnwatchHookLocksGpu(span). @XatiX once it's in, could you rerun Mewgenics (PPSA37318) on that head?

@winstrex

Copy link
Copy Markdown
Contributor

@XatiX thanks for the test and the stopgap. I ran c41c1e3 against our fix: it fails with "the unwatch hook waited for the GPU mutex", as expected. It measures your stopgap's contract (the hook never waits for the GPU mutex), not ours (the hook may wait for it when its caller holds no lock that the mutex's owner can need). In the test, the thread that holds the GPU mutex waits for the unwatching thread directly, which the driver doesn't do. The real cycle needs Imports().mutex in between, and agc_unwatch_hook_lock_order covers it.

I also checked indirect waits (memory waits, joins, idle waits). The only path that reaches the hook without the GPU mutex is the map-time import's own thread. The guest thread that joins it holds neither the GPU mutex nor the allocation registry or direct-memory lock at that point. Memory waits poll without the GPU mutex, and Driver::WaitIdle waits before taking it. With 53c3cd4 that thread no longer reaches a hook that would take the mutex.

To catch a regression of this kind, 8b14bde3c adds a check: the host imports mutex notes when it's held, and the unwatch hook aborts if it would take the GPU mutex while its thread holds that mutex without it. It's always on in builds without NDEBUG, and in release builds with APS5_ASSERT_GPU_LOCK, which agc_unwatch_hook_lock_order sets. @AlxcBTW it's a second commit to take after 53c3cd4.

@bunbeans2-arch

Copy link
Copy Markdown
Contributor

An AMD RDNA 4 run on Windows of 730c721773 as is, since the GPUs tested are lavapipe and NVIDIA. It conflicts with current main in Texture.cpp, so I built the head itself (base f8f5b24b77).

Windows 11 Pro 10.0.22631, AMD Radeon RX 9070 XT (1002:7550, driver 32.0.32015.7029, amdvlk64), MSYS2 UCRT64 g++ 16.1.0, Release. ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation; with VK_LOADER_DEBUG=layer the loader logs Insert instance layer "VK_LAYER_KHRONOS_validation" in every run of every test below that creates a Vulkan instance. 3 runs:

  • agc_driver_linear_target_cpu_writes passes 3 of 3 with linear target CPU write tests passed.
  • No VUID- line.

AI-assisted: yes (Claude Code)

@ycbuzi

ycbuzi commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Two Windows runs of this PR in titles that stop on its message. RTX 4070 SUPER, NVIDIA 616.92, Release. The base is main with #11580 (93d727afc5). I merged 730c721773 on top and swapped only libSceAgcDriver.prx.

Texture.cpp conflicts with that base, so these runs use my own resolution, not your head as is:

const auto destination = guestBytes <= SmallStoreBytes || !spanWatched ? std::optional<ShadowDestination>{} : ShadowDestinationFor(context, import, guestBegin, pieceEnd);

The libSceAgcDriver.prx build also had #11575 merged, which only touches libSceHttp2. All runs set APS5_PLAYGO_UNLISTED_CHUNKS=installed.

Title Without this PR With this PR
Scorn (PPSA07850) Stops at 270 s: storing storage image results pending over memory outside the write watch without the guest bytes saved before them is not implemented 2 runs of 12 minutes, neither stopped, 36 to 37 FPS
Hello Neighbor 2 (PPSA07426) 1 of 3 runs stopped at 90 s on the same message. The other 2 crashed at 120 s and 360 s at Hello Neighbor 2.exe+0x134ffeb, a write to address 0 2 runs of 12 minutes, neither stopped or crashed. Played by hand into the open-world area

Limits:

  • Scorn's picture: after the logos, every saved frame is black (9 of 9 across the second run, under scripted input). The message is gone but the title isn't playable, and I don't know if the black screen is related to this PR.
  • Hello Neighbor 2's crash: I can't say this PR fixes it. It may just not have been reached, and there are only 6 runs in total. The sixth, without this PR, ran 12 minutes with no stop, but its scripted input and saved frames only show it near the publisher logo, so I left it out of the table.

AI-assisted: yes (Claude Code)

@ycbuzi

ycbuzi commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

A third title for the runs above, and this one still stops with the PR: OCTOPATH TRAVELER II (PPSA09809), same machine and same libSceAgcDriver.prx (hash ca0971f95fbe), with its own conflict resolution as above.

Without the PR it stopped at 602 s on the original message, with a person playing into the first story scene. With the PR it stops at 465 s, scripted input, on the case your message now names:

AGC graphics: storing storage image results pending over memory outside the write watch without the guest bytes saved before them is not implemented (unit 0 of 1: its bytes differ from the copy taken at its upload)

So the path that compares the saved bytes refuses here. One run with the PR, and the input differs between the two runs, so I can't say the stop comes at the same point in the game. I still have this run's full log if a specific part of it helps.

Hello Neighbor 2 and Scorn did not hit this in their runs above.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.