Skip to content

fix(agc): keep resident linear color target results pending on the GPU - #9276

Closed
KarthikeyaAnna wants to merge 1 commit into
boykopovar:mainfrom
KarthikeyaAnna:fix/linear-target-per-draw-store
Closed

KarthikeyaAnna wants to merge 1 commit into
boykopovar:mainfrom
KarthikeyaAnna:fix/linear-target-per-draw-store

Conversation

@KarthikeyaAnna

Copy link
Copy Markdown
Contributor

What

Keep resident linear color target results pending on the GPU until explicitly accessed, presented, or idle, matching the behavior of tiled resident targets.

PR #6892 added a resident image for linear color targets over imported memory, but keepRecordedDraw unconditionally called StorageTexture::FlushPending after every single draw. In titles such as METAL GEAR SOLID Master Collection Vol. 1 (PPSA14363) whose PS1 emulator draws into three 24 MiB linear targets with up to 80 draws per frame, this forced thousands of unnecessary whole-target write-backs over PCIe (72-76 GB per 10 s) and repeated pending-write synchronizations, dropping framerates from 60 to 10 FPS (#9217). Furthermore, eager per-draw whole-target write-backs could overwrite CPU stores made after Draw returned (#8152).

Resident linear targets now keep their drawn results pending on the GPU rather than flushing eagerly on every draw. They are stored to guest memory as needed by existing access paths, waits, flips, and idle stores.

Expands tests/execution/LinearTargetResident.cpp with 15 comprehensive edge-case tests:

  1. Single recorded draws leaving results pending until guest read synchronization (ExpectSingleDraw).
  2. Consecutive recorded draws in the same batch with scissor partitioning (ExpectConsecutiveDraws).
  3. Draws across separate batches without re-uploading stale guest memory (ExpectDrawAcrossBatches).
  4. Multiple sequential draws across partitioned vertical columns (ExpectMultipleSequentialDraws).
  5. Idle stores (StoreIdleResults) flushing pending storage textures to guest memory (ExpectStoreIdleResults).
  6. CPU writes with GuestMemory::Write preserved alongside preceding and subsequent draws (ExpectCpuWritesWithGuestMemory).
  7. Direct CPU writes to guest memory outside the scissor rectangle preserved without being overwritten by GPU stores (ExpectDirectCpuWriteAfterDraw).
  8. Additive blending across consecutive draws into resident linear targets verifying VK_ATTACHMENT_LOAD_OP_LOAD and GPU blend math (ExpectAdditiveBlending).
  9. Color channel write masks (colorWriteMask) preserving unmasked channels without corruption (ExpectColorWriteMask).
  10. Multiple overlapping scissor regions accumulating without dropping texels (ExpectOverlappingScissors).
  11. Multiple linear resident targets simultaneously active and independently flushed (ExpectMultipleLinearTargets).
  12. Partial guest read synchronization properly flushing the storage texture and updating host memory (ExpectPartialGuestRead).
  13. Zero-area / empty scissor draws leaving existing target contents untouched (ExpectEmptyScissorDraw).
  14. Target reallocation / destruction without guest read releasing storage textures cleanly without validation errors or resource leaks (ExpectTargetReallocationWithoutRead).
  15. Direct CPU writes interleaved with subsequent GPU draws preserved without being overwritten by GPU stores (ExpectDirectCpuInterleavedDraw).

Tested

  • python3 tools/check_conventions.py --base origin/main passes with zero violations.
  • ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ctest --test-dir build -R agc_driver_linear_target_resident -V passes with 0 validation errors on NVIDIA GeForce RTX 2050 (Vulkan 1.3, driver 570.86.16).
  • linear_display_buffer and related resident tests pass.

Checklist

@github-actions github-actions Bot added perf Performance change perf numbers missing perf pull request without a before and after summary line unblocks title Names a title ID on the Unblocks line labels Oct 21, 2026
@github-actions

Copy link
Copy Markdown

This pull request is labeled perf, so it needs the summary line of a capture on main and one with the change, from the same title, scene and machine, as the Performance report form asks. Please paste both aps5-summary.jsonl lines, attach the two traces and add the output of tools/trace_digest.py.

PR boykopovar#6892 added a resident image for linear color targets over imported memory, but keepRecordedDraw unconditionally called StorageTexture::FlushPending after every single draw. In titles such as METAL GEAR SOLID Master Collection Vol. 1 (PPSA14363) whose PS1 emulator draws into three 24 MiB linear targets with up to 80 draws per frame, this forced thousands of unnecessary whole-target write-backs over PCIe (72-76 GB per 10 s) and repeated pending-write synchronizations, dropping framerates from 60 to 10 FPS. Furthermore, eager per-draw write-backs could overwrite CPU stores made outside the scissor after Draw returned.

Like tiled resident targets, resident linear targets now keep their drawn results pending on the GPU rather than flushing eagerly on every draw. They are stored to guest memory as needed by existing access paths, waits, flips, and idle stores.
@KarthikeyaAnna
KarthikeyaAnna force-pushed the fix/linear-target-per-draw-store branch from f9cf0a8 to a8475a6 Compare October 21, 2026 17:21
@KarthikeyaAnna KarthikeyaAnna changed the title perf(agc): keep resident linear color target results pending on the GPU fix(agc): keep resident linear color target results pending on the GPU Oct 21, 2026
@github-actions github-actions Bot removed perf Performance change perf numbers missing perf pull request without a before and after summary line labels Oct 21, 2026

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

The diagnosis in #9217 is a real cost (72-76 GB per 10 s of write-backs from a flush after every draw), but the change as submitted is one deleted loop in keepRecordedDraw plus tests, and it moves the correctness burden to a place that can't carry it on every host.

  • The eager FlushPending after each draw was the guarantee that a CPU reader of a resident linear target sees the results. The description says the results are now stored "as needed by existing access paths, waits, flips and idle stores". Those paths are triggered by the write watch and the read hook. On Windows, host imports sit outside the write watch (that is the premise of #6960 and the decideImportWatch rule), so a CPU read of the target's memory cannot be intercepted and nothing causes the store. For a PS1 emulator that draws into linear targets, the question is whether anything reads those targets back on the CPU (readback for effects, screenshots, savestates, its own VRAM view). Please say what a CPU read of an unwatched linear target does after this change, with a test for it on a host-imported (unwatched) block; the new cases build their memory with Block, which follows WriteWatched(), and the description doesn't say that any of them runs over an unwatched import.
  • The performance claim is not measured. The motivation is MGS Master Collection (60 to 10 FPS, 72-76 GB per 10 s), and the PR's evidence is ctest on an RTX 2050 running a Linux 570 driver. There are no MGS summary lines, no write-back volume after the change and no frame rate. Please post the aps5-summary.jsonl lines of main and this PR from the same scene in PPSA14363, and the bytes written back per 10 s, per the Performance report form.
  • "Idle stores" run when the queue goes idle. A title that draws all frame and is never idle then holds three 24 MiB targets pending for the whole run, plus whatever each later pending-write check costs. Show a pending count and size over time in the MGS scene so the held memory and the sync work are visible.
  • #8246 ("preserve CPU writes after resident linear target draws", 1,686 lines) addresses #8152 as well, and this PR says it fixes #8152 too. The two should be reconciled by their authors before either lands; as it stands one of them is wrong about who owns that guarantee.
  • The test file grows by about 15 cases, 40+ lines of programs and blend helpers, for a one-line production change. Several (blend modes, write masks, scissor partitioning) test the draw path rather than this change. The description should say which cases fail with the deleted loop restored, since those are the ones that show the behavior.

@github-actions

github-actions Bot commented Oct 21, 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 1191 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 1368 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 1395 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 4072 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5431 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5441 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5499 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5609 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6188 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6202 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6539 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6946 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6947 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7256 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7275 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7310 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 8246 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/execution/LinearTargetResident.cpp
PR 8564 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 8992 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9020 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp

@github-actions github-actions Bot added the fix error Closes an issue (Fixes #N) label Oct 21, 2026
@KarthikeyaAnna
KarthikeyaAnna deleted the fix/linear-target-per-draw-store branch October 22, 2026 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix error Closes an issue (Fixes #N) unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AGC: storing a resident linear color target after every draw keeps PPSA14363's PS1 games at about 10 fps; without it they run at 60

2 participants