On Windows, GuestMemory::Unwatch excludes every page of each 64 KiB
block it touches, so unwatching one page of the descriptor unit left the
whole unit unwatched and the "descriptor page lost write watching" check
failed. Unwatch the unit and return only its descriptor page with
Rewatch there, which is page-granular, so the unit is partly watched on
every platform. Linux keeps the single-page Unwatch.
Diagnosed by maskym on Windows 11 26300 with an RTX 2070 SUPER.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without it the driver's device is destroyed after main returns, and the
Vulkan validation layer aborts the test on exit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A wave64 program held as 32 invocations x 2 lanes decided s_cbranch_execz,
s_cbranch_vccz and s_cbranch_scc0 by balloting the per-lane "zero"
condition and requiring all 64 bits set. OpGroupNonUniformBallot sets the
bit of an inactive invocation to 0 (SPIR-V specification,
OpGroupNonUniformBallot: "The bit is set to one if the corresponding
invocation is active and the Predicate for that invocation evaluated to
true; otherwise, it is set to zero"), and an invocation that returned
from the entry point is no longer active. When one invocation stopped,
e.g. after a BDA fault, the zero branch was never taken for the others:
a loop leaving through s_cbranch_execz never ended.
The RDNA2 ISA branches when the whole register is zero ("if (EXEC == 0)",
"if (VCC == 0)", "if (SCC == 0)", SOPP S_CBRANCH_EXECZ / VCCZ / SCC0), so
the branch now asks whether any active invocation still has a lane with
the bit set, as the wave32 path does: not any(own lane set || other
lane set). Inactive invocations then count as lanes with the bit clear.
The non-zero kinds keep any(own || other).
The execution test runs a wave64 program in which lane 0 faults and
stops its invocation, then takes each zero and non-zero branch; before
this change the other lanes fell through all three zero branches.
AI-assisted: yes
Wait-free submissions on other queues ran on their own workers with no
order against queue 0, so a queue 0 submission received after them
could execute first. Uncharted: The Lost Legacy submits small compute
jobs on queue 0x50 and reuses their command and descriptor memory once
later queue 0 work has passed; with the async job up to a second late,
it read recycled memory (garbage descriptors, BDA faults, a zeroed PM4
buffer) while loading Sri Lanka. A queue 0 submission now waits until
no wait-free submission received before it is pending or executing on
another queue. Submissions that wait for something, and the wait-free
ones queued behind them, are not waited for, since they can wait for
this queue 0 work.
Uncharted 4's menu fps is unchanged (10.21 vs 10.24, two runs each).
Tests: testQueue0SubmissionAfterEarlierWaitFreeWork and
testQueue0SubmissionBehindWaitingQueue.
AI-assisted: yes
This reverts commit 780b33aea3, restoring the 13824 MiB direct memory pool.
With the pool at 12288 MiB, Astro Bot (PPSA21564) can no longer load its
level: DirectMemoryAllocator asserts "Can't allocate direct memory" for a
64 MB VideoPlayerTexture with 12228 of 12288 MB in use, then the process
faults about 26 s in (2 of 2 runs, RTX 4090, Linux, main cccdaa2734).
The same tree with only this merge reverted runs the full 150 s with no
direct memory assert (2 of 2 runs).
SPIRV-Cross emits the lock's acquire compare-exchange and release
exchange as relaxed Metal atomics, so the plain LDS loads and stores of
a locked 64-bit update were not ordered against the lock and contended
ds_add_u64 lost updates on an Apple M5 Pro. On MoltenVK the driver now sets
SpirvTarget::ldsLockFences, and the lock brackets its critical section
with workgroup-scope acquire and release OpMemoryBarriers, which become
threadgroup atomic_thread_fence with no execution barrier.
Co-Authored-By: Claude Opus 5.5 <[email protected]>