diff --git a/docs/launch-options.md b/docs/launch-options.md index 5230ae6e..22a56fc9 100644 --- a/docs/launch-options.md +++ b/docs/launch-options.md @@ -315,7 +315,7 @@ issue is closed, the strip was missed; delete both. | `ACDREAM_PROBE_CHILD_CELL` | c4 route 7 | `=1` | gates one `[child-cell]` line per Runtime committed-child canonical-cell write in `RuntimeLiveEntitySessionController`, `RuntimeEntityObjectLifetime`, `RuntimeEntityDirectory` (parent/child guid, old/new cell, cause tag) | print-only | `PhysicsDiagnostics.ProbeChildCellEnabled` | | `ACDREAM_PROBE_CLIPROUTE` | "throwaway apparatus — strip once §4 ships" | `=1` | print-on-change `[clip-route]` / `[clip-route-disp]` / `[clip-route-scis]` lines: outside-slice clip routing, region-SSBO bytes, terrain-UBO head, actual GL/RHI scissor state | print-only | `RenderingDiagnostics.ProbeClipRouteEnabled` | | `ACDREAM_PROBE_WALK_SHADOW` | Campaign FW3.2b-2 flip apparatus — dies with the flip commit | `=1` | runs the PRODUCTION retail frame walk per frame in SHADOW (no draws change) and prints one `[walk-shadow]` line per frame whose visited cell/building sets diverge from the old visibility path (the I5 dual-shadow pattern applied to the FW3 static cutover) | print-only; adds the per-frame CPU cost of the shadow walk while set | `RenderingDiagnostics.ProbeWalkShadowEnabled` | -| `ACDREAM_PROBE_WALK_ROOT` | FW3 visual-gate apparatus (stairwell-transition + cathedral-falls defects) — dies with those fixes | `=1` | after every interior/outdoor root flip, prints 8 `[walk-root]` lines (one per frame): viewer cell, root cell, camera-cell resolution, slice count, eye position, and the walk's visited cell list; also prints `[walk-part]` lines at every particle submission route (route name, owner ids, pre/post-depth-clear phase), `[walk-alpha]` lines at every retail alpha-queue drain, `[walk-dyn]` lines with each dynamic's outside-stage/viewcone verdicts (~every 120th product frame), and `[walk-portal]` lines with the walk PView's per-portal side/clip decisions (~every 90th frame) — together they pin which crossing frames root wrong, flood short, submit particles after the interior depth clear, or collapse an exit view at the portal walk | print-only; chatty near particle-heavy scenes while set | `RenderingDiagnostics.ProbeWalkRootEnabled` | +| `ACDREAM_PROBE_WALK_ROOT` | the cathedral Phase I instrumentation (synthesis doc `docs/research/2026-08-30-cathedral-synthesis.md`) — dies with that investigation | `=1` | prints `[walk-root]` rooting lines every ~30th frame AND for 8 frames after each interior/outdoor root flip (viewer/root cell, camera-cell resolution, slice count, the walk's OWN exit-view count, the TRUE root flood separated from the visited union, eye + forward); `[walk-cam]` replayable camera dumps (eye, viewport, full view-projection) every ~300 frames and at flips; `[falls]` lines on EVERY particle submission carrying a real falls owner (0xCF418000-13) with route + phase; `[falls-adm]` admission verdicts for F418 emitters (~every 120th ApplyRetailView); plus the earlier family — `[walk-part]` per-route submissions, `[walk-alpha]` drains, `[walk-dyn]` outside-stage verdicts (~120th product frame), `[walk-portal]` per-portal side/clip decisions (~90th frame) | print-only; chatty near particle-heavy scenes while set | `RenderingDiagnostics.ProbeWalkRootEnabled` | | `ACDREAM_PROBE_CONTACT_PLANE` | spike-only, 2026-05-20 | `=1` | gates one `[cp-write]` line per write to `CollisionInfo.ContactPlane*`/`LastKnownContactPlane*` fields (field, old→new, caller method via stack walk, source line); only logs on actual value changes | print-only, but performs a stack walk to identify the caller method when firing — real CPU cost per write while on (not just a string format); suppresses no-op writes to bound volume | `PhysicsDiagnostics.ProbeContactPlaneEnabled` | | `ACDREAM_PROBE_ENT` | #138 | `="1"` | Traces the persistent player entity across teleport streaming churn: presence in the render draw-set flat view vs. survival of the dynamics cull, to distinguish "missing from draw set" vs "present but culled" | print-only, "Observation-only — emits no behavior change" (doc comment). `LogPlayerDynOnChange` dedupes by transition to avoid per-frame spam. Marked STRIP-once-root-caused (like the dense-town FPS apparatus). | `EntityVanishProbe.Enabled` (`EntityVanishProbe.cs:23-24`), issue #138-B | | `ACDREAM_PROBE_FLAP` | "throwaway apparatus — strip once the flap mechanism is confirmed" | `=1` | EVERY FRAME (unthrottled, not change-gated) while the camera root is indoor: `[flap]` from `PortalVisibilityBuilder.Build` (portal side-test/traverse/cull/projection) + paired `[flap-cam]` from `PhysicsCameraCollisionProbe`/`[flap-sweep]` (FindCameraCell resolution, eye positions) | print-only, but unthrottled per-frame `StringBuilder` allocation + `Console.WriteLine` on multiple call sites while indoor — heavy log volume/allocation under sustained indoor play; does not alter rendered output | `RenderingDiagnostics.ProbeFlapEnabled` | diff --git a/src/AcDream.App/Rendering/RetailPViewPassExecutor.cs b/src/AcDream.App/Rendering/RetailPViewPassExecutor.cs index 2aaac5b9..633ba405 100644 --- a/src/AcDream.App/Rendering/RetailPViewPassExecutor.cs +++ b/src/AcDream.App/Rendering/RetailPViewPassExecutor.cs @@ -639,6 +639,29 @@ internal sealed partial class RetailPViewPassExecutor : sb.Append(id.ToString("x")); } ids = $" n={owners.Count} ids=[{sb}]"; + + // Phase I cathedral instrumentation: the REAL falls owners + // (outdoor landblock statics 0xCF418000-13 per the DAT-geometry + // report) — print EVERY submission that carries one, with the + // exact watched ids, unconditionally (never rate-limited; these + // are the money lines the six fix rounds never had). + System.Text.StringBuilder? falls = null; + foreach (uint id in owners) + { + if (id >= 0xCF418000u && id <= 0xCF418014u) + { + falls ??= new System.Text.StringBuilder(); + if (falls.Length > 0) falls.Append(','); + falls.Append(id.ToString("x")); + } + } + if (falls is not null) + { + Console.WriteLine( + $"[falls] route={route} phase=" + + $"{AcDream.Core.Rendering.RenderingDiagnostics.WalkRootPhase} " + + $"ids=[{falls}]"); + } } Console.WriteLine( $"[walk-part] {route}{ids} phase={AcDream.Core.Rendering.RenderingDiagnostics.WalkRootPhase}"); diff --git a/src/AcDream.App/Rendering/RetailPViewRenderer.cs b/src/AcDream.App/Rendering/RetailPViewRenderer.cs index d47d5778..2ca7db08 100644 --- a/src/AcDream.App/Rendering/RetailPViewRenderer.cs +++ b/src/AcDream.App/Rendering/RetailPViewRenderer.cs @@ -343,9 +343,15 @@ public sealed class RetailPViewRenderer walkExecutor!, ctx, clipAssembly, clearInteriorDepth, drawExitSeals); walkDriver = new Walk.WalkFrameDriver(walkExecutor!.Dispatcher, leafRenderer, _walkWorldData); + if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled) + { + AcDream.Core.Rendering.RenderingDiagnostics.WalkPortalProbeThisFrame = + _probeWalkRootFrame % 90 == 0; + } walkDriver.Collect( _frameWalk, ctx.ViewerCellId, walkCameraCell, walkLandscape, walkContext, ctx.ViewProjection, ctx.CameraWorldPosition); + AcDream.Core.Rendering.RenderingDiagnostics.WalkPortalProbeThisFrame = false; // FW4 slice 1: an interior root's terrain/sky/punch clip slices // come from THE WALK'S OWN outside_view — retail's one @@ -363,31 +369,55 @@ public sealed class RetailPViewRenderer viewportHeight); } + // Phase I cathedral instrumentation (synthesis §Phase I.3): the + // continuous rooting line SEPARATES the true root flood + // (InteriorFloodCells) from the visited union (root + look-ins — + // the conflation the review corpus indicted), and adds the + // walk's OWN exit-view count. [walk-cam] dumps a replayable + // camera every ~300 frames and at every root flip. if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled) { _probeWalkRootFrame++; bool outdoorNow = ctx.RootCell.IsOutdoorNode; - if (_probeWalkRootPrevOutdoor is bool prev && prev != outdoorNow) + bool flipped = _probeWalkRootPrevOutdoor is bool prev && prev != outdoorNow; + if (flipped) { _probeWalkRootFramesLeft = 8; Console.WriteLine( - $"[walk-root] ---- FLIP {(prev ? "OUT->IN" : "IN->OUT")} at frame {_probeWalkRootFrame} ----"); + $"[walk-root] ---- FLIP {(outdoorNow ? "IN->OUT" : "OUT->IN")} at frame {_probeWalkRootFrame} ----"); } _probeWalkRootPrevOutdoor = outdoorNow; + bool sample = _probeWalkRootFramesLeft > 0 || _probeWalkRootFrame % 30 == 0; if (_probeWalkRootFramesLeft > 0) - { _probeWalkRootFramesLeft--; - var cellsList = new List(walkDriver.VisitedCells); - cellsList.Sort(); - string cells = string.Join(",", cellsList.ConvertAll(c => c.ToString("x8")) - .GetRange(0, Math.Min(cellsList.Count, 10))); + if (sample) + { + List rootFlood = walkDriver.InteriorFloodCells; + string flood = string.Join(",", rootFlood + .GetRange(0, Math.Min(rootFlood.Count, 8)) + .ConvertAll(c => c.ToString("x8"))); + int walkOv = outdoorNow ? -1 : _frameWalk.InteriorOutsideView.ViewCount; + Vector3 fwd = walkContext.CyPlane.Normal; Console.WriteLine( $"[walk-root] f={_probeWalkRootFrame} out={(outdoorNow ? 1 : 0)} " + $"viewer=0x{ctx.ViewerCellId:X8} root=0x{ctx.RootCell.CellId:X8} " + $"res={ctx.CameraCellResolution} slices={clipAssembly.OutsideViewSlices.Length} " + + $"walkOv={walkOv} rootFlood={rootFlood.Count}[{flood}] " + + $"visited={walkDriver.VisitedCells.Count} bld={walkDriver.VisitedBuildings.Count} " + $"eye=({ctx.ViewerEyePos.X:F2},{ctx.ViewerEyePos.Y:F2},{ctx.ViewerEyePos.Z:F2}) " - + $"walkCells={walkDriver.VisitedCells.Count} bld={walkDriver.VisitedBuildings.Count} " - + $"cells=[{cells}]"); + + $"fwd=({fwd.X:F3},{fwd.Y:F3},{fwd.Z:F3})"); + } + if (flipped || _probeWalkRootFrame % 300 == 0) + { + Matrix4x4 vp = ctx.ViewProjection; + Console.WriteLine( + $"[walk-cam] viewer=0x{ctx.ViewerCellId:X8} " + + $"eye=({ctx.ViewerEyePos.X:R},{ctx.ViewerEyePos.Y:R},{ctx.ViewerEyePos.Z:R}) " + + $"vw={viewportWidth:R} vh={viewportHeight:R} vp=[" + + $"{vp.M11:R},{vp.M12:R},{vp.M13:R},{vp.M14:R}," + + $"{vp.M21:R},{vp.M22:R},{vp.M23:R},{vp.M24:R}," + + $"{vp.M31:R},{vp.M32:R},{vp.M33:R},{vp.M34:R}," + + $"{vp.M41:R},{vp.M42:R},{vp.M43:R},{vp.M44:R}]"); } } } diff --git a/src/AcDream.App/Rendering/Walk/WalkPView.cs b/src/AcDream.App/Rendering/Walk/WalkPView.cs index 3e5f7984..faeb9859 100644 --- a/src/AcDream.App/Rendering/Walk/WalkPView.cs +++ b/src/AcDream.App/Rendering/Walk/WalkPView.cs @@ -119,6 +119,14 @@ public sealed class WalkPView flags.InView = true; // portal polygon faces the viewer } } + if (AcDream.Core.Rendering.RenderingDiagnostics.WalkPortalProbeThisFrame) + { + Console.WriteLine( + $"[walk-portal] init cell={cell.CellId:x8} i={i} " + + $"dest={cell.Portals[i].OtherCellId:x8} d={d:F3} " + + $"pside={cell.Portals[i].PortalSide} " + + $"inview={(flags.InView ? 1 : 0)}"); + } } if (flags.InView) @@ -201,6 +209,12 @@ public sealed class WalkPView cell, portal.PortalSide, cell.PortalPolygons[portal.PolygonIndex], doClip: true, ctx, _clipScratch); + if (AcDream.Core.Rendering.RenderingDiagnostics.WalkPortalProbeThisFrame) + { + Console.WriteLine( + $"[walk-portal] clip cell={cell.CellId:x8} view={i} j={j} " + + $"dest={portal.OtherCellId:x8} n={n}"); + } if (n == 0) continue; if (portal.OtherCellId == 0xFFFFFFFFu) diff --git a/src/AcDream.Core/Vfx/ParticleSystem.cs b/src/AcDream.Core/Vfx/ParticleSystem.cs index 818587e6..07a17a30 100644 --- a/src/AcDream.Core/Vfx/ParticleSystem.cs +++ b/src/AcDream.Core/Vfx/ParticleSystem.cs @@ -321,6 +321,9 @@ public sealed class ParticleSystem : IParticleSystem rangeMultiplier = 1f; LastRetailViewEmitterVisitCount = 0; + _probeAdmissionDumpThisCall = + AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled + && ++_probeAdmissionCallCounter % 120 == 0; foreach (int handle in _worldSimulationHandles) { if (!_byHandle.TryGetValue(handle, out ParticleEmitter? emitter)) @@ -346,9 +349,33 @@ public sealed class ParticleSystem : IParticleSystem || float.IsNaN(maxDistance) || distance <= maxDistance); RefreshRenderableIndex(emitter, wasRenderable); + + // ACDREAM_PROBE_WALK_ROOT companion (Phase I cathedral + // instrumentation, throwaway): admission verdicts for the F418 + // watch set — the real falls owners 0xCF418000-13 and every + // F418-cell emitter — every ~120th ApplyRetailView pass. + if (_probeAdmissionDumpThisCall + && AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled + && ((emitter.AttachedObjectId >= 0xCF418000u + && emitter.AttachedObjectId <= 0xCF418014u) + || (emitter.OwnerCellId >> 16) == 0xF418u)) + { + Console.WriteLine( + $"[falls-adm] owner={emitter.AttachedObjectId:x} " + + $"cell={emitter.OwnerCellId:x8} " + + $"pos=({emitter.OwnerPosition.X:F1},{emitter.OwnerPosition.Y:F1},{emitter.OwnerPosition.Z:F1}) " + + $"dist={distance:F1} max={maxDistance:F1} " + + $"inSet={(visibleCellIds.Contains(emitter.OwnerCellId) ? 1 : 0)} " + + $"eligible={(emitter.ViewEligible ? 1 : 0)} " + + $"active={emitter.ActiveCount} pass={emitter.RenderPass}"); + } } } + // Phase I instrumentation rate limiter (see the probe block above). + private static uint _probeAdmissionCallCounter; + private static bool _probeAdmissionDumpThisCall; + /// /// Changes only render presentation. Logical lifetime is unaffected. ///