Skip to content

fix(agc): order deferred import settles with main-queue accesses - #8527

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
eliasbruno124-dev:fix/host-import-settle-order-20261020
Oct 23, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
eliasbruno124-dev:fix/host-import-settle-order-20261020

Conversation

@eliasbruno124-dev

@eliasbruno124-dev eliasbruno124-dev commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What

Order Windows deferred host-import settle reads with earlier and later main-queue accesses to the same memory. GTA III reports SYNC-HAZARD-WRITE-RACING-READ when a main-queue fill races the deferred four-byte copy on the separate settle queue.

Allocate batch commands from the main pool and submit on the main queue, with barriers around the copies. Submission remains asynchronous on the CPU. Reuse the existing import buffer, removing the alternate-family alias from this path. Retirement checks a bounded 60-second fence wait and propagates failure before releasing the batch's resources.

The ordering tests cover both settle-family configurations and reject reported SYNC-HAZARD- or VUID- messages when validation is enabled. Their byte assertions alone can pass on unfixed drivers. A separate regression injects timeout and device-loss results, checks error propagation and retained imports, then verifies exactly one release after a successful wait. The first-use check requires earlier main-queue reads to finish before rewatching. Both comparison arms include the scratch-copy barriers needed by the existing canaries.

Rebased linearly onto f40cdebce2, keeping both recorder selection flags from the conflict. Main queue/pool access is protected by GuestMemory::GpuMutex, including queue-0 commits, presentation and command allocation/free. Overlap refreshed on 2026-10-23: #9941 owns the separate synchronous initial-DCC settle change; #9670 adds a separate presentation queue. Neither is a dependency or a replacement for this correction.

Tested

  • Windows 11 Pro 10.0.26300, NVIDIA RTX 3060 Laptop GPU, driver 617.42, WinLibs GCC 15.2.0 r7, Release. Real Khronos layer with synchronization validation, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, sequential ctest -V.
  • Same 28 focused tests and final test sources: production GuestBufferMemory.cpp from main f40cdebce2 versus candidate a0ecec3d3. Baseline: four failing cases, two READ-RACING-WRITE and three WRITE-RACING-READ reports. Candidate: 28/28 pass, no skips or validation errors; longest case 2.94 seconds. The set covers deferred settles, both ordering configurations, retirement errors, import lifetime, flip stores and VideoOut presentation.
  • The prior unbounded implementation separately fails the new bounded-wait assertion. Timeout/device loss are injected return values after actual GPU work completes, not physical GPU hangs or a measured 60-second timeout. Project conventions and git diff --check pass.
  • Original GTA III, PPSA03527/01.003.000, was compared on the earlier full-runtime commits cabd80f47 and c64cfdee2: identical resources/settings, default deferred settles, empty private caches, no inputs or other unmerged PRs, all 61 observed runtime modules verified. Each observation was stopped after 300 seconds rather than reaching a normal guest exit.
Earlier GTA III validation reports Base Candidate
SYNC-HAZARD-WRITE-RACING-READ 3 0
VUID-VkImageViewCreateInfo-usage-02275 10 10

Those races concern a main-queue vkCmdFillBuffer and the settle vkCmdCopyBuffer, over 4096-, 65536- and 12288-byte buffers. The separate image-view errors remain. Renderer exports show the opening logo, not gameplay. This title evidence belongs to the named pre-rebase commits; the new review correction has native comparison coverage, not a fresh title run.

No FPS or loading-time gain is claimed. Main-queue batching latency, a real device-loss event, other GPU vendors, Linux and macOS were not measured locally. The earlier title observations had validation/profiling/readback enabled, and Git metadata work overlapped part of the candidate observation.

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 (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: NVIDIA RTX 3060 Laptop GPU, driver 617.42, Windows 11 Pro 10.0.26300
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 20, 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 8426 docs/dev/debt/libSceAgcDriver.md L119

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
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 1896 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 4448 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 4449 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
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 5058 docs/dev/debt/libSceAgcDriver.md
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5431 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/ARCHITECTURE.md
docs/dev/debt/libSceAgcDriver.md
PR 6044 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ARCHITECTURE.md
PR 6186 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6194 docs/dev/debt/libSceAgcDriver.md
PR 6197 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
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/ARCHITECTURE.md
docs/dev/debt/libSceAgcDriver.md
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6501 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6548 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6549 docs/dev/debt/libSceAgcDriver.md
PR 6612 docs/dev/debt/libSceAgcDriver.md
PR 6642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6788 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6820 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6918 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 6959 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6970 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6971 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6973 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6977 docs/dev/debt/libSceAgcDriver.md
PR 6987 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6989 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6995 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7002 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
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 7307 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 7449 docs/dev/debt/libSceAgcDriver.md
PR 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7465 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7618 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7622 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/ARCHITECTURE.md
docs/dev/debt/libSceAgcDriver.md
PR 7677 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7684 docs/dev/ARCHITECTURE.md
PR 7705 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7833 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7844 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7893 docs/dev/debt/libSceAgcDriver.md
PR 7910 docs/dev/debt/libSceAgcDriver.md
PR 8014 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8016 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 8223 docs/dev/debt/libSceAgcDriver.md
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8281 docs/dev/debt/libSceAgcDriver.md
PR 8319 docs/dev/debt/libSceAgcDriver.md
PR 8365 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ARCHITECTURE.md
docs/dev/debt/libSceAgcDriver.md
PR 8380 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 8542 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/ARCHITECTURE.md
docs/dev/debt/libSceAgcDriver.md
PR 8552 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8564 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8658 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 8740 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8999 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9017 docs/dev/debt/libSceAgcDriver.md
PR 9020 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9068 docs/dev/debt/libSceAgcDriver.md
PR 9077 docs/dev/debt/libSceAgcDriver.md
PR 9185 docs/dev/debt/libSceAgcDriver.md
PR 9186 docs/dev/debt/libSceAgcDriver.md
PR 9335 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 9356 docs/dev/ARCHITECTURE.md
docs/dev/debt/libSceAgcDriver.md
PR 9387 docs/dev/debt/libSceAgcDriver.md
PR 9393 docs/dev/debt/libSceAgcDriver.md
PR 9400 docs/dev/ARCHITECTURE.md
PR 9431 docs/dev/debt/libSceAgcDriver.md
PR 9432 docs/dev/debt/libSceAgcDriver.md
PR 9508 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/ARCHITECTURE.md
docs/dev/debt/libSceAgcDriver.md
PR 9521 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
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
docs/dev/debt/libSceAgcDriver.md
PR 9748 core/libs/prx/libSceAgcDriver/CMakeLists.txt
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/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.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 9965 docs/dev/ARCHITECTURE.md
PR 9999 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10000 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10049 docs/dev/debt/libSceAgcDriver.md
PR 10064 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 10193 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/ARCHITECTURE.md
docs/dev/debt/libSceAgcDriver.md
PR 10233 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@eliasbruno124-dev thanks, the barriers and the alias removal look right. Two questions before merge:

  1. Moving the settle batch to context.pool and context.queue makes it share two externally synchronized objects with the main recorder. submitSettleBatch holds settleQueueMutex, and settleUnwatchedImports only runs it while this thread holds GuestMemory::GpuMutex. Is every other vkQueueSubmit to context.queue, and every allocation, reset or free on context.pool, also made under GpuMutex? Please point to where that holds for the queue-0 committer and the present path, or add the lock the main queue uses.
  2. waitSettleBatch now waits with UINT64_MAX. A GPU hang during retirement would then block the thread holding GpuMutex forever, where before it gave up after 10 s. Is that intended, or would a long bounded wait followed by a fatal error be better?

@EagleBearerYS

Copy link
Copy Markdown
Contributor

@eliasbruno124-dev #8527 conflicts with main in one hunk of core/libs/prx/libSceAgcDriver/tests/Recorder.cpp: the other-family flag list in main(), where main added --host-import-lifetime-other-family-only next to your --settle-write-order-other-family-only.

In case it saves you a step, there's a merge of main 0def01b into your branch that you can take as it is: merge/8527-main (973af43) on our fork.

  • It keeps both flags, and docs/dev/ARCHITECTURE.md merged on its own. Your two commits are unchanged under it.
  • Linux: the settle and import tests pass (ctest -R 'settle|import', 33/33).
  • Windows: a branch with this merge underneath builds and passes on GitHub's Windows runner. Your settle tests need a GPU, so they skip there. They haven't run on a Windows GPU yet.

To take it: git fetch https://github.com/EagleBearerYS/AnyPS5 merge/8527-main && git merge --ff-only FETCH_HEAD.

For #9724's option 1, we built the synchronous settle on top of #8527: fix/agc-sync-settle-initial-dcc-main (580a37c), one commit on that merge.

EagleBearerYS added a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 22, 2026
…tisampled DCC import

Rush 2 Recompiled (PPSA69423) and Wave Race 64 Recompiled (PPSA69424)
stop on Windows with "initial multisampled DCC contents require watched
guest memory" (boykopovar#9724). Their
multisampled color target lives in a 784 KiB host import made at map
time. An import under 1 MiB stays compared until a later lookup reaps
its deferred settle read, and the titles draw to the target first.

When an initial multisampled DCC import needs a range whose import still
waits for its settle read, submit that read now if it is still wanted,
wait for its fence and return the import to the write watch before the
check. This builds on boykopovar#8527's ordering: the read goes through its
main-queue batch with barriers around it, so it runs after every earlier
main-queue access, and the wait happens under the GPU mutex that
submission holds.

An import that is not a whole number of 64 KiB blocks, such as these
titles' 0xc4000 bytes, never got a deferred read, and Rewatch refuses a
range that does not start and end on a block. Such an import now waits
for this path alone. It returns to the write watch with its partial
blocks whole, each stamped as a CPU store, unless another compared
import or a live storage image shares them.

Imports that stay compared for any other reason still do, an explicitly
unwatched range is still refused, and APS5_NO_DEFERRED_SETTLES still
keeps these imports compared.
EagleBearerYS added a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 22, 2026
…tisampled DCC import

Rush 2 Recompiled (PPSA69423) and Wave Race 64 Recompiled (PPSA69424)
stop on Windows with "initial multisampled DCC contents require watched
guest memory" (boykopovar#9724). Their
multisampled color target lives in a 784 KiB host import made at map
time. An import under 1 MiB stays compared until a later lookup reaps
its deferred settle read, and the titles draw to the target first.

When an initial multisampled DCC import needs a range whose import still
waits for its settle read, submit that read now if it is still wanted,
wait for its fence and return the import to the write watch before the
check. This builds on boykopovar#8527's ordering: the read goes through its
main-queue batch with barriers around it, so it runs after every earlier
main-queue access, and the wait happens under the GPU mutex that
submission holds.

An import that is not a whole number of 64 KiB blocks, such as these
titles' 0xc4000 bytes, never got a deferred read, and Rewatch refuses a
range that does not start and end on a block. Such an import now waits
for this path alone. It returns to the write watch with its partial
blocks whole, each stamped as a CPU store, unless another compared
import or a live storage image shares them.

Imports that stay compared for any other reason still do, an explicitly
unwatched range is still refused, and APS5_NO_DEFERRED_SETTLES still
keeps these imports compared.
@EagleBearerYS

Copy link
Copy Markdown
Contributor

@eliasbruno124-dev the synchronous settle for #9724 is up as a draft, #9941. It's stacked on this PR:

  • Your two commits are in it unchanged, under your name.
  • On top: the main merge from merge/8527-main, then the fix, a later main merge and the fix's FMASK checks. The fix submits a pending settle through your submitSettleBatch and waits for its fence before the initial multisampled DCC check.

If you'd rather carry the settle in #8527 yourself, say so, and we'll close #9941 and move the fix over to you.

@BKlaasWerkman

Copy link
Copy Markdown
Contributor

The race reproduces in an existing test on current main, on NVIDIA and Intel, and the production change here removes it.

Windows 11 Pro 10.0.26300, WinLibs GCC 15.2.0 r7, Release. NVIDIA GeForce RTX 3060 Ti (driver 617.42) and Intel Arc Graphics (Meteor Lake, 0x7d55, driver 101.9150), selected with ANYPS5_GPU. Khronos layer 1.4.363 with VK_KHRONOS_VALIDATION_VALIDATE_SYNC=true, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1.

On main 83462cc, agc_driver_flip_stores_small_results reports one hazard per run on both GPUs:

SYNC-HAZARD-WRITE-RACING-READ
vkQueueSubmit(): pSubmits[0].pCommandBuffers[0] WRITE_RACING_READ hazard detected. vkCmdCopyBuffer (from VkCommandBuffer ... submitted on the current VkQueue ...) writes to VkBuffer ..., which was previously read by another vkCmdCopyBuffer command (from VkCommandBuffer ... submitted on VkQueue ...).
Buffer copy region 0: { offset = 0, size = 65536 }

The branch conflicts with main only in tests/Recorder.cpp. I applied Graphics/src/GuestBufferMemory.cpp from 60ab136 onto main 83462cc (it applies cleanly) and ran ctest -R 'flip|present|video_out|pacing' --repeat until-fail:3 -V, 34 tests:

main main + GuestBufferMemory.cpp of this PR
RTX 3060 Ti 34/34, SYNC-HAZARD-WRITE-RACING-READ ×3 34/34, no hazard
Intel Arc 34/34, SYNC-HAZARD-WRITE-RACING-READ ×3 34/34, no hazard

No VUID- on either side. Intel Arc's graphics family has a single queue, so the settle queue there comes from another family. The PR's own regression test was not run, because of the Recorder.cpp conflict.

AI-assisted: yes (Claude Code)

@EagleBearerYS

Copy link
Copy Markdown
Contributor

@BKlaasWerkman thank you, that's a useful independent check: the hazard on main and its absence with this PR's GuestBufferMemory.cpp, on both NVIDIA and Intel, with the validation layer's sync checks on.

For the PR's own tests: merge/8527-main (973af43) on our fork is this branch with main merged and the Recorder.cpp conflict resolved, keeping both flags. It still merges cleanly with current main. If you have time, agc_import_settle_write_order and agc_import_settle_write_order_other_family on it would cover what your run couldn't. The second one matters most on your Intel Arc, where the settle queue comes from another family.

@BKlaasWerkman

Copy link
Copy Markdown
Contributor

@EagleBearerYS ran them. Setup: merge/8527-main (973af43) merged into main 812ed15 (clean merge). Same machine and settings as above: RTX 3060 Ti (617.42) and Intel Arc (101.9150), Windows 11 Pro 10.0.26300, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, Khronos layer 1.4.363 with VK_KHRONOS_VALIDATION_VALIDATE_SYNC=true. The layer loaded in every run, and no run has a VUID-.

  • A: that tree.
  • B: the same tree with Graphics/src/GuestBufferMemory.cpp from main, so the PR's tests run against the old settle path.

agc_import_settle_write_order and agc_import_settle_write_order_other_family, --repeat until-fail:5:

A B
RTX 3060 Ti 5/5 each, no hazard 5/5 each; SYNC-HAZARD-READ-RACING-WRITE and SYNC-HAZARD-WRITE-RACING-READ once per run in each test (×10 each)
Intel Arc 5/5 each, no hazard 5/5 each; the same hazards, ×10 each
  • Both tests print settle read between main-queue GPU writes passed in every run. _other_family ran on both GPUs; it did not skip.
  • On these two devices the write-order tests pass without the fix, and only the synchronization layer shows the race. If the PR is meant to catch a regression without the layer, the tests don't do that here.

ctest -R 'settle|import', 48 tests, once:

A B
RTX 3060 Ti 48/48, no hazard 47/48; agc_import_settle_first_use fails with (f) the import returned to the write watch while its earlier main-queue reads were still in flight
Intel Arc 48/48, no hazard 48/48

In B, on both GPUs, the hazards are the write-order pairs above and one SYNC-HAZARD-WRITE-RACING-READ in agc_driver_host_import_at_map. All of them are gone in A.

AI-assisted: yes (Claude Code)

@EagleBearerYS

Copy link
Copy Markdown
Contributor

@BKlaasWerkman thank you for running both tests, with and without the fix, on both GPUs, and the wider settle and import set too. It's useful for #9941 as well, which carries this PR's commits.

@BKlaasWerkman

Copy link
Copy Markdown
Contributor

The full suite under synchronization validation, main against main with this PR, on the same two GPUs (RTX 3060 Ti 617.42, Intel Arc 101.9150, Windows 11 Pro 10.0.26300). ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation (1.4.363) with VK_KHRONOS_VALIDATION_VALIDATE_SYNC=true; the layer loaded in every run.

main 812ed15, full ctest, 1999 tests. Each GPU reports 128 synchronization errors, all of three kinds:

RTX 3060 Ti Intel Arc
WRITE_RACING_READ: a main-queue vkCmdCopyBuffer writes a buffer an earlier vkCmdCopyBuffer on the other queue read 104 105
WRITE_RACING_READ: a main-queue vkCmdFillBuffer writes a buffer an earlier vkCmdCopyBuffer on the other queue read 6 5
WRITE_AFTER_WRITE between two vkCmdCopyBuffer, in agc_import_settle_first_use and agc_import_settle_earlier_import 18 18
tests with a hazard 56 55
  • The 58 tests in the union are mostly ones that only touch the settle path incidentally: for example agc_driver_dithered_alpha_to_coverage, agc_driver_msaa_dcc_clear_2/4/8 (10 each), the agc_driver_image_store_* set, agc_driver_layered_volume_*, agc_driver_guest_memory_lifetime_copy, agc_storage_split_writeback and agc_driver_flip_stores_small_results.
  • All of these tests pass on main; only the layer reports the races.

This PR (merge/8527-main 973af43 merged into 812ed15, the same tree as above), those 58 tests: 58/58 on both GPUs, and no synchronization error on either. The only validation messages left are VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (×2 on the RTX 3060 Ti, ×3 on Intel Arc), which is #6782's.

No other hazard signature appears anywhere in the full runs on either GPU, so on these two devices this PR covers every synchronization error the suite produces.

AI-assisted: yes (Claude Code)

@eliasbruno124-dev
eliasbruno124-dev force-pushed the fix/host-import-settle-order-20261020 branch from 60ab136 to a0ecec3 Compare October 23, 2026 11:25
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 23, 2026
@eliasbruno124-dev

Copy link
Copy Markdown
Contributor Author

@oneandonlydean addressed in a0ecec3d3, rebased linearly onto f40cdebce2 with both conflicting test-selection flags retained.

  1. The shared lock is GuestMemory::GpuMutex. Queue-0 commitDraw holds it through SubmitDue; submitOpenWork takes it before SubmitRecorded. Presentation holds it across recording/submission and normal QueuePresent; the late present reacquires it at line 143. Recorder allocation and release run under these paths; the deferred destruction thread does not own command buffers. Device construction/presentation-slot allocation happens under the lock before publication, and shutdown joins workers before resetting the device under it. The new resident-transfer path also asserts this lock. settleQueueMutex is additional settle-state serialization, not the shared queue/pool lock.
  2. waitSettleBatch now checks a 60-second wait. Timeout or device loss throws before draining the batch. The regression injects both results, verifies that the import buffer/memory remain retained, and checks one release after a successful wait. It completes actual GPU work before injecting failures; it does not deliberately hang the GPU.

Local Windows/RTX 3060 Laptop 617.42 comparison, real Khronos synchronization validation and caches disabled: the same 28 tests on main's production file and this head. Main has four failed cases and five synchronization reports; this head passes 28/28 with no skips or validation errors. The old unbounded wait also fails the new bounded-wait assertion. Conventions and whitespace checks pass.

@BKlaasWerkman your report caught a real limitation in the test result: byte assertions can pass despite the race. The new ordering tests now fail CTest on synchronization/validation error output when the layer is enabled. They do not promise to detect every race without validation. Thanks for the independent NVIDIA/Intel comparisons.

@EagleBearerYS please keep the initial-DCC correction in #9941; it is a separate change. This branch contains only deferred ordering and its review correction, so #9941 can follow this updated head.

The earlier GTA III evidence remains tied to its original commits. These review tests establish correctness and failure handling, not an FPS improvement.

Prepared with AI assistance.

@adriadam10 adriadam10 self-assigned this Oct 23, 2026
@oneandonlydean oneandonlydean self-assigned this Oct 23, 2026
@adriadam10
adriadam10 merged commit ea69535 into boykopovar:main Oct 23, 2026
12 checks passed
adriadam10 pushed a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 23, 2026
…tisampled DCC import

Rush 2 Recompiled (PPSA69423) and Wave Race 64 Recompiled (PPSA69424)
stop on Windows with "initial multisampled DCC contents require watched
guest memory" (boykopovar#9724). Their
multisampled color target lives in a 784 KiB host import made at map
time. An import under 1 MiB stays compared until a later lookup reaps
its deferred settle read, and the titles draw to the target first.

When an initial multisampled DCC import needs a range whose import still
waits for its settle read, submit that read now if it is still wanted,
wait for its fence and return the import to the write watch before the
check. This builds on boykopovar#8527's ordering: the read goes through its
main-queue batch with barriers around it, so it runs after every earlier
main-queue access, and the wait happens under the GPU mutex that
submission holds.

An import that is not a whole number of 64 KiB blocks, such as these
titles' 0xc4000 bytes, never got a deferred read, and Rewatch refuses a
range that does not start and end on a block. Such an import now waits
for this path alone. It returns to the write watch with its partial
blocks whole, each stamped as a CPU store, unless another compared
import or a live storage image shares them.

Imports that stay compared for any other reason still do, an explicitly
unwatched range is still refused, and APS5_NO_DEFERRED_SETTLES still
keeps these imports compared.
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the diff against the two points in the earlier review: the settle batch's queue/pool uses (commitDraw, submitOpenWork, presentation) all run under GpuMutex, and the fence wait is now bounded at 60 s with the failure propagated before any release. Barriers around the copies and the alias removal look right, the new ordering/wait-failure tests fail on the old path (independently confirmed on NVIDIA and Intel by @BKlaasWerkman); merged.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants