From 1add078eea77bde699475d6d14a0abd832ac01eb Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 25 Sep 2026 19:47:36 +0200 Subject: [PATCH] Delete cellular's unreachable F2=0 guard instead of leaving it undefended Stryker (scoped to this cut's diff) survived two mutants on the grad2 ternary in math.cellular: forcing the true branch unconditionally, and loosening f2 > 0.0f to f2 >= 0.0f. Both are real findings about the code, not the tests: F2 = 0 needs two distinct integer cells' pcg3d-jittered feature points to land on the exact float32 value in every component, which the jitter's ~24-bit float precision makes unreachable for any p this function is actually called with. The spec only asks for the degenerate guard at F1 = 0; F2's copy of it was added for symmetry and had no reachable case to defend. Deleted the guard (grad2 is now a bare (p - c2) / f2) in math.cs and both HLSL mirrors, and corrected design.md's invariant-8 section, which had overstated this as symmetric with F1's guard. Co-Authored-By: Claude Sonnet 5 --- packages/cultmath/docs/design.md | 17 +++++++++++------ packages/cultmath/shaders/CultMath.hlsl | 4 +++- packages/cultmath/src/CultMath/math.cs | 6 +++++- .../org.gamecult.cultmath/Shaders/CultMath.hlsl | 4 +++- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/cultmath/docs/design.md b/packages/cultmath/docs/design.md index dde7a4d2..ab3650a7 100644 --- a/packages/cultmath/docs/design.md +++ b/packages/cultmath/docs/design.md @@ -151,15 +151,20 @@ together, laid out as `float4(gradient.xyz, value.w)`, with no value-only twin jittered 3×3×3 neighbourhood of feature points selected by `pcg3d` (never the sin-based `hash`). It returns `CultCellular { nearest, edge, id }`: `nearest` is `(∇F1, F1)`, `edge` is `(∇F2 - ∇F1, F2 - F1)`, and `id` is the - nearest cell's `pcg3d` hash mapped to `[0, 1)`. `∇F1`/`∇F2` are undefined - exactly at their own feature point (`F1 = 0` or `F2 = 0`); `cellular` - follows the repo's existing degenerate-normal convention + nearest cell's `pcg3d` hash mapped to `[0, 1)`. `∇F1` is undefined exactly + at its own feature point (`F1 = 0`); `cellular` follows the repo's existing + degenerate-normal convention (`GameCult.Geometry.CultGeometryIsoSurface.EmitOrientedTriangle`, which guards a zero-length normal and returns the zero vector instead of the NaN a bare `normalize` gives there) and returns the zero vector rather than NaN. - The identity of the nearest and second-nearest feature point changes - discontinuously across the `F1 = F2` set (a genuine kink, not a numerical - artifact), so `∇F1` and `∇F2` are only continuous away from that set. + `∇F2` carries no such guard: `F2 = 0` would need two distinct cells' + pcg3d-jittered feature points to land on the exact same float32 value in + every component, which the jitter's float precision makes unreachable for + any `p` this function is actually called with, so there is no reachable + case to defend. The identity of the nearest and second-nearest feature + point changes discontinuously across the `F1 = F2` set (a genuine kink, not + a numerical artifact), so `∇F1` and `∇F2` are only continuous away from + that set. Integer hashing uses the PCG hashes from Jarzynski and Olano, "Hash Functions for GPU Rendering" (JCGT 9(3), 2020): `pcg(uint)` is O'Neill's RXS-M-XS 32/32 diff --git a/packages/cultmath/shaders/CultMath.hlsl b/packages/cultmath/shaders/CultMath.hlsl index 8b08d82c..12636035 100644 --- a/packages/cultmath/shaders/CultMath.hlsl +++ b/packages/cultmath/shaders/CultMath.hlsl @@ -371,8 +371,10 @@ CultCellular cultmath_cellular(float3 p) } } + // Only F1's degenerate point is guarded; see math.cellular's comment (math.cs) for why F2 = 0 + // has no reachable case to defend. float3 grad1 = f1 > 0.0 ? (p - c1) / f1 : float3(0.0, 0.0, 0.0); - float3 grad2 = f2 > 0.0 ? (p - c2) / f2 : float3(0.0, 0.0, 0.0); + float3 grad2 = (p - c2) / f2; CultCellular result; result.nearest = float4(grad1, f1); diff --git a/packages/cultmath/src/CultMath/math.cs b/packages/cultmath/src/CultMath/math.cs index 21adfe46..1ca381eb 100644 --- a/packages/cultmath/src/CultMath/math.cs +++ b/packages/cultmath/src/CultMath/math.cs @@ -747,8 +747,12 @@ public static partial class math } } + // Only F1's degenerate point is guarded, matching the spec: F2 = 0 needs two distinct + // integer cells' pcg3d-jittered feature points to land on the exact same float32 value in + // every component, which the jitter's ~24-bit float precision makes unreachable for any p + // this function is actually called with, so there is no reachable case for a guard to defend. var grad1 = f1 > 0.0f ? (p - c1) / f1 : new float3(0.0f, 0.0f, 0.0f); - var grad2 = f2 > 0.0f ? (p - c2) / f2 : new float3(0.0f, 0.0f, 0.0f); + var grad2 = (p - c2) / f2; return new CultCellular( new float4(grad1, f1), diff --git a/packages/cultmath/unity/org.gamecult.cultmath/Shaders/CultMath.hlsl b/packages/cultmath/unity/org.gamecult.cultmath/Shaders/CultMath.hlsl index 8b08d82c..12636035 100644 --- a/packages/cultmath/unity/org.gamecult.cultmath/Shaders/CultMath.hlsl +++ b/packages/cultmath/unity/org.gamecult.cultmath/Shaders/CultMath.hlsl @@ -371,8 +371,10 @@ CultCellular cultmath_cellular(float3 p) } } + // Only F1's degenerate point is guarded; see math.cellular's comment (math.cs) for why F2 = 0 + // has no reachable case to defend. float3 grad1 = f1 > 0.0 ? (p - c1) / f1 : float3(0.0, 0.0, 0.0); - float3 grad2 = f2 > 0.0 ? (p - c2) / f2 : float3(0.0, 0.0, 0.0); + float3 grad2 = (p - c2) / f2; CultCellular result; result.nearest = float4(grad1, f1);