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
unwrap_self copies one output region per PT_LOAD and nothing required the
regions to be disjoint in the input. Only the alignment padding was capped, so
200 PT_LOADs that all point at the same 2 MiB of a 2 MiB container produced
400 MiB, and the 65535 program headers the parser accepts could ask for
hundreds of gigabytes, ending in MemoryError or an OOM kill instead of a
Refused message.
The output size is now checked before the buffer is allocated. It may be up to
four times the container, and never less than 256 MiB.
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]>