Repository navigation
fix(agc): consume the write-watch faults of the driver's own CPU stores - #462
Merged
oneandonlydean merged 1 commit intoOct 4, 2026
Merged
Conversation
9 tasks done
A store the driver makes with the CPU into watched guest memory (a GuestMemory::Write, a changed-bytes write-back, a completion action's label store, the copy HLE's CPU path) faults its pages dirty in the write watch and stamps the range with MarkWritten. The faults stayed behind: collects are memoized per packet, so the next walk over those pages came later and stamped them again, as a CPU write made after every check in between. PPSA21564 fast-clears its normal G-buffer every frame with a register DCC key fill on the GPU. While that fill is still pending, the first draw into the target materializes the clear with a CPU store of the clear texels and a refresh of the resident image. A dozen draws later a walk found the store's faults, the target read as changed, and the image was uploaded again from guest memory, dropping the draws recorded in between. Lighting then sampled cleared normals and the level's moon and rocks went unlit in about half the frames. The driver's stores now consume those faults themselves: a walk before the store stamps earlier CPU writes as such, a walk after it takes the store's own faults as a driver store, and the range is stamped as before. On Windows the arena's range also holds host regions that were already in use when it was reserved (heaps, thread stacks). They are not write-watched, so a store into one is stamped as before, without a walk.
oneandonlydean
force-pushed
the
fix/agc-own-store-write-watch
branch
from
October 3, 2026 22:36
95477e7 to
bb1531f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A store the driver makes with the CPU into write-watched guest memory left its own write-watch faults behind. Those pages were later read as a new guest write and uploaded again over GPU results.
The stores concerned are
GuestMemory::Write, the changed-bytes write-back (WriteChanged), a completion action's label store (Recorder::AfterCompletions) and the CPU path of the copy HLE (VulkanDevice::CopyBuffer). Each one stores, which faults its pages dirty in the write watch, and then stamps the range withMarkWrittenas a driver store. The faults stayed in the watch. Collects are memoized per packet, so the next walk over those pages came later and stamped them again, this time as a CPU write made after every check in between. A resident image or buffer over that range then reads as changed by the guest and is refreshed from guest memory, which drops whatever the GPU wrote there since the driver's store.How PPSA21564 hits it, on my integration branch once the occlusion dumps stopped draining the device:
On
mainevery occlusion counter dump drains the device, so the key fill has usually landed by that draw, the clear goes to the GPU, and no CPU store happens. That hides the bug in this title, but any CPU store by the driver into watched memory takes the same path.The fix: a driver store now consumes its own faults.
GuestMemory::StoreOwnBytes(address, bytes, store), used by all four stores above, under the tracker lock:MarkWrittenstamps it, including the per-block driver pieces of fix(agc): keep a surface's results in the 64 KiB block it shares with its neighbour #441.GetWriteWatchrefuses them. A store into one of them falls back as well, through a newGuestArenaHostRegionOverlaps_nid_postfixin libc'sGuestArena.This builds on #441: it uses the driver pieces (
noteDriverStore), and its test extends #441'sagc_write_tracking. #441 is merged and the branch is rebased onto it.Tested
Linux (CachyOS, x86-64): full build with
BUILD_TESTING=ONandctestpass (146/146).Windows (MinGW, CI on my fork): build and
ctestpass.New
CheckOwnStoreinagc_write_tracking(tests/WriteTracking.cpp) checks that:Writeinto a collected block is stamped;Without the change it fails with "a walk after a driver store reports the store again as a newer write".
PPSA21564 on my integration branch, with the occlusion dump drains removed: the intro level's moon is dark in 104 of 241 frames (every frame kept) without this change, and in 0 of 241 with it. It has also passed repeated runs of my visual regression check there (checkpoint frames, flicker, shimmer, video tearing, FPS).
No other open PR touches these functions. perf(agc): query guest page access with PROCMAP_QUERY on Linux #157 also edits
GuestMemory.cpp(the maps cache) and already conflicts withmain. My fix(agc): describe the pages after a gap in a Linux host page query #442 (alsoGuestMemory.cpp) and fix(libSceFiber): pin fiber contexts writable under write tracking on Windows #283 (also libc'sGuestArena) merge cleanly with this branch.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)