v_interp_p1ll_f16, v_interp_p1lv_f16 and v_interp_p2_f16 round the exact
P10 * I + P0 once to f32 and P20 * J + S2 once to f16 (#697), and
v_fma_mixlo_f16 and v_fma_mixhi_f16 reuse the f16 step in every f16
rounding mode. The exact sum was computed in f64, so on a device without
Float64 every shader using them failed to compile:
64-bit float instructions need the Float64 capability, which the
device lacks
Apple GPUs have no f64; with MoltenVK 1.4.2 on Apple M2 this stops
ProsperoStore (PPSA99000), PokeMMO Prospero (PPSA98001), Regie5
(PPSA99520) and PS5 OpenGL Showcase (PPSA99005) at their first such
pixel shader.
Without Float64 these operations are now computed in 64-bit integers,
which the shader device profile already requires. The inputs are
flushed as before and decoded to integer significands. The 24 by 24 bit
product is exact in 64 bits. Both terms are normalized to bit 62, the
smaller one is shifted down with the bits it loses kept as a sticky bit,
and the exact sum is rounded once to f32 to nearest even, or to f16 in
the f16 rounding mode, with f32 and f16 subnormal steps. Overflow,
signed zeros, the round-down zero sign, NaN quieting and the order in
which NaN operands win follow the f64 path, without its f64 arithmetic.
The module then declares Int64 instead of Float64. Devices with Float64
keep the f64 path.
FPInterpolateF32 and FPInterpolateF16, which v_interp_p1ll_f16,
v_interp_p1lv_f16, v_interp_p2_f16, v_fma_mixlo_f16 and v_fma_mixhi_f16
translate to, set the same float64 requirement as the f64 instructions,
since their exact sums are computed in f64. They now set
float64Interpolation instead, and the module still declares Float64,
and still throws without it, for either requirement. Nothing changes in
the emitted SPIR-V; the next commit gives these two operations a path
for devices without Float64.
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>
Devices with DenormFlushToZero for f32 run the mixed denormal-flush programs with and
without the hardware mode at startup; when every result matches, add, sub, mul, fma,
floor and ceil in the default rounding mode drop their software flush.
8237 and 8238 are taken by MeshLayerBase and MeshLayerCount in #6959 and 8240 by DepthRemapBase in another open PR, so buildSpecializedModule would read a mesh module's IDs as workgroup offsets once both land.
A dispatch over maxComputeWorkGroupCount was split into vkCmdDispatchBase
chunks whose base reached the limit, which VUID-vkCmdDispatchBase-baseGroupX-00421
forbids, and NVIDIA refused the split outright.
Compute modules now declare WorkgroupOffset specialization constants (default 0)
that the recompiler adds to WorkgroupId and GlobalInvocationId and keeps
unmaterialized. Each chunk is recorded with vkCmdDispatch from group 0, bound to
a pipeline variant specialized with the chunk's base. The NVIDIA throw is gone;
device-memory LDS splits still throw.
Fixes#7375
Since 2688b7646 a read and written V# element is staged in device memory
up to 16 MiB when the direct dispatch's threads times the V# stride cover
at least half of it. A raw buffer has stride 0, so it never passed and
kept the 2 MiB written window: GL storage buffers compiled by Mesa stay
bound in place in the host import however hard a dispatch sweeps them.
The PS5 OpenGL Showcase's particle update (262,144 threads, each loading
and storing its own 32-byte record of an 8 MiB SSBO) took about 53 ms of
GPU per frame on an RTX 4060 bound in place; staged it takes about 0.1 ms
plus 2.2 ms of bulk copies.
The recompiler already records the furthest byte each buffer resource's
accesses reach past their base (maxByteExtent). The binding plan now
carries it per guest buffer element (DescriptorBinding::bufferExtent,
shader disk cache format 37), and a raw element uses it as its record
size in the sweep test. Strided elements are unchanged.
SPI_PS_INPUT_ENA/ADDR bit 14 (SAMPLE_COVERAGE_ENA) gives the pixel shader one
VGPR, after ANCILLARY, holding the samples the primitive covers. The decoder
refused it and the recompiler never loaded the VGPR. It is now loaded from
gl_SampleMaskIn[0] (sharing the input the partial quad sample mask already
declares). On a multisampled draw the hardware word is the whole pixel's
coverage while gl_SampleMaskIn holds only the invocation's samples when the
pixel shader runs per sample, so that case, and sample-rate or ancillary inputs
beside it, are refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A vertex-path program with LDS, GDS or memory writes refused any s_barrier. NGG passthrough
programs start with the s_barrier that orders GS_ALLOC_REQ, before any memory access, and some
write buffers later. A barrier that runs before every load, store, atomic, image or LDS access of
the program has nothing to order, so it emits no instruction, like the barrier of a program
without memory access. Barriers after a memory access still throw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ConvertF16F32 built an (x, 0.0) f32 pair and packed it, and unary f16 results
(v_rcp_f16, v_sqrt_f16, ...) were packed with a 0.0 high half: the constant half
went through the whole f32-to-f16 conversion each time, which without native
f16 is a bit-level rounding sequence. Both now convert the one value; the high
half of the bits is zero as before, so the results are the same.
Test: agc_f16_single_convert recompiles four v_rcp_f16 with and without native
f16 and requires no (x, 0.0) f32 pair; on main each case emits four.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBXM4pgkMHYEtqN1EQni2P
The data-hit verification keeps this branch's pending-span query on its ShaderMemory and takes #5958's CaptureInvocation, which retries a capture with converted samples.
Keeps this branch's tagVariantRuns declaration next to main's dumpRequest suffix parameter in Driver.hpp, and reads #6232's pointer-chain test words from its table's snapshot, where this branch keeps them.