Skip to content

perf(agc): hand aliased storage memory over without the guest round trip - #4286

Closed
AlxcBTW wants to merge 2 commits into
boykopovar:mainfrom
AlxcBTW:perf/alias-handover
Closed

AlxcBTW wants to merge 2 commits into
boykopovar:mainfrom
AlxcBTW:perf/alias-handover

Conversation

@AlxcBTW

@AlxcBTW AlxcBTW commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Sword of the Sea (PPSA25344) menu, with host imports write-watched: frame time 369 → 326 ms, GPU 128 → 104 ms per frame (and 495-517 → 355-362 ms with the unit shadow on). With Windows' default compare-mode imports the change is neutral; see Configurations below.

What

Unreal's render graph aliases transient resources: the memory of a storage image A that still has results pending is handed to a surface B of another format and shape in the same frame. StorageTexture::Refresh of B stored A's results first and then uploaded B from that memory. That is a retile and a store through guest memory (PCIe on host-imported memory), then a detile back, at every hand-over. On Sword of the Sea most of the frame's storage write-backs and refresh uploads were these round trips, and the refresh waited for them.

Where B's content does not come from guest memory, A's results now stay pending:

  • Clear keys: B's DCC keys are a clear code its format can hold, so B's upload is a GPU clear.
  • On the device: B's keys are uncompressed over host-imported memory, so B takes the units from A on the device: A's results are retiled into a device buffer and detiled into B. These are the bytes A's store would have written.

Only 64 KiB block units qualify, and only when:

  • the unit lies wholly inside B, and B's write-back stores it whole;
  • B holds no results of its own in it;
  • on the device, A's element also covers the unit whole and nothing stamped it since A's generation.

What happens to them afterwards:

  • Read first: if anything reads the memory first (the flush hook, another image's refresh), they are stored as before. So is a key change after the flush, which makes B's content come from memory again.
  • Superseded: once B's own results cover the block (markLayersPending), they are dropped. B's write-back then overwrites every byte they would have stored.
  • Stored later: a later store of them advances B's units past its stamps, while A still holds the results it handed over. If A wrote again since, its newer results are stamped as before.
  • Proof: B's refresh proof is not broken by those units alone.

With APS5_PROFILE_DRAW, a [storage] hand-over line counts the units every 10 s. There is no switch: the old order is kept wherever B's content comes from guest memory.

Measured

Sword of the Sea menu, Windows 11, RTX 5070 Laptop (AMD iGPU hidden). Two builds of main a11c8f7 plus the lab's local stack, one with and one without this commit; the libraries are otherwise the same. Rows of 180 s, alternating, with the figures per 10 s:

frame / GPU / worker ms FPS storage write-backs at refreshes uploads after a flush uploads from memory
without (a) 378 / 129 / 376 2.67 / 2.74 6394 MiB 4818 MiB 7050 MiB
with (a) 319 / 104 / 318 3.08 / 3.12 2870 MiB 2645 MiB 5124 MiB (+ 3054 MiB on the device)
without (b) 359 / 128 / 357 2.80 / 2.88 7332 MiB 5515 MiB 8053 MiB
with (b) 333 / 105 / 332 3.03 / 3.12 3026 MiB 2816 MiB 5369 MiB

Per 10 s with the change: 85.6 k units left pending under a refreshed surface (48.9 k of them taken on the device), 64.8 k superseded by the surface's own results, 3.7 k stored later, no fallbacks.

The picture at 1:1 does not change. The menu's dark specks grow at the same rate per frame with and without the change: 0.027 % per frame without, 0.026 % with. Interpolated to the same frame, the dark share of the sky band is 12.2 % and 12.1 % at frame 431, and 16.2 % and 16.0 % at frame 589. The top luma is 74 → 72 without the change and 75 → 71 with it. The specks are the depth fast clear that #3816 addresses, unrelated to this change.

Before this shape, an experimental build with a verify mode stored everything as before and checked every hand-over decision against what a later reader of the memory saw. It reported 0 mismatches in 4.26 M checked blocks over two 180 s runs.

Configurations

The rows above ran with host imports kept write-watched (APS5_WRITE_WATCH_IMPORTS=watch with a local change that lets it apply to Windows direct imports) and APS5_NO_UNIT_SHADOW=1. The hand-over removes write-backs and refresh uploads that watched imports trigger. Measured again on main d146ec1 vs main + this change, 180 s rows, alternating:

configuration main frame ms with this change recorded-store syncs per 10 s
watched imports, unit shadow on 495 / 517 355 / 362 ~30,000 → ~6,300
Windows defaults (compare-mode imports, unit shadow on) 301 / 285 292 / 292 ~35 → ~35 (no change)

So on Windows with default settings this change neither helps nor hurts this title; the gain applies where imports stay watched, which on Linux is the case when the import probe finds that the driver does not resolve write protection. A Linux measurement would show which applies there.

Tested

Windows 11 (26200), WinLibs GCC 15.2.0, Release, rebased onto main d146ec1.

  • agc_driver_alias_handover_memory fails on main with the refresh of a surface under clear keys stored the pending results of the image it aliases over its memory, and passes with this change. A 256x256 RGBA8 image A has results pending; a 128x128 RGBA16F surface B under 0000 keys over A's first two units is refreshed. Afterwards guest memory still holds the old bytes there and A stays pending. B's own results then supersede A's two units, and guest memory ends with B's results over B and A's elsewhere.
  • agc_driver_alias_handover covers B under clear keys and B from memory (taken on the device), each with three orders: A never read again, read before B wrote, and written again after the hand-over. Each order runs in unwatched and watched memory, with and without the hand-over (SetAliasHandOverForTests). The guest bytes and B's texels are the same both ways, and the hand-over counts are the expected ones: units handed over, superseded and stored later.
  • GPU suites on NVIDIA (RTX 5070 Laptop, AMD iGPU hidden), ctest -R "^agc_": 424/424 pass, including both agc_driver_alias_handover tests. Before the rebase, on main bab7b32: 420/422 (agc_write_tracking and agc_driver_prepared_shaders_registered_workgroup_sync failed the same way without this change).
  • Non-GPU suite (Lenovo node, ctest -E "^agc_driver|^video_out|^guest_video", on d146ec1 plus this change): 582/583 pass. log_macros fails there ("a short line differs"); the change does not touch it.
  • Conventions: tools/check_conventions.py --base origin/main passes.

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 in TechnicalDebt
  • 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
  • AI-assisted: yes (Claude Code)

🤖 Generated with Claude Code

A title's transient allocator (UE's RDG transient aliasing) gives the memory of a storage image A
that still has results pending to a surface B of another format and shape. Refresh of B stored A's
results first (a retile and a store through guest memory, PCIe on host-imported memory), then
uploaded B from that memory, at every such hand-over; on Sword of the Sea most of the frame's
storage write-backs and refresh uploads were these round trips.

Where B's content does not come from guest memory, A's results now stay pending:
- B's DCC keys are a clear code its format can hold: the upload is a GPU clear;
- B's keys are uncompressed over host-imported memory: B takes the units from A on the device (A's
  results retiled into a device buffer, detiled into B), the bytes A's store would write.
Only 64 KiB block units that lie wholly inside B, that B's write-back stores whole, that B holds no
results of its own in and, on the device, that A's element covers whole and nothing stamped since
A's generation. They are stored as before when anything reads the memory first (the flush hook,
another image's refresh; a key change after the flush stores them first), dropped once B's own
results cover the block (markLayersPending: B's write-back then overwrites every byte they would
have stored), and a late store of them advances B's units past its stamps while A still holds the
results it handed over. B's refresh proof is not broken by those units alone.

With APS5_PROFILE_DRAW a "[storage] hand-over" line counts the units every 10 s.

Test: agc_driver_alias_handover (B under clear keys and from memory; A never read again, read
before B wrote, written again after the hand-over; unwatched and watched memory): the same guest
bytes and the same texels of B with and without the hand-over (SetAliasHandOverForTests), and the
expected units handed over, superseded and stored later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013fa2MbmdFLVr8YHuVjucHE
@github-actions

github-actions Bot commented Oct 12, 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
#3874 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp L1974

@Scramblrrrr

Copy link
Copy Markdown
Contributor

Preparing a Windows native cross-test of your fd0bfc6 change, with your author retained. It applies cleanly in our newer-main native-test checkout: https://github.com/Scramblrrrr/AnyPS5/tree/codex/alias-hand-over-native-cross-test , head 83550be (parent 2da9b8f, containing our warm host-hole test).

Not built, tested or staged yet. I will run both alias hand-over fixtures plus nearby storage/recorder checks after the active 007 run. The actual game still uses our older runtime-image-table integration, so this native branch is not a title-running stack. Reviewing #3813 and the full final #3777 chain as prerequisites for updating that integration; no competing implementation.

AI-assisted: Codex. No title data uploaded.

@AlxcBTW

AlxcBTW commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

@Scramblrrrr thanks for cross-testing. A note so the numbers line up: the gain depends on the import mode, see the Configurations section I added to the description. With Windows' default compare-mode imports the change was neutral on our title (301/285 vs 292/292 ms). With imports kept write-watched it was 495-517 → 355-362 ms. So if your title stack runs default imports, expect the fixtures to pass and little frame-time change; a watched-import config, or Linux where the probe leaves imports watched, is where the hand-over shows.

AI-assisted: yes (Claude Code)

@Scramblrrrr

Copy link
Copy Markdown
Contributor

Source-only follow-up prepared on the actual 007 integration: https://github.com/Scramblrrrr/AnyPS5/tree/codex/first-light-alias-hand-over-cross-test , head 97737cb (full hash available on branch).

Starting from the tested/running clip-cull head 188d801, this carries the complete merged #3777 chain (including global-idle scoping and waking queue zero), the complete merged #3813 lifetime/tests chain, then #4286 fd0bfc6. All original authors retained; the carries applied cleanly through three-way integration. No local depth-reuse bypass added.

Not built, tested or staged yet. The current game remains graphics 188d801 with the fresh clip cache. Native storage lifetime, idle-writeback, alias hand-over and nearby recorder checks are next after this run. The separate newer-main native branch 83550be also remains prepared, not tested. No FPS claim for this storage stack.

AI-assisted: Codex. No title data uploaded.

@Scramblrrrr

Copy link
Copy Markdown
Contributor

Windows native cross-test on the actual 007 integration passed 13/13 selected tests in 9.38 s, no skips, on RTX 5070 Ti / driver 617.14. Head 97737cb, branch https://github.com/Scramblrrrr/AnyPS5/tree/codex/first-light-alias-hand-over-cross-test . Carries #4286 fd0bfc6 after the complete #3777 and #3813 chains, on our tested clip/cull integration. Original authors retained.

Both alias_handover and alias_handover_memory passed, as did storage_upload_lifetime, storage_upload_failure, store_idle_results, graphics, BDA, GPU buffer formats, draw-cache reuse with GPL on/off and all three clip/cull modes. This is the retail integration branch; the separate newer-main branch 83550be remains untested.

Not staged into 007 yet and no 007 FPS claim. The previous game run reached the rendered menu and then hit existing #2975 during campaign loading, so I am adding the already merged #3523 before the next run. That source-only follow-up is 903d503 and is building now.

AI-assisted: Codex. No title data uploaded.

@blawesom

Copy link
Copy Markdown
Contributor

Pre-review found a deterministic test failure on Linux/RADV — flagging early since full CI doesn't run automatically on PRs.

Independent pre-review (CachyOS, kernel 7.2.9, GCC 16.2.1, Ninja, Release, BUILD_TESTING=ON, AMD RX 9070 XT / RADV GFX1201):

  • The PR builds and tools/check_conventions.py passes.
  • agc_driver_alias_handover fails 5/5 runs (the --alias-handover-memory-only sibling passes):
AGC graphics: alias hand-over (watched), B under clear keys, A read before B wrote, Off: A read again as A lost its results

The failing assertion is the imageHolds(imageA, 256, 256, red) check after imageA->Refresh() — A loses its results after the alias hand-over on this setup. Along the way the log shows storage image …: guest memory changed while GPU results were pending; keeping the CPU's blocks several times, which may be related to the watched-memory path.

Everything else in the suite passes (922/923 — only this test). Since the assertion is GPU-result visibility, this may be driver/timing-sensitive (RADV here) — if it's green on your setup, it's worth a look at whether the read-back path has a race the RADV timing exposes. Reproduce with:

ctest -R agc_driver_alias_handover --output-on-failure

Happy to provide the full test output or run any variant that would help narrow it down.

@AlxcBTW

AlxcBTW commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

@blawesom thanks, that's exactly the kind of check this needed. It's notable that the failing arm is Off (hand-over disabled, i.e. the old order), in watched memory, after A was read before B wrote. Together with the guest memory changed while GPU results were pending; keeping the CPU's blocks lines, that points at the watched-memory path on Linux treating the driver's own stores as CPU writes and dropping A's results. If so, it's an assumption in the test's Off expectations that holds on Windows but not on Linux, rather than a race in the hand-over. I'm reproducing it on Linux now (WSL2 with lavapipe first) and will either fix the test's expectations for that arm, or the path itself if it's a real bug, and report back here. The full log of the failing run would help. Thanks for the offer.

AI-assisted: yes (Claude Code)

… imports

The watched arm forced host imports to stay write-watched. Linux compares the imports of a device
whose own reads and writes of an import resolve the write protection (the import probe), and there
the forced watch reads the device's accesses as CPU stores: RADV imports host memory as a writable
amdgpu userptr, which every command submission faults in writable again, so each collect after a
submission stamps the whole import; lavapipe writes the import with the CPU. Results under those
stamps give way to the CPU's bytes, so the Off order lost A's results on RADV (A read before B
wrote, then A read again) and B's on lavapipe, in a configuration main never runs on these devices.

On Linux the watched arm now follows the probe and reports "host imports are compared on this
device" where it compares. Windows keeps the forced watch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013fa2MbmdFLVr8YHuVjucHE
@AlxcBTW

AlxcBTW commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

@blawesom thanks, the 5/5 made this easy to pin down. The hand-over isn't involved, and main is fine in the configuration it actually runs on your card: the test's watched arm forced a configuration main never uses on RADV. Fixed in 0a82d7b.

Cause. The watched arm called SetImportWatch(ImportWatch::Watch), which overrides the import probe. On Linux, ProbeImportWriteProtection checks whether the device's own reads and writes of a host import resolve the userfaultfd write protection. If they do, imported ranges are compared instead of watched, and your output should have [write-watch] host imports resolve write protection: yes (...) near the top.

On amdgpu they do. RADV imports a host pointer as a userptr BO without the read-only flag (libdrm's amdgpu_create_bo_from_user_mem passes ANONONLY | REGISTER | VALIDATE). Every command submission that carries it faults all of its pages in writable again: amdgpu_cs_parser_bos → amdgpu_ttm_tt_get_user_pages → hmm_range_fault with HMM_PFN_REQ_WRITE. With async uffd-wp, that write fault only clears the WP bit (do_wp_page), so the next PAGEMAP_SCAN reports every page of the import as written. The tracker then stamps all of A's blocks as CPU stores, although no byte changed.

In "A read before B wrote, Off", A's refresh sees CPU stores over the units it still holds pending (the half B doesn't cover). The drop rule gives those blocks to the CPU, which is your keeping the CPU's blocks line. A re-uploads the 0x55 fill there, and the check fails. That log line alone isn't the signal, by the way: the unwatched arm prints it four times on NVIDIA and on lavapipe too.

Fix. On Linux the watched arm now follows the probe. Where imports are compared, it prints host imports are compared on this device: alias hand-over (watched) not tested; the unwatched arm and agc_driver_alias_handover_memory run as before. Windows keeps the forced watch, which passes on NVIDIA. There is no driver change.

Verified.

  • Windows 11, RTX 5070 Laptop. A diagnostic build (not in the branch) emulates amdgpu. It marks imports as stamping their pages at import, as the probe reports on RADV, and after each submission it write-faults every page of the import without changing a byte. With it, the test fails exactly as on your machine, 3/3: alias hand-over (watched), B under clear keys, A read before B wrote, Off: A read again as A lost its results, with the same log line. Without the emulation it passes.
  • Linux, lavapipe (GitHub runner, kernel 6.17; diagnostics run). The probe reports yes (0 of 4 scratch pages written after a GPU read, 4 after a GPU write, 0 after the import), so lavapipe compares its imports too. With the fix, the watched arm reports "not tested" and both alias tests pass. Forced to watch (the old test), lavapipe fails as well, at a different check: A read before B wrote: B's results did not reach guest memory under Off. Its copies into the import are CPU stores.
  • Full suites. ctest -R ^agc_ on Windows/NVIDIA: 424 of 424 passed. Fork CI with 0a82d7b merged into main 2ca6198: Linux/lavapipe 940 of 940 passed (check_skips green), Windows 934 of 934.
  • tools/check_conventions.py passes.

Could you rerun ctest -R agc_driver_alias_handover -V on RADV with 0a82d7b? The watched arm should now report "not tested". Please also paste the [write-watch] host imports resolve write protection line, so I can check the probe's numbers on GFX1201.

AI-assisted: yes (Claude Code)

@blawesom

Copy link
Copy Markdown
Contributor

Reran on RADV with 0a82d7b as requested — the fix works here.

The probe line on GFX1201:

[write-watch] host imports resolve write protection: yes (4 of 4 scratch pages written after a GPU read, 4 after a GPU write, 4 after the import); imported ranges are compared
[write-watch] dma-buf imports keep write protection: yes (0 of 4 scratch pages written after a GPU read, 0 after a GPU write, 0 after the import, 1 seen after a CPU store); imported ranges stay watched

ctest -R agc_driver_alias_handover -V (same machine as the original 5/5 failure — CachyOS, kernel 7.2.9, GCC 16.2.1, Release, RX 9070 XT / RADV GFX1201):

  • agc_driver_alias_handover: Passed (0.42 s) — the watched arm now reports host imports are compared on this device: alias hand-over (watched) not tested, exactly as predicted
  • agc_driver_alias_handover_memory: Passed

Full suite: 923/923 passed (was 922/923 — the only failure is gone). Conventions still pass.

For the record on the original failure mode: the machine reports 4 of 4 ... after a GPU read — i.e. amdgpu's HMM_PFN_REQ_WRITE fault-in on every submission, matching your root-cause analysis. The old test forcing ImportWatch::Watch over the compare-mode probe was indeed testing a configuration this device never runs.

@AlxcBTW

AlxcBTW commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

@blawesom thanks for the rerun. The probe numbers match the mechanism: 4 of 4 pages are reported written after a GPU read, so RADV compares its imports, and the watched arm correctly reports "not tested" there. From our side the PR is ready: Windows/NVIDIA, Linux/lavapipe (fork CI) and now RADV GFX1201 all pass.

AI-assisted: yes (Claude Code)

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Two things before this can be tested:

  • Texture.cpp:1626 and Texture.cpp:2174 flush with the hand-over filters and then flush again without them when the keys turn out different (the fallbacks counter). Falling back after the fact isn't allowed here; please choose the path from the keys before the first flush, or throw.
  • Which configuration that main selects by itself gets faster? Windows defaults are neutral (301/285 vs 292/292 ms), the 495-517 → 355-362 ms row forces watched imports, and blawesom's probe line shows RADV compares host imports as well. For +930 lines in StorageTexture it needs a before/after on a title in such a configuration.

@AlxcBTW

AlxcBTW commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

@oneandonlydean fair points, and the second one decides it. In the configurations main selects by itself I have no gain to show:

  • Windows defaults (compare mode, unit shadow on): neutral. Your 301/285 vs 292/292 ms. A storage census on Sword of the Sea shows why. The hand-over cuts storage-refresh stores from about 8.5 GB to 3.2 GB per 10 s, but with the unit shadow those round trips stay in VRAM and cost almost nothing, so FPS and GPU busy are unchanged (92 vs 91 ms GPU per frame).
  • The 495-517 → 355-362 ms row forces watched imports, which main doesn't choose on Windows.
  • Linux: lavapipe and RADV (blawesom's probe) both compare host imports, so they fall in the same case as Windows. I have no Linux machine where the probe keeps imports watched.

So +930 lines in StorageTexture for a path no default configuration benefits from isn't worth carrying, and the after-the-fact fallback you flagged would need a redesign on top. I'm closing this. If a configuration turns up where main watches imports and storage round trips show in the frame time, I'll come back with a before/after from that configuration and the path chosen from the keys up front. Thanks for the careful review, and thanks again to @blawesom for the RADV runs.

AI-assisted: yes (Claude Code)

@AlxcBTW AlxcBTW closed this Oct 12, 2026
@AlxcBTW
AlxcBTW deleted the perf/alias-handover branch October 12, 2026 18:57
0xcrstn pushed a commit to 0xcrstn/AnyPS5 that referenced this pull request Oct 15, 2026
Cherry-picked from fd0bfc6 (boykopovar#4286) and ported to
current main: the watched-memory and hand-over paths drop the layers'
saved originals through layerOriginal and cancel stored saves as main
does, and the WatchedImportsStampPages condition main removed is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0xcrstn added a commit to 0xcrstn/AnyPS5 that referenced this pull request Oct 15, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
0xcrstn added a commit to 0xcrstn/AnyPS5 that referenced this pull request Oct 15, 2026
…he FPS candidate

Brings in ufc6/feedback (origin/main 760ffa6, boykopovar#6197, boykopovar#4286, boykopovar#5955) on top
of the four FPS topics and boykopovar#6168. Conflict resolutions:
- Execution Draw.cpp: keep the binding plan's single Regions() collection
  after the stage loop, adding the regions of reused or data-hit stages, and
  upstream's data-hit counters and cacheDrawStages signature.
- Graphics Draw.cpp: recordDrawCommands takes both upstream's firstIndex and
  the dirty-record bound state; a stencil replay, like a face or channel
  split, drops the bound state.
- The binding plan falls back to the full capture for stages whose plan uses
  image tables (ShaderMemory::UsesImageTables).

Unbuilt and untested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0xcrstn added a commit to 0xcrstn/AnyPS5 that referenced this pull request Oct 15, 2026
…nd supersede handed-over units before saving

HostImportSpanning (boykopovar#6197) joined registered ranges whose boundary falls
inside a 64 KiB unit, so with unit shadows such surfaces took the
GPU-direct path that main refuses for them (agc_storage_split_writeback,
agc_driver_storage_write_back_overhang). The whole-surface import now
goes through surfaceImport, which keeps the per-window import, refuses
a join across a unit-misaligned boundary under unit shadows and joins
otherwise, so 64 KiB-aligned pages keep the spanning import.

The boykopovar#4286 port superseded handed-over units after main's capture of
unsaved layers, whose flush stored the alias's dropped results first
(agc_driver_alias_handover); the supersede now runs before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0xcrstn added a commit to 0xcrstn/AnyPS5 that referenced this pull request Oct 15, 2026
…ersede removes

The boykopovar#4286 port turned originalValid = false into clearing every layer's
saved original of the source image, but layerOriginal now also holds the
guest bytes a write-back outside the write watch needs. Once the
supersede ran before the unsaved capture, the source's remaining pending
units lost their saved bytes and its next write-back threw
(agc_driver_alias_handover, agc_driver_alias_handover_memory). Only the
dropped units now lose their saved bytes and stored saves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants