test: the variable is compile-time-constant vs runtime Blend — one line to confirm

The inlining experiment partitioned everything, just not along the axis it was
framed on:

  inlined != FORCENOINLINE  : 0          inlining is not the variable
  FORCENOINLINE == op stack : 5000/5000  test-TU carve == other-TU op, always
  inlined == verbatim       : 4874/5000  126 differ, in the SAME TU

The test-TU carve matches the operator stack across a TU boundary perfectly and
disagrees with the verbatim inside its own TU, so neither TU nor inlining is it.
Sorting the five implementations by the one remaining difference splits them
exactly: A (GetMazeDensity) and C (verbatim) hold Blend as a compile-time
constant; B (FSdfCarveOp, a member) and both parameter versions hold it as
runtime data. A == C, B == Inl == Noi, and the groups differ. Every observation
today fits that and nothing else does.

Mechanism: under /fp:fast, folding Blend * 2.0f to the literal 4.0f enables a
contraction in SmoothStep01's 3.0f - 2.0f*x -- one rounding instead of two --
that the runtime form cannot get.

This matters beyond the bug: an op's parameters are DATA by design, which is the
entire point of the refactor, so they can never be compile-time literals again.
The ULP difference is therefore inherent and permanent for every archetype port,
and no care in transcription will remove it. That is the real reason bit-identity
is unachievable here -- the earlier /fp:fast note named the right compiler flag
for the wrong reason.

CarveConstBlend added: identical to CarveInlined except Blend is a compile-time
constant. Predicted to match the verbatim 5000/5000 and differ from the runtime
form on exactly 126.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 14:53:02 +02:00
parent 34f8ca7953
commit 7189d51b7b
2 changed files with 99 additions and 14 deletions
+49
View File
@@ -631,3 +631,52 @@ translation unit, once `FORCEINLINE` and once `FORCENOINLINE`.
inputs are proven bit-identical, so only the evaluation can differ.
---
## 2026-07-27 — the variable is COMPILE-TIME CONSTANT vs RUNTIME DATA. One line left to confirm.
**The inlining experiment partitioned the measurements perfectly, just not the way it was framed:**
```
inlined carve != FORCENOINLINE carve : 0 <-- inlining is NOT the variable
FORCENOINLINE == operator stack : 5000/5000 <-- test-TU carve == other-TU op, ALWAYS
inlined == verbatim : 4874/5000 <-- 126 differ, IN THE SAME TU
```
The test-TU carve matches the operator stack **in a different TU** perfectly, yet disagrees with the
verbatim **in its own TU**. So neither the TU boundary nor inlining is the variable. Sorting the five
implementations by the one remaining difference:
| Implementation | `Blend` is | Group |
|---|---|---|
| `GetMazeDensity` (A) | `const float Blend = 2.0f` | **compile-time constant** |
| `MazeCoreVerbatim` (C) | `const float Blend = 2.0f` | **compile-time constant** |
| `FSdfCarveOp` (B) | a class member | **runtime data** |
| `CarveInlined` | a parameter | **runtime data** |
| `CarveNoInline` | a parameter | **runtime data** |
A == C. B == CarveInlined == CarveNoInline. The two groups differ. **Every single observation from
today fits that split, and nothing else does.**
**Mechanism:** under `/fp:fast`, folding `Blend * 2.0f` to the literal `4.0f` at compile time enables
a contraction in `SmoothStep01`'s `3.0f - 2.0f*x` — one rounding instead of two — that the runtime
form cannot get. ~1 ULP.
**Why this matters far beyond the bug:** an operator's parameters are **data by design** — that is
the entire point of the refactor. They can never go back to being compile-time literals. So this
ULP-level difference is **inherent and permanent** for every archetype port, and no amount of care in
transcription will remove it. That is the real, precise reason bit-identity is unachievable here —
not the vague `/fp:fast` hand-wave I put in the docs earlier, which happened to name the right
compiler flag for the wrong reason.
**Confirming line added:** `CarveConstBlend` — identical to `CarveInlined` except `Blend` is a
compile-time constant. Predicted: matches the verbatim 5000/5000, differs from the runtime form on
exactly 126.
**UNVERIFIED:** that prediction.
**Next single action:** rebuild, read `CARVE VARIABLE ISOLATION`. If it lands as predicted: correct
`OPSTACK-PLAN §2.6`, `AUDIT §C9` and the test's INFO text with the real reason, delete the diagnostic
scaffolding, and **resume Phase 1 step 3** — the port is proven correct (SDF exact on 126/126, only
the final rounding differs, 0 isosurface crossings).
---
@@ -169,6 +169,35 @@ namespace
return InDensity - Carve * Base * 2.0f;
}
/**
* LA DERNIÈRE VARIABLE. Identique à `CarveInlined` à UNE chose près : `Blend` est ici une
* CONSTANTE DE COMPILATION, comme dans `GetMazeDensity` et dans la copie verbatim — au lieu
* d'être une donnée d'exécution comme dans `FSdfCarveOp` (un membre) ou `CarveInlined` (un
* paramètre).
*
* L'expérience d'inlining a partitionné les mesures exactement ainsi :
* A (GetMazeDensity) == C (verbatim) → tous deux Blend CONSTANT
* B (FSdfCarveOp) == CarveInlined == CarveNoInline → tous trois Blend À L'EXÉCUTION
* et les deux groupes diffèrent. Sous /fp:fast, replier `Blend * 2.0f` en `4.0f` à la
* compilation autorise une contraction que la forme à l'exécution n'obtient pas.
*
* Si cette fonction colle au verbatim 5000/5000 ET diffère de `CarveInlined` sur 126, la cause
* est établie sans ambiguïté — et elle est INHÉRENTE à la pile d'opérateurs, dont les
* paramètres sont par construction des données et non des littéraux.
*
* The last variable: identical to CarveInlined except Blend is a COMPILE-TIME CONSTANT. If this
* matches the verbatim 5000/5000 and differs from CarveInlined on 126, the cause is settled —
* and it is INHERENT to the op stack, whose parameters are data by design.
*/
FORCEINLINE float CarveConstBlend(float Sdf, float Base, float InDensity)
{
const float Blend = 2.0f;
if (Sdf >= Blend) { return InDensity; }
float Carve = FMath::Clamp((Blend - Sdf) / (Blend * 2.0f), 0.0f, 1.0f);
Carve = SmoothStep01(Carve);
return InDensity - Carve * Base * 2.0f;
}
/** Les params Maze de la strate Maze de la fixture, bornes Z de runtime comprises. */
bool ResolveMazeParams(const VoxelForgeTest::FTestWorld& World, FMazeGenerationParams& Out,
int32& OutTopVoxelZ, int32& OutBottomVoxelZ)
@@ -424,34 +453,41 @@ bool FVoxelForgeOpStackMazeTest::RunTest(const FString& Parameters)
// ── L'expérience décisive : inline vs FORCENOINLINE, même unité, même source. ──
int32 InlineVsNoInline = 0, NoInlineMatchesStack = 0, InlineMatchesVerbatim = 0;
int32 ConstMatchesVerbatim = 0, ConstVsRuntimeBlend = 0;
for (int32 i = 0; i < N; ++i)
{
const float PX = (float)Points[i].X, PY = (float)Points[i].Y, PZ = (float)Points[i].Z;
const FVoxelOpSample S = CoreStack.EvalSample(PX, PY, PZ);
const float Inl = -CarveInlined(S.Sdf, 2.0f, Core.BaseDensity, Core.BaseDensity);
const float Noi = -CarveNoInline(S.Sdf, 2.0f, Core.BaseDensity, Core.BaseDensity);
const float Cst = -CarveConstBlend(S.Sdf, Core.BaseDensity, Core.BaseDensity);
const float Ver = MazeCoreVerbatim(PX, PY, PZ, Core, World.Settings->Seed);
const float Stk = -S.Density;
if (!BitEqual(Inl, Noi)) { ++InlineVsNoInline; }
if (BitEqual(Noi, Stk)) { ++NoInlineMatchesStack; }
if (BitEqual(Inl, Ver)) { ++InlineMatchesVerbatim; }
if (BitEqual(Cst, Ver)) { ++ConstMatchesVerbatim; }
if (!BitEqual(Cst, Inl)) { ++ConstVsRuntimeBlend; }
}
AddInfo(FString::Printf(
TEXT("INLINING EXPERIMENT (%d samples, same TU, same source, only inlining differs):\n")
TEXT(" inlined carve != FORCENOINLINE carve : %d\n")
TEXT(" FORCENOINLINE == operator stack : %d / %d\n")
TEXT(" inlined == verbatim : %d / %d\n")
TEXT(" IF the first number is nonzero, the cause is FLOATING-POINT CONTRACTION under\n")
TEXT(" /fp:fast, not a logic error: SmoothStep01 is x*x*(3-2x), and 3.0f - 2.0f*x is\n")
TEXT(" exactly the shape MSVC fuses into an FMA (one rounding instead of two, ~1 ULP).\n")
TEXT(" A and C are straight-line inlined code; the operator stack goes through a\n")
TEXT(" VIRTUAL call, so FSdfCarveOp::Eval is compiled out-of-line and gets a different\n")
TEXT(" contraction decision. The earlier three-way tested the TU boundary, which is the\n")
TEXT(" WRONG VARIABLE -- this tests the right one.\n")
TEXT(" IF the first number is zero, contraction is NOT it and the operator stack has a\n")
TEXT(" real logic bug that survives every reading so far."),
N, InlineVsNoInline, NoInlineMatchesStack, N, InlineMatchesVerbatim, N));
TEXT("CARVE VARIABLE ISOLATION (%d samples, ALL in this one translation unit):\n")
TEXT(" inlined != FORCENOINLINE : %d (inlining is not the variable)\n")
TEXT(" FORCENOINLINE == operator stack : %d / %d\n")
TEXT(" runtime-Blend == verbatim : %d / %d\n")
TEXT(" CONST-Blend == verbatim : %d / %d <-- the tell\n")
TEXT(" CONST-Blend != runtime-Blend : %d\n")
TEXT(" The three carve forms are character-identical apart from ONE thing: whether\n")
TEXT(" `Blend` is a compile-time constant (GetMazeDensity, verbatim) or runtime data\n")
TEXT(" (FSdfCarveOp holds it as a member; the parameter versions above mimic that).\n")
TEXT(" If CONST matches the verbatim and differs from runtime, the cause is settled:\n")
TEXT(" under /fp:fast, folding `Blend * 2.0f` to 4.0f at compile time enables a\n")
TEXT(" contraction in SmoothStep01's `3.0f - 2.0f*x` that the runtime form cannot get.\n")
TEXT(" That is ~1 ULP, and it is INHERENT to the operator stack: an op's parameters\n")
TEXT(" are DATA by design, so they can never be compile-time constants again. Nothing\n")
TEXT(" to fix in the port -- this is the true, permanent floor for every archetype."),
N, InlineVsNoInline, NoInlineMatchesStack, N,
InlineMatchesVerbatim, N, ConstMatchesVerbatim, N, ConstVsRuntimeBlend));
}
MutableGen->OriginSpineRadius = SavedSpine;