Skip to content

fix(agc): don't pass a label wait on memory its own queue already reset - #13883

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
hugodearaujo:fix/agc-own-unlanded-label
Nov 1, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
hugodearaujo:fix/agc-own-unlanded-label

Conversation

@hugodearaujo

@hugodearaujo hugodearaujo commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

What

A WAIT_REG_MEM passes as soon as guest memory satisfies its compare. When a queue resets a label right after its wait on it passed and then waits on the same label again, the reset may not have reached memory yet: it is recorded on the GPU behind other work (or still queued on the worker), and memory still shows the other queue's old value. The second wait then passes on that stale value, without any new store from the other queue.

The Elder Scrolls V: Skyrim (PPSA03747) runs into this during gameplay. Queue 0 and compute queue 0x28 hand off through two labels. Queue 0 stores 1 to 0x112a800020 twice per round, and 0x28 waits for 1 and stores 0 after each wait. After both of those passes, 0x28 stores 1 to 0x112a800b80, which queue 0 waits on and resets before its next round. A label trace (local logging, not part of this PR) shows queue 0x28 passing its wait on 0x112a800020 on stale memory: once in each of the two traced runs. Excerpt from the first run. store lines are noteLabelStore calls (the store's serial, stamp). wait lines are waitMemory returns: exit 0 is the poll loop's memory check, exit 1 the memory check at entry, and memory is the value read on return.

[lbl] 112823833087 us store q 0x0 0x112a800020 stamp 991575 value 0x1
[lbl] 112823833092 us wait q 0x28 0x112a800020 ref 0x1 received 990912 held 0 land 0 -> exit 0 after 34347 us, memory 0x1
[lbl] 112823833166 us store q 0x28 0x112a800020 stamp 991579 value 0x0
[lbl] 112823838622 us store q 0x28 0x112a800b80 stamp 991596 value 0x1
[lbl] 112823838668 us wait q 0x28 0x112a800020 ref 0x1 received 991438 held 0 land 0 -> exit 1 after 0 us, memory 0x1
[lbl] 112823838683 us store q 0x28 0x112a800020 stamp 991598 value 0x0

The second wait passed on the 1 from stamp 991575, which the first wait had already consumed. Queue 0x28's reset at stamp 991579 was recorded on the GPU 5.5 ms earlier and was still not in memory. Queue 0 made no store in between. After that extra pass, 0x28 counts its two passes per round from a different store of queue 0, so it sets 0x112a800b80 after queue 0's first store of a round instead of after the second. Queue 0's wait on 0x112a800b80 then no longer means that 0x28 consumed and reset queue 0's second store. Queue 0 can start its next round while that store is still 1, and its next store of 1 merges with it: one reset covers both. Then 0x28 waits on 0x112a800020 for a store queue 0 already made, and queue 0 waits on 0x112a800b80 for a store 0x28 makes only after that wait. That is the deadlock below.

On main the window is mostly hidden: Driver::storeBufferFill calls VulkanDevice::WaitIdle() for every buffer fill it stores on the CPU, and queue 0x28 makes such fills between its waits, so its reset usually lands in time. With that wait limited to the fill's range (a local experiment, not part of this PR), the title freezes every time: presents stop at 191 s and 322 s, with

[gpu] queue 0x0 WAIT_REG_MEM at 0x112a800b80 timed out after 1000ms (function 3 ref 0x1 mask 0xffffffff value 0x0)
[gpu] queue 0x28 WAIT_REG_MEM at 0x112a800020 timed out after 1000ms (function 3 ref 0x1 mask 0xffffffff value 0x0)
[gpu] queue 0x20 WAIT_REG_MEM at 0x112a800a40 timed out after 1000ms (function 3 ref 0x1 mask 0xffffffff value 0x0)

and queues 0x20 and 0x28 stay in those waits. Queue 0x20's label is a third pair of the same kind with queue 0, stuck because queue 0 is.

0x112a800020 is the label from stop 4 of discussion #11508. #12923 fixed another way to pass on it: storedSince releasing a later wait on a store the queue had already taken and reset. That path rejects the case above too (the reset took the store). Here the wait passes on guest memory, before the label history is consulted.

How

  • Recorder::UnlandedOwnLabel(address, bytes, queue) reports, for each dword of a 4- or 8-byte label, whether queue owns its newest tracked store: either queued on the queue's worker and not recorded yet, or recorded in a batch the recorder has not reaped yet. The queued and the recorded table are compared by stamp, so the newest store of either kind counts. A store with a later recorded GPU write over it (overwritten) doesn't count. The result is the value those own stores leave and a mask of their dwords. Another queue's store that is newer than the queue's own takes that dword out of the mask. The lookup takes only the label table mutex, like LookupLabel. An entry leaves the table when its batch is reaped, after the GPU stored the value. From then on, memory decides again.
  • OwnStoreRejects builds the value the wait would read once those own stores land: own dwords from the table, the other dwords from memory. It checks that value with the wait's own compare. In waitMemory, memory satisfying the compare counts only if that value satisfies it too. This applies at entry, in the poll loop and on the pipelined queue-0 path in execute. The wait's mask and function decide as before. A reset of a dword, or of bits, the mask ignores holds nothing, and a later store of another queue still releases the wait. While an own store holds the wait, pollService reaps when it sleeps, the same as for a label behind a completion, so the entry leaves once the store landed.
  • The other release paths (storedSince, the pending-label table, held at submit) and landing waits (requireMemory) are unchanged.

Tested

Linux (Arch, RADV, RX 9070 XT), Release.

  • Driver cases in agc_driver (tests/Submit.cpp, testLabelResetByItsQueueBeforeItLanded, testWideLabelPartlyResetByItsQueueBeforeItLanded). Queue 0x20 waits for queue 0's label, records a 4 MiB buffer fill on the GPU, resets the label and waits on it again. This runs three ways: a 4-byte wait; an 8-byte wait where the queue resets only the low dword; an 8-byte wait where it resets only the high dword. The reset goes to the GPU (checked with gpuLabels) and has not landed when the second wait runs, so that wait must stay blocked until queue 0 stores the label again. On the first revision of this PR (14bcc1a206), both 8-byte variants fail: an 8-byte wait passed on a label whose low dword its own queue had already reset, before the reset reached memory, or the wait expired, and the same for the high dword. The 4-byte variant passes there. Earlier, with OwnStoreRejects returning false, it failed in 2 of 2 runs. On this head all three pass, in 3 of 3 runs.

  • Recorder::UnlandedOwnLabel cases in agc_driver_recorder and agc_recorder_completion_labels (tests/Recorder.cpp, unlandedOwnLabelTests): a queue's recorded store is its own and nobody else's; another queue's newer store replaces it; 8-byte composition; another queue's newer high dword leaves only the low dword own; an overwritten store doesn't count; a queued own store counts until forgotten; nothing is left after the batch is reaped.

  • Wait-level cases in the same tests (unlandedOwnStoreWaitTests). They call Pm4::WaitSatisfiedUnchecked and OwnStoreRejects with real WAIT_REG_MEM and WAIT_REG_MEM_64 packets on real memory, so they check whether the wait passes:

    • an own reset holds a 4-byte wait; an own store of the awaited value doesn't;
    • an own recorded reset with a newer queued store of another queue, and an own queued reset with a newer recorded store of another queue: the wait passes;
    • low-only and high-only resets of an 8-byte wait hold it;
    • mixed ownership holds it: an own low reset with another queue's high store, and an own 8-byte reset whose high dword another queue stored later;
    • masks: a mask that ignores the reset dword (an 8-byte mask on the high dword, a WAIT_REG_MEM_64 with a zero high mask) and a 4-byte mask that ignores the changed bits let the wait pass; an own store that clears a compared bit holds it;
    • another queue's later store over the reset dword (low and high) releases the wait;
    • nothing holds after the batch is reaped.

    On 14bcc1a206, 6 of these 16 cases fail: both cross-table orderings, low-only, high-only and both mixed-ownership cases. The others pass on both.

  • ^agc_.*(label|wait|queue|submit|pipeline|eop|gated|idle) (113 tests) with Khronos validation 1.4.363 (VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, ANYPS5_NO_SHADER_CACHE=1, ANYPS5_REQUIRE_VULKAN=1, VK_LOADER_DEBUG=layer shows Insert instance layer "VK_LAYER_KHRONOS_validation" and Inserted device layer "VK_LAYER_KHRONOS_validation"). Results are the same per test on main 828ac0b and on this head (111 passed, 2 failed), with no VUID- lines. agc_idle_publish_wait fails on both with the idle publish's bytes are wrong on this GPU. agc_small_tiled_shadow_label and both agc_driver_copied_writer_label_order tests skip on both. agc_driver_pipeline_profile is not built on both.

  • Skyrim (PPSA03747), 480 s runs from the same save, Cross every 30 s until 420 s, same machine. fix(shader): decide each resource branch condition once per plan #11506 is included on every build because Skyrim needs it to reach gameplay. The range-limited fill wait above is only a way to make the race show up. It is not part of this PR.

Build Runs Froze Presents stop at
main 18f5949 + #11506 2 0 (462 s, run end)
+ range-limited fill wait 3 3 322 s, 191 s, 191 s
+ range-limited fill wait + this PR (first revision) 4 0 (462 s, run end)
+ range-limited fill wait + this PR (this head) 2 0 (466 s and 461 s, run end)
main 18f5949 + #11506 + this PR (first revision) 2 0 (462 s, run end)

Each frozen run ends with the three timeouts quoted in What. The runs that didn't freeze have no WAIT_REG_MEM timeout and are in Helgen gameplay at the end. The rows marked first revision carry an earlier revision (04beb65af0), cherry-picked cleanly onto those 18f5949-based builds. It differs from 14bcc1a206 only by code comments and a debug switch that was off in the runs. The this-head row is 14bcc1a206 and e11df016ff, cherry-picked cleanly onto the same repro build. Two of the frozen runs and both main runs also had APS5_PROFILE_DRAW=1 and a trace window. The third frozen run and all the runs with this PR had neither.

Checklist

A WAIT_REG_MEM passes as soon as guest memory satisfies its compare. A queue
that resets a label right after its wait on it passed, and waits on it again
before that reset reached memory (the store is recorded on the GPU behind
other work, or still queued), found the old value in memory and passed the
second wait without a new store from the other queue. Skyrim (PPSA03747)
does this on queue 0x28: two passes on 0x112a800020 per signal of queue 0
let the two queues drift a step apart, and later signals overlap and get
lost until both wait forever.

Recorder::UnlandedOwnLabel reports the value a queue's own newest stores of
a label leave once they land, when every awaited dword's newest tracked
store is that queue's, still queued or in a batch not reaped yet, and no
later recorded GPU write covers it. A wait whose memory satisfies the
compare while such a store rejects it keeps waiting (at entry, in the poll
loop and on the pipelined queue-0 path), and reaps while it sleeps so the
store's entry leaves once it landed. Landing waits are unchanged.

@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.

Reviewed — changes requested

UnlandedOwnLabel can report an older recorded store as the newest own-queue store when a newer queued entry for the same dword belongs to another queue.

What I ran

Reviewed head 14bcc1a2064b6e6c61960c821c507929989509c3 against base 8f55414024c614b32548254e59e80bd3f0085231; the merge preview was clean. Environment: Debian Linux x86_64, Python 3.11.2. No local build or test was run.

python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 13883
gh pr checks 13883 --repo boykopovar/AnyPS5
git show 14bcc1a2064b6e6c61960c821c507929989509c3:core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
git show 14bcc1a2064b6e6c61960c821c507929989509c3:core/libs/prx/libSceAgcDriver/Execution/src/Driver/Synchronization/WaitMemory.cpp

Results

Check Result
Diff and conventions Passed.
Dependency/current-base preview Passed.
Source review Found a stale-entry selection case in the new queued/recorded label lookup.
Hosted CI Still queued when checked.
Local build/tests Not run under the no-build initial-review rule.
Relevant unedited output
All conventions checks passed.

The precheck also reported that its ABI-audit and test-quality helper scripts were absent from the isolated PR checkout; those helper-launch errors are not source findings.

Findings

Blocking

unlandedOwnLabel first selects queuedLabels[dword] only when that entry's queue equals the caller. If a queued entry exists for another queue, it falls through to labels[dword]. With an older recorded entry belonging to the waiting queue, the function returns that old own-queue value even though a queued store from another queue is also tracked. The reverse ordering can also be misclassified: an older queued own-queue entry wins over a newer recorded entry from another queue. The default configuration keeps queued labels separate from recorded labels, so both maps can contain the same dword.

OwnStoreRejects then uses this stale own value to hold a memory-satisfied wait. Compare the recorded and queued entries by their stamps for each dword, then report an own store only if the newest tracked entry belongs to the waiting queue and is not overwritten. Add tests for both cross-map orderings. The current tests exercise an own queued entry and an other-queue recorded entry separately, but do not put entries from both maps at the same dword.

Non-blocking

None.

Sources

Not verified

  • The hosted checks were queued; I did not wait for them to finish.
  • I did not build or run the AGC tests, execute the reported Skyrim traces, or independently reproduce the reported GPU results.
  • No negative-control test was run.

To get this to a good state

Select the newest tracked label entry across both maps and add regression cases for a newer queued other-queue entry and a newer recorded other-queue entry. I will re-check the same source paths and tests on the updated head.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@hugodearaujo Reviewed at 14bcc1a206: the new guard still allows the stale-memory race for a partial reset of a wide label. In Recorder.cpp:1331–1343, unlandedOwnLabel returns no value unless every awaited dword belongs to the waiting queue. For an 8-byte equality wait (reference 1, mask 0xffffffffffffffff), memory can still contain 1 while this queue has a pending 4-byte reset of the low dword to 0. If the high dword is unchanged and untracked, or its latest zero came from another queue, the lookup returns nullopt. OwnStoreRejects is then false and the memory fast path passes, although this queue's preceding reset will make the compared value 0.

An extracted-method CPU probe reproduces both cases; adding an own-queue high-dword entry makes the guard reject as expected. This is a remaining instance of the original race, rather than a new regression. Please account for relevant partial own stores when deciding whether memory may satisfy a wait, while preserving mask/compare semantics and allowing a genuinely later producer store to release it. Add regression cases for low-only and high-only resets of a wide wait, mixed ownership, and masks that ignore the reset bits. The existing mixed-ownership lookup assertion only checks nullopt; it does not check whether the wait incorrectly passes.

A second point: one case can turn into a hang.

  1. overwritten is only set by recorded GPU writes (NotePendingWrite). If the guest CPU writes the releasing value after the queue's own reset was recorded but before that reset lands (for example while it sits behind earlier GPU work), memory satisfies the wait, UnlandedOwnLabel still reports the own reset, the wait is refused, the reap then lands the reset over the CPU's value, and the wait never passes (the timeout only reports). On main this wait passes, which matches the hardware order, since the reset came first. The late rule already handles this kind of check with the entry's generation and GuestMemory::UnchangedSince (as in lookupLabel). Could unlandedOwnLabel skip a dword whose memory changed after its entry's group closed, and could a test cover a CPU write in that window?
  2. Optional: every wait whose memory already satisfies now takes labelTableMutex, and a refused wait takes it on each spin of the loop condition. Checking the recordedLabels count (and whether any queued labels exist) before locking would keep the common empty-table case lock-free, as the write-noting path does.

@github-actions

github-actions Bot commented Oct 31, 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 7963 core/libs/prx/libSceAgcDriver/tests/Submit.cpp L697, L1182-1183

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 1368 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 1395 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 5021 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Synchronization/WaitMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 5609 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6182 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6187 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6188 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6192 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6209 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6501 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 6905 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6970 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7048 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7457 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7464 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 7604 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7978 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/tests/Submit.cpp
PR 8484 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 8707 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 8874 core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 9176 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 9287 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 9289 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 9335 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
PR 9356 core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 9387 core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 9445 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 9712 core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 9884 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 9898 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10026 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10193 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10249 core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 10382 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 10442 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10773 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 10920 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 11444 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 13251 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 13793 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 13809 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 13863 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Synchronization/WaitMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
PR 13920 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Synchronization/WaitMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 13928 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 13929 core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 13942 core/libs/prx/libSceAgcDriver/tests/Recorder.cpp

@github-actions github-actions Bot added the agc libSceAgc* label Oct 31, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 14bcc1a merged onto main 828ac0b · tested 2026-10-31 11:39 UTC · 24 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,319 passed · 6 failed (0 new, 2 also fail on main, 4 unstable on main) · 41 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,366 of 3,048 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (866 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_bda_device 58.8 s, ⚠️ agc_driver_graphics 41.4 s, ⚠️ agc_driver_depth_clear_hle 36.0 s, ⚠️ agc_driver_graphics_maps_fallback 30.7 s
Intermittent validation messages (1, seen once in the parallel run, not reproduced when the test was re-run alone 2 times; not counted against this PR)
agc_driver_depth_clear_hle: VUID-vkCmdCopyBuffer-commonparent (6x)
Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_depth_clear_hle: failed in 1 of the last 10 main runs
agc_driver_submit_progress: failed in 6 of the last 10 main runs
agc_submit_progress: failed in 6 of the last 10 main runs
agc_submit_progress_no_timeline: failed in 8 of the last 10 main runs
Failing on main too (2, not counted against this PR)
guest_mmap_executable
guest_socket_options_multicast
Validation IDs already present on main (3 IDs in 384 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (384 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 376 more
VUID-vkCmdCopyImage-srcImage-01548 (12 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_sampling, and 4 more
VUID-vkCmdCopyImage-srcImage-01551 (12 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_sampling, and 4 more
Skipped (41)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_msaa_dcc_compared_import_2: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_4: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_8: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_stencil_ref_export: skipped, the device lacks VK_EXT_shader_stencil_export
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_main: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_fresh: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_labels: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is eithe
Reproduce
git fetch origin 14bcc1a2064b6e6c61960c821c507929989509c3 828ac0b2ae74aac181ba128fb59b08a1534f2041
git checkout 828ac0b2ae74aac181ba128fb59b08a1534f2041 && git merge --no-ff 14bcc1a2064b6e6c61960c821c507929989509c3
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

UnlandedOwnLabel took a queued entry only when it was the caller's own and
fell back to the recorded one otherwise, so an older recorded store of the
waiting queue counted as its newest even when another queue had a newer
queued store of the dword, and an older own queued store won over a newer
recorded store of another queue. It also answered nothing unless every
awaited dword was the queue's own, so an 8-byte wait passed on memory that
the queue's pending reset of one dword was about to change.

The lookup now takes the newest tracked store of each dword, queued or
recorded, by stamp, and reports the value and the dwords whose newest store
is the queue's own and not overwritten. OwnStoreRejects composes what the
wait would read once those stores land (own dwords from the table, the rest
from memory) and keeps the wait only when that value fails the compare, so
the wait's mask and function decide as before and another queue's later
store of a dword still releases it.
@JJenkx

JJenkx commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

Windows GPU run of head 14bcc1a206, merged onto main 8f55414024 (clean merge, local only).

Windows 11 Enterprise 10.0.22621, Ryzen 9 5950X, Radeon RX 6700 XT with AMD proprietary driver 26.10.1 (Vulkan 1.4.315), WinLibs GCC 15.2.0 UCRT r7, Ninja, Release, BUILD_TESTING=ON. Khronos validation layer 1.4.363 requested for every test (VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation).

  • All 109 CTest entries of agc_driver_recorder_tests, agc_driver_tests, which builds the test files this PR changes: pass, 3 of 3 runs each (--repeat until-fail:3), no failures, no skips.
  • They create Vulkan instances on the RX 6700 XT: the validation layer was inserted 282 times across the runs and reported no VUIDs.

AI-assisted: yes (Claude Code).

@hugodearaujo

Copy link
Copy Markdown
Contributor Author

@MarcZX10 Thanks, fixed in e11df016ff. unlandedOwnLabel now looks at both tables for each dword and takes the newest entry by stamp (queued and recorded stamps come from the same eventSerial). A dword counts as the waiting queue's own only when that newest entry is the queue's own and not overwritten. Both orderings are now tests: an own recorded reset under a newer queued store of another queue, and an own queued reset under a newer recorded store of another queue. In both, the wait passes on memory, and the lookup reports no own dword. On 14bcc1a206 both cases fail (the wait was held).

@oneandonlydean Thanks for the CPU probe. The partial reset is fixed in e11df016ff. UnlandedOwnLabel now returns a value and a dword mask: the dwords whose newest tracked store is the queue's own (not overwritten), with those stores' values. OwnStoreRejects builds the value the wait would read once those stores land: own dwords from the table, the other dwords from memory. It holds the wait only when memory satisfies the compare and that value doesn't. The wait's own mask and function do the compare, so a reset of a dword or bits the mask ignores doesn't hold it. A later store of another queue to a dword takes that dword out of the mask, so it still releases the wait.

New cases (wait-level ones call Pm4::WaitSatisfiedUnchecked and OwnStoreRejects on real memory with real WAIT_REG_MEM/WAIT_REG_MEM_64 packets, so they check whether the wait passes, not only the lookup):

  • agc_driver (tests/Submit.cpp): the existing end-to-end case now also runs with an 8-byte wait where queue 0x20 resets only the low dword, and then only the high dword, recorded on the GPU behind a 4 MiB fill. On 14bcc1a206 both fail with an 8-byte wait passed on a label whose low/high dword its own queue had already reset .... On e11df016ff they pass.

  • agc_driver_recorder / agc_recorder_completion_labels (tests/Recorder.cpp, unlandedOwnStoreWaitTests):

    • low-only reset of a wide wait and high-only reset of a wide wait (both held);
    • mixed ownership: own low reset with another queue's newer high store (held); own 8-byte reset whose high dword another queue stored later (still held on the low dword);
    • masks: a wide wait whose mask ignores the reset dword, a WAIT_REG_MEM_64 with a zero high mask after a high reset, and a 4-byte mask that ignores the bits the own store changes (all pass); an own store that clears a compared bit (held);
    • a later producer store over the reset dword, low and high (released); an own store that leaves the awaited value (passes); everything passes again after the batch is reaped.

    On 14bcc1a206, 6 of these 16 cases fail: the two cross-table orderings above, low-only, high-only and both mixed-ownership cases. The others are guards and pass on both.

^agc_.*(label|wait|queue|submit|pipeline|eop|gated|idle) under VK_LAYER_KHRONOS_validation gives the same per-test results on main 828ac0b2ae and on e11df016ff, with no VUID- lines. Skyrim with the range-limited fill wait (the repro build that froze 3 of 3 runs) plus e11df016ff: 2 runs of 480 s, no freeze, no WAIT_REG_MEM timeout, presents until the end of each run, in Helgen gameplay. Details are in the updated description.

On the CPU store in the window: you're right that the PR doesn't handle it. One correction about main, though. There, the wait passes, then the queue's own reset lands over the CPU's value, so memory ends with the reset value where hardware ends with the CPU's. Main loses that store too. With this PR the wait waits for the next store instead of passing once on a value that is then lost. I haven't changed this here, because the check you suggest isn't reliable for these labels yet:

  • generation is 0 for a group that closes after its batch was submitted, and queued entries have none;
  • UnchangedSince also sees the driver's own MarkWritten stamps on the shared label block;
  • UnchangedSinceCollected walks the pages again on every call, and a refused wait checks on every spin. Its CPU stamps are per page, and the label pages hold other labels too.
  • If it reports a false "changed", the guard stops working and the original race comes back. If it reports a false "unchanged", it can hang.

I'd rather make that a follow-up with its own test: check only when the poll sleeps, and only for a dword whose entry has a closed generation. Tell me if you want it in this PR instead.

On the lock: unchanged. The lookup runs only when memory already satisfies the compare, and it takes only the label table mutex, as LookupLabel does. A refused wait lasts only while one of its own stores is in flight. A lock-free precheck would also need a count for the queued table, which doesn't have one yet. I can add one if you think it's worth it.

AI-assisted: yes (Claude Code)

@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.

Re-checked — new head, looks good

The updated lookup compares queued and recorded stores by stamp per dword, resolving the cross-table ordering defect, and the added tests exercise both orderings and partial 64-bit waits.

What I ran

Reviewed head e11df016ff92148801f56aca1de34da6e6d0c9cf against PR base 8f55414024c614b32548254e59e80bd3f0085231 on Linux x86_64 with Python 3.11.2 and GCC 12.2.0. The precheck's merge preview was clean and the head/base stayed unchanged.

python3 tools/pr_precheck.py --repo boykopovar/AnyPS5 --pr 13883
git diff --check 8f55414024c614b32548254e59e80bd3f0085231...e11df016ff92148801f56aca1de34da6e6d0c9cf
python3 tools/check_conventions.py --base 8f55414024c614b32548254e59e80bd3f0085231 --head e11df016ff92148801f56aca1de34da6e6d0c9cf

The diff check, conventions, and merge preview passed. The precheck's ABI and test-quality helpers were missing from its isolated checkout; the ABI audit does not apply to this change. Hosted Linux was skipped and Windows is still running.

The author reports that the focused recorder/submit tests passed three times on Windows 11 with an RX 6700 XT and the validation layer loaded, with no VUIDs; the same per-test results were reported on main and the PR head. The author also reports two 480-second Skyrim runs without the prior wait freeze. I did not run these tests or gameplay reproduction myself.

Sources

Not verified

I did not build or run the C++ tests locally, independently repeat the GPU/Skyrim runs, or test on console. Windows CI is pending; Linux and specialized CI jobs were skipped.

The CPU-write race can stay in a separate follow-up: the author reports it also exists on the base branch, and solving it needs separate race-safe detection and tests. I don't see evidence here that a lock-free counter is needed; measure contention before adding that complexity.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head e11df01 merged onto main 73f4f7e · tested 2026-10-31 14:52 UTC · 29 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,335 passed · 5 failed (0 new, 4 also fail on main, 1 unstable on main) · 48 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,388 of 3,072 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (878 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_bda_device 40.1 s, ⚠️ agc_driver_graphics 32.7 s, ⚠️ agc_driver_graphics_maps_fallback 30.4 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_submit_progress: failed in 7 of the last 10 main runs
Failing on main too (4, not counted against this PR)
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
agc_driver_job_manager_compute_capacity: Failed in the parallel run; also passed only on retry in 181 other runs in the last 7 days
Validation IDs already present on main (3 IDs in 387 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (387 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 379 more
VUID-vkCmdCopyImage-srcImage-01548 (12 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_sampling, and 4 more
VUID-vkCmdCopyImage-srcImage-01551 (12 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_plane_sampling, and 4 more
Skipped (48)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_msaa_dcc_compared_import_2: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_4: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_8: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_dcc: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_deferred: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or plat
Reproduce
git fetch origin e11df016ff92148801f56aca1de34da6e6d0c9cf 73f4f7e06e4f210a8c6570e335fd654dc0a86134
git checkout 73f4f7e06e4f210a8c6570e335fd654dc0a86134 && git merge --no-ff e11df016ff92148801f56aca1de34da6e6d0c9cf
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@adriadam10 adriadam10 self-assigned this Nov 1, 2026
@adriadam10
adriadam10 merged commit 588f2f6 into boykopovar:main Nov 1, 2026
12 of 13 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the unlanded-own-store lookup (newest entry per dword by stamp across the queued and recorded tables, mask/function handling in OwnStoreRejects), the cross-table and partial 8-byte cases from the earlier reviews, and the driver and recorder tests. NVIDIA and Windows runs are green; merged.

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

Labels

agc libSceAgc*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants