fix(render): DynamicLast admits only the walk''s own root flood
The [dyn-route] trace pinned the through-wall remote player (visible from 0xF4180101 AND outdoors while parented at 0xF4180112 - a cell with no retail sightline chain from either): the legacy visibility builder invents cross-building cell views at the cathedral (REAL 3-5-plane cones for 0x112, not the zero-plane trapdoor), the viewcone admits his sphere, and he rides the last dynamics pass - post-clear on interior roots (walls'' depth wiped) and post-world outdoors. The stage-set split (synthesis plan step 4): an interior-parented dynamic may ride DynamicLast ONLY when its parent cell is in THE WALK''S OWN ROOT FLOOD (oracle-trace-conformant; retail draws look-in occupants inside the landscape stage through the composed portal chain, and unreachable cells'' occupants not at all). The frame product''s build input gains RootFloodCells (the driver''s InteriorFloodCells as a per-frame set); non-walk/diagnostic frames and the comparison wrapper keep the legacy drawableCells meaning. The [dyn-route] probe logs the new rootflood-excluded state. Hermetic 6,762/0; InstalledDat walk conformance 40/1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4686a1ac74
commit
fc2e6b79bc
3 changed files with 61 additions and 7 deletions
|
|
@ -71,6 +71,10 @@ public sealed class RetailPViewRenderer
|
||||||
private readonly HashSet<uint> _cellParticleOwnerScratch = new();
|
private readonly HashSet<uint> _cellParticleOwnerScratch = new();
|
||||||
private readonly HashSet<uint> _dynamicParticleOwnerScratch = new();
|
private readonly HashSet<uint> _dynamicParticleOwnerScratch = new();
|
||||||
|
|
||||||
|
// The walk's TRUE root flood as a set, rebuilt per frame for the
|
||||||
|
// DynamicLast stage gate (the stage-set split — synthesis plan step 4).
|
||||||
|
private readonly HashSet<uint> _rootFloodSetScratch = new();
|
||||||
|
|
||||||
// MP-Alloc (2026-07-05): the frame's entity partition (ByCell/OutdoorStatic/
|
// MP-Alloc (2026-07-05): the frame's entity partition (ByCell/OutdoorStatic/
|
||||||
// Dynamics), reused across frames instead of `new`ing a Result (a Dictionary
|
// Dynamics), reused across frames instead of `new`ing a Result (a Dictionary
|
||||||
// + 2 Lists, plus one List<WorldEntity> per visible cell) every DrawInside
|
// + 2 Lists, plus one List<WorldEntity> per visible cell) every DrawInside
|
||||||
|
|
@ -507,6 +511,16 @@ public sealed class RetailPViewRenderer
|
||||||
HashSet<uint> outsideStageFlood =
|
HashSet<uint> outsideStageFlood =
|
||||||
walkActive ? walkDriver!.VisitedCells : drawableCells;
|
walkActive ? walkDriver!.VisitedCells : drawableCells;
|
||||||
|
|
||||||
|
// The stage-set split: the walk's TRUE root flood (never the
|
||||||
|
// visited union) keys which interior-parented dynamics may ride
|
||||||
|
// the last pass — see BuildOutsideDynamicRoutes' sibling gate.
|
||||||
|
_rootFloodSetScratch.Clear();
|
||||||
|
if (walkActive)
|
||||||
|
{
|
||||||
|
foreach (uint cellId in walkDriver!.InteriorFloodCells)
|
||||||
|
_rootFloodSetScratch.Add(cellId);
|
||||||
|
}
|
||||||
|
|
||||||
if (_sceneFrameProduct is not null)
|
if (_sceneFrameProduct is not null)
|
||||||
{
|
{
|
||||||
frameView = _sceneFrameProduct.BuildAndBorrow(
|
frameView = _sceneFrameProduct.BuildAndBorrow(
|
||||||
|
|
@ -517,7 +531,11 @@ public sealed class RetailPViewRenderer
|
||||||
outsideStageFlood,
|
outsideStageFlood,
|
||||||
ctx.Cells,
|
ctx.Cells,
|
||||||
ctx.AnimatedEntityIds,
|
ctx.AnimatedEntityIds,
|
||||||
ctx.RootCell.IsOutdoorNode);
|
ctx.RootCell.IsOutdoorNode,
|
||||||
|
// Non-walk frames (diagnostic fakes / legacy fallback)
|
||||||
|
// keep the pre-split admission: drawableCells WAS the
|
||||||
|
// flood in the legacy meaning.
|
||||||
|
walkActive ? _rootFloodSetScratch : drawableCells);
|
||||||
frameViewBorrowed = true;
|
frameViewBorrowed = true;
|
||||||
frameEntityPasses!.BeginEntityFrame(in frameView);
|
frameEntityPasses!.BeginEntityFrame(in frameView);
|
||||||
entityFrameOpen = true;
|
entityFrameOpen = true;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ internal readonly record struct RenderScenePViewBuildInput(
|
||||||
HashSet<uint> DrawableCells,
|
HashSet<uint> DrawableCells,
|
||||||
IRetailPViewCellSource Cells,
|
IRetailPViewCellSource Cells,
|
||||||
HashSet<uint>? AnimatedEntityIds,
|
HashSet<uint>? AnimatedEntityIds,
|
||||||
bool RootIsOutdoor);
|
bool RootIsOutdoor,
|
||||||
|
IReadOnlySet<uint> RootFloodCells);
|
||||||
|
|
||||||
internal readonly record struct RenderFrameProductComparisonSnapshot(
|
internal readonly record struct RenderFrameProductComparisonSnapshot(
|
||||||
bool Enabled,
|
bool Enabled,
|
||||||
|
|
@ -188,7 +189,8 @@ internal sealed class RenderScenePViewFrameProductController :
|
||||||
HashSet<uint> drawableCells,
|
HashSet<uint> drawableCells,
|
||||||
IRetailPViewCellSource cells,
|
IRetailPViewCellSource cells,
|
||||||
HashSet<uint>? animatedEntityIds,
|
HashSet<uint>? animatedEntityIds,
|
||||||
bool rootIsOutdoor)
|
bool rootIsOutdoor,
|
||||||
|
IReadOnlySet<uint> rootFloodCells)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(portalFrame);
|
ArgumentNullException.ThrowIfNull(portalFrame);
|
||||||
ArgumentNullException.ThrowIfNull(clipAssembly);
|
ArgumentNullException.ThrowIfNull(clipAssembly);
|
||||||
|
|
@ -215,7 +217,8 @@ internal sealed class RenderScenePViewFrameProductController :
|
||||||
drawableCells,
|
drawableCells,
|
||||||
cells,
|
cells,
|
||||||
animatedEntityIds,
|
animatedEntityIds,
|
||||||
rootIsOutdoor);
|
rootIsOutdoor,
|
||||||
|
rootFloodCells);
|
||||||
_builder.Build(_exchange, frameSequence, in input);
|
_builder.Build(_exchange, frameSequence, in input);
|
||||||
return _exchange.BorrowLatest(
|
return _exchange.BorrowLatest(
|
||||||
scene.Generation,
|
scene.Generation,
|
||||||
|
|
@ -235,8 +238,12 @@ internal sealed class RenderScenePViewFrameProductController :
|
||||||
HashSet<uint>? animatedEntityIds,
|
HashSet<uint>? animatedEntityIds,
|
||||||
uint tupleLandblockId,
|
uint tupleLandblockId,
|
||||||
bool rootIsOutdoor,
|
bool rootIsOutdoor,
|
||||||
Vector3 cameraWorldPosition = default)
|
Vector3 cameraWorldPosition = default,
|
||||||
|
IReadOnlySet<uint>? rootFloodCells = null)
|
||||||
{
|
{
|
||||||
|
// Comparison/diagnostic path: absent an explicit walk root flood,
|
||||||
|
// drawableCells keeps the pre-stage-split admission (drawableCells
|
||||||
|
// WAS the flood in the legacy meaning these fixtures pin).
|
||||||
RenderFrameView view = BuildAndBorrow(
|
RenderFrameView view = BuildAndBorrow(
|
||||||
portalFrame,
|
portalFrame,
|
||||||
clipAssembly,
|
clipAssembly,
|
||||||
|
|
@ -245,7 +252,8 @@ internal sealed class RenderScenePViewFrameProductController :
|
||||||
drawableCells,
|
drawableCells,
|
||||||
cells,
|
cells,
|
||||||
animatedEntityIds,
|
animatedEntityIds,
|
||||||
rootIsOutdoor);
|
rootIsOutdoor,
|
||||||
|
rootFloodCells ?? drawableCells);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Compare(
|
Compare(
|
||||||
|
|
@ -1417,6 +1425,33 @@ internal sealed class RenderScenePViewFrameBuilder
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The stage-set split (synthesis plan step 4, [dyn-route]-pinned
|
||||||
|
// 2026-08-30): an interior-parented dynamic rides the LAST pass
|
||||||
|
// ONLY when its parent cell is in THE WALK'S OWN ROOT FLOOD.
|
||||||
|
// Retail draws a look-in cell's occupants inside the landscape
|
||||||
|
// stage through the composed portal chain, and an unreachable
|
||||||
|
// cell's occupants not at all — the legacy visibility builder
|
||||||
|
// invents cross-building cell views at the cathedral (real
|
||||||
|
// 3-5-plane cones for 0xF4180112 from roots with no sightline),
|
||||||
|
// which let a remote player draw post-clear/post-world through
|
||||||
|
// opaque walls. The walk's flood is oracle-trace-conformant;
|
||||||
|
// key the route on it.
|
||||||
|
if (indoor && !input.RootFloodCells.Contains(parentCellId!.Value))
|
||||||
|
{
|
||||||
|
if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled)
|
||||||
|
{
|
||||||
|
string state = $"parent={parentCellId.Value:x8} rootflood-excluded";
|
||||||
|
if (!_probeDynRouteStates.TryGetValue(record.Source.LocalEntityId, out string? prev)
|
||||||
|
|| prev != state)
|
||||||
|
{
|
||||||
|
_probeDynRouteStates[record.Source.LocalEntityId] = state;
|
||||||
|
Console.WriteLine(
|
||||||
|
$"[dyn-route] id={record.Source.LocalEntityId:x} {state}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Sphere(in record, out Vector3 center, out float radius);
|
Sphere(in record, out Vector3 center, out float radius);
|
||||||
bool visible = indoor
|
bool visible = indoor
|
||||||
? input.Viewcone.SphereVisibleInCell(
|
? input.Viewcone.SphereVisibleInCell(
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,8 @@ public sealed class RenderScenePViewFrameProductTests
|
||||||
[Cell],
|
[Cell],
|
||||||
EmptyCellSource.Instance,
|
EmptyCellSource.Instance,
|
||||||
[],
|
[],
|
||||||
RootIsOutdoor: true);
|
RootIsOutdoor: true,
|
||||||
|
RootFloodCells: new HashSet<uint> { Cell });
|
||||||
builder.Build(exchange, frameSequence, in input);
|
builder.Build(exchange, frameSequence, in input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue