Repository navigation
Conversation
…ettle boykopovar#1441 compares every Windows host import because a driver can mark imported pages written again shortly after the import window (1.2-2 ms on the NVIDIA driver in boykopovar#1434), and those marks dropped GPU results. The comparisons make PPSA22520 spend 6.8 s of every 10 s revalidating resources: about 7.5 FPS instead of 59. Imports stay watched. Each import keeps a copy of its bytes for a settle period (1 s, APS5_IMPORT_SETTLE_MS): a page marked in that period whose bytes still equal the copy is stamped maybe written, like the import window's own marks, and a page whose bytes changed is a CPU store. One walk of the import at the next collect after the period applies the same rule to the rest. An import whose copy would exceed 256 MiB, or 512 MiB with the copies already kept, is compared, as is every import under APS5_IMPORT_SETTLE_MS=0 or APS5_WRITE_WATCH_IMPORTS=unwatch, which is now read on Windows too; =watch keeps plain watching. Unwatching a range or giving it fresh private backing drops its copy. On Windows the DCC first-write test now rewrites the imported block with its own bytes between the import and the write-back, so it reproduces boykopovar#1434 on every driver: it fails in every run with plain watching. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
A second title for this, with a different workload: ccleste (PPSA59322), a homebrew port that renders through RADV (Mesa's Vulkan driver on the PS5) and writes its frames through storage images. #1441 halved its frame rate here, and this PR restores it. Windows 11 Pro (26200), RTX 5070 Ti (driver 610.88), WinLibs GCC 15.2.0 r7, Release,
Without it, every draw re-uploads ccleste's storage images in full (reason To isolate the cause beforehand, I set only I only measured the frame-rate side; I did not test the #1434 side here. AI-assisted (Claude Code). |
|
A possible gap in the byte check that I'd like to confirm with you. I found it by reading the code and can't run the Windows path here (Linux only), so it may not show up in practice. Only I ran a standalone copy of Minimal fix: let the Driver walk refresh the copy. StampKind settlingKind(WriteTracker& tracker, std::uintptr_t page, StampKind kind) {
if (kind == StampKind::ImportWindow) return kind;
for (auto& entry : tracker.settling) {
...
if (kind == StampKind::Driver) { std::memcpy(saved, current, WritePageBytes); return kind; }
if (std::memcmp(saved, current, WritePageBytes) == 0) return StampKind::ImportWindow;
...Could you run this on Windows? It's a test in the style of Require(ImportWatched(base, Block, [] { return true; }, true), "the import callback failed");
StoreOwnBytes(base + 8, 1, [&] { bytes[8] = 0x44; });
generations[0] = TrackerGeneration();
bytes[8] = bytes[8];
CollectWritesUncached(base, Block);
Require(state(base) == BlockMaybeWritten, "a late mark over the driver's own store read as a CPU store");GPU writes that go straight into the import ( AI-assisted: yes |
A store the driver makes into a settling import (storeOwn) left the copy with the bytes from before it, so a late mark over the stored GPU results compared as changed and read as a CPU store. The stored range now refreshes the copy. A GPU write straight into the import (MarkWritten) cannot, as its bytes land later; a late mark over it still reads as a CPU store, as without settling, since treating it as maybe written loses CPU stores made after the GPU write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@fisherjoey thanks, good catch, the For |
|
Ran this on the driver that shows #1434's late marking, as asked in #1434. Setup: Windows 10 (19045), RTX 3060 Laptop GPU, NVIDIA 616.92, MinGW-w64 GCC 15.2.0 (WinLibs r7), Release. This PR's head
No title was run. 🤖 Addressed by Claude Code |
|
Tested on Windows with another title and GPU. Setup: Windows 11 22621, Ryzen 7 5700X, RTX 3070, Dreaming Sarah (PPSA02929), GCC 15.2.0 UCRT Release. PR merged onto main
On main, Limits: runs were under a minute and stayed on the title screen and main menu, not gameplay. No crash and no visible difference in that time. AI-assisted: yes (build, log analysis and this write-up). |
|
Closing this, #2096 fixed the same regression on main a different way (shared aliases keep their write tracking, direct imports compare against a texture snapshot). On main d70b899 Animal Well now runs at 587–588 presents/10 s with 0 full rescache walks, the same as with this PR, so there's nothing left for it to do. Thanks @popcoder565 and @Rohan03x for testing on the 616.92 driver and Dreaming Sarah, and @fisherjoey for the review. |
What
#1441 dropped PPSA22520 (Animal Well) on Windows from about 59 to 7.5 FPS. This restores the frame rate and keeps #1434 fixed.
Since #1441, Windows compares every host import instead of write-watching it, because the NVIDIA driver marks imported pages written again about 1.2-2 ms after the import window, and those marks dropped GPU results (#1434). The comparisons make every resource revalidation over an import a full walk. In PPSA22520 (Animal Well) that is 6.8 s of every 10 s and the frame rate falls from about 59 to 7.5 FPS.
Imports stay watched again, and the late marks are told apart from CPU stores by their bytes:
GuestMemory::ImportWatchedkeeps a copy of the import's bytes for a settle period after the import (1 s,APS5_IMPORT_SETTLE_MS). The late marks arrive about 2 ms after the import; the period only needs to outlast them, and a longer one only keeps the copy longer.ImportWindow(maybe written), as the import window's own marks already are. Write-back keeps the GPU results over such a block.StoreOwnBytesand the otherstoreOwnpaths) refresh the copy, so a late mark over GPU results it wrote back still reads maybe written.APS5_IMPORT_SETTLE_MS=0. Its import entry records that, so a refresh keeps it compared. Unwatching a range or giving it fresh private backing drops its copy.APS5_WRITE_WATCH_IMPORTSis now read on Windows too:unwatchcompares every import, as fix(agc): compare Windows host imports after late driver writes #1441 does;watchkeeps imports watched without settling, the behaviour before fix(agc): compare Windows host imports after late driver writes #1441.TechnicalDebt records the settle period, that it assumes a driver finishes marking within it, that a CPU store of identical bytes in the period reads as maybe written, and that a GPU write straight into a settling import (
MarkWritten) cannot refresh the copy, as its bytes land later, so a late mark over it reads as a CPU store, as without settling. Treating those pages as maybe written instead makesagc_driver_recorder_testslose a CPU store made after a GPU write ("a CPU store into a unit with results pending was not seen by the refresh"). Settling an import on an event instead (a GPU read of it and its fence, as #1434 suggests) would remove the timing assumption, but it can't be checked on a driver that doesn't mark late, and the import sites hold the host import and tracker locks.A settle period that only reclassified marks by time was not enough:
agc_driver_recorder_teststhen publishes a stale unit shadow over a real CPU store ("a stale unit was published"), becausePublishShadowonly dropsBlockWrittenunits. The byte check is what keeps that test passing.Tested
Windows 11, RTX 4070 SUPER (driver 610.88), WinLibs GCC 15.2.0 r7, Release.
AGC: on Windows (NVIDIA) a new host import's pages are marked written after ImportWatched's window, so write-backs drop GPU results #1434, deterministically: this driver does not show the late marking on its own (
agc_driver_dcc_keys_first_writeandagc_driver_srgb8_color_targetpass 30 of 30 with plain watching). On Windows, the write-watched run ofagc_driver_dcc_keys_first_writenow rewrites every page of the imported block with its own bytes between the import and the write-back, as the driver does, and pins a long settle period so a slow runner cannot race it. WithAPS5_WRITE_WATCH_IMPORTS=watchit fails in every run with AGC: on Windows (NVIDIA) a new host import's pages are marked written after ImportWatched's window, so write-backs drop GPU results #1434's message (the first write: the write-back stored 0 written and 0 cleared bytes of 65536, expected 128 written); by default and withAPS5_WRITE_WATCH_IMPORTS=unwatchit passes.Frame rate, PPSA22520,
APS5_PROFILE_DRAW=1, 10 s windows:[present]per 10 s[rescache] revalidatemain4b6ab6bagc_write_tracking: a new Windows case (with its settle period pinned to 100 ms) imports two blocks, then checks that a page rewritten with its own bytes in the settle period reads maybe written, that a changed page and a store next to the import read written, that the closing walk applies the same rule, that a mark after the period reads written, that an import watched without settling trusts its marks, that a late mark over the driver's own store reads maybe written, that a CPU store after a GPU write into a settling import reads written, and that unwatching a settling import leaves it unwatched.agc_driver_recorder_tests(not registered in ctest) passes, including the unit-shadow stale publish case and the Windows import-watch default.8 mutations each fail a test: unchanged pages trusted as CPU stores, changed pages not trusted, no closing walk, imports not registered, the driver's stores not refreshing the copy, a settle period that never ends, Windows comparing by default, and Windows not settling by default.
Full ctest on this branch alone, on top of
main: 479/479.Linux and macOS: the change is Windows-only (
#ifdef _WIN32), not built there.Related: #1599 keeps GPU results over write-back blocks whose bytes the CPU left unchanged (images up to 1 MiB); it is compatible with this and does not restore watching. #1310 and #1361 speed up write tracking and import lookups on Linux.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code