Skip to content

fix(agc): save the guest bytes of pending storage results when their range leaves the write watch - #5657

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
oneandonlydean:fix/storage-save-at-unwatch
Oct 14, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
oneandonlydean:fix/storage-save-at-unwatch

Conversation

@oneandonlydean

@oneandonlydean oneandonlydean commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

What

Fixes the regression reported in #361 for Disney Epic Mickey: Rebrushed (PPSA15657). @johnmaia bisected it to #5596: on Windows main the title stops during the level load or before the main menu's Continue with

[videoout] presentation failed: AGC graphics: storing storage image results pending over memory outside the write watch without the guest bytes saved before them is not implemented

and the same build with #5596 reverted stays in the level for 10 minutes (comment).

Since #5596, singly mapped direct memory on Windows is private memory. importAllocation finds it writable and imports it directly (GuestBufferMemory.cpp:894), and decideImportWatch makes every direct import on Windows leave the watch (:542, :933 → GuestMemory::ImportWatched(..., false) → unwatchLocked, GuestMemory.cpp:1223). Before #5596 the same memory was a shared view, imported through MapAlias, and stayed watched. A storage image uploaded while the range was watched saves no guest bytes (Texture.cpp:2163, :2212, :3835, :3970). If its results were still pending when the range was imported, its next store found the range neither watched nor saved, which is the throw #5024 left (Texture.cpp:4339, :4486).

This saves the bytes at the moment the range leaves the watch:

  • GuestMemory::SetUnwatchHook: called before Unwatch and before an ImportWatched that does not stay watched, while the range's write stamps are still current.
  • StorageTexture::SaveBeforeUnwatch: every image with results pending over the range reads the guest bytes of its pending units that have none, then collects the range. It records the pieces that the keep rule of writeBackLayers leaves to the CPU for the writes the watch saw, with the unit's generation.
  • The next store leaves those pieces to the CPU and compares the rest with the saved bytes, as fix(agc): keep CPU writes when a pending storage result is stored over untracked memory #5024 does. Refresh counts them as CPU-written.
  • The hook needs the GPU lock. A map-time import made without the lock (ImportMappedRanges) is deferred to its locked retry when pending results overlap the run, as it already is when it would retire an import. Without the lock, the hook throws.

What still throws is in the debt entry: units without a write generation, and ranges that leave the watch through a failed collect.

Tested

Linux (CachyOS, GCC 16.2.1, RTX 4090), Release, BUILD_TESTING=ON, on main e33258a:

  • agc_driver_store_over_untracked has three new cases: the range leaves the watch through Unwatch and through an ImportWatched that does not stay watched, with the CPU write after the transition, and with the CPU write before it. On main it throws the message above. With this PR it passes in 3 of 3 runs. Without the recorded pieces, the write-before case loses all 4096 CPU-written bytes.
  • ctest -R "guest_memory|agc_driver_graphics|storage|write_watch": 18 of 18 pass.
  • ctest -R "agc_|guest_": 854 of 855 pass. agc_driver_bc_block_copy fails the same way on unmodified main.
  • tools/check_conventions.py --base origin/main passes.

Not run on Windows or with PPSA15657. @johnmaia, could you run the title with the same input sequence?

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
  • Unblocks: PPSA15657 Windows: resuming a write fault overwrites the guest red zone when AVX state is live #361
  • GPUs tested: NVIDIA RTX 4090, Linux
  • AI-assisted: yes (Claude Code)

…range leaves the write watch

On Windows, singly mapped direct memory is private since boykopovar#5596, so its host
import is a direct import that leaves the write watch instead of a shared
alias that stays watched. A storage image whose results were pending when
its memory was imported had no saved guest bytes, and storing them threw.

GuestMemory now calls a hook before Unwatch and before an ImportWatched
that does not stay watched. Every image with results pending over the
range saves the guest bytes of its pending units that have none, then
collects and records the pieces the keep rule leaves to the CPU for the
writes the watch saw. The next store and Refresh leave those pieces to the
CPU and compare the rest with the saved bytes. A map-time import made
without the GPU lock is deferred to the locked retry when pending results
overlap the range.
@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
#4841 core/libs/prx/libSceAgcDriver/Graphics/include/Texture.hpp L194
#4878 docs/dev/debt/libSceAgcDriver.md L52-53
#5324 docs/dev/debt/libSceAgcDriver.md L51-52

…e the range leaves the write watch

A draw made outside the GPU lock (agc_driver_recorded_kept_bytes) imports a target's memory and leaves the watch there; the hook threw instead of saving. It now takes the lock, as FlushPending does.
@oneandonlydean
oneandonlydean merged commit e46c695 into boykopovar:main Oct 14, 2026
8 checks passed
@johnmaia

Copy link
Copy Markdown
Contributor

@oneandonlydean ran PPSA15657 on this PR as asked: Windows 11, RTX 4080, head db4fd5f merged into main ac35f59, the game relinked with that build's relinker, the same steps (title, Continue into Dark Beauty Castle). It still stops on the same throw, about 2 minutes after Continue, during or just after the level load:

terminate called after throwing an instance of 'std::runtime_error'
  what():  AGC graphics: storing storage image results pending over memory outside the write watch without the guest bytes saved before them is not implemented
Build Runs Result
main a310431 2 throw, before or during the level load
main ac35f59 + this PR 1 same throw, about 2 min after Continue
main a310431 with #5596 reverted 1 level loaded, 10 min, no throw

One run only, so I can't say whether it comes later than without the PR. The log is available if useful.

AI-assisted: yes (Claude Code)

@oneandonlydean

Copy link
Copy Markdown
Collaborator Author

@johnmaia thanks for running it. The fix in #5657 only covers ranges that leave the write watch through a host import or Unwatch, so the title is taking another path to that throw. Could you run it once more on the same build with

APS5_TRACE_FLUSH=1 APS5_TRACE_HOST_IMPORT=1 APS5_TRACE_MEMORY=1

and attach the last ~300 lines of the log before the throw? We need the [flush] image 0x... line that fails, plus the [gpu] host import ... and [memory] ... lines for the same address range. The output is large, so a compressed log is fine.

@johnmaia

Copy link
Copy Markdown
Contributor

@oneandonlydean ran it again with APS5_TRACE_FLUSH=1 APS5_TRACE_HOST_IMPORT=1 APS5_TRACE_MEMORY=1 on main d8888df (#5657 included). Same steps: Windows 11, RTX 4080, game relinked with that build. The throw came 17 s after Continue, from the present path:

[videoout] presentation failed: AGC graphics: storing storage image results pending over memory outside the write watch without the guest bytes saved before them is not implemented

The flush trace has no line for the image that throws: the last [flush] image lines (about 20 lines earlier) are for other images (0x20816c0000, 0x2081750000, 0x2081870000, 0x207eec0000, 0x204ddb0000, each "keeps N CPU-written 64 KiB blocks ... generation 0"). Right before the throw, the log only shows a run of [memory] remap fixed ... prot=0xf2 + [gpu] host import ... ok pairs for 64 KiB to 9.4 MiB ranges. The last 300 lines before the throw:

log (300 lines)
[flush] image 0x204dda0000+0x10000 for idle 0x0+0xffffffffffffffff
[flush] image 0x204dce0000+0x10000 for idle 0x0+0xffffffffffffffff
[flush] image 0x204dcc0000+0x10000 for idle 0x0+0xffffffffffffffff
[flush] image 0x204dca0000+0x10000 for idle 0x0+0xffffffffffffffff
[flush] image 0x204dcf0000+0x20000 for idle 0x0+0xffffffffffffffff
[flush] image 0x207a740000+0x60000 for idle 0x0+0xffffffffffffffff
[flush] image 0x207efc0000+0x140000 for idle 0x0+0xffffffffffffffff
[memory] remap fixed 000000208a720000+0x30000 prot=0xf2 phys=0xa6350000
[flush] image 0x204ddb0000+0x140000 for idle 0x0+0xffffffffffffffff
[flush] image 0x2076740000+0x3fc0000 for idle 0x0+0xffffffffffffffff
[flush] image 0x2081c50000+0x20000 for idle 0x0+0xffffffffffffffff
[flush] image 0x206f6f0000+0xab0000 for idle 0x0+0xffffffffffffffff
[flush] image 0x206f6f0000+0x870000 for idle 0x0+0xffffffffffffffff
[flush] image 0x201d2b0000+0x870000 for idle 0x0+0xffffffffffffffff
[flush] image 0x202a2e0000+0x1fe0000 for idle 0x0+0xffffffffffffffff
[gpu] host import of 0x208a720000+0x30000 ok (4168 live, 2423.2 MiB live, 2662.7 MiB ever)
[memory] remap fixed 0000001721820000+0x10000 prot=0xf2 phys=0xa6380000
[flush] image 0x2074740000+0xff0000 keeps 150 CPU-written 64 KiB blocks (first stored byte at +0x960000, 1 ranges stored, generation 0)
[gpu] host import of 0x1721820000+0x10000 ok (4169 live, 2423.3 MiB live, 2662.7 MiB ever)
[memory] remap fixed 000000208a760000+0x960000 prot=0xf2 phys=0xa6390000
[gpu] host import of 0x1721000000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721010000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721020000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721030000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721040000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721050000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721060000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721070000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721080000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721090000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17210a0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17210b0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17210c0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17210d0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17210e0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17210f0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721100000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721110000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721120000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721130000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721140000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721150000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721160000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721170000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721180000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721190000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17211a0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17211b0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17211c0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17211d0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17211e0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17211f0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721200000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721210000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721220000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721230000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721240000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721250000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721260000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721270000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721280000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721290000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17212a0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17212b0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17212c0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17212d0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17212e0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17212f0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721300000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721310000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721320000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721330000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721340000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721350000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721360000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721370000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721380000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721390000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17213a0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17213b0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17213c0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17213d0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17213e0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17213f0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721400000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721410000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721420000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721430000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721440000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721450000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721460000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721470000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721480000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721490000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17214a0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17214b0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17214c0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17214d0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17214e0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17214f0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721500000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721510000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721520000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721530000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721540000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721550000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721560000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721570000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721580000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721590000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17215a0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17215b0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17215c0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17215d0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17215e0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17215f0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721600000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721610000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721620000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721630000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721640000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721650000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721660000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721670000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721680000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721690000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17216a0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17216b0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17216c0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17216d0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17216e0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17216f0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721700000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721710000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721720000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721730000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721740000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721750000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721760000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721770000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721780000+0x10000 retired: the range was mapped again
[gpu] host import of 0x1721790000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17217a0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17217b0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17217c0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17217d0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x17217e0000+0x10000 retired: the range was mapped again
[gpu] host import of 0x208a760000+0x960000 ok (4043 live, 2424.7 MiB live, 2672.1 MiB ever)
[memory] remap fixed 0000001721830000+0x10000 prot=0xf2 phys=0xa6cf0000
[gpu] host import of 0x1721830000+0x10000 ok (4044 live, 2424.8 MiB live, 2672.2 MiB ever)
[memory] remap fixed 000000208b760000+0x30000 prot=0xf2 phys=0xa6d00000
[gpu] host import of 0x208b760000+0x30000 ok (4045 live, 2425.0 MiB live, 2672.4 MiB ever)
[memory] remap fixed 000000208b7a0000+0x10000 prot=0xf2 phys=0xa6d30000
[gpu] host import of 0x208b7a0000+0x10000 ok (4046 live, 2425.0 MiB live, 2672.4 MiB ever)
[memory] remap fixed 0000001721840000+0x10000 prot=0xf2 phys=0xa6d40000
[flush] image 0x2075740000+0x870000 keeps 135 CPU-written 64 KiB blocks (first stored byte at +0x870000, 0 ranges stored, generation 0)
[gpu] host import of 0x1721840000+0x10000 ok (4047 live, 2425.1 MiB live, 2672.5 MiB ever)
[memory] remap fixed 000000208b7b0000+0x960000 prot=0xf2 phys=0xa6d50000
[gpu] host import of 0x208b7b0000+0x960000 ok (4048 live, 2434.5 MiB live, 2681.9 MiB ever)
[memory] remap fixed 0000001721850000+0x10000 prot=0xf2 phys=0xa76b0000
[gpu] host import of 0x1721850000+0x10000 ok (4049 live, 2434.5 MiB live, 2681.9 MiB ever)
[memory] remap fixed 000000208c7b0000+0x30000 prot=0xf2 phys=0xa76c0000
[gpu] host import of 0x208c7b0000+0x30000 ok (4050 live, 2434.7 MiB live, 2682.1 MiB ever)
[memory] remap fixed 0000001721860000+0x10000 prot=0xf2 phys=0xa76f0000
[gpu] host import of 0x1721860000+0x10000 ok (4051 live, 2434.8 MiB live, 2682.2 MiB ever)
[memory] remap fixed 000000208c7f0000+0x10000 prot=0xf2 phys=0xa7700000
[gpu] host import of 0x208c7f0000+0x10000 ok (4052 live, 2434.9 MiB live, 2682.2 MiB ever)
[memory] remap fixed 0000001721870000+0x10000 prot=0xf2 phys=0xa7710000
[gpu] host import of 0x1721870000+0x10000 ok (4053 live, 2434.9 MiB live, 2682.3 MiB ever)
[memory] remap fixed 0000001800f60000+0x170000 prot=0xf2 phys=0xa7720000
[gpu] host import of 0x1800f60000+0x170000 ok (4054 live, 2436.4 MiB live, 2683.7 MiB ever)
[memory] remap fixed 000000208c800000+0x960000 prot=0xf2 phys=0xa7890000
[gpu] host import of 0x208c800000+0x960000 ok (4055 live, 2445.7 MiB live, 2693.1 MiB ever)
[memory] remap fixed 0000001721880000+0x10000 prot=0xf2 phys=0xa81f0000
[gpu] host import of 0x1721880000+0x10000 ok (4056 live, 2445.8 MiB live, 2693.2 MiB ever)
[memory] remap fixed 000000208d800000+0x10000 prot=0xf2 phys=0xa8200000
[gpu] host import of 0x208d800000+0x10000 ok (4057 live, 2445.9 MiB live, 2693.2 MiB ever)
[memory] remap fixed 0000001721890000+0x10000 prot=0xf2 phys=0xa8210000
[gpu] host import of 0x1721890000+0x10000 ok (4058 live, 2445.9 MiB live, 2693.3 MiB ever)
[memory] remap fixed 000000204dd80000+0x20000 prot=0xf2 phys=0xa8220000
[gpu] host import of 0x204dd80000+0x20000 ok (4059 live, 2446.0 MiB live, 2693.4 MiB ever)
[memory] remap fixed 00000017218a0000+0x10000 prot=0xf2 phys=0xa8240000
[flush] image 0x204c4c0000+0x60000 keeps 5 CPU-written 64 KiB blocks (first stored byte at +0x50000, 1 ranges stored, generation 0)
[gpu] host import of 0x17218a0000+0x10000 ok (4060 live, 2446.1 MiB live, 2693.5 MiB ever)
[memory] remap fixed 000000208d810000+0x960000 prot=0xf2 phys=0xa8250000
[gpu] host import of 0x208d810000+0x960000 ok (4061 live, 2455.5 MiB live, 2702.9 MiB ever)
[memory] remap fixed 000000208e810000+0x40000 prot=0xf2 phys=0xa8bb0000
[flush] image 0x204c540000+0x60000 keeps 6 CPU-written 64 KiB blocks (first stored byte at +0x60000, 0 ranges stored, generation 0)
[gpu] host import of 0x208e810000+0x40000 ok (4062 live, 2455.7 MiB live, 2703.1 MiB ever)
[memory] remap fixed 00000017218b0000+0x10000 prot=0xf2 phys=0xa8bf0000
[flush] image 0x204d5f0000+0x100000 keeps 15 CPU-written 64 KiB blocks (first stored byte at +0xf0000, 1 ranges stored, generation 0)
[gpu] host import of 0x17218b0000+0x10000 ok (4063 live, 2455.8 MiB live, 2703.2 MiB ever)
[flush] image 0x204d6f0000+0x10000 keeps 1 CPU-written 64 KiB blocks (first stored byte at +0x10000, 0 ranges stored, generation 0)
[memory] remap fixed 000000208e850000+0x10000 prot=0xf2 phys=0xa8c00000
[flush] image 0x204dc70000+0x10000 keeps 1 CPU-written 64 KiB blocks (first stored byte at +0x10000, 0 ranges stored, generation 0)
[flush] image 0x204dc90000+0x10000 keeps 1 CPU-written 64 KiB blocks (first stored byte at +0x10000, 0 ranges stored, generation 0)
[flush] image 0x204dcb0000+0x10000 keeps 1 CPU-written 64 KiB blocks (first stored byte at +0x10000, 0 ranges stored, generation 0)
[flush] image 0x204dcd0000+0x10000 keeps 1 CPU-written 64 KiB blocks (first stored byte at +0x10000, 0 ranges stored, generation 0)
[gpu] host import of 0x208e850000+0x10000 ok (4064 live, 2455.9 MiB live, 2703.2 MiB ever)
[flush] image 0x204dd20000+0x60000 keeps 6 CPU-written 64 KiB blocks (first stored byte at +0x60000, 0 ranges stored, generation 0)
[memory] remap fixed 000000208e860000+0x40000 prot=0xf2 phys=0xa8c10000
[gpu] host import of 0x208e860000+0x40000 ok (4065 live, 2456.1 MiB live, 2703.5 MiB ever)
[memory] remap fixed 00000017218c0000+0x10000 prot=0xf2 phys=0xa8c50000
[gpu] host import of 0x17218c0000+0x10000 ok (4066 live, 2456.2 MiB live, 2703.5 MiB ever)
[memory] remap fixed 000000208e8a0000+0x10000 prot=0xf2 phys=0xa8c60000
[gpu] host import of 0x208e8a0000+0x10000 ok (4067 live, 2456.2 MiB live, 2703.6 MiB ever)
[flush] image 0x204e070000+0x3fc0000 keeps 961 CPU-written 64 KiB blocks (first stored byte at +0x3c10000, 1 ranges stored, generation 0)
[memory] remap fixed 00000017218d0000+0x10000 prot=0xf2 phys=0xa8c70000
[flush] image 0x2052300000+0x870000 keeps 135 CPU-written 64 KiB blocks (first stored byte at +0x870000, 0 ranges stored, generation 0)
[gpu] host import of 0x17218d0000+0x10000 ok (4068 live, 2456.3 MiB live, 2703.7 MiB ever)
[memory] remap fixed 000000208e8b0000+0x40000 prot=0xf2 phys=0xa8c80000
[gpu] host import of 0x208e8b0000+0x40000 ok (4069 live, 2456.5 MiB live, 2703.9 MiB ever)
[memory] remap fixed 000000208e8f0000+0x960000 prot=0xf2 phys=0xa8cc0000
[gpu] host import of 0x208e8f0000+0x960000 ok (4070 live, 2465.9 MiB live, 2713.3 MiB ever)
[memory] remap fixed 00000017218e0000+0x10000 prot=0xf2 phys=0xa9620000
[gpu] host import of 0x17218e0000+0x10000 ok (4071 live, 2466.0 MiB live, 2713.4 MiB ever)
[memory] remap fixed 00000017218f0000+0x10000 prot=0xf2 phys=0xa9630000
[gpu] host import of 0x17218f0000+0x10000 ok (4072 live, 2466.0 MiB live, 2713.4 MiB ever)
[memory] remap fixed 0000001721900000+0x10000 prot=0xf2 phys=0xa9640000
[gpu] host import of 0x1721900000+0x10000 ok (4073 live, 2466.1 MiB live, 2713.5 MiB ever)
[memory] remap fixed 000000204c520000+0x10000 prot=0xf2 phys=0xa9650000
[gpu] host import of 0x204c520000+0x10000 ok (4074 live, 2466.2 MiB live, 2713.5 MiB ever)
[memory] remap fixed 000000208f8f0000+0x10000 prot=0xf2 phys=0xa9660000
[gpu] host import of 0x208f8f0000+0x10000 ok (4075 live, 2466.2 MiB live, 2713.6 MiB ever)
[memory] remap fixed 0000001721910000+0x10000 prot=0xf2 phys=0xa9670000
[gpu] host import of 0x1721910000+0x10000 ok (4076 live, 2466.3 MiB live, 2713.7 MiB ever)
[memory] remap fixed 00000020522c0000+0x10000 prot=0xf2 phys=0xa9680000
[flush] image 0x2020e20000+0x400000 keeps 33 CPU-written 64 KiB blocks (first stored byte at +0x210000, 1 ranges stored, generation 0)
[gpu] host import of 0x20522c0000+0x10000 ok (4077 live, 2466.4 MiB live, 2713.7 MiB ever)
[memory] remap fixed 0000001721920000+0x10000 prot=0xf2 phys=0xa9690000
[flush] image 0x2020a20000+0x400000 keeps 47 CPU-written 64 KiB blocks (first stored byte at +0x2f0000, 1 ranges stored, generation 0)
[gpu] host import of 0x1721920000+0x10000 ok (4078 live, 2466.4 MiB live, 2713.8 MiB ever)
[memory] remap fixed 000000208f900000+0x960000 prot=0xf2 phys=0xa96a0000
[flush] image 0x2081510000+0x50000 keeps 5 CPU-written 64 KiB blocks (first stored byte at +0x50000, 0 ranges stored, generation 0)
[flush] image 0x2081900000+0x50000 keeps 5 CPU-written 64 KiB blocks (first stored byte at +0x50000, 0 ranges stored, generation 0)
[flush] image 0x20815a0000+0x50000 keeps 5 CPU-written 64 KiB blocks (first stored byte at +0x50000, 0 ranges stored, generation 0)
[gpu] host import of 0x208f900000+0x960000 ok (4079 live, 2475.8 MiB live, 2723.2 MiB ever)
[flush] image 0x2081630000+0x50000 keeps 5 CPU-written 64 KiB blocks (first stored byte at +0x50000, 0 ranges stored, generation 0)
[flush] image 0x20816c0000+0x50000 keeps 5 CPU-written 64 KiB blocks (first stored byte at +0x50000, 0 ranges stored, generation 0)
[flush] image 0x2081750000+0x50000 keeps 5 CPU-written 64 KiB blocks (first stored byte at +0x50000, 0 ranges stored, generation 0)
[memory] [flush] image 0x20817e0000+0x50000 keeps 5 CPU-written 64 KiB blocks (first stored byte at +0x50000, 0 ranges stored, generation 0)
remap fixed 0000002081330000+0x10000 prot=0xf2 phys=0xaa000000
[flush] image 0x2081870000+0x50000 keeps 5 CPU-written 64 KiB blocks (first stored byte at +0x50000, 0 ranges stored, generation 0)
[flush] image 0x207eec0000+0x100000 keeps 14 CPU-written 64 KiB blocks (first stored byte at +0xe0000, 1 ranges stored, generation 0)
[gpu] host import of 0x2081330000+0x10000 ok (4080 live, 2475.9 MiB live, 2723.2 MiB ever)
[memory] remap fixed 0000001721930000+0x10000 prot=0xf2 phys=0xaa010000
[gpu] host import of 0x1721930000+0x10000 ok (4081 live, 2475.9 MiB live, 2723.3 MiB ever)
[memory] remap fixed 0000002090900000+0x10000 prot=0xf2 phys=0xaa020000
[gpu] host import of 0x2090900000+0x10000 ok (4082 live, 2476.0 MiB live, 2723.4 MiB ever)
[memory] remap fixed 0000001721940000+0x10000 prot=0xf2 phys=0xaa030000
[gpu] host import of 0x1721940000+0x10000 ok (4083 live, 2476.0 MiB live, 2723.4 MiB ever)
[memory] remap fixed 0000002090910000+0x30000 prot=0xf2 phys=0xaa040000
[gpu] host import of 0x2090910000+0x30000 ok (4084 live, 2476.2 MiB live, 2723.6 MiB ever)
[memory] remap fixed 0000001721950000+0x10000 prot=0xf2 phys=0xaa070000
[gpu] host import of 0x1721950000+0x10000 ok (4085 live, 2476.3 MiB live, 2723.7 MiB ever)
[memory] remap fixed 0000002090950000+0x10000 prot=0xf2 phys=0xaa080000
[gpu] host import of 0x2090950000+0x10000 ok (4086 live, 2476.4 MiB live, 2723.7 MiB ever)
[memory] remap fixed 0000002090960000+0x40000 prot=0xf2 phys=0xaa090000
[gpu] host import of 0x2090960000+0x40000 ok (4087 live, 2476.6 MiB live, 2724.0 MiB ever)
[memory] remap fixed 00000020909a0000+0x960000 prot=0xf2 phys=0xaa0d0000
[gpu] host import of 0x20909a0000+0x960000 ok (4088 live, 2486.0 MiB live, 2733.4 MiB ever)
[memory] remap fixed 0000001721960000+0x10000 prot=0xf2 phys=0xaaa30000
[gpu] host import of 0x1721960000+0x10000 ok (4089 live, 2486.0 MiB live, 2733.4 MiB ever)
[memory] remap fixed 00000020919a0000+0x10000 prot=0xf2 phys=0xaaa40000
[gpu] host import of 0x20919a0000+0x10000 ok (4090 live, 2486.1 MiB live, 2733.5 MiB ever)
[memory] remap fixed 00000020919b0000+0x40000 prot=0xf2 phys=0xaaa50000
[gpu] host import of 0x20919b0000+0x40000 ok (4091 live, 2486.4 MiB live, 2733.7 MiB ever)
[memory] remap fixed 0000001721970000+0x10000 prot=0xf2 phys=0xaaa90000
[gpu] host import of 0x1721970000+0x10000 ok (4092 live, 2486.4 MiB live, 2733.8 MiB ever)
[memory] remap fixed 00000020919f0000+0x960000 prot=0xf2 phys=0xaaaa0000
[gpu] host import of 0x20919f0000+0x960000 ok (4093 live, 2495.8 MiB live, 2743.2 MiB ever)
[memory] remap fixed 0000001721980000+0x10000 prot=0xf2 phys=0xab400000
[gpu] host import of 0x1721980000+0x10000 ok (4094 live, 2495.9 MiB live, 2743.2 MiB ever)
[memory] remap fixed 00000020811b0000+0x30000 prot=0xf2 phys=0xab410000
[gpu] host import of 0x20811b0000+0x30000 ok (4095 live, 2496.0 MiB live, 2743.4 MiB ever)
[memory] remap fixed 0000001721990000+0x10000 prot=0xf2 phys=0xab440000
[gpu] host import of 0x1721990000+0x10000 ok (4096 live, 2496.1 MiB live, 2743.5 MiB ever)
[memory] remap fixed 00000020929f0000+0x10000 prot=0xf2 phys=0xab450000
[gpu] host import of 0x20929f0000+0x10000 ok (4097 live, 2496.2 MiB live, 2743.5 MiB ever)
[memory] remap fixed 00000017219a0000+0x10000 prot=0xf2 phys=0xab460000
[gpu] host import of 0x17219a0000+0x10000 ok (4098 live, 2496.2 MiB live, 2743.6 MiB ever)
[flush] image 0x204ddb0000+0x140000 keeps 19 CPU-written 64 KiB blocks (first stored byte at +0x140000, 0 ranges stored, generation 0)
[memory] remap fixed 0000002092a00000+0x40000 prot=0xf2 phys=0xab470000
[gpu] host import of 0x2092a00000+0x40000 ok (4099 live, 2496.5 MiB live, 2743.9 MiB ever)
[memory] remap fixed 0000002092a40000+0x960000 prot=0xf2 phys=0xab4b0000
[gpu] host import of 0x2092a40000+0x960000 ok (4100 live, 2505.9 MiB live, 2753.2 MiB ever)
[memory] remap fixed 00000017219b0000+0x10000 prot=0xf2 phys=0xabe10000
[gpu] host import of 0x17219b0000+0x10000 ok (4101 live, 2505.9 MiB live, 2753.3 MiB ever)
[memory] remap fixed 0000002093a40000+0x10000 prot=0xf2 phys=0xabe20000
[gpu] host import of 0x2093a40000+0x10000 ok (4102 live, 2506.0 MiB live, 2753.4 MiB ever)
[memory] remap fixed 0000002093a50000+0x40000 prot=0xf2 phys=0xabe30000
[gpu] host import of 0x2093a50000+0x40000 ok (4103 live, 2506.2 MiB live, 2753.6 MiB ever)
[memory] remap fixed 00000017219c0000+0x10000 prot=0xf2 phys=0xabe70000
[gpu] host import of 0x17219c0000+0x10000 ok (4104 live, 2506.3 MiB live, 2753.7 MiB ever)
[memory] remap fixed 0000002093a90000+0x960000 prot=0xf2 phys=0xabe80000
[gpu] host import of 0x2093a90000+0x960000 ok (4105 live, 2515.7 MiB live, 2763.0 MiB ever)
[memory] remap fixed 00000017219d0000+0x10000 prot=0xf2 phys=0xac7e0000
[videoout] presentation failed: AGC graphics: storing storage image results pending over memory outside the write watch without the guest bytes saved before them is not implemented
[gpu] host import of 0x17219d0000+0x10000 ok (4106 live, 2515.7 MiB live, 2763.1 MiB ever)
terminate called after throwing an instance of 'std::runtime_error'
  what():  AGC graphics: storing storage image results pending over memory outside the write watch without the guest bytes saved before them is not implemented
FATAL: abort() (SIGABRT) on thread 23996
    #0 0x00007ffc681cb5e7 libc.prx+0xb5e7

The full log is 1.4 MB (150 KB gzip). I can send it however suits you.

AI-assisted: yes (Claude Code)

@oneandonlydean

Copy link
Copy Markdown
Collaborator Author

Thanks, the trace helps. Every image it stores is at generation 0, so their memory was already outside the write watch when their results were written: this isn't a range leaving the watch with results pending, which is all #5657 and #5671 handle. The remaps and retired imports before the throw don't drop the watch either.

What does leave pending results on such memory without saved bytes is an image copy or fill into memory another image overlaps: it dropped the saved bytes of every unit of that other image, including units it never touched that still had results pending, and the next store of those throws. #5707 limits that to the units the copy or fill covers. Could you try a build of main with #5707 and #5671 both applied? If it still throws, please attach the full log (a gist is fine).

@oneandonlydean

Copy link
Copy Markdown
Collaborator Author

@johnmaia #5761 is main with #5671, #5679, #5707 and #5760 applied (branch oneandonlydean:test/storage-writeback-1014, full build green). #5760 covers what your trace and #5711 point to: a write-back done on the GPU over memory outside the write watch left no saved bytes, so the next store threw. Could you build it and rerun Epic Mickey? If it still throws, please attach the log with the same traces.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants