T2 slice 3 (BR-4): draw-driven flood gating - per-building frustum pre-gate, 48m seed cap DELETED
Retail floods a building's interior exactly when its shell DRAWS and an aperture survives the view: DrawBuilding (Ghidra 0x0059f2a0) -> per-view viewconeCheck on the shell -> portal-BSP walk -> ConstructView(CBldPortal) side test + GetClip-vs-view + GetVisible. There is NO distance constant anywhere on that chain (verifier-confirmed, flood-gate-shape adjusted). Port: - GameWindow's outdoor-node gather: per-BUILDING frustum pre-gate on the aperture bounds (Building.PortalBounds - the tight flood-purposes equivalent of the shell viewconeCheck), iterating the per-landblock BuildingRegistries. Replaces the Chebyshev<=1 landblock cell-sweep. Also the proper fix for the 2026-06-07 'FPS drops when I look out' problem the Chebyshev hack approximated: dozens of AABB tests instead of an O(all loaded cells) portal sweep. - OutdoorBuildingSeedDistance 48f -> infinity (the binary visibility pop at ~48 m - the confirmed #109 mechanism candidate - is gone; admission is now the screen clip per portal, retail's GetClip gate). - The legacy clipRoot==null look-in path keeps its 48 m: it is T4 deletion scope; improving doomed code wastes effort. Closes the building-flood-seeding-48m-cutoff divergence (culling area, adjusted-confirmed). Suites: App 226 green (flood gates included). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
529dfcfee9
commit
88f3ce1fa0
2 changed files with 32 additions and 15 deletions
|
|
@ -192,7 +192,6 @@ public sealed class GameWindow : IDisposable
|
|||
// yet rooted; the clipRoot flip + OutsideView terrain integration is the cutover step).
|
||||
private LoadedCell? _outdoorNode;
|
||||
private readonly List<LoadedCell> _outdoorNodeBuildingCells = new();
|
||||
private readonly HashSet<uint> _outdoorNodeSeenLbs = new();
|
||||
private readonly HashSet<uint> _outdoorSceneParticleEntityIds = new();
|
||||
private readonly HashSet<uint> _visibleSceneParticleEntityIds = new();
|
||||
private string? _lastRenderSignature;
|
||||
|
|
@ -7464,26 +7463,38 @@ public sealed class GameWindow : IDisposable
|
|||
if (viewerRoot is null && viewerCellId != 0u)
|
||||
{
|
||||
_outdoorNodeBuildingCells.Clear();
|
||||
_outdoorNodeSeenLbs.Clear();
|
||||
int onGridX = playerLb.HasValue ? (int)((playerLb.Value >> 24) & 0xFFu) : -1;
|
||||
int onGridY = playerLb.HasValue ? (int)((playerLb.Value >> 16) & 0xFFu) : -1;
|
||||
foreach (var onEntry in _worldState.LandblockEntries)
|
||||
// T2 (BR-4): draw-driven flood gating. Retail floods a building's
|
||||
// interior exactly when its shell DRAWS and an aperture survives
|
||||
// the view (DrawBuilding Ghidra 0x0059f2a0: per-view viewconeCheck
|
||||
// → portal-BSP walk → ConstructView's GetClip; NO distance
|
||||
// constant anywhere on the chain). Port: a per-BUILDING frustum
|
||||
// pre-gate on the aperture bounds (Building.PortalBounds — the
|
||||
// tight equivalent of the shell viewconeCheck for FLOOD purposes),
|
||||
// replacing the old Chebyshev≤1 landblock cell-sweep; the 48 m
|
||||
// seed cap dies with it (RetailPViewRenderer seeds at ∞). The
|
||||
// per-portal admission stays BuildFromExterior's screen clip
|
||||
// (empty clip = no seed) — retail's GetClip-vs-view gate.
|
||||
// Per-building iteration is also the FPS fix the 2026-06-07
|
||||
// Chebyshev hack approximated: dozens of AABB tests instead of an
|
||||
// O(all loaded cells) portal sweep.
|
||||
foreach (var registry in _buildingRegistries.Values)
|
||||
{
|
||||
uint onLb = (onEntry.LandblockId >> 16) & 0xFFFFu;
|
||||
if (playerLb.HasValue)
|
||||
foreach (var b in registry.All())
|
||||
{
|
||||
int gX = (int)((onLb >> 8) & 0xFFu);
|
||||
int gY = (int)(onLb & 0xFFu);
|
||||
if (Math.Max(Math.Abs(gX - onGridX), Math.Abs(gY - onGridY)) > 1) continue;
|
||||
if (b.HasPortalBounds
|
||||
&& !AcDream.App.Rendering.FrustumCuller.IsAabbVisible(
|
||||
frustum, b.PortalBounds.Min, b.PortalBounds.Max))
|
||||
continue;
|
||||
|
||||
foreach (uint cid in b.EnvCellIds)
|
||||
if (_cellVisibility.TryGetCell(cid, out var bc) && bc is not null)
|
||||
_outdoorNodeBuildingCells.Add(bc);
|
||||
}
|
||||
if (!_outdoorNodeSeenLbs.Add(onLb)) continue;
|
||||
foreach (var onCell in _cellVisibility.GetCellsForLandblock(onLb))
|
||||
_outdoorNodeBuildingCells.Add(onCell);
|
||||
}
|
||||
_outdoorNode = AcDream.App.Rendering.OutdoorCellNode.Build(viewerCellId);
|
||||
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeFlapEnabled)
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[outdoor-node] cell=0x{viewerCellId:X8} nearbyCells={_outdoorNodeBuildingCells.Count} (R-A2 per-building floods)"));
|
||||
$"[outdoor-node] cell=0x{viewerCellId:X8} nearbyCells={_outdoorNodeBuildingCells.Count} (T2 frustum-gated per-building floods)"));
|
||||
}
|
||||
|
||||
uint playerCellId = _physicsEngine.DataCache?.CellGraph.CurrCell?.Id ?? 0u;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,13 @@ public sealed class RetailPViewRenderer
|
|||
|
||||
// R-A2: per-building flood grouping, reused across frames (inner lists cleared each frame).
|
||||
private readonly Dictionary<uint, List<LoadedCell>> _buildingGroups = new();
|
||||
private const float OutdoorBuildingSeedDistance = 48f;
|
||||
|
||||
// T2 (BR-4): retail has NO distance constant on the flood-admission chain
|
||||
// (DrawBuilding → portal walk → ConstructView: viewconeCheck + side test +
|
||||
// GetClip + GetVisible only). The old 48 m seed cap is replaced by the
|
||||
// caller's per-building frustum pre-gate on aperture bounds (GameWindow's
|
||||
// gather); seeds themselves are unbounded.
|
||||
private const float OutdoorBuildingSeedDistance = float.PositiveInfinity;
|
||||
|
||||
public RetailPViewRenderer(
|
||||
GL gl,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue