Repository navigation
fix(agc-driver): keep a live-backed snapshot mismatch from failing the dispatch - #5484
Conversation
…e dispatch A captured word that no longer matches a live import or mirror only means the capture raced a guest write; the GPU reads the live bytes either way, as the hardware would. The compare now counts such mismatches and reports them with the snapshot, the region and the first differing offset at most every 10 s instead of throwing; APS5_SNAPSHOT_CHECK_FATAL=1 restores the throw, and a snapshot-backed region still fails, now naming the snapshot, the region and the first differing offset. PPSA15304 (engine: Unreal Engine 5) hit the throw within 65 to 91 s of reaching its title screen.
|
Demon's Souls (PPSA01341 v1.07) hits the same throw, What changes the bytes, from a run with every mismatch logged:
This PR stops the throw, but the dispatch then reads the new bytes. On hardware the dispatch reads memory when it runs, and the capture is what stands for that moment here. Could the regions that differ be bound from the captured bytes instead? That keeps the capture's view and doesn't fail the dispatch. #1787 named gate-time snapshots as the missing piece for a related case. I can test the PR on this stack and post the results here. AI-assisted: yes |
|
Thanks for the trace, that matches what PPSA15304 does: its job threads refill per-frame tables while a capture is in flight, and the mismatch is the title's own later write, not corruption. Binding the differing regions from the captured bytes is the right follow-up: it keeps the capture's view of the memory, which is what stands for the moment the dispatch runs on hardware. It needs a snapshot buffer per differing region and a path that prefers it over the import, so it is a change of its own; this PR only stops the dispatch from failing, as the title cannot continue past the throw at all. I would keep the two apart: this one first, the captured-bytes binding as a follow-up PR that can also take the gate-time snapshots #1787 asked for. A run of this PR on your stack would help, yes. If the load survives, the one mismatch with a GPU write behind it (the region read raw on write evidence) is the case I would look at first, since that one is not covered by reading the new bytes. |
|
Run of this PR (67973cd) on the Demon's Souls stack from my comment above: The level load survived. The level started at about t+155 to t+215 and ran for 240 to 300 s, until an NVIDIA Xid 31 at t+451 (MMU fault, copy engine CE0, PTE write). I saw the same Xid once before on a build without this PR, so I can't tie it to this change. Mismatches: 11607 were logged and none were thrown. All of them were in group-adopted dispatches, none inline. The inline queue 0x50 case that killed my earlier runs didn't come up in this one.
The GPU-write case you asked about occurred 12 times, in four bursts between t+215 and t+397:
So here the raw read on evidence returned bytes from before queue 0's dispatch wrote the region. The capture holds the stale values, and the live region holds the descriptor the writer produced. The other 18 writer-backed mismatches weren't raw reads on evidence (streak 0, the dwords were never sampled). They were queue 0x30/0x20 reads near 0x24ed7fdd0/0x24ed7fd60 with the same writer 0x248881200, and queue 0x50 shaders 0x2497f1300/0x2497f8300 over 0x39b904eb0 with writer shader 0x24afe2c00. AI-assisted: yes |
|
Thanks for running it. The 12 writer-backed cases settle the question for this PR: the raw read on write evidence had sampled the region before queue 0's dispatch stored the descriptor there, so the capture held stale values and the live bytes were the right ones; the dispatches that went on with them raised nothing. Reading the live bytes is what the hardware would have seen, since the writer ran before these readers in the title's own order. That also narrows the follow-up: binding from the captured bytes is only right when the capture is newer than every writer of the region, which needs the writer's completion to be part of the decision; a snapshot taken before a pending GPU writer landed must not win over the import. I would build the follow-up around that check rather than preferring the capture as such. The Xid 31 is a separate thing (PTE write on the copy engine); if it repeats on AI-assisted: yes |
|
Windows run with Onimusha: Way of the Sword (PPSA27836, RE Engine), where current Setup: Windows 11 Pro 26200, WinLibs GCC 15.2.0 r7, Release, RTX 5070 (driver 617.42).
The PR merges cleanly into |
|
Checked the diff (only |
What
GuestBufferMemory::AddSnapshotcompares a captured word with the registered memory that backs it and throwsguest snapshot differs from registered memoryon a mismatch. For a region backed by a live host import or mirror the mismatch only means the capture raced a guest write: the GPU reads the live bytes either way, as the hardware would, so the dispatch is sound.A mismatch on a live-backed region is now counted and reported at most every 10 s with the snapshot, the region and the first differing offset, and the dispatch continues.
APS5_SNAPSHOT_CHECK_FATAL=1restores the throw. A snapshot-backed region still fails, and its message now names the snapshot, the region and the first differing offset too.Tested
0aa4cb1c2.agc_driver_graphicsandagc_driver_bda_devicepass. No new test: the race needs a guest write between the capture and the compare.mainthe throw ended the title 65 to 91 s after the title screen in three runs (a 4 byte snapshot of a live import). With the change the mismatches are rare (7 in a 15 minute run through menu and cutscene, each reported) and the runs reached the first cutscene with the expected image.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code