diag(render): Phase A8.F — portal-frame visual-gate triage apparatus
Env-gated diagnostics (off by default; do not affect the default game): - ACDREAM_A8_DUMP_PV=1: PortalVisibilityBuilder dumps local→NDC→clipped portal geometry + OutsideView poly count for the first 2 Build calls per camera cell. - ACDREAM_PROBE_ENVCELL=1: [opaque] line dumps the opaque cell-render stats (cells/tris) BEFORE the per-cell transparent loop overwrites _envCellRenderer.Stats. Used to diagnose the A8.F visual-gate failure (see handoff doc). Gated behind ACDREAM_A8_INDOOR_BRANCH=1 like the rest of the indoor branch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
452ee5b9a1
commit
7c3ee438bd
2 changed files with 42 additions and 0 deletions
|
|
@ -11069,6 +11069,9 @@ public sealed class GameWindow : IDisposable
|
||||||
currentEnvCellIds.Add(id);
|
currentEnvCellIds.Add(id);
|
||||||
gl.Disable(EnableCap.Blend);
|
gl.Disable(EnableCap.Blend);
|
||||||
_envCellRenderer!.Render(AcDream.App.Rendering.Wb.WbRenderPass.Opaque, currentEnvCellIds);
|
_envCellRenderer!.Render(AcDream.App.Rendering.Wb.WbRenderPass.Opaque, currentEnvCellIds);
|
||||||
|
// TEMP A8.F triage (strip after): opaque wall-render stats BEFORE the transparent loop overwrites them.
|
||||||
|
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeEnvCellEnabled && _a8OpaqueDumped.Add(cameraCell.CellId))
|
||||||
|
Console.WriteLine($"[opaque] camCell=0x{cameraCell.CellId:X8} cells={_envCellRenderer.Stats.CellsRendered} tris={_envCellRenderer.Stats.TrianglesDrawn} filterCnt={currentEnvCellIds.Count}");
|
||||||
|
|
||||||
// Phase A8.F (#2): translucent cell geometry clipped to each cell's portal-chain
|
// Phase A8.F (#2): translucent cell geometry clipped to each cell's portal-chain
|
||||||
// region (stencil BIT 2). Opaque cells already clip correctly via depth (left
|
// region (stencil BIT 2). Opaque cells already clip correctly via depth (left
|
||||||
|
|
@ -11378,6 +11381,9 @@ public sealed class GameWindow : IDisposable
|
||||||
|
|
||||||
private readonly HashSet<(uint cellId, ulong gfxObjId)> _phaseA8AuditLogged = new();
|
private readonly HashSet<(uint cellId, ulong gfxObjId)> _phaseA8AuditLogged = new();
|
||||||
|
|
||||||
|
// TEMP A8.F triage (strip after): one-shot-per-camCell guard for the [opaque] wall-render probe.
|
||||||
|
private static readonly System.Collections.Generic.HashSet<uint> _a8OpaqueDumped = new();
|
||||||
|
|
||||||
private void EmitEnvCellProbe(int ourBldgs, int otherBldgs, int filterCnt)
|
private void EmitEnvCellProbe(int ourBldgs, int otherBldgs, int filterCnt)
|
||||||
{
|
{
|
||||||
if (!AcDream.Core.Rendering.RenderingDiagnostics.ProbeEnvCellEnabled) return;
|
if (!AcDream.Core.Rendering.RenderingDiagnostics.ProbeEnvCellEnabled) return;
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,12 @@ public static class PortalVisibilityBuilder
|
||||||
private const int MaxReprocessPerCell = 4;
|
private const int MaxReprocessPerCell = 4;
|
||||||
private const float PortalSideEpsilon = 0.01f; // matches CellVisibility.PointInCellEpsilon
|
private const float PortalSideEpsilon = 0.01f; // matches CellVisibility.PointInCellEpsilon
|
||||||
|
|
||||||
|
// TEMP diagnostic (Phase A8.F visual-gate triage; strip after): ACDREAM_A8_DUMP_PV=1 dumps the
|
||||||
|
// local→NDC→clipped portal geometry for the first 2 Build calls per distinct camera cell.
|
||||||
|
private static readonly bool s_pvDump =
|
||||||
|
Environment.GetEnvironmentVariable("ACDREAM_A8_DUMP_PV") == "1";
|
||||||
|
private static readonly Dictionary<uint, int> s_pvDumpCount = new();
|
||||||
|
|
||||||
/// <param name="lookup">Resolve a full cell id to its LoadedCell, or null if not loaded.</param>
|
/// <param name="lookup">Resolve a full cell id to its LoadedCell, or null if not loaded.</param>
|
||||||
/// <param name="buildingMembership">Optional: true if a cell id is in the camera building's cell
|
/// <param name="buildingMembership">Optional: true if a cell id is in the camera building's cell
|
||||||
/// set. When provided, a neighbour OUTSIDE the set routes to CrossBuildingViews instead of
|
/// set. When provided, a neighbour OUTSIDE the set routes to CrossBuildingViews instead of
|
||||||
|
|
@ -64,6 +70,18 @@ public static class PortalVisibilityBuilder
|
||||||
var queue = new Queue<LoadedCell>();
|
var queue = new Queue<LoadedCell>();
|
||||||
queue.Enqueue(cameraCell);
|
queue.Enqueue(cameraCell);
|
||||||
|
|
||||||
|
bool pvDump = false;
|
||||||
|
if (s_pvDump)
|
||||||
|
{
|
||||||
|
lock (s_pvDumpCount)
|
||||||
|
{
|
||||||
|
s_pvDumpCount.TryGetValue(cameraCell.CellId, out int dc);
|
||||||
|
if (dc < 2) { s_pvDumpCount[cameraCell.CellId] = dc + 1; pvDump = true; }
|
||||||
|
}
|
||||||
|
if (pvDump)
|
||||||
|
Console.WriteLine($"[pv-dump] camCell=0x{cameraCell.CellId:X8} portals={cameraCell.Portals.Count} polyLists={cameraCell.PortalPolygons.Count} vp[M11={viewProj.M11:F3} M22={viewProj.M22:F3} M33={viewProj.M33:F3} M34={viewProj.M34:F3} M43={viewProj.M43:F3} M44={viewProj.M44:F3}]");
|
||||||
|
}
|
||||||
|
|
||||||
while (queue.Count > 0)
|
while (queue.Count > 0)
|
||||||
{
|
{
|
||||||
var cell = queue.Dequeue();
|
var cell = queue.Dequeue();
|
||||||
|
|
@ -80,15 +98,21 @@ public static class PortalVisibilityBuilder
|
||||||
var poly = cell.PortalPolygons[i];
|
var poly = cell.PortalPolygons[i];
|
||||||
if (poly == null || poly.Length < 3) continue;
|
if (poly == null || poly.Length < 3) continue;
|
||||||
|
|
||||||
|
bool dx = pvDump && cell.Portals[i].OtherCellId == 0xFFFF;
|
||||||
|
|
||||||
// Portal-side test: only traverse a portal the camera is on the interior side of
|
// Portal-side test: only traverse a portal the camera is on the interior side of
|
||||||
// (mirrors CellVisibility.GetVisibleCells + retail's 'seen' flag). Culls back-facing
|
// (mirrors CellVisibility.GetVisibleCells + retail's 'seen' flag). Culls back-facing
|
||||||
// portals so we never feed a degenerate/wrong-facing projection downstream.
|
// portals so we never feed a degenerate/wrong-facing projection downstream.
|
||||||
if (i < cell.ClipPlanes.Count && !CameraOnInteriorSide(cell, i, cameraPos))
|
if (i < cell.ClipPlanes.Count && !CameraOnInteriorSide(cell, i, cameraPos))
|
||||||
|
{
|
||||||
|
if (dx) Console.WriteLine($"[pv-dump] EXIT-CULLED(side) cell=0x{cell.CellId:X8} p{i} localN={poly.Length} hasClipPlane={(i < cell.ClipPlanes.Count)}");
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Project to NDC, then normalize to CCW for the CCW-only ScreenPolygonClip
|
// Project to NDC, then normalize to CCW for the CCW-only ScreenPolygonClip
|
||||||
// (ProjectToNdc preserves input winding; portal dat polygons may be CW).
|
// (ProjectToNdc preserves input winding; portal dat polygons may be CW).
|
||||||
Vector2[] portalNdc = PortalProjection.ProjectToNdc(poly, cell.WorldTransform, viewProj);
|
Vector2[] portalNdc = PortalProjection.ProjectToNdc(poly, cell.WorldTransform, viewProj);
|
||||||
|
if (dx) Console.WriteLine($"[pv-dump] EXIT-PROJ cell=0x{cell.CellId:X8} p{i} localN={poly.Length} ndcN={portalNdc.Length} local0=({poly[0].X:F2},{poly[0].Y:F2},{poly[0].Z:F2}) ndc=[{string.Join(" ", System.Array.ConvertAll(portalNdc, v => $"({v.X:F2},{v.Y:F2})"))}]");
|
||||||
if (portalNdc.Length < 3) continue;
|
if (portalNdc.Length < 3) continue;
|
||||||
EnsureCcw(portalNdc);
|
EnsureCcw(portalNdc);
|
||||||
|
|
||||||
|
|
@ -99,12 +123,21 @@ public static class PortalVisibilityBuilder
|
||||||
var clipped = ScreenPolygonClip.Intersect(portalNdc, vp.Vertices);
|
var clipped = ScreenPolygonClip.Intersect(portalNdc, vp.Vertices);
|
||||||
if (clipped.Length >= 3) clippedRegion.Add(new ViewPolygon(clipped));
|
if (clipped.Length >= 3) clippedRegion.Add(new ViewPolygon(clipped));
|
||||||
}
|
}
|
||||||
|
if (dx) Console.WriteLine($"[pv-dump] EXIT-CLIP cell=0x{cell.CellId:X8} p{i} currentViewPolys={currentView.Polygons.Count} clipResult={clippedRegion.Count}");
|
||||||
if (clippedRegion.Count == 0) continue; // portal not visible through this chain
|
if (clippedRegion.Count == 0) continue; // portal not visible through this chain
|
||||||
|
|
||||||
var portal = cell.Portals[i];
|
var portal = cell.Portals[i];
|
||||||
|
|
||||||
if (portal.OtherCellId == 0xFFFF)
|
if (portal.OtherCellId == 0xFFFF)
|
||||||
{
|
{
|
||||||
|
if (pvDump)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[pv-dump] EXIT cell=0x{cell.CellId:X8} p{i} localN={poly.Length} ndcN={portalNdc.Length} clipPolys={clippedRegion.Count}");
|
||||||
|
Console.WriteLine($"[pv-dump] local=[{string.Join(" ", System.Array.ConvertAll(poly, v => $"({v.X:F2},{v.Y:F2},{v.Z:F2})"))}]");
|
||||||
|
Console.WriteLine($"[pv-dump] ndc=[{string.Join(" ", System.Array.ConvertAll(portalNdc, v => $"({v.X:F3},{v.Y:F3})"))}]");
|
||||||
|
foreach (var cp in clippedRegion)
|
||||||
|
Console.WriteLine($"[pv-dump] clipped({cp.Vertices.Length})=[{string.Join(" ", System.Array.ConvertAll((Vector2[])cp.Vertices, v => $"({v.X:F3},{v.Y:F3})"))}]");
|
||||||
|
}
|
||||||
// Exit portal -> outdoors visible through this (clipped) opening.
|
// Exit portal -> outdoors visible through this (clipped) opening.
|
||||||
foreach (var cp in clippedRegion) frame.OutsideView.Add(cp);
|
foreach (var cp in clippedRegion) frame.OutsideView.Add(cp);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -135,6 +168,9 @@ public static class PortalVisibilityBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pvDump)
|
||||||
|
Console.WriteLine($"[pv-dump] OUTSIDEVIEW polys={frame.OutsideView.Polygons.Count} bfsCellViews={frame.CellViews.Count} crossBldg={frame.CrossBuildingViews.Count}");
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue