diag(render/physics): flap root-caused to physics rest µm-jitter; refute prior diagnoses

Apparatus + handoff for the indoor flap. Confirmed (primary evidence): the flap is the
portal-flood clip being µm-sensitive at the threshold, driven by a ~1-8µm jitter in the
player RenderPosition (physics resting position not bit-stable; Lerp surfaces it). REFUTES
the 2026-06-07 see-through/EnvCell/outdoor-node diagnosis (ModelId GfxObj 0x01000A2B IS the
solid exterior) AND an enqueue-once attempt (retail propagates late slices via AddToCell;
the existing PropagatesNewSlicesToExit test caught it; reverted). Adds: Build determinism
test, A8CellAudit gfxobj dump, [pv-input] 6dp probe + [render-sig] outRoot/bshell fields.
No functional fix shipped. Next: higher-precision physics rest trace -> port retail
kill_velocity/contact rest-stability. Canonical: docs/research/2026-06-08-flap-rootcause-physics-rest-handoff.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-08 09:16:12 +02:00
parent d0b65c4170
commit d6aa526dd3
6 changed files with 300 additions and 1 deletions

View file

@ -7560,6 +7560,19 @@ public sealed class GameWindow : IDisposable
clipAssembly = pviewResult.ClipAssembly;
envCellShellFilter = pviewResult.DrawableCells;
_interiorPartition = pviewResult.Partition;
// Flap root-cause apparatus (2026-06-07): per-frame, the EXACT Build inputs at 6 dp +
// the resulting flood count. The live flap shows flood flipping 2↔6 at an eye/player
// identical to cm; this answers whether the inputs differ sub-cm (jitter) or are
// byte-identical (nondeterminism). See RenderingDiagnostics.ProbePvInputEnabled.
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbePvInputEnabled && pvFrame is not null)
{
var vp = envCellViewProj;
char pvOutRoot = ReferenceEquals(clipRoot, _outdoorNode) ? 'Y' : 'n';
Console.WriteLine(System.FormattableString.Invariant(
$"[pv-input] outRoot={pvOutRoot} flood={pvFrame.OrderedVisibleCells.Count} eye=({camPos.X:F6},{camPos.Y:F6},{camPos.Z:F6}) player=({playerViewPos.X:F6},{playerViewPos.Y:F6},{playerViewPos.Z:F6}) vp=[{vp.M11:F6} {vp.M13:F6} {vp.M22:F6} {vp.M31:F6} {vp.M33:F6} {vp.M41:F6} {vp.M42:F6} {vp.M43:F6}]"));
}
sigPvFrame = pviewResult.PortalFrame;
sigClipAssembly = pviewResult.ClipAssembly;
sigDrawableCells = pviewResult.DrawableCells;
@ -9190,6 +9203,22 @@ public sealed class GameWindow : IDisposable
sb.Append(" outdoorRootObjs=").Append(outdoorRootObjectCount);
sb.Append(" liveDynDraw=").Append(liveDynamicDrawnCount);
// Diagnosis 2026-06-07: draw-vs-occlude probe for the see-through residual.
// outRoot=Y means clipRoot is the synthetic outdoor node (eye outdoors). bshell=total/withMesh
// counts the building ModelId exterior shells queued in partition.Outdoor for this frame — the
// GfxObj exteriors that SHOULD draw the solid walls. Correlate with ids= (the flooded interior
// cells): if bshell=N/N and ids=[node only] but the wall is still see-through, the exterior is
// failing to rasterize (draw/clip bug, not EnvCell sidedness); if ids includes interior cells,
// the outdoor flood is drawing interiors over the exterior.
sb.Append(" outRoot=").Append(ReferenceEquals(clipRoot, _outdoorNode) ? 'Y' : 'n');
if (partition is not null)
{
int shellTotal = 0, shellMesh = 0;
foreach (var e in partition.Outdoor)
if (e.IsBuildingShell) { shellTotal++; if (e.MeshRefs.Count > 0) shellMesh++; }
sb.Append(" bshell=").Append(shellTotal).Append('/').Append(shellMesh);
}
if (outdoorPortalDrawn || exteriorPvFrame is not null || exteriorClipAssembly is not null)
{
sb.Append(" extPortal=").Append(outdoorPortalDrawn ? 'Y' : 'n');

View file

@ -129,6 +129,20 @@ public static class RenderingDiagnostics
public static bool ProbeShellEnabled { get; set; } =
Environment.GetEnvironmentVariable("ACDREAM_PROBE_SHELL") == "1";
/// <summary>
/// Flap root-cause apparatus (2026-06-07). When true, the indoor render path emits ONE
/// <c>[pv-input]</c> line per frame with the EXACT PortalVisibilityBuilder.Build inputs at HIGH
/// precision (camera eye + player position to 6 dp, plus orientation-sensitive view-projection
/// elements) alongside the resulting flood cell count. The live flap shows the flood set flipping
/// 2↔6 at an eye/player that is identical to cm; this probe answers whether the Build INPUTS differ
/// below cm precision (sub-cm view jitter → robustness fix) or are byte-identical while the output
/// still flips (nondeterminism → surgical bug). Runs WITHOUT the heavy <c>[flap]</c>/<c>[render-sig]</c>
/// spam so the log stays diffable. Throwaway apparatus — strip once the jitter source is pinned.
/// Initial state from <c>ACDREAM_PROBE_PVINPUT=1</c>.
/// </summary>
public static bool ProbePvInputEnabled { get; set; } =
Environment.GetEnvironmentVariable("ACDREAM_PROBE_PVINPUT") == "1";
// Cell-change gate for EmitVis. The probe fires once per distinct root cell
// so launch.log stays readable under motion (the per-frame call is a no-op
// when the root is unchanged). Sentinel 0 = "no root yet" — the first real