T4 (BR-6): ONE visibility gate - ACME BFS deleted from the frame, legacy second render path deleted
The one-gate rule (feedback_render_one_gate) is now structural: - The per-frame ACME BFS (CellVisibility.ComputeVisibilityFromRoot) is GONE from the frame. Its only production consumer was the cameraInsideCell boolean - which is exactly 'viewerRoot is not null' (the TryGetCell that produced viewerRoot already proves cells are loaded; ComputeVisibilityFromRoot returned null iff root was null). A full second visibility computation ran every frame to derive a boolean we already had. The method + its tests remain as quarantined non-production code (dual-live-visibility-computations, confirmed). - The clipRoot==null mini-pipeline is DELETED (legacy-outdoor-branch- remnant, adjusted-confirmed): the outdoor partition draw, the Chebyshev look-in gather, the DrawPortal invocation and the dynamics fallback. clipRoot is null only when NO viewer cell exists (pre-login, fly/debug cameras, transient gaps) - those frames draw flat through the dispatcher; every normal outdoor frame is the outdoor node. - DELETED with it: InteriorRenderer (class file - its only caller was the legacy branch), RetailPViewRenderer.DrawPortal + RetailPViewPortalDrawContext (the look-in product; outdoor-root frames flood buildings via MergeNearbyBuildingFloods inside DrawInside), the _exteriorPortal*/_outdoorRootNoCells fields. Per frame there is now exactly ONE visibility computation (PortalVisibilityBuilder) and ONE render path (DrawInside). Suites: build green, App 226 green, Core baseline (1398 + 4 pre-existing #99-era). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a6aec8c32f
commit
4a307d33b5
3 changed files with 20 additions and 360 deletions
|
|
@ -169,7 +169,6 @@ public sealed class GameWindow : IDisposable
|
|||
// R1 (render redesign): the per-cell DrawInside flood + its per-frame entity partition.
|
||||
// _interiorRenderer is constructed once both renderers exist; _interiorPartition is rebuilt
|
||||
// each frame on an indoor root (null on the outdoor root).
|
||||
private AcDream.App.Rendering.InteriorRenderer? _interiorRenderer;
|
||||
private AcDream.App.Rendering.RetailPViewRenderer? _retailPViewRenderer;
|
||||
private AcDream.App.Rendering.PortalDepthMaskRenderer? _portalDepthMask;
|
||||
private AcDream.App.Rendering.InteriorEntityPartition.Result? _interiorPartition;
|
||||
|
|
@ -182,9 +181,6 @@ public sealed class GameWindow : IDisposable
|
|||
// three renderers so each re-binds binding=2 immediately before its own draw.
|
||||
// U.4 replaces the NoClip() frame with one built from the portal-visibility result.
|
||||
private ClipFrame? _clipFrame;
|
||||
private readonly HashSet<uint> _outdoorRootNoCells = new(0);
|
||||
private readonly HashSet<uint> _exteriorPortalLandblocks = new();
|
||||
private readonly List<LoadedCell> _exteriorPortalCandidateCells = new();
|
||||
|
||||
// Phase 3 (render unification, 2026-06-07): the synthetic outdoor cell node — the outdoor
|
||||
// world as a flood-graph cell (spec 2026-06-07-render-unification-outdoor-as-cell). Rebuilt
|
||||
|
|
@ -1840,8 +1836,6 @@ public sealed class GameWindow : IDisposable
|
|||
_gl, _wbMeshAdapter!.MeshManager!, _envCellFrustum);
|
||||
_envCellRenderer.Initialize(_meshShader!);
|
||||
|
||||
// R1: the per-cell DrawInside flood. Both renderers exist here (just constructed).
|
||||
_interiorRenderer = new AcDream.App.Rendering.InteriorRenderer(_envCellRenderer!, _wbDrawDispatcher!);
|
||||
_clipFrame ??= ClipFrame.NoClip();
|
||||
_retailPViewRenderer = new AcDream.App.Rendering.RetailPViewRenderer(
|
||||
_gl, _clipFrame, _envCellRenderer!, _wbDrawDispatcher!);
|
||||
|
|
@ -7314,8 +7308,13 @@ public sealed class GameWindow : IDisposable
|
|||
LoadedCell? viewerRoot = null;
|
||||
if (viewerCellId != 0u && _cellVisibility.TryGetCell(viewerCellId, out var viewerRegCell))
|
||||
viewerRoot = viewerRegCell;
|
||||
var visibility = _cellVisibility.ComputeVisibilityFromRoot(viewerRoot, viewerEyePos);
|
||||
bool cameraInsideCell = visibility?.CameraCell is not null;
|
||||
// T4 (BR-6): the per-frame ACME BFS (ComputeVisibilityFromRoot) is
|
||||
// DELETED from the frame — it ran a full second visibility
|
||||
// computation whose only production consumer was this boolean,
|
||||
// which is exactly "the viewer root resolved to a loaded interior
|
||||
// cell" (TryGetCell above already proves cells are loaded). The
|
||||
// PView flood is the ONE visibility gate (feedback_render_one_gate).
|
||||
bool cameraInsideCell = viewerRoot is not null;
|
||||
|
||||
// Retail render routing is owned by the collided camera/viewer cell.
|
||||
// The player cell still owns lighting state, but it must not force an
|
||||
|
|
@ -7732,114 +7731,19 @@ public sealed class GameWindow : IDisposable
|
|||
}
|
||||
else
|
||||
{
|
||||
if (_interiorRenderer is not null)
|
||||
{
|
||||
_outdoorRootNoCells.Clear();
|
||||
var outdoorPartition = AcDream.App.Rendering.InteriorEntityPartition.Partition(
|
||||
_outdoorRootNoCells, _worldState.LandblockEntries);
|
||||
sigOutdoorRootObjectCount = outdoorPartition.OutdoorStatic.Count;
|
||||
|
||||
// T1: static world first (shells + scenery)…
|
||||
if (outdoorPartition.OutdoorStatic.Count > 0)
|
||||
{
|
||||
_interiorRenderer.DrawEntityBucket(
|
||||
camera,
|
||||
frustum,
|
||||
playerLb,
|
||||
animatedIds,
|
||||
outdoorPartition.OutdoorStatic,
|
||||
visibleCellIds: null);
|
||||
}
|
||||
|
||||
_exteriorPortalLandblocks.Clear();
|
||||
_exteriorPortalCandidateCells.Clear();
|
||||
// FPS (2026-06-07): the outdoor look-in (DrawPortal -> BuildFromExterior) seeds only
|
||||
// from exit portals within MaxSeedDistance (48 m) of the camera. A landblock is 192 m,
|
||||
// so any cell that could seed is in the player's landblock or an immediate neighbour;
|
||||
// cells further out are already discarded by BuildFromExterior's per-portal cutoff.
|
||||
// Iterating EVERY cell in EVERY loaded landblock (near radius 4 = up to 81 LBs) just to
|
||||
// discard them is an O(all loaded cells) sweep every outdoor frame — the cause of the
|
||||
// "FPS drops as soon as I look out" report. Restrict candidates to the 1-ring around the
|
||||
// player (Chebyshev <= 1 in landblock grid). No behaviour change: the excluded cells are
|
||||
// all > 48 m away and were already culled by the seed-distance cutoff.
|
||||
int playerGridX = playerLb.HasValue ? (int)((playerLb.Value >> 24) & 0xFFu) : -1;
|
||||
int playerGridY = playerLb.HasValue ? (int)((playerLb.Value >> 16) & 0xFFu) : -1;
|
||||
foreach (var entry in _worldState.LandblockEntries)
|
||||
{
|
||||
uint lbPrefix = (entry.LandblockId >> 16) & 0xFFFFu;
|
||||
if (playerLb.HasValue)
|
||||
{
|
||||
int gX = (int)((lbPrefix >> 8) & 0xFFu);
|
||||
int gY = (int)(lbPrefix & 0xFFu);
|
||||
if (Math.Max(Math.Abs(gX - playerGridX), Math.Abs(gY - playerGridY)) > 1)
|
||||
continue;
|
||||
}
|
||||
if (!_exteriorPortalLandblocks.Add(lbPrefix))
|
||||
continue;
|
||||
|
||||
foreach (var cell in _cellVisibility.GetCellsForLandblock(lbPrefix))
|
||||
_exteriorPortalCandidateCells.Add(cell);
|
||||
}
|
||||
|
||||
if (_exteriorPortalCandidateCells.Count > 0 && _retailPViewRenderer is not null)
|
||||
{
|
||||
var portalResult = _retailPViewRenderer.DrawPortal(
|
||||
new AcDream.App.Rendering.RetailPViewPortalDrawContext
|
||||
{
|
||||
CandidateCells = _exteriorPortalCandidateCells,
|
||||
ViewerEyePos = viewerEyePos,
|
||||
ViewProjection = envCellViewProj,
|
||||
CellLookup = id => _cellVisibility.TryGetCell(id, out var c) ? c : null,
|
||||
Camera = camera,
|
||||
CameraWorldPosition = camPos,
|
||||
Frustum = frustum,
|
||||
PlayerLandblockId = playerLb,
|
||||
AnimatedEntityIds = animatedIds,
|
||||
RenderCenterLbX = renderCenterLbX,
|
||||
RenderCenterLbY = renderCenterLbY,
|
||||
RenderRadius = _nearRadius,
|
||||
MaxSeedDistance = 48f,
|
||||
LandblockEntries = _worldState.LandblockEntries,
|
||||
SetTerrainClipUbo = uboId => _terrain?.SetClipUbo(uboId),
|
||||
// T1: look-in — PUNCH building entry apertures to far-Z so
|
||||
// the flooded interior shows through the doorway. Safe:
|
||||
// dynamics draw after this whole block.
|
||||
DrawExitPortalMasks = sliceCtx =>
|
||||
DrawRetailPViewPortalDepthWrite(sliceCtx, envCellViewProj,
|
||||
forceFarZ: true),
|
||||
});
|
||||
|
||||
if (portalResult is not null)
|
||||
{
|
||||
sigOutdoorPortalDrawn = true;
|
||||
sigExteriorPvFrame = portalResult.PortalFrame;
|
||||
sigExteriorClipAssembly = portalResult.ClipAssembly;
|
||||
sigExteriorDrawableCells = portalResult.DrawableCells;
|
||||
sigExteriorPartition = portalResult.Partition;
|
||||
}
|
||||
}
|
||||
|
||||
// T1: …then ALL dynamics last (after the look-in punched +
|
||||
// drew interiors), depth-tested, never hard-clipped.
|
||||
if (outdoorPartition.Dynamics.Count > 0)
|
||||
{
|
||||
sigLiveDynamicDrawnCount = outdoorPartition.Dynamics.Count;
|
||||
_interiorRenderer.DrawEntityBucket(
|
||||
camera,
|
||||
frustum,
|
||||
playerLb,
|
||||
animatedIds,
|
||||
outdoorPartition.Dynamics,
|
||||
visibleCellIds: null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_wbDrawDispatcher!.Draw(camera, _worldState.LandblockEntries, frustum,
|
||||
neverCullLandblockId: playerLb,
|
||||
visibleCellIds: null,
|
||||
animatedEntityIds: animatedIds);
|
||||
}
|
||||
// T4 (BR-6): the old clipRoot==null mini-pipeline (outdoor
|
||||
// partition + Chebyshev look-in gather + DrawPortal + dynamics
|
||||
// fallback) is DELETED — it was the SECOND render path the
|
||||
// one-gate rule forbids (legacy-outdoor-branch-remnant,
|
||||
// adjusted-confirmed). clipRoot is null only when NO viewer
|
||||
// cell exists at all (pre-login, fly/debug cameras, transient
|
||||
// streaming gaps — the outdoor node covers every normal outdoor
|
||||
// frame): draw the world flat through the dispatcher; floods
|
||||
// resume the moment a viewer cell resolves.
|
||||
_wbDrawDispatcher!.Draw(camera, _worldState.LandblockEntries, frustum,
|
||||
neverCullLandblockId: playerLb,
|
||||
visibleCellIds: null,
|
||||
animatedEntityIds: animatedIds);
|
||||
}
|
||||
|
||||
// Phase U.3: close the world-geometry clip bracket opened above. From here down the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue