Skip to content

fix(agc): ignore unchanged imported texture blocks - #2984

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
MarcZX10:fix/agc-ignore-unchanged-watched-blocks
Oct 10, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
MarcZX10:fix/agc-ignore-unchanged-watched-blocks

Conversation

@MarcZX10

Copy link
Copy Markdown
Contributor

What

Fixes #2767. When write tracking reports a 64 KiB texture block as possibly written, compare the block with the image snapshot before treating it as changed. If the bytes are identical, keep the pending GPU image and clear the false CPU-write stamp. This avoids redundant uploads when a host import remains write-watched but did not change guest bytes.

The issue reports the current failure on three NVIDIA setups and confirms the patch on those systems; an Intel report also reproduces the failure with import watching forced. No open PR overlaps this change.

Tested

  • Linux Release: built agc_driver_dcc_keys_first_write_tests
  • CTest agc_driver_dcc_keys_first_write, repeated 5 times: passed 5/5
  • tools/check_conventions.py --base upstream/main passed
  • git diff --check passed
  • Open PR overlap scan: no overlap

The local llvmpipe run reports that write-watch is unavailable, so the import-watch portion of this execution test was skipped locally. The hardware-specific reproduction and patched results are documented by the reporters in #2767.

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; silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

@github-actions github-actions Bot added the agc libSceAgc* label Oct 10, 2026
@fma-rc

fma-rc commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

Tested this head (c061095, merged cleanly onto main 262c031) on the hardware the linked issue is about, since the description says the watched path was skipped on llvmpipe.

Arch Linux (kernel 7.2.9), GCC 16.2.1, Release, ANYPS5_REQUIRE_VULKAN=1. RTX 5080 (NVIDIA 615.78.08), where host imports stay write-watched, and a Radeon 610M on RADV, where they are compared.

RTX 5080 RADV, Radeon 610M
main 262c031, agc_driver_dcc_keys_first_write fails (an unchanged pending image was uploaded again) passes
this PR, agc_driver_dcc_keys_first_write passes 10 of 10 passes
this PR, full ctest 708 of 708 708 of 708

ctest reports 4 video_out_flip_* tests as skipped in the RTX 5080 run, and those plus 3 guest_video_out_* tests in the RADV run; they are counted in the 708.

The diff is the patch from the issue unchanged, so this is the change the three NVIDIA reports there were made with. Not tested: Windows.

@milescoviello

Copy link
Copy Markdown
Contributor

The description notes that the import-watch part of agc_driver_dcc_keys_first_write was skipped on llvmpipe. It only runs where the device's own host import resolves the write protection, which CI's devices don't do, so this fix has no test there. Here is one that doesn't depend on the driver, as a single commit on top of this PR's head (c0610950):

milescoviello/AnyPS5:test/2984-import-window, commit a086c534.

  • RunImportWindow (in DccKeysFirstWrite.cpp, Linux only) opens an import window itself over a pending storage image with GuestMemory::ImportWatched and stores the byte already there. That leaves the same BlockMaybeWritten stamp the NVIDIA import leaves, so the image must stay pending and unchanged. A store of a new byte inside a window must still be seen by the next Refresh. Devices whose imports are compared print that the window is not tested, as the recorder's (w) test does.
  • One TechnicalDebt.md entry: an identical-byte CPU store inside a window can't be told apart from the import, and images tracked by layer (not by 64 KiB unit) still upload again.

Tested (Linux, RTX 5090 Laptop GPU 610.57.04 and Intel Arrow Lake ANV, Mesa 26.1.8):

agc_driver_dcc_keys_first_write
this PR + test, NVIDIA passes 3 of 3 (0.5 s)
this PR + test, Intel (imports compared) passes; prints that the import window is not tested
without the fix, new test only, NVIDIA fails at the new check: import window: a pending image was uploaded again after an import that stored nothing new

tools/check_conventions.py passes. Feel free to fold it into this PR or leave it.

AI-assisted: yes

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the diff: the block is only reused when compareUntracked finds it byte-identical to the snapshot, one 64 KiB unit at a time, so a real write is still seen. Local build and libSceAgcDriver ctest (344 tests) pass on this head merged onto main. The NVIDIA failure and fix are confirmed by @fma-rc (RTX 5080) and @milescoviello (RTX 5090 Laptop), thanks. Merging; the extra driver-independent test in the thread is welcome as a separate PR.

@adriadam10
adriadam10 merged commit 3db065a into boykopovar:main Oct 10, 2026
15 checks passed
@milescoviello

Copy link
Copy Markdown
Contributor

@adriadam10 @MarcZX100 thanks for merging this. Two things, the second one more important:

1. The driver-independent test, rebased onto main: milescoviello/AnyPS5:test/dcc-import-window (one commit, 63649253; it cherry-picks cleanly onto current main ed79bde). I can't open PRs yet under the intake rule, so it's there for whoever can carry it.

2. On current main this fix no longer holds on NVIDIA. agc_driver_dcc_keys_first_write fails again with an unchanged pending image was uploaded again on the merge commit 3db065a and on main (59a8cba and ed79bde), while this PR's head c061095 passes. A first-parent bisect of the 88 merges between this PR's base 4a747c4 and the merge, applying c061095 at each step, points to #2699 (926ad9e, "read a fast-cleared surface's guest bytes only when the import is compared").

#2699 makes the DCC fast-clear upload leave originalValid false when the range is write-watched. This PR's comparison needs that snapshot: compareUntracked returns false without one, so a unit of a fast-cleared surface that an import window stamped BlockMaybeWritten counts as changed again. Each change is right on its own; they conflict where imports stay watched. The test's surface is fast-cleared (its keys start at 0000), so it hits exactly this path.

Linux, RTX 5090 Laptop GPU (610.57.04), agc_driver_dcc_keys_first_write with the test commit applied:

Tree Result
main ed79bde fails 3/3: an unchanged pending image was uploaded again
main ed79bde, only the new import-window part (existing watched run skipped) fails 3/3: import window: a pending image was uploaded again after an import that stored nothing new
main 59a8cba with #2699's three-line clear-path change and its two test lines reverted passes 3/3 (whole test); Intel ANV passes too
this PR's head c061095 passes 3/3

So the new test catches the regression by itself. It doesn't rely on the import behaving like NVIDIA's, so it should fail on any device whose imports stay watched. I haven't written a fix: it means choosing between #2699's saving and the snapshot this comparison needs (for example, keeping the snapshot on the clear path when the range's import can leave import-window stamps).

AI-assisted: yes

milescoviello added a commit to milescoviello/AnyPS5 that referenced this pull request Oct 11, 2026
…ched memory

range is write-watched. Since boykopovar#2984, Refresh turns a BlockMaybeWritten
unit into BlockUnchanged only through compareUntracked, which needs that
copy. Where a host import leaves its range watched and making it stamps
the imported pages (the probe's writtenAtImport, 4 of 4 on NVIDIA's
Linux driver), the import window stamps the surface, so a pending
fast-cleared image counted as changed again and
agc_driver_dcc_keys_first_write failed on main (boykopovar#3179).

HostImports now keeps whether making each kind of import stamped the
probe's pages, and WatchedImportsStampPages reports whether an import
that leaves its range watched can stamp it. The clear path skips the
copy only when no such import can, as oneandonlydean proposed in boykopovar#3179.
Devices whose probe saw no pages written at the import, devices without
host imports and Windows (whose alias imports don't disarm the guest
views) keep boykopovar#2699's saving.
An unprobed watch (APS5_WRITE_WATCH_IMPORTS=watch) keeps the copy.

The fast-clear check in agc_driver_dcc_keys_first_write now expects the
copy exactly when the range is unwatched or such a stamp is possible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
milescoviello added a commit to milescoviello/AnyPS5 that referenced this pull request Oct 11, 2026
agc_driver_dcc_keys_first_write only exercises the import window when the
device's own host import resolves the write protection of the imported
pages, as the NVIDIA driver does on Linux. CI's devices do not, so the
comparison that boykopovar#2984 adds for BlockMaybeWritten units is not run there.

RunImportWindow opens an import window itself over a pending storage
image with GuestMemory::ImportWatched and stores the byte already there,
which leaves the same stamp without depending on the driver: the image
must stay pending and unchanged. A store of a new byte inside a window
must still be seen. Devices whose imports are compared print that the
window is not tested, as the recorder's (w) test does. Linux only: the
Windows path compares imports.

TechnicalDebt records that an identical-byte CPU store inside a window
cannot be told apart from the import, and that images tracked by layer
still upload again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adriadam10 pushed a commit that referenced this pull request Oct 11, 2026
…ched memory

range is write-watched. Since #2984, Refresh turns a BlockMaybeWritten
unit into BlockUnchanged only through compareUntracked, which needs that
copy. Where a host import leaves its range watched and making it stamps
the imported pages (the probe's writtenAtImport, 4 of 4 on NVIDIA's
Linux driver), the import window stamps the surface, so a pending
fast-cleared image counted as changed again and
agc_driver_dcc_keys_first_write failed on main (#3179).

HostImports now keeps whether making each kind of import stamped the
probe's pages, and WatchedImportsStampPages reports whether an import
that leaves its range watched can stamp it. The clear path skips the
copy only when no such import can, as oneandonlydean proposed in #3179.
Devices whose probe saw no pages written at the import, devices without
host imports and Windows (whose alias imports don't disarm the guest
views) keep #2699's saving.
An unprobed watch (APS5_WRITE_WATCH_IMPORTS=watch) keeps the copy.

The fast-clear check in agc_driver_dcc_keys_first_write now expects the
copy exactly when the range is unwatched or such a stamp is possible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adriadam10 pushed a commit that referenced this pull request Oct 11, 2026
agc_driver_dcc_keys_first_write only exercises the import window when the
device's own host import resolves the write protection of the imported
pages, as the NVIDIA driver does on Linux. CI's devices do not, so the
comparison that #2984 adds for BlockMaybeWritten units is not run there.

RunImportWindow opens an import window itself over a pending storage
image with GuestMemory::ImportWatched and stores the byte already there,
which leaves the same stamp without depending on the driver: the image
must stay pending and unchanged. A store of a new byte inside a window
must still be seen. Devices whose imports are compared print that the
window is not tested, as the recorder's (w) test does. Linux only: the
Windows path compares imports.

TechnicalDebt records that an identical-byte CPU store inside a window
cannot be told apart from the import, and that images tracked by layer
still upload again.

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*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

agc_driver_dcc_keys_first_write fails where host imports stay write-watched (NVIDIA, Linux): cause and patch

4 participants