NVIDIA's Windows driver reads a host import of guest memory far slower than a buffer it allocated.
Since draw vertex and index ranges are bound in place from host imports (#2253, measured on Linux), Astro
Bot's Crash Site on Windows 11 with an RTX 5070 Ti spends 164 ms of every frame in draws on the GPU, and
45 ms with the ranges copied as before; with the same build, 4.71 fps in place against 7.52 copied. An
RTX 3060 shows the same (draws 32.3 against 24.3 ms per frame), while AMD's Windows driver reads them in
place about as fast as a copy (RX 9070 XT: 69.8 against 73.1 ms), so copying there only costs CPU.
The device decides once (DrawInputsInPlaceUsable, Context::drawInputsInPlace): ranges are copied with
VK_DRIVER_ID_NVIDIA_PROPRIETARY on Windows and bound in place everywhere else. APS5_DRAW_INPUTS_IN_PLACE=1
binds them in place and APS5_NO_DRAW_INPUTS_IN_PLACE=1 copies them on any device, the second winning.
agc_draw_inputs_in_place_driver checks the decision per driver and host; agc_draw_inputs_copied checks
that an aligned imported range is copied and its read not noted when the context copies, whatever the
inherited environment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GN8FJFTedayRXLHythXkZ4
The direct-device case already drains its own work before returning. Skip the shared singleton wait after its explicit shutdown so the full suite can retain the fallback and counter assertions without throwing ProcessShutdown.
Co-Authored-By: Codex <noreply@openai.com>
A CPU read of DISPATCH_INDIRECT arguments waited only through the flush
hook, which waits for the ranges recorded work noted as pending writes.
An address-based store (FLAT/GLOBAL, or through a descriptor the
recompiler cannot resolve to a static buffer) is not recorded as a
pending write: its batch leases the registered allocations, and only the
guest pin waiter finished that batch. So the thread-dimension,
misaligned and disabled paths, the fill-kernel path and the device's CPU
fallback read the arguments from before an address-based producer ran.
GTA V (PPSA04264) dies loading Story on such a read: a 64-thread clear
kernel read {160, 288, 0xffffffff} as thread dimensions
(3x288x4294967295 groups), bytes its 1-thread setup kernel had not yet
overwritten with {n, 1, 1}, and threw "workgroup count
3x288x4294967295 exceeds device limits".
Every CPU read of dispatch arguments at execution now first finishes the
recorder up to the newest batch recorded with a lease
(Graphics::FinishLeaseWork). The group read-ahead still reads without
waiting; a dispatch it resolved is adopted only when the execution-time
read gives the same counts, and one that differs is dropped and counted
in the [groupcap] line. The device limit error names the argument
address and cpuReason, and the driver adds the queue, path and
read-ahead outcome. The [indirect] read time includes the lease wait,
and the [recorder] sync-site table records the offset of
FinishLeaseWork's caller. The CPU reads that still miss address-based
writes (indirect draw arguments and count, the copy kernel's count
record, the awaits-title checks) are listed in the technical debt.
IndirectArgumentsPendingProducer covers the execution-time CPU argument
reads on compute queues and the pending-label CPU fallback in the Vulkan
device. Its sixth case records a real argument label behind a host Vulkan
event in an earlier batch and an address-based producer in the later open
batch. The event releases only after that producer batch is submitted;
a bounded watchdog releases for cleanup but fails the case. It requires
a deferred producer lease, an unsubmitted producer at the argument
decision, cpuReason 2 and the fresh arguments and counter result. Small
valid stale counts keep a broken-wait control finite on every driver.
Exact CPU-reason and watchdog diagnostics identify the failing path. This
replaces a spinning workload whose earlier label could retire before
the fallback decision on lavapipe, allowing a false green with cpuReason 0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>
agc_driver_mix_precision skipped on devices without Float64 because
v_fma_mixlo_f16 and v_fma_mixhi_f16 were computed in f64. They now
compile on such devices too, so the test runs everywhere.
The hardware rows cover rounding to nearest even only. The test now
also builds one shader per path that evaluates 2,048 random rows in all
32 modes: the eight float modes with each of the four f16 rounding
modes, which v_fma_mixlo_f16 and v_fma_mixhi_f16 use. The rows draw f16
deltas, f32 coordinates and bases from random bits, random finite
values and special values (zeros, subnormals, the largest finites,
infinities, NaNs), and a third of the bases nearly or exactly cancel the
product. Each result must match a CPU reference built on f64: the exact
product, the sum rounded to odd, one rounding at the target precision
in the mode, and the same overflow, zero and NaN rules.
On devices with Float64 the f64 path is checked against the same
reference.
agc_driver_interpolation_modes skipped on devices without Float64. It
now runs its 32 rows measured on RDNA2, for the three 16-bit
interpolation instructions in all eight float modes, through the integer
path on every device, and through the f64 path as well where the device
has Float64. The shader for one row set is built for either path, and
reports which path a mismatch came from.
Image and sampler table capacity came only from the regular descriptor
limits. MoltenVK reports Metal's argument table limits there (256 sampled
images and 16 samplers per stage), below the typed heaps the tables are
reserved on top of, so both capacities were 0 and every table draw or
dispatch was refused as if the device lacked descriptor indexing. Its
update-after-bind limits are 1000000 sampled images and 500000 samplers.
- descriptorBindingSampledImageUpdateAfterBind is queried and enabled, and
the update-after-bind limits are read when either it or the storage
buffer feature is present.
- A table whose regular capacity is 0 takes its capacity from the
update-after-bind limits. Its binding is update-after-bind and its set
layout and pool are update-after-bind ones, in ShaderResources and in
DescribeSetLayouts alike. A table that fits the regular limits is
unchanged.
- An update-after-bind set layout and its allocation are checked against
the update-after-bind sampled image and sampler limits when the device
has descriptorBindingSampledImageUpdateAfterBind. Vulkan counts the
descriptors of such sets against those limits in place of the regular
ones, and requires them to be at least the regular ones only when the
feature is supported, so without it, and for storage images, the
regular limits are kept as before. Storage buffers keep their own
update-after-bind flag.
- Compute pipeline capture and replay accept update-after-bind sampled
image and sampler bindings.
Every OpGroupNonUniformBallot now goes through SpirvModule::AddSubgroupBallot.
A repeated ballot of the same predicate in the same block becomes an
OpCopyObject of the first ballot. Within one block the active invocations do
not change, so the result is identical. The cache is cleared at every label,
function boundary, function call and demote, and helper functions keep their
own cache.
When SPIRV-Tools is enabled, block merging and specialization can still put
two ballots of one predicate into the same block after emission, so
ValidateAndOptimizeSpirv collapses repeated block ballots the same way on
every module it returns, optimized or not.
NVIDIA 580's SPIR-V compiler segfaulted (libnvidia-gpucomp+0xe3facc) on GTA V
compute shaders that ballot one predicate many times in a block. One such
shader had 163 repeated ballots across three blocks. With them collapsed, both
crashing captures (compute-fe506552cec99bfc and compute-c02e5e3f35010fa5)
compile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Filled rectangles (VGT_PRIMITIVE_TYPE 7 and 17) use the same single-sample MSAA_ENABLE path as triangles; PPSA03541 stops on a rect-list draw otherwise.
Co-authored-by: Cursor <cursoragent@cursor.com>
Use representable viewport endpoints only when the draw has no depth attachment and its pixel shader does not read PositionZ. Preserve XY coverage, clip-space mode, depth clamping, and validation of non-finite inputs.
Balatro (PPSA21402) reaches rendering on Apple M1 with MoltenVK after this change instead of stopping at VK_EXT_depth_range_unrestricted. Depth-dependent draws retain their original behavior.