Skip to content

fix(agc): accept a compared host import for an initial multisampled DCC import - #9940

Open
EagleBearerYS wants to merge 4 commits into
boykopovar:mainfrom
EagleBearerYS:fix/agc-compared-import-initial-dcc
Open

EagleBearerYS wants to merge 4 commits into
boykopovar:mainfrom
EagleBearerYS:fix/agc-compared-import-initial-dcc

Conversation

@EagleBearerYS

@EagleBearerYS EagleBearerYS commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

What

Rush 2 Recompiled (PPSA69423) and Wave Race 64 Recompiled (PPSA69424) stop at their first multisampled DCC target with AGC graphics: initial multisampled DCC contents require watched guest memory (#9724). On Linux, babyhuey saw it with lavapipe on main 8b3a6ba: Rush 2 at 6.4 s, Wave Race 64 at 1.5 s (comment).

Why Linux refuses. Linux has no deferred settle. When the write-watch probe finds that host imports lose their write protection, it compares every import instead of watching it. The probe then logs [write-watch] host imports resolve write protection: yes (…); imported ranges are compared.

  • lavapipe does this.
  • So does RADV: janzezno's RX 7900 GRE (RADV NAVI31) logs the same line (#9781).
  • initialDccClear takes a write generation of the color, DCC and CMASK ranges and requires each one to be watched. So a target in such an import is refused at its first use.

MarcZX10 asked to keep the reproductions split by that probe result (comment). This PR is his case (2), Linux comparison mode. The Windows case (1), a watched import whose deferred settle is still pending, is #9941.

The change:

  • ComparedHostImportCovers(context, address, bytes): true when a live host import marked unwatched covers the whole range. It returns false on Windows, so nothing changes there.
  • initialDccClear: a range that isn't watched but lies in such an import is read once and kept as a copy, with NewestStamp taken after the read. Watched ranges take their generation as before.
  • checkDccCurrent on reuse: a compared range is current while its NewestStamp is unchanged and its bytes equal the copy (EqualsCommittedUnsynced).
    • The compare runs at most once per collect epoch. It uses the same memo as the storage-texture compare: the epoch and ForgetSerial of the last equal compare.
    • Otherwise the range gets a watched range's refusals: writes to a resident multisampled DCC target or its metadata are not implemented, and the pending-write refusal.
  • absorbDccFill: compared metadata takes the landed key bytes of a whole fill as its new copy.
  • FMASK (second commit): feat(agc): expose expanded FMASK for tracked native owners #7948's FMASK range of an eight-sample target gets the same path, at its import check and at reuse. Four-sample targets such as Rush 2's never take an FMASK range (use.samples == 8u), so this commit doesn't change them.
  • Still refused:
    • a range that is unwatched without such an import, such as an explicit GuestMemory::Unwatch;
    • partial imports, misalignment and overlapping planes.

What a compared range can't see: a guest CPU store of the bytes already there. That is comparison mode's general limit, and the storage-texture compare has it too. A driver or GPU store into the range's 64 KiB blocks counts as a change through NewestStamp, same-value stores included. The debt entry says both.

Cost. Each use of a resident target with compared ranges takes one NewestStamp per compared range and at most one memcmp per range per collect epoch. That replaces a refusal at the first draw. memcmp on a cloud Xeon at 2.80 GHz:

Range Compare
0xc4000 bytes (Rush 2's import) 40-44 µs
1 MiB 65-72 µs
8 MiB 0.7-0.87 ms
1920x1080 RGBA8 at 4 samples (33 MB) 6-7 ms

The memory cost is one copy of each compared range. A cheaper current check, such as a hash per 64 KiB block, would be follow-up work.

Commits:

  1. fix(agc): accept a compared host import for an initial multisampled DCC import. It is on a main that has feat(agc): expose expanded FMASK for tracked native owners #7948, so Recreate keeps both resets, and the end of initialDccClear keeps feat(agc): expose expanded FMASK for tracked native owners #7948's FMASK check.
  2. fix(agc): accept a compared host import for a multisampled target's FMASK range.
  3. test(agc): skip the watched-import control case where imports report pages written (test only). Mode 5 forces the planes' imports to stay watched, as a control. On a device whose probe reports watched pages written at the import or after a GPU read, the watched planes see those writes and the reuse check refuses, so mode 5 now runs only where the probe reports neither, the condition test(agc): skip the forced-watch shadow tests where imports report pages written #10138 uses for the forced-watch shadow tests.
  4. docs(debt): shorten the native multisampled DCC fast clear entry (docs only): the entry is under the 1,000-character limit, with the same cases.

Tested

  • agc_driver_msaa_dcc_compared_import_{2,4,8} (new, DccComparedImportTests in tests/execution/AlphaToCoverage.cpp).
    • The test requires the probe's own choice of comparison mode, and that each plane's import reports unwatched. A device that keeps imports watched, or has no host imports, skips with a device-limit message.
    • Each mode states its write-watch mode:
Mode Write-watch mode Case Expected
0 comparison color, DCC and CMASK each in a compared import draw, resolve to the DCC clear plus the drawn sample; a reuse draw resolves the same
1 comparison CPU store of a new byte into the color plane next draw refused (color range)
2 comparison CPU store of a new DCC key refused (DCC range)
3 comparison same-value driver store into CMASK refused (CMASK range), through NewestStamp
4 explicitly unwatched color Unwatched, nothing imported refused at the import, as on main
5 watched (control) imports forced to stay watched right clear on both trees; skipped where the probe reports watched pages written at the import or after a GPU read
6 comparison only the DCC plane imported refused on main, passes
7-10 comparison, 8 samples modes 0, "only the FMASK compared", 1 and 3 for the FMASK the FMASK passes its import checks
11 explicitly unwatched, 8 samples FMASK Unwatched refused at the import
  • Other cases:
    • DccRefillTests mode 6, a metadata fill in forced comparison mode, used to expect requires watched guest memory. It now expects the fill to clear the target.
    • DccFastClearTests mode 4 (an explicit Unwatch over the DCC metadata) is unchanged. It refuses on both trees.
  • Red/green on lavapipe (cloud Linux, ANYPS5_REQUIRE_VULKAN=1 GALLIUM_OVERRIDE_CPU_CAPS=avx):
    • Red, main's product files with the new tests:
      • compared_import_2, _4 and _8 fail with the title's text.
      • msaa_dcc_clear_4 fails with multisampled DCC target reuse requires watched guest memory (the refill case).
    • Green: _4 and clear_4 pass, 20/20 each with --repeat until-fail:20. The 2- and 8-sample tests skip, because lavapipe renders color only at 1 and 4 samples.
    • The FMASK commit:
      • Without the FMASK commit, _8 fails with native FMASK contents require watched guest memory.
      • With the commit, modes 7-10 pass the FMASK import checks before the device limit, and mode 11 is refused.
      • A mutant without ComparedHostImportCovers in the FMASK check fails mode 11.
  • Not run on lavapipe:
  • ctest -R 'msaa|dcc|fmask|multisample': 38/38, 6 skipped for sample counts.
  • GitHub's runners:
    • This head (run): on Linux, red 3/3 with main's product files (compared_import_2, _4 and _8 fail), and green ×10 (13 tests; 5 skip on lavapipe for their sample counts). The libc and libkernel suites pass on Linux. On Windows it builds and passes ×10, and its Vulkan tests skip.
    • On Windows, the libkernel suite's guest_raise_in_system_call failed on both arms, with and without this PR. It also timed out twice on main's own Windows Build today (run, run). This PR changes nothing in libkernel.
    • The libc and libkernel suites ran with APS5_SKIP_TIMING_TESTS=1, which relaxes precise_wait's median bounds on GitHub's hosted runners.
    • An earlier run of the first commit alone (run): Linux red 3/3 on main, green ×10, suites pass; Windows builds, its Vulkan tests skip, suites pass.
  • Validation layer:
    • This head (run): llvmpipe (LLVM 20.1.2) with agc_driver_msaa_dcc_compared_import_4 and _8, agc_driver_msaa_dcc_clear_4, agc_driver_sample_multisample_target and agc_driver_native_fmask, one run on each tree. With the change, agc_driver_msaa_dcc_compared_import_4, agc_driver_msaa_dcc_clear_4 and agc_driver_sample_multisample_target pass, and agc_driver_msaa_dcc_compared_import_8 and agc_driver_native_fmask skip on llvmpipe. main's tree has three of the five: agc_driver_msaa_dcc_clear_4 and agc_driver_sample_multisample_target pass and agc_driver_native_fmask skips. None fail. The layer loaded 3 times on main and 5 with the change. Both logs report only VUID-VkDeviceCreateInfo-pNext-pNext, so the change adds no VUID- message.
    • The first commit's run (run) reports only VUID-VkDeviceCreateInfo-pNext-pNext, on both arms.
  • Title runs in Linux comparison mode (babyhuey, comment): lavapipe (Mesa 25.2.8) under Xvfb, 45 s per title, on main 895f47f and on that main with this head merged. Both arms log imported ranges are compared.
    • Rush 2 Recompiled (PPSA69423) and Wave Race 64 Recompiled (PPSA69424) stop with initial multisampled DCC contents require watched guest memory on main.
    • With this PR both get past it, to the device cannot render color with the target's sample count. That is lavapipe's limit: it renders color only at 1 and 4 samples.
  • Windows, by @klosixgaming-arch (comment): NVIDIA GeForce RTX 5090 (617.42), Windows 11 24H2, MinGW-w64 GCC 15.2.0.
    • The probe there keeps imports watched: [write-watch] host imports of write-watched memory keep the write watch: yes.
    • agc_driver_msaa_dcc_state and agc_driver_msaa_dcc_clear_2, _4 and _8 pass.
    • agc_driver_msaa_dcc_compared_import_2, _4 and _8 skip with their Linux-only message, as this PR intends on Windows.
  • AMD Radeon 860M on Linux (RADV, Mesa 26.2.5), by @Lander0606 (comment): 0c1f6b5 against main eb8dcfe, ctest -R 'msaa|dcc|fmask|multisample', three runs without and three with the Khronos validation layer (1.4.341). The probe picks comparison mode there, with pages reported written at the import and after a GPU read.
    • main: 35 of 35 pass in every run. 0c1f6b5: 35 of 38, with agc_driver_msaa_dcc_compared_import_2, _4 and _8 failing in every run, in mode 5 alone, with writes to a resident multisampled DCC target or its metadata are not implemented. With mode 5 skipped in a local edit, all three pass at 2, 4 and 8 samples, so the compared-import path holds on RADV.
    • The layer loaded in every run, and no run logs a VUID- line.
    • The third commit skips mode 5 on such a device.
  • The third commit, 54e2b57 (now 0045c93) (run): on GitHub's Linux runner (lavapipe) the probe reports no page written at the import or after a read, so mode 5 still runs. Of the msaa|dcc|fmask|multisample set's 38 tests, 32 pass 10 runs in a row and 6 skip for sample counts (agc_driver_msaa_dcc_compared_import_4 passes; _2 and _8 skip); none fail. On Windows, 5 pass 10 runs in a row and 33 skip without a Vulkan device. The libc and libkernel suites pass on both, run with APS5_SKIP_TIMING_TESTS=1, which relaxes precise_wait's median bounds on GitHub's hosted runners. GitHub's runners can't show the red: it needs a device whose probe reports those writes, as the 860M's does.
  • The 860M again, at 4df0813, by @Lander0606 (comment), same setup: 38 of 38 pass in all six runs, three with and three without the layer, none skipped. agc_driver_msaa_dcc_compared_import_2, _4 and _8 print the control-case skip line and then pass, with all five FMASK cases at 8 samples. On lavapipe on the same machine (Mesa 26.2.5), _4 and _8 pass without that line, so mode 5 still runs there, and _2 skips for the sample count. The layer loaded in every run, and no run logs a VUID- line.
  • Windows on real GPUs, by @BKlaasWerkman (comment): 4df0813 merged into main 15e71db (clean) and compared with that main on the same machine; Windows 11 Pro, WinLibs GCC 15.2.0, Release, ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1, with the Khronos validation layer (1.4.363) loaded; the 1,282 tests tools/select_tests.py picks for this diff.
    • NVIDIA GeForce RTX 3060 Ti (617.42): 1,272 pass, 7 skip, 3 fail. Intel Arc Graphics (Meteor Lake, 101.9150): 1,251 pass, 29 skip, 2 fail. Against main, the only difference on each GPU is the 3 new tests, which skip.
    • On Windows the probe reports that host imports keep the write watch (4,096 of 4,096 scratch pages reported written at the import, none after GPU reads or writes), so agc_driver_msaa_dcc_compared_import_2, _4 and _8 skip on both GPUs with their "Linux comparison mode only" text, as intended.
    • No other test changes result, agc_driver_alpha_to_coverage included, and the validation messages are the same as main's on both GPUs. The failures fail on main too: the agc_driver_sample_order trio on NVIDIA (test(agc): read the sample order tests' depth through a sampled copy #9056), and agc_storage_write_back and agc_driver_multisample_retirement on Intel.
  • CI's Linux job mirrored on a cloud Linux machine: 1852/1855. The failures are the machine's three IPv6 socket tests.
  • check_conventions.py: passes with no warning: the fourth commit shortens the multisampled DCC debt entry from 1,585 characters to 970, under the 1,000 limit. No comment lines are added.
  • Linear history on main 672c38f, after adriadam10's review (review): the four commits were replayed on main with no merge: 27371e9 → ff167c3, 0c1f6b5 → 1fc53ab, 54e2b57 → 0045c93, 4df0813 → e2449e2.
    • ff167c3 is a resolved replay, not a plain pick: commit 1 resolves feat(agc): expose expanded FMASK for tracked native owners #7948's FMASK-range code in MultisampleTarget.cpp as the earlier merge did. e2449e2's tree is identical to that merge of 4df0813 with main 672c38f, and it merges cleanly with main 00198bf.
    • The debt file keeps main's Multisampled CMASK state and Multisampled DCC CMASK without fast clears entries as they are. This PR changes only the Native multisampled DCC fast clears entry (970 characters) and the Compressed FMASK reads entry (watched or compared planes). python3 tools/check_conventions.py --base origin/main passes against main 00198bf.
    • On GitHub's runners, ^agc_driver_msaa_dcc_ with agc_driver_native_fmask, agc_driver_sample_multisample_target, agc_driver_sample_order, agc_driver_multisample_retirement and agc_storage_write_back, ×10 on that tree (run): Linux: 12 run, 7 pass, 5 skip (agc_driver_native_fmask and the 2- and 8-sample cases, for lavapipe's sample counts), 0 fail. Windows: 12 run, 1 pass (agc_driver_msaa_dcc_state), 11 skip (no Vulkan device), 0 fail. The libc and libkernel suites pass on both, run with APS5_SKIP_TIMING_TESTS=1, which relaxes precise_wait's median bounds on GitHub's hosted runners.
    • Each of the first three commits passes the same set ×3 at that commit, with the same counts on both runners (run, run, run). The fourth commit is docs only.

Overlap:

Checklist

@MarcZX10

Copy link
Copy Markdown
Contributor

I reviewed the Linux comparison-mode path and its control cases. The change admits an initial DCC range only when a live unwatched host import covers the whole range, snapshots bytes and the post-read write stamp, then validates unchanged bytes/stamps on reuse; explicit unwatch, partial/misaligned/overlapping planes remain refused. The test additions exercise compare-mode detection, changed data, partial imports, explicit-unwatch controls and the Rush 2 import shape. I ran git diff --check upstream/main...refs/review/9940; it passes. The current-base conventions check reports zero errors and one existing-style debt-length warning for the expanded DCC entry. Hosted eligibility, intake, conventions and labels pass; product/GPU jobs are skipped by policy. The author reports Linux lavapipe/RADV evidence and a paired focused suite, but these are not hosted product results.

This head is 3 commits ahead/67 behind current main; my changed-path comparison found no files modified by main since the merge base that this PR itself edits. Please refresh/recheck the integration before merge, especially the two PRs' combined behavior. #9941's description requests #9940 first; it also reports a combined synthetic tree passing 77/77, which is useful but not a substitute for the final rebased heads. I found no independent code defect in this pass. I could not build locally because the filesystem is full.

@github-actions

github-actions Bot commented Oct 22, 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
PR 8123 core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp L786-811, L813, L815
docs/dev/debt/libSceAgcDriver.md L82
PR 9012 core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp L135

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/GuestBufferMemory.cpp
PR 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1896 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 4448 core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 5058 docs/dev/debt/libSceAgcDriver.md
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5372 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6194 docs/dev/debt/libSceAgcDriver.md
PR 6197 core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6223 docs/dev/debt/libSceAgcDriver.md
PR 6273 docs/dev/debt/libSceAgcDriver.md
PR 6280 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6548 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6549 docs/dev/debt/libSceAgcDriver.md
PR 6642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6804 docs/dev/debt/libSceAgcDriver.md
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6918 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 6971 core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
PR 6977 docs/dev/debt/libSceAgcDriver.md
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 7256 docs/dev/debt/libSceAgcDriver.md
PR 7275 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7449 docs/dev/debt/libSceAgcDriver.md
PR 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 7464 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 7505 docs/dev/debt/libSceAgcDriver.md
PR 7514 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 7677 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7910 docs/dev/debt/libSceAgcDriver.md
PR 8014 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8223 core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8319 docs/dev/debt/libSceAgcDriver.md
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8520 docs/dev/debt/libSceAgcDriver.md
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8998 core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
PR 9248 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9335 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9356 docs/dev/debt/libSceAgcDriver.md
PR 9387 docs/dev/debt/libSceAgcDriver.md
PR 9393 docs/dev/debt/libSceAgcDriver.md
PR 9431 docs/dev/debt/libSceAgcDriver.md
PR 9432 docs/dev/debt/libSceAgcDriver.md
PR 9592 docs/dev/debt/libSceAgcDriver.md
PR 9672 docs/dev/debt/libSceAgcDriver.md
PR 9673 docs/dev/debt/libSceAgcDriver.md
PR 9674 docs/dev/debt/libSceAgcDriver.md
PR 9675 docs/dev/debt/libSceAgcDriver.md
PR 9676 docs/dev/debt/libSceAgcDriver.md
PR 9700 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9842 docs/dev/debt/libSceAgcDriver.md
PR 9891 docs/dev/debt/libSceAgcDriver.md
PR 9892 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9893 docs/dev/debt/libSceAgcDriver.md
PR 9895 docs/dev/debt/libSceAgcDriver.md
PR 9897 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9898 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9904 docs/dev/debt/libSceAgcDriver.md
PR 9905 docs/dev/debt/libSceAgcDriver.md
PR 9906 docs/dev/debt/libSceAgcDriver.md
PR 10046 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10049 core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10193 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10233 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10359 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 10435 docs/dev/debt/libSceAgcDriver.md
PR 10442 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp

@MarcZX10

Copy link
Copy Markdown
Contributor

I rechecked exact head 0c1f6b5571bdc3930db428d09927ddf6e9f852e3 against current main fcd455870cb9610d929f744719a4d20ffd544499. git diff --check is clean; current-main conventions report 0 errors and the documented 1,585-character DCC debt-entry warning. The PR reports useful lavapipe red/green evidence, including CPU-write and same-value driver-store cases, plus a merged-main test run of 38/38 with six sample-count skips. The exact-head GitHub validation run and Linux comparison-mode title run are still listed pending, and eight-sample paths are not exercised on lavapipe; I have not treated those as passed. Hosted eligibility/conventions/intake/report pass; GPU/product jobs are skipped by path policy.

I reviewed the paired sequencing too: #9940 is the first integration step, followed by #9941’s Windows pending-settle/rewatch handling. #9941 describes a combined test/9724-both-main resolution with 77/77 focused tests; preserve and rerun that combined case after both are current. I found no separate blocker in the submitted comparison-mode coverage. The documented limitation that a same-value CPU store is invisible remains important and is accurately stated. Please update the pending exact-head validation and title evidence when available, and consider shortening the over-limit debt paragraph in line with the conventions warning.

@babyhuey

Copy link
Copy Markdown
Contributor

Linux title run in comparison mode, which the description lists as pending: lavapipe (Mesa 25.2.8, llvmpipe (LLVM 20.1.2, 256 bits)) under Xvfb in an Ubuntu 24.04 container, 45 s per title. The red arm is main 895f47f4ee; the green arm is that main with this PR's head 0c1f6b5571 merged (7d00aab5fa, clean).

Both arms log the probe result this PR targets:

[write-watch] host imports resolve write protection: yes (0 of 4 scratch pages written after a GPU read, 4 after a GPU write, 0 after the import); imported ranges are compared
Title main With this PR
Rush 2 Recompiled (PPSA69423) 8.0 s: initial multisampled DCC contents require watched guest memory 8.3 s: the device cannot render color with the target's sample count
Wave Race 64 Recompiled (PPSA69424) 1.7 s: initial multisampled DCC contents require watched guest memory 1.7 s: the device cannot render color with the target's sample count

So both titles get past the #9724 stop. The next stop is the framebufferColorSampleCounts check in MultisampleTarget.cpp. lavapipe renders color at 1 and 4 samples only, as the description notes, so their targets use 2 or 8 samples, and that stop is lavapipe's limit.

One note for anyone repeating this in Docker: the default seccomp profile blocks userfaultfd, and the run then logs write watch unavailable: userfaultfd failed (Operation not permitted); guest memory is compared instead and never takes this PR's path (both arms stop on the DCC message). --security-opt seccomp=unconfined lets the UFFD_USER_MODE_ONLY fallback open it. My lavapipe data on #9724 was from the blocked mode.

@MarcZX10

Copy link
Copy Markdown
Contributor

The new Linux title run is useful: both games pass the original DCC refusal on the merged main + PR tree and reach lavapipe’s unsupported sample-count check. The PR’s validation-layer run is still listed as pending. CONTRIBUTING requires the focused AGC tests on main and the change with VK_LAYER_KHRONOS_validation, ANYPS5_NO_SHADER_CACHE=1 and ANYPS5_REQUIRE_VULKAN=1, including confirming no new VUIDs and naming the GPU. Could you attach the final-head paired validation results before merge? Also, upstream main is now 895f47f4; since the PR’s merge commit includes earlier main eb8dcfe, please rebase/merge current main and rerun the focused suite so the final result covers current integration.

@EagleBearerYS
EagleBearerYS marked this pull request as ready for review October 23, 2026 00:02
@klosixgaming-arch

Copy link
Copy Markdown
Contributor

Hardware & Environment

  • OS: Windows 11 Version 24H2 (OS Build 26100.3194)
  • CPU: AMD Ryzen 9 9950X3D (16 Cores, 32 Threads)
  • GPU: NVIDIA GeForce RTX 5090 32 GB (Driver: 617.42)
  • Toolchain: MinGW-w64 GCC 15.2.0, Ninja 1.13.0, CMake 3.31.6

Test Methodology & Verification

  1. Security Audit:

    • Inspected changes across core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp, core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp, core/libs/prx/libSceAgcDriver/tests/execution/AlphaToCoverage.cpp, and docs/dev/debt/libSceAgcDriver.md.
    • Verified memory safety of DCC/FMASK range copies, proper bounds checking, and absence of untrusted networking or file operations.
  2. Windows / NVIDIA Host Import Verification:

    • On Windows 11 with driver 617.42, host imports keep write protection ([write-watch] host imports of write-watched memory keep the write watch: yes).
    • Verified that ComparedHostImportCovers correctly guards Windows platforms via #ifdef _WIN32 returning false, ensuring that platforms with functional write watches preserve their exact behavior without overhead.
  3. Test Results:

    • Recompiled and executed the full multisampled DCC and comparison test suite:
      • agc_driver_msaa_dcc_state: PASSED (0.07s)
      • agc_driver_msaa_dcc_clear_2: PASSED (0.75s)
      • agc_driver_msaa_dcc_clear_4: PASSED (0.95s)
      • agc_driver_msaa_dcc_clear_8: PASSED (0.68s)
      • agc_driver_msaa_dcc_compared_import_{2,4,8}: SKIPPED as intended with skipped, Linux comparison mode only: Windows host imports settle on a second queue or keep their write watch.
    • All 7 tests executed cleanly (100% pass / safe skip).
  4. Conventions:

    • python tools/check_conventions.py --base origin/main reported 0 errors (1 non-blocking warning for debt entry length in docs/dev/debt/libSceAgcDriver.md:73).

LGTM!

AI-assisted: yes. Written with AI assistance and reviewed by me.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

Thanks, @klosixgaming-arch. That's the Windows side this PR should leave as it is, now checked on an RTX 5090: your probe keeps imports watched, the multisampled DCC tests pass, and the compared-import tests skip as intended. The Tested section now lists your run.

@MarcZX10 MarcZX10 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 Linux compared-import approach is distinct from #9941’s pending-Windows-settle path, and the branch declares the intended order (this PR before #9941). Its reported lavapipe title runs and test comparison are useful for that older tree.

I cannot approve the current integration yet. The merge base is eb8dcfebaa; this head is 618 commits behind current main 777bdbb4f8 (3 commits ahead). Since that base, current main has changed the same AGC integration paths: 511 lines in GuestBufferMemory.cpp, 119 in the AGC CMake registrations, and 18 in GuestBufferMemory.hpp, along with the debt index. Please rebase and preserve the declared ordering before #9941, then rerun the focused comparison-mode/MSAA DCC tests on current main and the refreshed head and recheck the combined tree. The current PR’s product jobs are skipped by policy, so I am relying only on the test runs actually reported in its description for the old base.

One convention warning also remains: the multisampled DCC debt entry is 1,585 characters (the prior main entry was 1,232). Please trim it to the unresolved behavior and assumptions so the refreshed conventions check no longer reports the debt-length warning.

@Lander0606

Copy link
Copy Markdown

A real-GPU run in comparison mode: AMD iGPU on Linux (RADV). Head 0c1f6b5571 against the main it merges, eb8dcfebaa. The compared-import cases pass on RADV, but the three new tests fail on the watched control case (mode 5).

Setup

  • Radeon 860M (Ryzen AI 7 350, 1002:1114, RADV KRACKAN1), Mesa 26.2.5, Vulkan 1.4.354, Fedora 44, kernel 7.2.11.
  • Built in an Ubuntu 24.04 container with GCC 13.3, Release, BUILD_TESTING=ON, ANYPS5_ENABLE_SPIRV_TOOLS=ON. The tests ran on the Fedora host, with VK_DRIVER_FILES limited to RADV.
  • ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 ctest -j1 -V -R 'msaa|dcc|fmask|multisample', three runs without and three with VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation (1.4.341).
  • The probe picks comparison mode here, with pages reported written at the import and after a GPU read:
    [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
    
Without the layer With the layer
main eb8dcfebaa (35 tests) 35/35, 35/35, 35/35 35/35, 35/35, 35/35
this PR 0c1f6b5571 (38 tests) 35/38, 35/38, 35/38 35/38, 35/38, 35/38

The three failures are agc_driver_msaa_dcc_compared_import_2, _4 and _8, in every run, all with the same message:

AGC graphics: writes to a resident multisampled DCC target or its metadata are not implemented (color range 0x7f..., 4 samples)

Unlike lavapipe, the 860M renders 2 samples, so _2 runs here instead of skipping.

Where it stops. I added a temporary print of the mode at the top of the loop, and one in copyCurrent. Both edits were local only and are reverted; the results above come from the unmodified head.

  • 2 and 4 samples: modes 0 to 4 pass, and the test stops in mode 5.
  • 8 samples: modes 7 to 11 pass (all five FMASK cases print their expected lines), then modes 0 to 4, then it stops in mode 5.
  • Not the compared path: in mode 5 the planes are watched, and the refusal comes from the watched path's reuse check. The print in copyCurrent fired only for the expected refusals of modes 1-3 and 9-10.
  • With mode 5 skipped (again a local edit), mode 6 passes as well, and all three tests print Multisampled DCC compared host import tests passed at 2, 4 and 8 samples.

So the compared-import path this PR adds works on RADV: the imports, the reuse, and the refusals of modes 1-3 and 9-10. What fails is mode 5, which forces SetImportWatch(..., Watch) on a device whose probe reports pages written at the import and after a GPU read. That matches #10122, where the forced-watch shadow tests fail on this adapter for the same reason. #10138 skips those tests when ProbeImportWriteProtection fails or reports pages written at the import or after a GPU read. The same condition on mode 5 would keep the control case on devices where watched imports hold, and run everything else here.

Same machine, lavapipe (llvmpipe, Mesa 26.2.5): _4 and _8 pass, and _2 skips for the sample count. The probe there reports 0 ... after a GPU read, 4 after a GPU write, 0 after the import.

Validation: the loader inserted VK_LAYER_KHRONOS_validation 33 times per run on main and 36 on the head (VK_LOADER_DEBUG=layer). No log contains was not found but was requested by env var VK_INSTANCE_LAYERS, and no run logs a VUID- line, including the failing ones.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

Thanks, @Lander0606, that pins it down: the three tests failed only in mode 5, the control that forces the imports to stay watched, and on a device whose probe reports watched pages written at the import and after a GPU read, that control can't hold. 54e2b57 runs mode 5 only where the probe reports neither, the condition #10138 uses for the forced-watch shadow tests, so lavapipe still runs it and the 860M skips it. Your run is in the Tested section. If you have time, could you run ctest -R 'msaa|dcc|fmask|multisample' on the 860M at this head?

@Lander0606

Copy link
Copy Markdown

@EagleBearerYS, here's the rerun on the 860M at the current head 4df08139c0 (54e2b57a66 plus the debt-entry commit), again without merging onto main. All 38 tests pass in every run.

Same setup as before: Radeon 860M, RADV KRACKAN1, Mesa 26.2.5, Fedora 44, kernel 7.2.11. ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 ctest -j1 -V -R 'msaa|dcc|fmask|multisample', three runs without and three with VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation (1.4.341).

Without the layer With the layer
4df08139c0 (38 tests) 38/38, 38/38, 38/38 38/38, 38/38, 38/38
  • agc_driver_msaa_dcc_compared_import_2, _4 and _8 pass and aren't skipped. Each prints the watched-import control case is skipped: the device reports watched host memory written when it imports or reads it, then Multisampled DCC compared host import tests passed.
  • 8 samples: all five FMASK cases print their lines, from color, DCC, CMASK and FMASK compared: imported and reused through an explicitly unwatched FMASK: refused at the initial import.
  • No other skips: ctest reports no skipped test in the set.
  • lavapipe on the same machine (Mesa 26.2.5): _4 and _8 pass without the control-case skip line, so mode 5 still runs there. _2 skips for the sample count, as before.
  • Validation: the loader inserted VK_LAYER_KHRONOS_validation 36 times per run (VK_LOADER_DEBUG=layer). No log contains was not found but was requested by env var VK_INSTANCE_LAYERS, and no run logs a VUID- line.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

Thanks, @Lander0606, that closes it: the 860M now runs the whole set with the control case skipped where it can't hold. It's in the Tested section.

@BKlaasWerkman

Copy link
Copy Markdown

Windows run on NVIDIA and Intel. The runs above are all Linux, where the probe picks comparison mode. Head 4df0813 was merged into main 15e71db (clean merge) and compared with that main on the same machine.

Setup: Windows 11 Pro 10.0.26300, WinLibs GCC 15.2.0 r7, Release. Environment: VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation (1.4.363), ANYPS5_REQUIRE_VULKAN=1 and ANYPS5_NO_SHADER_CACHE=1. The run covers the 1,282 tests that tools/select_tests.py selects for this diff.

GPU this PR main
NVIDIA GeForce RTX 3060 Ti (617.42) 1272 passed, 7 skipped, 3 failed the same, plus the 3 new skips
Intel Arc Graphics (Meteor Lake, 101.9150) 1251 passed, 29 skipped, 2 failed the same, plus the 3 new skips
  • On Windows the probe reports that host imports keep the write watch: 4096 of 4096 scratch pages are reported written at the import, and none after GPU reads or writes.
  • So agc_driver_msaa_dcc_compared_import_2, _4 and _8 skip on both GPUs with skipped, Linux comparison mode only: Windows host imports settle on a second queue or keep their write watch, as the tests intend.
  • No other test changes result, including agc_driver_alpha_to_coverage, and the validation messages are the same as main's on both GPUs.
  • The failures also occur on main:
    • On NVIDIA, the agc_driver_sample_order trio fails.
    • On Intel, agc_storage_write_back and agc_driver_multisample_retirement fail.

AI-assisted: yes (Claude Code)

@adriadam10 adriadam10 self-assigned this Oct 23, 2026
@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

Thanks, @BKlaasWerkman, a Windows comparison against main on two GPUs under the layer is what this PR was missing, since the earlier runs were all in Linux comparison mode. It shows the three new tests skipping where Windows imports keep their write watch, and nothing else changing. It's in the Tested section now.

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@MarcZX10 4df0813 trims the multisampled DCC debt entry below main's length, and conventions pass on it. Could you re-review?

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 24, 2026

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main moved and this PR now conflicts with it, only in docs/dev/debt/libSceAgcDriver.md. The code in MultisampleTarget.cpp, GuestBufferMemory.* and the CMake merges cleanly, but replaying the commits one by one stops in MultisampleTarget.cpp (the #7948 merge commit is now part of main), so please update the branch against main yourself, with a single linear history.

Resolution for the debt file:

  • keep main's Multisampled CMASK state entry as it is (it now lists the 0xcccccccc/0xdddddddd/0xeeeeeeee/0xffffffff patterns; this PR doesn't touch it);
  • keep main's new Multisampled DCC CMASK without fast clears entry;
  • keep this PR's Compressed FMASK reads entry (watched or compared planes).

Then re-run python3 tools/check_conventions.py --base origin/main (the DCC entry must stay under 1,000 characters) and the focused tests on the rebased tree.

@MarcZX10 your three requests on 0c1f6b5571 look addressed at 4df08139c0 (debt entry 970 characters, RADV 860M 38/38, Windows RTX 3060 Ti / Arc against main). Could you re-review once the author pushes the rebased branch?

…CC import

Rush 2 Recompiled (PPSA69423) and Wave Race 64 Recompiled (PPSA69424)
stop with "AGC graphics: initial multisampled DCC contents require
watched guest memory"
(boykopovar#9724). On Linux with
lavapipe, the write-watch probe finds that device accesses to a host
import trip the write watch, so every imported range is compared rather
than watched. A multisampled DCC target whose color, DCC or CMASK range
lies in such an import was then refused at its first draw: importing the
target takes a write generation for each range, and a compared range has
none.

A range that is not watched but lies in a live host import the probe
compares is now copied when the target is imported, and its metadata
again when a whole fill is taken as its fast clear. Each later use
compares the range with that copy, at most once per collect epoch, and
counts any driver or GPU store over its 64 KiB blocks since the copy as
a write, so a guest store of new bytes or any store by the driver
refuses the resident target as a watched range does. A range left
unwatched outside such an import is refused as before.

This covers the Linux comparison mode, not the Windows deferred settle
(BF-150): compared imports on Windows still refuse.
…MASK range

be write-watched at the initial import ("native FMASK contents require
watched guest memory") and at each reuse ("native FMASK sampling requires
watched guest memory"). In Linux comparison mode a host import over the
FMASK unwatches it, so the target is refused one check after the color,
DCC and CMASK ranges that the previous commit accepts.

Treat an FMASK range that a compared host import covers
(ComparedHostImportCovers) as the color and metadata ranges are treated:
keep a copy and its NewestStamp at the initial import, skip it in the
import's UnchangedSinceAll, and at reuse require the stamp and the bytes
to be unchanged, comparing them at most once per collect epoch. An
explicitly unwatched FMASK range is still refused.

Four-sample targets take no FMASK range, so this changes nothing for them.

Refs boykopovar#9724
…pages written

DccComparedImportTests' mode 5 forces the write watch on the planes' host imports as a control. On a device whose import probe reports watched pages written at the import or after a GPU read, as RADV on a Radeon 860M does, the watched planes see those writes and the reuse check refuses with the resident-DCC message, so the three compared-import tests failed there in mode 5 alone. Mode 5 now runs only where the probe reports neither, the condition boykopovar#10138 uses for the forced-watch shadow tests; lavapipe still runs it.
@EagleBearerYS
EagleBearerYS force-pushed the fix/agc-compared-import-initial-dcc branch from 4df0813 to e2449e2 Compare October 24, 2026 11:36
@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@adriadam10 Thanks, done. The branch is now four commits on main 672c38f with no merge, at e2449e2, and it merges cleanly with main 00198bf.

  • The debt file keeps main's Multisampled CMASK state and Multisampled DCC CMASK without fast clears entries as they are, and this PR's Compressed FMASK reads entry (watched or compared planes).
  • python3 tools/check_conventions.py --base origin/main passes, and the multisampled DCC entry is 970 characters.
  • The focused tests pass on the rebased tree on both runners ×10 (run) and at each code commit ×3 (run, run, run). The Tested section has the counts.

@MarcZX10 the rebased branch is pushed. Could you re-review?

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 24, 2026

This branch has not been deployed

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

Labels

unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants