From e6fe4c611ae54a90600c6e38f8cdca96c25b5b6d Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 8 Jun 2026 12:51:48 +0200 Subject: [PATCH] =?UTF-8?q?diag(render):=20[portal-churn]=20probe=20?= =?UTF-8?q?=E2=80=94=20per-Build=20re-enqueue=20+=20reciprocal=20pre/post?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 4 summary-emit adapted from the plan: the plan's Invariant($"a" + $"b" + sb) form passes a string to FormattableString.Invariant (which requires a FormattableString) and does not compile; merged the two interpolated fragments into one literal and appended the already-invariant-formatted reciprocal detail outside the Invariant call. Same output. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Rendering/PortalVisibilityBuilder.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs b/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs index e421e3a4..52e5c364 100644 --- a/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs +++ b/src/AcDream.App/Rendering/PortalVisibilityBuilder.cs @@ -112,6 +112,12 @@ public static class PortalVisibilityBuilder var popCounts = new Dictionary(); // per-cell pop count for the MaxReprocessPerCell cap var trace = PortalBuildTrace.Start(cameraCell, cameraPos); + // [portal-churn] apparatus (2026-06-08): when ProbePortalChurnEnabled, accumulate re-enqueue churn + // + reciprocal pre/post region counts, emitted as one summary line at end of Build. Inert when off. + bool churnProbe = AcDream.Core.Rendering.RenderingDiagnostics.ProbePortalChurnEnabled; + int churnReenqueues = 0; + var churnReciprocal = churnProbe ? new System.Text.StringBuilder(256) : null; + bool pvDump = false; if (s_pvDump) { @@ -295,6 +301,9 @@ public static class PortalVisibilityBuilder var preReciprocalClip = eyeInsideOpening ? CloneViewPolygons(clippedRegion) : null; int preReciprocalCount = clippedRegion.Count; ApplyReciprocalClip(clippedRegion, portal.OtherPortalId, neighbour, viewProj); + if (churnProbe) + churnReciprocal!.Append(System.FormattableString.Invariant( + $" recip[0x{neighbourId:X8} {preReciprocalCount}->{clippedRegion.Count}]")); if (clippedRegion.Count == 0) { if (preReciprocalClip is null) @@ -324,6 +333,7 @@ public static class PortalVisibilityBuilder dist = NearestPortalVertexDistance(poly, cell.WorldTransform, cameraPos); todo.Insert(neighbour, dist); inserted = true; + if (churnProbe) churnReenqueues++; } trace?.Add($"portal cell=0x{cell.CellId:X8} p{i}->0x{neighbourId:X8} addCell polys={clippedRegion.Count} clipVerts={clipVerts} recip={preReciprocalCount}->{clippedRegion.Count} grew={grew} queued={inserted} dist={(float.IsNaN(dist) ? "na" : dist.ToString("F2"))}"); } @@ -338,6 +348,18 @@ public static class PortalVisibilityBuilder EmitFlapProbe(cameraCell, cameraPos, viewProj, frame); trace?.Emit(frame); + if (churnProbe) + { + int maxPop = 0; uint maxCell = 0; int rePopped = 0; + foreach (var kv in popCounts) + { + if (kv.Value > maxPop) { maxPop = kv.Value; maxCell = kv.Key; } + if (kv.Value > 1) rePopped++; + } + Console.WriteLine(System.FormattableString.Invariant( + $"[portal-churn] root=0x{cameraCell.CellId:X8} cells={frame.OrderedVisibleCells.Count} reEnqueues={churnReenqueues} rePoppedCells={rePopped} maxPop=0x{maxCell:X8}:{maxPop}") + churnReciprocal); + } + return frame; }