Repository navigation
feat(shader): implement v_interp_p1ll_f16, v_interp_p1lv_f16 and v_interp_p2_f16 - #697
Conversation
|
I measured these three on RDNA2 hardware (gfx1035, same VOP3 encodings as gfx1013).
As both steps need an exact fused result, emitting SPIR-V |
a4c3873 to
88cc96c
Compare
Measured on RDNA2 in boykopovar#697: v_interp_p1ll_f16 and v_interp_p1lv_f16 are a fused multiply-add rounded once to f32, and v_interp_p2_f16 rounds the exact P20 * J + S2 once to f16. The separate multiply and add matched about 85% and 4% of the measured rows. Both steps now use FPInterpolateF32 and FPInterpolateF16: the f16 delta times the f32 coordinate is exact in f64, the sum is rounded to odd and then once to nearest even at the target precision, so no driver can split or reround it. Infinite and NaN operands take the unbounded f64 result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kSh1x5fPGrREAeRnUPKMP
|
Thanks for the quick rework. I ran it against 384 more hardware rows (gfx1035), this time with specials: ±0, ±inf, quiet and signaling NaNs of both signs, f32 max, f16 65504, denormals, and products that overflow f16 or f32. Finite results, infinities, overflow to inf and the sign of zero all match your model (282/282). Two NaN cases don't:
So the order is: a NaN in the delta or the coordinate, quieted; else an invalid product or sum gives
|
Measured on RDNA2 in boykopovar#697: an invalid product or sum (inf * 0, inf - inf) gives the negative default NaN, 0xffc00000 or 0xfe00, and beats a NaN addend. A NaN delta, then a NaN coordinate, still wins, quieted; a NaN addend is returned quieted only when the product is valid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kSh1x5fPGrREAeRnUPKMP
|
Thanks again, that's exactly the data I was missing. Pushed b93e8f6:
invalid products and sums now give 0xffc00000 / 0xfe00, and the NaN order
is a NaN delta, then a NaN coordinate, then an invalid product or sum, then
a NaN addend, each NaN quieted. I ran your six rows through the new ops
with a local compute harness (lavapipe has no barycentrics, so CI can't run
them): all six match. The previous commit fails the three p1ll rows. 25,600
random rows per step, including NaN/inf operands, still match the exact
reference. Removed the "not measured" NaN note from TechnicalDebt. Full
ctest 359/359.
…On Tue, Oct 6, 2026, 10:57 Adrià ***@***.***> wrote:
*adriadam10* left a comment (boykopovar/AnyPS5#697)
<#697 (comment)>
Thanks for the quick rework. I ran it against 384 more hardware rows
(gfx1035), this time with specials: ±0, ±inf, quiet and signaling NaNs of
both signs, f32 max, f16 65504, denormals, and products that overflow f16
or f32. Finite results, infinities, overflow to inf and the sign of zero
all match your model (282/282). Two NaN cases don't:
1. *Invalid operations give the negative default NaN.* inf × 0 and inf
− inf return 0xffc00000 for p1ll/p1lv and 0xfe00 for p2 (35/35 rows).
The PR returns 0x7fc00000 / 0x7e00.
2. *An invalid product beats a NaN addend.* With P10/P20 × I/J = inf ×
0 and P0 (or S2) a NaN, the hardware returns the invalid NaN above, not the
quieted addend, e.g. p1ll(P10 = -inf, I = 0, P0 = 0xfe01) = 0xffc00000;
p2(P20 = -0, J = -inf, S2 = 0x7fc00000) = 0xfe00. Everywhere else, the
first NaN input in the order P10/P20, I/J, P0/S2, quieted, is what the
hardware returns (441/451 rows).
So the order is: a NaN in the delta or the coordinate, quieted; else an
invalid product or sum gives 0xffc00000/0xfe00; else a NaN addend,
quieted. Rows from the run, if you want them as test vectors (inputs as raw
bits: P10/P20 f16, I/J f32, P0 f16 or S2 f32 → result):
op P10/P20 I/J P0/S2 hardware
p1ll 0x0000 0x7f800000 0x7bff 0xffc00000
p1ll 0xfc00 0x00000000 0xfe01 0xffc00000
p1ll 0x7c00 0x7f7fffff 0xfc00 0xffc00000
p2 0x0000 0x7f800000 0x4f000000 0xfe00
p2 0x8000 0xff800000 0x7fc00000 0xfe00
p2 0xfe01 0x3f800000 0x47800000 0xfe01
—
Reply to this email directly, view it on GitHub
<#697?email_source=notifications&email_token=COACQRL3JKYRSP7MUGUDUNT5SSXXZA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTMMBRGI4DMNRRGEZKM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-6012866112>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/COACQROS6AJWDGD552JJW435SSXXZAVCNFSNUABGKJSXA33TNF2G64TZHMYTGMRSGI2TANRWGY5US43TOVSTWNJXGE2TMNZVGY2THILWAI>
.
Triage notifications, keep track of coding agent tasks and review pull
requests on the go with GitHub Mobile for iOS
<https://github.com/notifications/mobile/ios/COACQRNAXAGTOSYFZBIJIPL5SSXXZA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTMMBRGI4DMNRRGEZKM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KUZTPN52GK4S7NFXXG>
and Android
<https://github.com/notifications/mobile/android/COACQRLFXA6BLDOHDSWXQ435SSXXZA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTMMBRGI4DMNRRGEZKM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2K4ZTPN52GK4S7MFXGI4TPNFSA>.
Download it today!
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
1590230 to
b93e8f6
Compare
Measured on RDNA2 in boykopovar#697: v_interp_p1ll_f16 and v_interp_p1lv_f16 are a fused multiply-add rounded once to f32, and v_interp_p2_f16 rounds the exact P20 * J + S2 once to f16. The separate multiply and add matched about 85% and 4% of the measured rows. Both steps now use FPInterpolateF32 and FPInterpolateF16: the f16 delta times the f32 coordinate is exact in f64, the sum is rounded to odd and then once to nearest even at the target precision, so no driver can split or reround it. Infinite and NaN operands take the unbounded f64 result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on RDNA2 in boykopovar#697: an invalid product or sum (inf * 0, inf - inf) gives the negative default NaN, 0xffc00000 or 0xfe00, and beats a NaN addend. A NaN delta, then a NaN coordinate, still wins, quieted; a NaN addend is returned quieted only when the product is valid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
b93e8f6 to
bd0a0e5
Compare
|
Heads-up: this PR is on hold, not rejected. It relies on how |
…terp_p2_f16 Decode the three VOP3 16-bit interpolation instructions (attribute, channel and high-half select in the src0 field, the I/J VGPR in src1, P0 or the first step's result in src2) and translate them with a new GetInterpolationParameterF16 IR value. It reads the selected 16-bit half of each vertex's parameter through VK_KHR_fragment_shader_barycentric, so P10 and P20 are vertex differences rounded to f16; both steps are a multiply and an add, as in the f32 forms. An input in FP16_INTERP_MODE whose low half is defaulted is still declared at its slot when its high half is read, and the high half takes its default from USE_DEFAULT_ATTR1 and DEFAULT_VAL_ATTR1. Without barycentrics, on an input without FP16_INTERP_MODE or on a pass-through input the instructions throw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Prerequisite from boykopovar#697; retain original author and feature scope. (cherry picked from commit 73777c7)
Measured on RDNA2 in boykopovar#697: v_interp_p1ll_f16 and v_interp_p1lv_f16 are a fused multiply-add rounded once to f32, and v_interp_p2_f16 rounds the exact P20 * J + S2 once to f16. The separate multiply and add matched about 85% and 4% of the measured rows. Both steps now use FPInterpolateF32 and FPInterpolateF16: the f16 delta times the f32 coordinate is exact in f64, the sum is rounded to odd and then once to nearest even at the target precision, so no driver can split or reround it. Infinite and NaN operands take the unbounded f64 result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Prerequisite from boykopovar#697; retain original author and feature scope. (cherry picked from commit 6534b25)
Measured on RDNA2 in boykopovar#697: an invalid product or sum (inf * 0, inf - inf) gives the negative default NaN, 0xffc00000 or 0xfe00, and beats a NaN addend. A NaN delta, then a NaN coordinate, still wins, quieted; a NaN addend is returned quieted only when the product is valid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Prerequisite from boykopovar#697; retain original author and feature scope. (cherry picked from commit bd0a0e5)
Replay six published gfx1035 tuples through the interpolation arithmetic emitters with runtime buffer inputs. Rotate the rows for a second dispatch and check inputs and guards. This runs without fragment barycentrics. Oracle: boykopovar#697 (comment) Use exact expanded FP32 arguments to isolate arithmetic from guest half conversion, SPI, LDS and guest mode handling. No new opcode semantics or native RDNA2/PS5 execution are claimed.
|
The device draw in
Full ctest on the 9070 XT: 459/459 (main alone 458/458). This only says the emitted SPIR-V matches the PR's reference model on these drivers. It doesn't settle the float-mode questions from #981. Tested and written by Claude (AI assistant) on a contributor's machine. |
…terp_p2_f16 Decode the three VOP3 16-bit interpolation instructions (attribute, channel and high-half select in the src0 field, the I/J VGPR in src1, P0 or the first step's result in src2) and translate them with a new GetInterpolationParameterF16 IR value. It reads the selected 16-bit half of each vertex's parameter through VK_KHR_fragment_shader_barycentric, so P10 and P20 are vertex differences rounded to f16; both steps are a multiply and an add, as in the f32 forms. An input in FP16_INTERP_MODE whose low half is defaulted is still declared at its slot when its high half is read, and the high half takes its default from USE_DEFAULT_ATTR1 and DEFAULT_VAL_ATTR1. Without barycentrics, on an input without FP16_INTERP_MODE or on a pass-through input the instructions throw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Prerequisite from boykopovar#697; retain original author and feature scope. (cherry picked from commit 73777c7)
Measured on RDNA2 in boykopovar#697: v_interp_p1ll_f16 and v_interp_p1lv_f16 are a fused multiply-add rounded once to f32, and v_interp_p2_f16 rounds the exact P20 * J + S2 once to f16. The separate multiply and add matched about 85% and 4% of the measured rows. Both steps now use FPInterpolateF32 and FPInterpolateF16: the f16 delta times the f32 coordinate is exact in f64, the sum is rounded to odd and then once to nearest even at the target precision, so no driver can split or reround it. Infinite and NaN operands take the unbounded f64 result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Prerequisite from boykopovar#697; retain original author and feature scope. (cherry picked from commit 6534b25)
Measured on RDNA2 in boykopovar#697: an invalid product or sum (inf * 0, inf - inf) gives the negative default NaN, 0xffc00000 or 0xfe00, and beats a NaN addend. A NaN delta, then a NaN coordinate, still wins, quieted; a NaN addend is returned quieted only when the product is valid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Prerequisite from boykopovar#697; retain original author and feature scope. (cherry picked from commit bd0a0e5)
Replay six published gfx1035 tuples through the interpolation arithmetic emitters with runtime buffer inputs. Rotate the rows for a second dispatch and check inputs and guards. This runs without fragment barycentrics. Oracle: boykopovar#697 (comment) Use exact expanded FP32 arguments to isolate arithmetic from guest half conversion, SPI, LDS and guest mode handling. No new opcode semantics or native RDNA2/PS5 execution are claimed.
…terp_p2_f16 Decode the three VOP3 16-bit interpolation instructions (attribute, channel and high-half select in the src0 field, the I/J VGPR in src1, P0 or the first step's result in src2) and translate them with a new GetInterpolationParameterF16 IR value. It reads the selected 16-bit half of each vertex's parameter through VK_KHR_fragment_shader_barycentric, so P10 and P20 are vertex differences rounded to f16; both steps are a multiply and an add, as in the f32 forms. An input in FP16_INTERP_MODE whose low half is defaulted is still declared at its slot when its high half is read, and the high half takes its default from USE_DEFAULT_ATTR1 and DEFAULT_VAL_ATTR1. Without barycentrics, on an input without FP16_INTERP_MODE or on a pass-through input the instructions throw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Prerequisite from boykopovar#697; retain original author and feature scope. (cherry picked from commit 73777c7)
Measured on RDNA2 in boykopovar#697: v_interp_p1ll_f16 and v_interp_p1lv_f16 are a fused multiply-add rounded once to f32, and v_interp_p2_f16 rounds the exact P20 * J + S2 once to f16. The separate multiply and add matched about 85% and 4% of the measured rows. Both steps now use FPInterpolateF32 and FPInterpolateF16: the f16 delta times the f32 coordinate is exact in f64, the sum is rounded to odd and then once to nearest even at the target precision, so no driver can split or reround it. Infinite and NaN operands take the unbounded f64 result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Prerequisite from boykopovar#697; retain original author and feature scope. (cherry picked from commit 6534b25)
Measured on RDNA2 in boykopovar#697: an invalid product or sum (inf * 0, inf - inf) gives the negative default NaN, 0xffc00000 or 0xfe00, and beats a NaN addend. A NaN delta, then a NaN coordinate, still wins, quieted; a NaN addend is returned quieted only when the product is valid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Prerequisite from boykopovar#697; retain original author and feature scope. (cherry picked from commit bd0a0e5)
Replay six published gfx1035 tuples through the interpolation arithmetic emitters with runtime buffer inputs. Rotate the rows for a second dispatch and check inputs and guards. This runs without fragment barycentrics. Oracle: boykopovar#697 (comment) Use exact expanded FP32 arguments to isolate arithmetic from guest half conversion, SPI, LDS and guest mode handling. No new opcode semantics or native RDNA2/PS5 execution are claimed.
Measured on RDNA2 in boykopovar#697: v_interp_p1ll_f16 and v_interp_p1lv_f16 are a fused multiply-add rounded once to f32, and v_interp_p2_f16 rounds the exact P20 * J + S2 once to f16. The separate multiply and add matched about 85% and 4% of the measured rows. Both steps now use FPInterpolateF32 and FPInterpolateF16: the f16 delta times the f32 coordinate is exact in f64, the sum is rounded to odd and then once to nearest even at the target precision, so no driver can split or reround it. Infinite and NaN operands take the unbounded f64 result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on RDNA2 in boykopovar#697: an invalid product or sum (inf * 0, inf - inf) gives the negative default NaN, 0xffc00000 or 0xfe00, and beats a NaN addend. A NaN delta, then a NaN coordinate, still wins, quieted; a NaN addend is returned quieted only when the product is valid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…terp_p2_f16 Decode the three VOP3 16-bit interpolation instructions (attribute, channel and high-half select in the src0 field, the I/J VGPR in src1, P0 or the first step's result in src2) and translate them with a new GetInterpolationParameterF16 IR value. It reads the selected 16-bit half of each vertex's parameter through VK_KHR_fragment_shader_barycentric, so P10 and P20 are vertex differences rounded to f16; both steps are a multiply and an add, as in the f32 forms. An input in FP16_INTERP_MODE whose low half is defaulted is still declared at its slot when its high half is read, and the high half takes its default from USE_DEFAULT_ATTR1 and DEFAULT_VAL_ATTR1. Without barycentrics, on an input without FP16_INTERP_MODE or on a pass-through input the instructions throw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on RDNA2 in boykopovar#697: v_interp_p1ll_f16 and v_interp_p1lv_f16 are a fused multiply-add rounded once to f32, and v_interp_p2_f16 rounds the exact P20 * J + S2 once to f16. The separate multiply and add matched about 85% and 4% of the measured rows. Both steps now use FPInterpolateF32 and FPInterpolateF16: the f16 delta times the f32 coordinate is exact in f64, the sum is rounded to odd and then once to nearest even at the target precision, so no driver can split or reround it. Infinite and NaN operands take the unbounded f64 result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on RDNA2 in boykopovar#697: an invalid product or sum (inf * 0, inf - inf) gives the negative default NaN, 0xffc00000 or 0xfe00, and beats a NaN addend. A NaN delta, then a NaN coordinate, still wins, quieted; a NaN addend is returned quieted only when the product is valid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
bd0a0e5 to
8f663d0
Compare
|
Taking this off hold for #981. I rebased it on The float mode now drives the interpolation. I measured
Tests. The new Tested: RADV and CI's lavapipe, full ctest 492/492 after the rebase. |
|
Post-merge main Build |
…s without Float64 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 (boykopovar#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. (cherry picked from commit f13e05e)
…s without Float64 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 (boykopovar#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.
…s without Float64 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 (boykopovar#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.
What
Implements the three VOP3 16-bit interpolation instructions, the last unimplemented VOP3 opcodes on the progress page (shaders 1120 → 1123 of 1166).
VOP3Interp_gfx10and AMD's machine-readable RDNA2 ISA; encodings cross-checked withllvm-mc -mcpu=gfx1030. Modifiers LLVM does not encode (src0-slot abs/neg, op_sel, omod on the f16 result) and non-VGPR sources throw.GetInterpolationParameterF16IR value reads the selected 16-bit half of each vertex's parameter throughVK_KHR_fragment_shader_barycentric. P10/P20 are the vertex differences rounded to f16. Each step is a multiply and an add, as inv_interp_p1_f32/v_interp_p2_f32. P2 writes throughwriteF16, which keeps the destination's high 16 bits.FP16_INTERP_MODE,USE_DEFAULT_ATTR1andDEFAULT_VAL_ATTR1fromSPI_PS_INPUT_CNTLare now read. An input whose low half is defaulted is still declared at its slot when its high half is read; before this, emission refused it ("reads a default value, not a parameter").FP16_INTERP_MODE, and on pass-through inputs. All of this is recorded in TechnicalDebt.Related, independent: #651 produces the high-half interpolant mapping these instructions read.
Tested
Linux (Ubuntu 24.04, GCC 13.3), lavapipe (Mesa 25.2). Full
ctest: 289/289 pass.agc_shader_memory: newverifyF16PixelParameterSlotscovers which inputs are declared per vertex for high- and low-half reads with each half defaulted.agc_driver_interpolation_f16:fragmentShaderBarycentric: draws triangles with distinct packed-f16 vertex parameters and compares every covered pixel bit for bit against the ISA formula (both halves, p1lv with both S2 halves, clamp, a negated I, defaulted high halves, preserved destination high bits, and an f16-rounded delta).spirv-val --target-env vulkan1.1.Not tested: lavapipe has no
VK_KHR_fragment_shader_barycentric, so the draw is skipped here and in CI; it has not run on any device. No title or homebrew was tested. The f16 rounding of P10/P20 and the unfused multiply-add are assumptions, not hardware measurements.Checklist
main; no other open PR implements the same functions.mdfiles or images added to the repository