fix(render)+feat(diag): Phase A8 — sky-when-inside-building + per-cell audit probe
Two changes for visual-gate-#1 follow-up. After the pool aliasing fix (prior commit), walls + objects render cleanly but three residual symptoms remain: missing floor, purple wall tint, no sky through windows. This commit addresses one and adds the probe for the second. Sky fix: The blanket `!cameraInsideCell` skip of the sky pass was inherited from when the indoor-cell concept was sealed dungeons. With Phase A8's RenderInsideOutAcdream pipeline, cottages render through their portals to outside — and the user expects sky visible through windows + doorways. WB's VisibilityManager.RenderInsideOut assumes sky has already been rendered as the far-depth backdrop before stencil setup. New gate: `!cameraInsideCell || cameraInsideBuilding`. Sky renders inside cottages (building → portals), skipped inside true dungeons (no portals). The Step 4 stencil-gated outdoor pass composites terrain + scenery through portal silhouettes on top of the sky. Per-cell audit probe (ACDREAM_A8_AUDIT=1): One-shot dump per (cellId, gfxObjId) pair in the active snapshot: - renderData null/non-null status - batches count + total IndexCount - per-batch CullMode + IsTransparent + IsAdditive + bindless-handle-zero The first visual gate showed tris=135 for 18 cells — way too low if cell meshes were complete (expected ~20+ tris/cell). The audit dump will identify whether (a) some cells aren't uploading, (b) some batches have zero indices, or (c) batches' CullModes are getting them culled at typical viewing angles. Without this probe, we'd be back to speculation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
375f9a7b9b
commit
772d69c7a6
2 changed files with 81 additions and 3 deletions
|
|
@ -7257,8 +7257,18 @@ public sealed class GameWindow : IDisposable
|
|||
|
||||
// Phase G.1: sky renderer — draws the far-plane-infinity
|
||||
// celestial meshes FIRST so the rest of the scene z-tests
|
||||
// on top of them (depth mask off, no depth writes). Skipped
|
||||
// when indoors; dungeons fully block sky visibility.
|
||||
// on top of them (depth mask off, no depth writes).
|
||||
//
|
||||
// Phase A8 fix (2026-05-28 visual-gate-#1 follow-up): also
|
||||
// render sky when cameraInsideBuilding=true — cottages have
|
||||
// portals to outside, and the user expects sky visible through
|
||||
// windows + doorways. The blanket `!cameraInsideCell` skip
|
||||
// was incorrect for cottages (only correct for sealed dungeon
|
||||
// cells). When inside a building, Step 4's stencil-gated
|
||||
// outdoor pass will composite terrain + scenery through the
|
||||
// portal silhouettes; sky needs to be there as the far-depth
|
||||
// backdrop. WB's pipeline (VisibilityManager.RenderInsideOut)
|
||||
// assumes the sky pass already ran before stencil setup.
|
||||
//
|
||||
// Mirrors retail's LScape::draw at 0x00506330 which calls
|
||||
// GameSky::Draw(0) (sky pass) BEFORE the landblock DrawBlock
|
||||
|
|
@ -7267,7 +7277,8 @@ public sealed class GameWindow : IDisposable
|
|||
// cylinder 0x01004C42/0x01004C44) need to overlay terrain
|
||||
// and entities to look volumetric — see the post-scene
|
||||
// RenderWeather call further below.
|
||||
if (!cameraInsideCell)
|
||||
bool renderSky = !cameraInsideCell || cameraInsideBuilding;
|
||||
if (renderSky)
|
||||
{
|
||||
_skyRenderer?.RenderSky(camera, camPos, (float)WorldTime.DayFraction,
|
||||
_activeDayGroup, kf, environOverrideActive);
|
||||
|
|
@ -10951,6 +10962,8 @@ public sealed class GameWindow : IDisposable
|
|||
$"vao={vao} prog={prog}");
|
||||
}
|
||||
|
||||
private readonly HashSet<(uint cellId, ulong gfxObjId)> _phaseA8AuditLogged = new();
|
||||
|
||||
private void EmitEnvCellProbe(int ourBldgs, int otherBldgs, int filterCnt)
|
||||
{
|
||||
if (!AcDream.Core.Rendering.RenderingDiagnostics.ProbeEnvCellEnabled) return;
|
||||
|
|
@ -10965,6 +10978,16 @@ public sealed class GameWindow : IDisposable
|
|||
$"[envcells] cells={stats.CellsRendered} tris={stats.TrianglesDrawn} " +
|
||||
$"ourBldgs={ourBldgs} otherBldgs={otherBldgs} filterCnt={filterCnt} " +
|
||||
$"poolTotal={pool.PoolTotal} poolHwm={pool.SnapshotPoolHwm}");
|
||||
|
||||
// Phase A8 audit (visual-gate-#1 follow-up): one-shot per
|
||||
// (cellId, gfxObjId) pair dump of cell mesh batch state — to find
|
||||
// why polys (e.g., floors) might not render. Set ACDREAM_A8_AUDIT=1.
|
||||
if (string.Equals(Environment.GetEnvironmentVariable("ACDREAM_A8_AUDIT"), "1", StringComparison.Ordinal)
|
||||
&& _envCellRenderer is not null)
|
||||
{
|
||||
foreach (var line in _envCellRenderer.CollectCellAuditLines(_phaseA8AuditLogged))
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitStencilProbe(string op)
|
||||
|
|
|
|||
|
|
@ -103,6 +103,61 @@ public sealed unsafe class EnvCellRenderer : IDisposable
|
|||
return (poolTotal, hwm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Phase A8 audit probe (2026-05-28 visual-gate-#1 follow-up).
|
||||
/// One-shot per (cellId, gfxObjId) pair: dumps batch counts + CullModes +
|
||||
/// transparency flags + bindless-handle-non-zero status, so the operator
|
||||
/// can read offline and identify why specific polys (e.g., floors) aren't
|
||||
/// rendering. Set <c>ACDREAM_A8_AUDIT=1</c> to enable.
|
||||
/// <para>Returns a deduplicated audit-line list per Render snapshot
|
||||
/// (one entry per (cellId, gfxObjId) seen in BatchedByCell). The caller
|
||||
/// (GameWindow EmitEnvCellProbe) prints these and tracks which pairs
|
||||
/// have already been logged.</para>
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> CollectCellAuditLines(HashSet<(uint cellId, ulong gfxObjId)> alreadyLogged)
|
||||
{
|
||||
var lines = new List<string>();
|
||||
lock (_renderLock)
|
||||
{
|
||||
var snap = _activeSnapshot;
|
||||
foreach (var (cellId, gfxDict) in snap.BatchedByCell)
|
||||
{
|
||||
foreach (var (gfxObjId, transforms) in gfxDict)
|
||||
{
|
||||
var key = (cellId, gfxObjId);
|
||||
if (alreadyLogged.Contains(key)) continue;
|
||||
alreadyLogged.Add(key);
|
||||
|
||||
var rd = _meshManager.TryGetRenderData(gfxObjId);
|
||||
if (rd is null)
|
||||
{
|
||||
lines.Add($"[a8-audit] cell=0x{cellId:X8} gfx=0x{gfxObjId:X10} instances={transforms.Count} renderData=null");
|
||||
continue;
|
||||
}
|
||||
int totalIdx = 0;
|
||||
var cullModes = new HashSet<DatReaderWriter.Enums.CullMode>();
|
||||
int translucent = 0;
|
||||
int additive = 0;
|
||||
int zeroHandle = 0;
|
||||
foreach (var b in rd.Batches)
|
||||
{
|
||||
totalIdx += b.IndexCount;
|
||||
cullModes.Add(b.CullMode);
|
||||
if (b.IsTransparent) translucent++;
|
||||
if (b.IsAdditive) additive++;
|
||||
if (b.BindlessTextureHandle == 0) zeroHandle++;
|
||||
}
|
||||
var cullList = string.Join(",", cullModes);
|
||||
lines.Add(
|
||||
$"[a8-audit] cell=0x{cellId:X8} gfx=0x{gfxObjId:X10} instances={transforms.Count} " +
|
||||
$"isSetup={rd.IsSetup} batches={rd.Batches.Count} totalIdx={totalIdx} " +
|
||||
$"cull=[{cullList}] translucent={translucent} additive={additive} zeroHandle={zeroHandle}");
|
||||
}
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constructor + Initialize
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue