Say where intrinsic rules are pinned and that NaN does not propagate
The mirror compiles under using static CultMath.math, so it proves composition, not intrinsic rules; HlslSemanticsTests alone pins those. design.md also states where FMin/FMax/Saturate turn NaN or infinity into numbers and tells callers to check isnan first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
19e23db211
commit
d4c43ed265
2 changed files with 19 additions and 4 deletions
|
|
@ -46,8 +46,8 @@ That makes these HLSL rules, not options:
|
||||||
sides rather than leaning on implicit scalar splat.
|
sides rather than leaning on implicit scalar splat.
|
||||||
- Intrinsics return HLSL's types and edge cases: `sign` returns `int`/`intN`
|
- Intrinsics return HLSL's types and edge cases: `sign` returns `int`/`intN`
|
||||||
(NaN gives 0); `step(y, x)` is `x < y ? 0 : 1` (NaN gives 1), which is how dxc
|
(NaN gives 0); `step(y, x)` is `x < y ? 0 : 1` (NaN gives 1), which is how dxc
|
||||||
lowers it, in C# and in the HLSL mirror; float `min`, `max`, `clamp`, and
|
lowers it, in C# and in the HLSL mirror; float and double `min` and `max`, and
|
||||||
`saturate` follow the DXIL `FMin`/`FMax`/`Saturate` operations dxc emits for
|
float `clamp` and `saturate`, follow the DXIL `FMin`/`FMax`/`Saturate` operations dxc emits for
|
||||||
them: `min(a, b)` is `a < b ? a : b`, `max(a, b)` is `a >= b ? a : b`, a NaN
|
them: `min(a, b)` is `a < b ? a : b`, `max(a, b)` is `a >= b ? a : b`, a NaN
|
||||||
operand returns the other (so `saturate(NaN)` is 0), `clamp(x, a, b)` is
|
operand returns the other (so `saturate(NaN)` is 0), `clamp(x, a, b)` is
|
||||||
`min(max(x, a), b)`, and `saturate(x)` is `min(1, max(0, x))`, which maps -0
|
`min(max(x, a), b)`, and `saturate(x)` is `min(1, max(0, x))`, which maps -0
|
||||||
|
|
@ -59,6 +59,16 @@ dxc marks float arithmetic and compares `fast` (no NaNs) unless a value is
|
||||||
GPUs are not guaranteed. CultMath defines the CPU result by dxc's lowering and
|
GPUs are not guaranteed. CultMath defines the CPU result by dxc's lowering and
|
||||||
the DXIL operation spec (DirectXShaderCompiler `docs/DXIL.rst`).
|
the DXIL operation spec (DirectXShaderCompiler `docs/DXIL.rst`).
|
||||||
|
|
||||||
|
NaN does not reliably propagate through CultMath, just as it does not on the
|
||||||
|
GPU. Because `min`, `max`, and `saturate` return the non-NaN operand, some
|
||||||
|
functions turn NaN or infinity into numbers: `smoothstep(a, a, a)` is 0;
|
||||||
|
infinite inputs to `smoothstep` and the intercept helpers give numbers; the
|
||||||
|
bezier, `smoothstep01`, and `smootherstep` functions return finite values for
|
||||||
|
NaN input; and `normalize((NaN, 2.41))` is `(NaN, 2.41e20)`, because the NaN
|
||||||
|
length is clamped to the epsilon and the other component is divided by it.
|
||||||
|
Callers that must detect bad data should check `isnan` before calling these
|
||||||
|
functions.
|
||||||
|
|
||||||
Deferred until a consumer needs them: `float4x4`, `transpose`, `determinant`,
|
Deferred until a consumer needs them: `float4x4`, `transpose`, `determinant`,
|
||||||
and HLSL's one-based `_11`..`_33` element names.
|
and HLSL's one-based `_11`..`_33` element names.
|
||||||
|
|
||||||
|
|
@ -88,7 +98,10 @@ DirectXShaderCompiler, and
|
||||||
The mirror test compares bit patterns (any NaN equals any NaN; -0 differs from
|
The mirror test compares bit patterns (any NaN equals any NaN; -0 differs from
|
||||||
0). It proves the text of `CultMath.hlsl` computes the same float32 results as
|
0). It proves the text of `CultMath.hlsl` computes the same float32 results as
|
||||||
C# `math` on the CPU, not that a GPU agrees: driver `sin` precision alone makes
|
C# `math` on the CPU, not that a GPU agrees: driver `sin` precision alone makes
|
||||||
`cultmath_hash` and value noise differ bit for bit on hardware.
|
`cultmath_hash` and value noise differ bit for bit on hardware. The mirror is
|
||||||
|
compiled under `using static CultMath.math;`, so its intrinsics are C# `math`
|
||||||
|
itself: the test proves the composition in the text, not the intrinsic rules,
|
||||||
|
which only `HlslSemanticsTests` pins.
|
||||||
|
|
||||||
Where HLSL is silent, CultMath keeps its own decisions and does not defer to
|
Where HLSL is silent, CultMath keeps its own decisions and does not defer to
|
||||||
Unity.Mathematics: `normalize` is safe (divides by `max(length, 1e-20)`),
|
Unity.Mathematics: `normalize` is safe (divides by `max(length, 1e-20)`),
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ public sealed class HlslSourceCompatibilityTests
|
||||||
/// negatives, NaN, large values, integer lattice points), special pairs, and vectors whose first two
|
/// negatives, NaN, large values, integer lattice points), special pairs, and vectors whose first two
|
||||||
/// components tie (x0.x == x0.y in snoise). This proves the text of CultMath.hlsl computes the same
|
/// components tie (x0.x == x0.y in snoise). This proves the text of CultMath.hlsl computes the same
|
||||||
/// float32 results as C# math on the CPU. It does not prove GPU agreement: driver sin precision alone
|
/// float32 results as C# math on the CPU. It does not prove GPU agreement: driver sin precision alone
|
||||||
/// makes cultmath_hash and value noise differ bit for bit on hardware.
|
/// makes cultmath_hash and value noise differ bit for bit on hardware. The mirror's intrinsics are C#
|
||||||
|
/// math itself (it compiles under using static CultMath.math), so this proves the composition in the
|
||||||
|
/// text, not the intrinsic rules; HlslSemanticsTests alone pins those.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public void EveryMirrorFunctionMatchesCSharpMath()
|
public void EveryMirrorFunctionMatchesCSharpMath()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue