Skip to content

perf(agc): compare sampled snapshots outside the write watch only after a write-watch mark - #5892

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
WallisSkill:fix/cat-quest-3-ppsa15065
Oct 14, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
WallisSkill:fix/cat-quest-3-ppsa15065

Conversation

@WallisSkill

@WallisSkill WallisSkill commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What

Split as asked in the review: this PR now carries only the sampled-snapshot change. The audio, storage write-back and allocator commits will come as separate PRs, in the order CnRJay suggested (allocator, storage write-back, audio), once this one is merged. My account has no merged PR yet, so the contributing rules allow only one open PR at a time.

perf(agc): compare sampled snapshots outside the write watch only after a write-watch mark. The commit is SoRadGaming's (f75234e, authorship kept), offered in their comment above. It replaces my earlier version of the same idea, because it:

  • stores the key when the snapshot is created, which saves one full compare per new snapshot;
  • has no fallback switch;
  • brings a test and counters, and updates the technical-debt entry.

The only change I made is rebasing it onto current main: the [textures] counter line now prints both the image-table counters from main and the new untracked-snapshot counters.

On Windows every direct host import leaves the write watch. A sampled texture snapshot over that memory has no write generation, so every cache hit compared the whole snapshot with guest memory. In Cat Quest III's overworld that was about 63,000 compares (150 GiB) every five seconds, two thirds of the queue worker. The snapshot is now keyed like StorageTexture::compareUntracked (#4878):

  • the write-watch hint of the range;
  • the newest driver store;
  • the forget serial;
  • the newest recorded GPU write.

It is compared again only when that key changes.

Tested

Windows 11 Pro 26200, MinGW GCC 15.2.0, NVIDIA GeForce RTX 5070 (driver 616.92).

  • agc_driver_untracked_snapshot_hint (the new test) and the agc_driver_partially_resident* tests: 3 of 3 pass; the new test passes 20 of 20 repeated runs.

  • Cat Quest III (PPSA15065): overworld, standing still after loading the save, APS5_TRACE_SECONDS=240:20, warm caches, one build, same scene in both runs.

    frame p50 / p90 / p99 ms presents in 20 s queue worker busy
    key off (main behaviour) 36.3 / 39.9 / 46.4 545 99.5 %
    this commit 9.1 / 10.3 / 12.0 2167 98.7 %

    Summary lines (APS5_PAD_INPUT_FILE removed):

    {"commit":"6786710dec77","dirty":true,"env":{"APS5_AUDIO_LATENCY_MS":"100","APS5_DBG_UNTRACKED_MODE":"0","APS5_TRACE_SECONDS":"240:20"},"gpu":"NVIDIA GeForce RTX 5070","driver":"NVIDIA 616.92","os":"Windows 10.0.26200","resolution":"1920x1080","window":{"spec":"seconds 240:20","first_present":15221,"ms":20021.077},"presents":545,"frame_ms":{"p50":36.304,"p90":39.931,"p99":46.441,"max":52.634},"gpu_busy_ms_per_present":2.271,"worker_busy_pct":{"queue worker 0x0":99.543},"top_phases_ms_per_frame":{"Driver.Packet":35.987,"Driver.Packet.draw":35.138,"Driver.Draw":35.075,"Driver.Draw.draw_and_resource_release":31.54,"Graphics.Draw":30.857,"Graphics.Draw.draw_and_resource_release":29.997,"Driver.Draw.shader_compile_and_link":2.296,"Shader.DrawStage":1.448},"trace_dropped":true,"trace":"aps5-trace-6786710dec77-19516.json.gz"}
    {"commit":"6786710dec77","dirty":true,"env":{"APS5_AUDIO_LATENCY_MS":"100","APS5_DBG_UNTRACKED_MODE":"1","APS5_TRACE_SECONDS":"240:20"},"gpu":"NVIDIA GeForce RTX 5070","driver":"NVIDIA 616.92","os":"Windows 10.0.26200","resolution":"1920x1080","window":{"spec":"seconds 240:20","first_present":27133,"ms":20000.03},"presents":2167,"frame_ms":{"p50":9.075,"p90":10.31,"p99":12.025,"max":18.678},"gpu_busy_ms_per_present":2.302,"worker_busy_pct":{"queue worker 0x0":98.669},"top_phases_ms_per_frame":{"Driver.Packet":8.834,"Driver.Packet.draw":8.135,"Driver.Draw":8.088,"Driver.Draw.draw_and_resource_release":5.354,"Graphics.Draw":5.21,"Graphics.Draw.draw_and_resource_release":4.58,"Driver.Draw.shader_compile_and_link":1.593,"Shader.DrawStage":0.888},"trace_dropped":true,"trace":"aps5-trace-6786710dec77-35268.json.gz"}

    The traces (aps5-trace-6786710dec77-19516, -35268) are attached in a comment.

  • SoRadGaming's ASTRO BOT (PPSA21564) numbers and test results are in their comment above.

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 (the comments come with the docs/dev/debt/libSceAgcDriver.md change)
  • 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
  • Unblocks: none (frame rate in PPSA15065 Cat Quest III (PPSA15065) #5888 and PPSA21564; not a stop)
  • GPUs tested: NVIDIA GeForce RTX 5070, driver 616.92, Windows 11 26200 (the tests above and the title); SoRadGaming: AMD Radeon RX 9070 XT, driver 32.0.23033.1002, Windows 11
  • AI-assisted: yes (Claude Code); I reviewed and tested every change

🤖 Generated with Claude Code

@WallisSkill

Copy link
Copy Markdown
Contributor Author

@WallisSkill
WallisSkill force-pushed the fix/cat-quest-3-ppsa15065 branch from 063c45a to 1e5d3ab Compare October 14, 2026 16:45
@WallisSkill WallisSkill changed the title fix: Cat Quest III (PPSA15065) loading, audio, black screen and overworld frame rate fix: Cat Quest III (PPSA15065) audio, black screen and overworld frame rate Oct 14, 2026
@SoRadGaming

Copy link
Copy Markdown
Contributor

Independent confirmation of the third commit (ea8a7b9, sampled snapshots over untracked memory), plus a test for it. I had written the same change for ASTRO BOT (PPSA21564) before seeing this PR, and only measured that commit, not the other three.

Host: Windows 11 Pro 26200, Ryzen 7 5800X3D, Radeon RX 9070 XT (driver 32.0.23033.1002), WinLibs GCC 15.2.0, Release.

Effect on ASTRO BOT's title screen

main with the snapshot hint
frame p50 1183.5 ms 1142.2 ms
queue worker 0x0 busy 1162 ms per frame 1127 ms per frame
Graphics.Draw 814 ms per frame 745 ms per frame
Resources.Complete 421 ms per frame 357 ms per frame

A test

It is in SoRadGaming/AnyPS5@f75234e9d, as an --untracked-hint mode of agc_driver_partially_resident_snapshot_tests, registered as agc_driver_untracked_snapshot_hint (Windows; skipped elsewhere). The surface leaves the watch through GuestMemory::Unwatch, and the flush hook counts the guest reads of each lookup. It checks:

  • 50 collect epochs without a store make no compare;
  • a CPU write takes a new snapshot;
  • a store of the same bytes costs one compare and keeps the snapshot;
  • a MarkWritten driver store costs one compare and keeps it.

Results:

  • On main it fails at the first epoch without a store.
  • With my version it passed 30 of 30 runs.
  • With ea8a7b9 every check holds except one: after each new snapshot, the first lookup still compares once. That is because the stamp is stored only on a hit.

My version takes the key before the snapshot is read and stores it with a new entry, unless Recorder::SnapshotWriteOverlaps reports a recorded write over the surface. That saves one full compare per new or refreshed snapshot. Feel free to take the test, the store-at-creation part, or both.

ctest -R "agc_|guest_memory|write_watch|write_tracking": 537 of 538 pass with my version. agc_driver_layered_volume_vertex_no_gpl ("slice 1 texel (0, 0) channel 0 is 64, expected 255") fails the same way on main on this GPU.

AI-assisted (Claude Code); the builds and runs are on my machine.

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

These four commits are separate changes to three libraries. CONTRIBUTING asks for one topic per PR, and here it matters beyond form: each part has a different risk and a different reviewer.

  • Commit 3 (snapshot compares over untracked memory) is the clear win: 44 → 11 ms p50 here. SoRadGaming independently confirmed it on ASTRO BOT with a test. On its own it would be easy to review against #4878's compareUntracked stamp, which it reuses.
  • Commit 4 (a per-thread cache of freed blocks behind the driver's operator new/delete) has the widest reach: it replaces the global allocation operators of libSceAgcDriver on Windows. It needs a native TLS slot, --exclude-symbols so other libraries don't bind to the operators through the import library, and a size-class choice based on _msize. That deserves its own PR and its own look at blocks that cross module boundaries (allocated in one .prx, freed in another), plus thread exit (what happens to a thread's cached blocks when the thread ends).
  • Commit 2 (latest pending writer, and superseding aliased units) changes which results FindPending returns and which get stored. That is a correctness change to the storage write-back that every title relies on, and APS5_NO_ALIAS_SUPERSEDE=1 is a new switch to keep the old behaviour. Maintainers have asked for that kind of fallback switch to stay off main.
  • Commit 1 (AudioOut per-port locks, THREAD_PRIORITY_HIGHEST, APS5_AUDIO_LATENCY_MS) is audio, with a new tuning variable.

Splitting them in this order (3, then 4, then 2, then 1), each with its own Tested section, would let commit 3 land quickly. The others would then get the scrutiny they need. The frame-rate table already isolates commits 3 and 4 through their switches, so that evidence carries over.

@WallisSkill
WallisSkill force-pushed the fix/cat-quest-3-ppsa15065 branch from 1e5d3ab to 3379246 Compare October 14, 2026 17:55
@WallisSkill WallisSkill changed the title fix: Cat Quest III (PPSA15065) audio, black screen and overworld frame rate perf(agc): compare sampled snapshots outside the write watch only after a write-watch mark Oct 14, 2026
@WallisSkill

Copy link
Copy Markdown
Contributor Author

@CnRJay thanks, split as suggested. This PR now carries only the sampled-snapshot change. The allocator, storage write-back and audio commits are ready on their own branches and will follow in that order once this one lands; my account can have only one open PR until one is merged. The storage write-back one no longer has APS5_NO_ALIAS_SUPERSEDE.

@SoRadGaming thanks for the test and the store-at-creation version. I took your commit as is, with your authorship, and only rebased it onto current main: the [textures] counter line now prints main's image-table counters and your untracked-snapshot counters. Your test passes here 20 of 20 runs on an RTX 5070. I also tried keying hint-less ranges by the collect epoch, as compareUntracked does: 8.98 against 9.08 ms p50 in Cat Quest III, within noise, so I left it out. The Cat Quest III numbers for this commit are in the description; the traces follow in the next comment.

@WallisSkill

Copy link
Copy Markdown
Contributor Author

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the key logic against StorageTexture::compareUntracked (#4878): the key is taken before the snapshot is read or compared, a snapshot read over pending recorded writes gets no key, and an odd forget serial yields no key, so the fallback is the byte compare. The debt entry matches.

The only conflict with main was the adjacent line in docs/dev/debt/libSceAgcDriver.md; I rebased and resolved it. agc_driver_untracked_snapshot_hint is Windows-only (skips on Linux), so the Windows build is the gate; merging once it is green.

…er a write-watch mark

On Windows every direct host import leaves the write watch, and a sampled
texture snapshot over it was compared with guest memory at every lookup:
its generation stays 0, so UnchangedSince never holds. ASTRO BOT
(PPSA21564) compared about 31 GB per 10 s at the title.

The lookup now keys such a snapshot by UntrackedHint, NewestStamp,
ForgetSerial and the recorder's newest write note, taken before the
snapshot is read or compared, as StorageTexture::compareUntracked does,
and skips the compare while the key is unchanged and no recorded work
writes the surface. The byte compare still decides after any mark.

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

Copy link
Copy Markdown
Collaborator

main moved again (#5906 changed the same technical-debt line in docs/dev/debt/libSceAgcDriver.md). I rebased and kept both statements in that entry: main's "snapshot reuse limited to draws of one collect epoch" and this PR's sampled-snapshot wording. No code changed. libSceAgcDriver build and ctest are green here except agc_driver_tessellation, which also segfaults on main; agc_driver_untracked_snapshot_hint is Windows-only, so merging once the Windows build is green.

@adriadam10
adriadam10 force-pushed the fix/cat-quest-3-ppsa15065 branch from 5121acc to 404075b Compare October 14, 2026 21:33
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

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

Pull request Conflicting files (lines on main) Same exports Same new files
#6060 docs/dev/debt/libSceAgcDriver.md L58-59

@adriadam10
adriadam10 merged commit 922abe6 into boykopovar:main Oct 14, 2026
8 checks passed
ivica00 added a commit to ivica00/AnyPS5 that referenced this pull request Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants