test: three-way says the fault is mine — compare the SDF channels directly
A vs C = 0 differ. Identical source in two different translation units produces
identical results, so the compiler was never the cause and the operator stack
differs for a logic reason. That kills the /fp:fast story for the fourth time
running, and for the first time points at code I own.
Reading has failed three times: the ctor clamps, cell FloorToInt, NodeCenter,
EdgeOpen's hashes and salts, the {-1,0}^3 sweep and its add order, the capsule
fold, and the carve are all identical to the verbatim copy line by line. So stop
reading and measure one level deeper.
MazeCoreVerbatim now optionally returns its SDF and edge count, and the three-way
compares SDF channels directly instead of inferring from densities:
SDF identical, density differs -> fault is in FSdfCarveOp
SDF differs -> fault is in FLatticeCorridorSource
It reports the split across all 126 mismatches and dumps the first one in hex
with the verbatim edge count, so a differing edge SET (a cache-key bug) shows up
as a count mismatch rather than needing to be inferred.
Docs to walk back once the cause is known, listed in OPSTACK-PROGRESS so they are
corrected once with the right explanation: OPSTACK-PLAN 2.6's "bit-identity is
unachievable" note, AUDIT C9's first consequence, and this test's own INFO text.
C9's second half -- that UBT's FP default differs by toolchain and the MP model
assumes bit-reproducible terrain -- stands; it came from the engine source, not
from this test.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -537,3 +537,55 @@ diagnostic-only and comes out once the answer is in.
|
||||
the THREE-WAY block. Phase 1 step 3 still paused.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-27 — THREE-WAY VERDICT: the fault is MINE, in the operator stack.
|
||||
|
||||
```
|
||||
A generator TU vs B opstack TU : 126 differ
|
||||
A generator TU vs C test TU : 0 differ <-- identical source, different TU, SAME result
|
||||
B opstack TU vs C test TU : 126 differ
|
||||
```
|
||||
|
||||
**A == C settles it: the source is stable across translation units.** So the compiler was never the
|
||||
cause, and the operator stack differs for a **logic** reason. Fourth hypothesis dead — but this one
|
||||
points at code I own, which is the first time the answer has been actionable.
|
||||
|
||||
**Correction to walk back in the docs** (not yet done — do it once the cause is known, so it is
|
||||
corrected with the right explanation rather than twice):
|
||||
- `OPSTACK-PLAN §2.6`'s green note claims bit-identity is unachievable because of `/fp:fast`.
|
||||
**False.** `A == C` proves identical source reproduces exactly across TUs here.
|
||||
- `AUDIT-2026-07.md §C9`'s *first* consequence ("refactors cannot be bit-identical") is likewise
|
||||
false and must go. **C9's second half stands** — UBT's FP default genuinely differs by toolchain,
|
||||
read straight out of `VCToolChain.cs` / `ClangToolChain.cs`, and the MP model does assume
|
||||
bit-reproducible terrain. That half was never inferred from this test.
|
||||
- The test's own INFO text ("this is the expected floor... /fp:fast") is wrong for the same reason
|
||||
and gets rewritten with the real cause.
|
||||
|
||||
**Also learned, and worth keeping:** setting `FPSemantics` on VoxelForge costs the module the
|
||||
engine's shared PCH and exposes ~30 missing includes across seven files. Recorded in `Build.cs`.
|
||||
|
||||
### Where the fault is NOT
|
||||
|
||||
Read line by line against the verbatim copy, all identical: the ctor's `FMath::Max` clamps, the
|
||||
cell `FloorToInt`, `NodeCenter`, `EdgeOpen`'s hashes and salts, the `{-1,0}³` sweep and its add
|
||||
order, the capsule loop, the `FMath::Min` fold, the carve's clamp/smoothstep/subtract, and the four
|
||||
structural-post no-ops. Three readings said "identical" and the measurement disagrees, so **reading
|
||||
is not going to find it** — hence more instrument, less staring.
|
||||
|
||||
### The instrument now in place
|
||||
|
||||
`MazeCoreVerbatim` optionally returns its **SDF** and edge count, and the three-way compares the SDF
|
||||
channels directly instead of inferring from densities:
|
||||
|
||||
- **SDF identical, density differs** ⇒ fault is in `FSdfCarveOp`.
|
||||
- **SDF differs** ⇒ fault is in `FLatticeCorridorSource` (edge set or capsule fold).
|
||||
|
||||
It also reports the split across all 126 mismatches, and dumps the first one with raw hex plus the
|
||||
verbatim edge count — so if the edge SETS differ (a cache-key bug) that shows up as a count mismatch
|
||||
immediately.
|
||||
|
||||
**UNVERIFIED:** the instrumentation.
|
||||
|
||||
**Next single action:** rebuild, read `FIRST B-vs-C MISMATCH`. It names the file to open.
|
||||
|
||||
---
|
||||
|
||||
@@ -68,7 +68,8 @@ namespace
|
||||
* passages omis), parce que c'est là que le bisect a montré l'écart survivre.
|
||||
*/
|
||||
float MazeCoreVerbatim(float WorldX, float WorldY, float WorldZ,
|
||||
const FMazeGenerationParams& Params, int32 Seed)
|
||||
const FMazeGenerationParams& Params, int32 Seed,
|
||||
float* OutSdf = nullptr, int32* OutNumEdges = nullptr)
|
||||
{
|
||||
const float CS = FMath::Max(Params.CellSize, 1.0f);
|
||||
const FVector Pos(WorldX, WorldY, WorldZ);
|
||||
@@ -116,6 +117,9 @@ namespace
|
||||
MazeSDF = FMath::Min(MazeSDF, VoxelSDF::Capsule(Pos, E.A, E.B, R));
|
||||
}
|
||||
|
||||
if (OutSdf) { *OutSdf = MazeSDF; }
|
||||
if (OutNumEdges) { *OutNumEdges = Edges.Num(); }
|
||||
|
||||
// Rugosité omise volontairement (variante du bisect).
|
||||
const float Blend = 2.0f;
|
||||
if (MazeSDF < Blend)
|
||||
@@ -336,15 +340,50 @@ bool FVoxelForgeOpStackMazeTest::RunTest(const FString& Parameters)
|
||||
|
||||
const int32 N = FMath::Min(NumMazeSamples, 5000);
|
||||
int32 DiffAB = 0, DiffAC = 0, DiffBC = 0;
|
||||
int32 SdfDiffers = 0, SdfSame_DensityDiffers = 0, FirstBad = -1;
|
||||
for (int32 i = 0; i < N; ++i)
|
||||
{
|
||||
const float X = (float)Points[i].X, Y = (float)Points[i].Y, Z = (float)Points[i].Z;
|
||||
|
||||
float VerbSdf = 0.0f; int32 VerbEdges = 0;
|
||||
const float A = MutableGen->GetMazeDensity(X, Y, Z, Core);
|
||||
const float B = CoreStack.EvalMC(X, Y, Z);
|
||||
const float C = MazeCoreVerbatim(X, Y, Z, Core, World.Settings->Seed);
|
||||
const FVoxelOpSample BS = CoreStack.EvalSample(X, Y, Z);
|
||||
const float B = -BS.Density;
|
||||
const float C = MazeCoreVerbatim(X, Y, Z, Core, World.Settings->Seed, &VerbSdf, &VerbEdges);
|
||||
|
||||
if (!BitEqual(A, B)) { ++DiffAB; }
|
||||
if (!BitEqual(A, C)) { ++DiffAC; }
|
||||
if (!BitEqual(B, C)) { ++DiffBC; }
|
||||
if (!BitEqual(B, C))
|
||||
{
|
||||
++DiffBC;
|
||||
if (FirstBad < 0) { FirstBad = i; }
|
||||
// LA question, posée directement au lieu d'être déduite d'une densité :
|
||||
// les deux SDF sont-ils identiques ? Si oui, la faute est dans le carve.
|
||||
if (BitEqual(BS.Sdf, VerbSdf)) { ++SdfSame_DensityDiffers; } else { ++SdfDiffers; }
|
||||
}
|
||||
}
|
||||
|
||||
if (FirstBad >= 0)
|
||||
{
|
||||
const float X = (float)Points[FirstBad].X, Y = (float)Points[FirstBad].Y, Z = (float)Points[FirstBad].Z;
|
||||
float VerbSdf = 0.0f; int32 VerbEdges = 0;
|
||||
const float C = MazeCoreVerbatim(X, Y, Z, Core, World.Settings->Seed, &VerbSdf, &VerbEdges);
|
||||
const FVoxelOpSample BS = CoreStack.EvalSample(X, Y, Z);
|
||||
const float BMC = -BS.Density;
|
||||
auto Bits = [](float V) { return *reinterpret_cast<const uint32*>(&V); };
|
||||
AddInfo(FString::Printf(
|
||||
TEXT("FIRST B-vs-C MISMATCH at (%.0f, %.0f, %.0f):\n")
|
||||
TEXT(" SDF stack %.9g [0x%08X] verbatim %.9g [0x%08X] %s\n")
|
||||
TEXT(" MC stack %.9g [0x%08X] verbatim %.9g [0x%08X]\n")
|
||||
TEXT(" verbatim edge count %d - CellSize %.9g - CorridorRadius %.9g - BaseDensity %.9g\n")
|
||||
TEXT(" across all mismatches: SDF differs %d, SDF identical but density differs %d"),
|
||||
X, Y, Z,
|
||||
BS.Sdf, Bits(BS.Sdf), VerbSdf, Bits(VerbSdf),
|
||||
BitEqual(BS.Sdf, VerbSdf) ? TEXT("<- SDF IDENTICAL, fault is in the CARVE")
|
||||
: TEXT("<- SDF DIFFERS, fault is in the lattice/capsule"),
|
||||
BMC, Bits(BMC), C, Bits(C),
|
||||
VerbEdges, Core.CellSize, Core.CorridorRadius, Core.BaseDensity,
|
||||
SdfDiffers, SdfSame_DensityDiffers));
|
||||
}
|
||||
|
||||
MutableGen->OriginSpineRadius = SavedSpine;
|
||||
|
||||
Reference in New Issue
Block a user