refactor(render): detach packed pview product from production
This commit is contained in:
parent
ef12170a3c
commit
c9fb7e7d4c
4 changed files with 54 additions and 180 deletions
|
|
@ -347,20 +347,13 @@ internal sealed class FrameRootCompositionPhase
|
||||||
live.EntityEffects,
|
live.EntityEffects,
|
||||||
d.Log);
|
d.Log);
|
||||||
IWorldSceneFramePhase? worldSceneRenderer = null;
|
IWorldSceneFramePhase? worldSceneRenderer = null;
|
||||||
CurrentRenderSceneOracle? currentRenderSceneOracle =
|
// Campaign FW4: the retail walk reads RenderSceneShadowRuntime
|
||||||
interaction.RetainedUi?.Screenshots is not null
|
// directly. The old current-partition oracle, shadow comparison, and
|
||||||
&& d.Options.AutomationArtifactDirectory is not null
|
// packed PView frame product no longer participate in production or
|
||||||
? new CurrentRenderSceneOracle()
|
// automation; their null snapshot slots remain until the diagnostic
|
||||||
: null;
|
// contract cleanup later in this campaign.
|
||||||
RenderSceneShadowComparisonController? renderSceneShadowComparison =
|
CurrentRenderSceneOracle? currentRenderSceneOracle = null;
|
||||||
currentRenderSceneOracle is not null
|
RenderSceneShadowComparisonController? renderSceneShadowComparison = null;
|
||||||
&& live.RenderSceneShadow is not null
|
|
||||||
? new RenderSceneShadowComparisonController(
|
|
||||||
live.RenderSceneShadow,
|
|
||||||
currentRenderSceneOracle,
|
|
||||||
message => d.Log("[UI-PROBE] " + message),
|
|
||||||
acknowledgeDirty: false)
|
|
||||||
: null;
|
|
||||||
RenderScenePViewFrameProductController? renderFrameProduct = null;
|
RenderScenePViewFrameProductController? renderFrameProduct = null;
|
||||||
{
|
{
|
||||||
// Campaign V slice V6j: the world scene is composed on BOTH arms.
|
// Campaign V slice V6j: the world scene is composed on BOTH arms.
|
||||||
|
|
@ -487,17 +480,8 @@ internal sealed class FrameRootCompositionPhase
|
||||||
live.SkyRenderer,
|
live.SkyRenderer,
|
||||||
content.ParticleSystem,
|
content.ParticleSystem,
|
||||||
live.ParticleRenderer);
|
live.ParticleRenderer);
|
||||||
renderFrameProduct =
|
// The retained scene is the walk's production object source. The
|
||||||
live.RenderSceneShadow is not null
|
// old dispatcher/selection observer remains detached.
|
||||||
? new RenderScenePViewFrameProductController(
|
|
||||||
live.RenderSceneShadow,
|
|
||||||
currentRenderSceneOracle,
|
|
||||||
message => d.Log("[UI-PROBE] " + message),
|
|
||||||
live.DrawDispatcher!)
|
|
||||||
: null;
|
|
||||||
// After G4 the retained product is the production object source.
|
|
||||||
// The old dispatcher/selection observer is intentionally detached;
|
|
||||||
// automation still compares the independently built PView route list.
|
|
||||||
live.DrawDispatcher!.SetCurrentRenderSceneObserver(null);
|
live.DrawDispatcher!.SetCurrentRenderSceneObserver(null);
|
||||||
live.SelectionScene.SetCurrentRenderSceneObserver(null);
|
live.SelectionScene.SetCurrentRenderSceneObserver(null);
|
||||||
worldSceneRenderer = new WorldSceneRenderer(
|
worldSceneRenderer = new WorldSceneRenderer(
|
||||||
|
|
@ -512,7 +496,7 @@ internal sealed class FrameRootCompositionPhase
|
||||||
new WorldScenePViewRenderer(
|
new WorldScenePViewRenderer(
|
||||||
new RetailPViewRenderer(
|
new RetailPViewRenderer(
|
||||||
currentRenderSceneOracle,
|
currentRenderSceneOracle,
|
||||||
renderFrameProduct,
|
live.RenderSceneShadow,
|
||||||
// Campaign FW3.2b-2: the walk's production world
|
// Campaign FW3.2b-2: the walk's production world
|
||||||
// data, published/retired with each landblock by the
|
// data, published/retired with each landblock by the
|
||||||
// render publisher (FW3.1).
|
// render publisher (FW3.1).
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ public sealed class RetailPViewRenderer
|
||||||
{
|
{
|
||||||
private readonly InteriorEntityPartition.IObserver? _partitionObserver;
|
private readonly InteriorEntityPartition.IObserver? _partitionObserver;
|
||||||
private readonly ICurrentRenderPViewObserver? _candidateObserver;
|
private readonly ICurrentRenderPViewObserver? _candidateObserver;
|
||||||
private readonly RenderScenePViewFrameProductController?
|
private readonly RenderSceneShadowRuntime? _renderSceneShadow;
|
||||||
_sceneFrameProduct;
|
|
||||||
private readonly PortalVisibilityFrame _mainPortalFrameScratch = new();
|
private readonly PortalVisibilityFrame _mainPortalFrameScratch = new();
|
||||||
private readonly ClipFrameAssembly _clipAssemblyScratch = new();
|
private readonly ClipFrameAssembly _clipAssemblyScratch = new();
|
||||||
private readonly ViewconeCuller _viewconeScratch = new();
|
private readonly ViewconeCuller _viewconeScratch = new();
|
||||||
|
|
@ -133,7 +132,7 @@ public sealed class RetailPViewRenderer
|
||||||
|
|
||||||
internal RetailPViewRenderer(
|
internal RetailPViewRenderer(
|
||||||
InteriorEntityPartition.IObserver? partitionObserver,
|
InteriorEntityPartition.IObserver? partitionObserver,
|
||||||
RenderScenePViewFrameProductController? sceneFrameProduct = null,
|
RenderSceneShadowRuntime? renderSceneShadow = null,
|
||||||
Walk.WalkBuildingRegistry? walkBuildings = null,
|
Walk.WalkBuildingRegistry? walkBuildings = null,
|
||||||
Walk.WalkLandscapeAssembler? walkLandscape = null,
|
Walk.WalkLandscapeAssembler? walkLandscape = null,
|
||||||
CellVisibility? walkCellRegistry = null)
|
CellVisibility? walkCellRegistry = null)
|
||||||
|
|
@ -146,7 +145,7 @@ public sealed class RetailPViewRenderer
|
||||||
: null;
|
: null;
|
||||||
_partitionObserver = partitionObserver;
|
_partitionObserver = partitionObserver;
|
||||||
_candidateObserver = partitionObserver as ICurrentRenderPViewObserver;
|
_candidateObserver = partitionObserver as ICurrentRenderPViewObserver;
|
||||||
_sceneFrameProduct = sceneFrameProduct;
|
_renderSceneShadow = renderSceneShadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// T2 (BR-4): retail has NO distance constant on the flood-admission chain
|
// T2 (BR-4): retail has NO distance constant on the flood-admission chain
|
||||||
|
|
@ -225,7 +224,7 @@ public sealed class RetailPViewRenderer
|
||||||
// required — the walk submits through WbDrawDispatcher.SubmitOrderedStream
|
// required — the walk submits through WbDrawDispatcher.SubmitOrderedStream
|
||||||
// and needs a real GPU frame/encoder (RequireWalkSubmission), which no
|
// and needs a real GPU frame/encoder (RequireWalkSubmission), which no
|
||||||
// test IRetailPViewPassExecutor fake can supply. Whenever a concrete
|
// test IRetailPViewPassExecutor fake can supply. Whenever a concrete
|
||||||
// executor IS present, the packed entity-route product must ALSO be
|
// executor IS present, the retained render scene must ALSO be
|
||||||
// wired with all three walk registries — a scene product without the
|
// wired with all three walk registries — a scene product without the
|
||||||
// walk data (or vice versa) is a production miswiring, not a
|
// walk data (or vice versa) is a production miswiring, not a
|
||||||
// legacy/diagnostic shape, so it fails loud rather than silently
|
// legacy/diagnostic shape, so it fails loud rather than silently
|
||||||
|
|
@ -237,12 +236,12 @@ public sealed class RetailPViewRenderer
|
||||||
&& _walkCellRegistry is not null
|
&& _walkCellRegistry is not null
|
||||||
&& _walkWorldData is not null;
|
&& _walkWorldData is not null;
|
||||||
if (walkExecutor is not null
|
if (walkExecutor is not null
|
||||||
&& _sceneFrameProduct is not null
|
&& _renderSceneShadow is not null
|
||||||
&& !walkRegistriesReady)
|
&& !walkRegistriesReady)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
"RetailPViewRenderer has a concrete pass executor and a "
|
"RetailPViewRenderer has a concrete pass executor and a "
|
||||||
+ "RenderScenePViewFrameProductController but the walk registries "
|
+ "RenderSceneShadowRuntime but the walk registries "
|
||||||
+ "(WalkBuildingRegistry/WalkLandscapeAssembler/CellVisibility) are "
|
+ "(WalkBuildingRegistry/WalkLandscapeAssembler/CellVisibility) are "
|
||||||
+ "not all wired — the Campaign FW3.2b-2 static cutover requires "
|
+ "not all wired — the Campaign FW3.2b-2 static cutover requires "
|
||||||
+ "every piece together; see FrameRootComposition's "
|
+ "every piece together; see FrameRootComposition's "
|
||||||
|
|
@ -250,7 +249,7 @@ public sealed class RetailPViewRenderer
|
||||||
}
|
}
|
||||||
bool walkActive =
|
bool walkActive =
|
||||||
walkExecutor is not null
|
walkExecutor is not null
|
||||||
&& _sceneFrameProduct is not null
|
&& _renderSceneShadow is not null
|
||||||
&& walkRegistriesReady;
|
&& walkRegistriesReady;
|
||||||
|
|
||||||
// Campaign FW3.4a: THE ONE WALK. Builds walkContext/walkLandscape/
|
// Campaign FW3.4a: THE ONE WALK. Builds walkContext/walkLandscape/
|
||||||
|
|
@ -320,7 +319,7 @@ public sealed class RetailPViewRenderer
|
||||||
}
|
}
|
||||||
|
|
||||||
_walkWorldData!.BeginFrame(
|
_walkWorldData!.BeginFrame(
|
||||||
_sceneFrameProduct!.SceneQuery,
|
_renderSceneShadow!.Query,
|
||||||
ctx.PlayerLandblockId ?? 0u,
|
ctx.PlayerLandblockId ?? 0u,
|
||||||
ctx.RenderCenterLbX,
|
ctx.RenderCenterLbX,
|
||||||
ctx.RenderCenterLbY);
|
ctx.RenderCenterLbY);
|
||||||
|
|
@ -496,16 +495,11 @@ public sealed class RetailPViewRenderer
|
||||||
ctx.ViewProjection,
|
ctx.ViewProjection,
|
||||||
_viewconeScratch);
|
_viewconeScratch);
|
||||||
|
|
||||||
|
// Packed frame routes no longer participate in production. These
|
||||||
|
// placeholders keep the standalone legacy/fake path self-contained
|
||||||
|
// until FW4 removes that shell completely.
|
||||||
IRenderFrameEntityPassExecutor? frameEntityPasses = null;
|
IRenderFrameEntityPassExecutor? frameEntityPasses = null;
|
||||||
if (_sceneFrameProduct is not null)
|
|
||||||
{
|
|
||||||
frameEntityPasses = passes as IRenderFrameEntityPassExecutor
|
|
||||||
?? throw new InvalidOperationException(
|
|
||||||
"The production frame product requires a packed entity-pass executor.");
|
|
||||||
}
|
|
||||||
RenderFrameView frameView = default;
|
RenderFrameView frameView = default;
|
||||||
bool frameViewBorrowed = false;
|
|
||||||
bool entityFrameOpen = false;
|
|
||||||
_candidateObserver?.BeginPViewFrame();
|
_candidateObserver?.BeginPViewFrame();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -531,32 +525,11 @@ public sealed class RetailPViewRenderer
|
||||||
_rootFloodSetScratch.Add(cellId);
|
_rootFloodSetScratch.Add(cellId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_sceneFrameProduct is not null)
|
// The walk reads the retained scene directly. Rebuild the former
|
||||||
{
|
// WorldEntity partition only for the standalone fallback and
|
||||||
frameView = _sceneFrameProduct.BuildAndBorrow(
|
// explicitly enabled comparison/probe paths.
|
||||||
pvFrame,
|
|
||||||
clipAssembly,
|
|
||||||
viewcone,
|
|
||||||
walkActive ? walkDriver!.LookInCellTurns : [],
|
|
||||||
walkActive ? walkDriver : null,
|
|
||||||
outsideStageFlood,
|
|
||||||
ctx.Cells,
|
|
||||||
ctx.AnimatedEntityIds,
|
|
||||||
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;
|
|
||||||
frameEntityPasses!.BeginEntityFrame(in frameView);
|
|
||||||
entityFrameOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The retained scene product is the production object source.
|
|
||||||
// Rebuild the former WorldEntity partition only for the standalone
|
|
||||||
// fallback and explicitly enabled comparison/probe paths.
|
|
||||||
InteriorEntityPartition.Result? partition = null;
|
InteriorEntityPartition.Result? partition = null;
|
||||||
if (_sceneFrameProduct is null || LegacyPartitionDiagnosticsEnabled)
|
if (!walkActive || LegacyPartitionDiagnosticsEnabled)
|
||||||
{
|
{
|
||||||
InteriorEntityPartition.Partition(
|
InteriorEntityPartition.Partition(
|
||||||
_partitionResult,
|
_partitionResult,
|
||||||
|
|
@ -568,11 +541,14 @@ public sealed class RetailPViewRenderer
|
||||||
partition = _partitionResult;
|
partition = _partitionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderFrameDiagnosticCounts counts = frameViewBorrowed
|
RenderProjectionCounts retainedCounts = walkActive
|
||||||
? frameView.DiagnosticCounts
|
? _renderSceneShadow!.Counts
|
||||||
|
: default;
|
||||||
|
RenderFrameDiagnosticCounts counts = walkActive
|
||||||
|
? WalkDiagnosticCounts(retainedCounts)
|
||||||
: LegacyDiagnosticCounts(partition!);
|
: LegacyDiagnosticCounts(partition!);
|
||||||
RenderProjectionCounts sourceCounts = frameViewBorrowed
|
RenderProjectionCounts sourceCounts = walkActive
|
||||||
? frameView.SourceDigest.Counts
|
? retainedCounts
|
||||||
: LegacySourceCounts(partition!);
|
: LegacySourceCounts(partition!);
|
||||||
// prepareCells is exactly "main flood ∪ look-in cells" — the cells
|
// prepareCells is exactly "main flood ∪ look-in cells" — the cells
|
||||||
// this traversal actually reached, i.e. retail's in-view set.
|
// this traversal actually reached, i.e. retail's in-view set.
|
||||||
|
|
@ -761,27 +737,14 @@ public sealed class RetailPViewRenderer
|
||||||
// Outdoor-cell unattached emitters drew in the landscape stage.
|
// Outdoor-cell unattached emitters drew in the landscape stage.
|
||||||
passes.DrawUnattachedSceneParticles(ctx, outdoorCells: false);
|
passes.DrawUnattachedSceneParticles(ctx, outdoorCells: false);
|
||||||
|
|
||||||
if (entityFrameOpen)
|
|
||||||
{
|
|
||||||
frameEntityPasses!.CompleteEntityFrame(in frameView);
|
|
||||||
entityFrameOpen = false;
|
|
||||||
}
|
|
||||||
_candidateObserver?.CompletePViewFrame();
|
_candidateObserver?.CompletePViewFrame();
|
||||||
_sceneFrameProduct?.CompleteProduction(in frameView);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
if (entityFrameOpen)
|
|
||||||
frameEntityPasses!.AbortEntityFrame();
|
|
||||||
_candidateObserver?.AbortPViewFrame();
|
_candidateObserver?.AbortPViewFrame();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (frameViewBorrowed)
|
|
||||||
_sceneFrameProduct!.Release(in frameView);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// R-A2: group the nearby building cells by BuildingId and run one per-building flood per group
|
// R-A2: group the nearby building cells by BuildingId and run one per-building flood per group
|
||||||
|
|
@ -2408,6 +2371,27 @@ public sealed class RetailPViewRenderer
|
||||||
|| AcDream.Core.Rendering.RenderingDiagnostics.ProbeFlapEnabled
|
|| AcDream.Core.Rendering.RenderingDiagnostics.ProbeFlapEnabled
|
||||||
|| AcDream.App.Streaming.EntityVanishProbe.Enabled;
|
|| AcDream.App.Streaming.EntityVanishProbe.Enabled;
|
||||||
|
|
||||||
|
private static RenderFrameDiagnosticCounts WalkDiagnosticCounts(
|
||||||
|
RenderProjectionCounts source)
|
||||||
|
{
|
||||||
|
int dynamics = checked(
|
||||||
|
source.LiveDynamicRoot
|
||||||
|
+ source.ActiveAnimatedStatic
|
||||||
|
+ source.EquippedChild);
|
||||||
|
return new RenderFrameDiagnosticCounts(
|
||||||
|
source.OutdoorStatic,
|
||||||
|
source.IndoorCellStatic,
|
||||||
|
dynamics,
|
||||||
|
TransformCount: source.Total,
|
||||||
|
OpaqueClassificationCount: 0,
|
||||||
|
AlphaClassificationCount: 0,
|
||||||
|
LightSetCount: 0,
|
||||||
|
SelectionPartCount: 0,
|
||||||
|
RouteCandidateCount: source.Total,
|
||||||
|
EntityCandidateCount: source.Total,
|
||||||
|
MeshPartCount: 0);
|
||||||
|
}
|
||||||
|
|
||||||
internal static RenderFrameDiagnosticCounts LegacyDiagnosticCounts(
|
internal static RenderFrameDiagnosticCounts LegacyDiagnosticCounts(
|
||||||
InteriorEntityPartition.Result partition)
|
InteriorEntityPartition.Result partition)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,7 @@ public sealed class FrameRootCompositionTests
|
||||||
IReadOnlyList<CompiledCall> calls = CompiledCallGraph.Read(compose);
|
IReadOnlyList<CompiledCall> calls = CompiledCallGraph.Read(compose);
|
||||||
|
|
||||||
int resources = CallIndex(calls, typeof(RenderFrameResourceController), ".ctor");
|
int resources = CallIndex(calls, typeof(RenderFrameResourceController), ".ctor");
|
||||||
int comparison = CallIndex(
|
int scene = CallIndex(calls, typeof(WorldSceneRenderer), ".ctor", resources + 1);
|
||||||
calls,
|
|
||||||
typeof(RenderSceneShadowComparisonController),
|
|
||||||
".ctor",
|
|
||||||
resources + 1);
|
|
||||||
int scene = CallIndex(calls, typeof(WorldSceneRenderer), ".ctor", comparison + 1);
|
|
||||||
int automation = CallIndex(
|
int automation = CallIndex(
|
||||||
calls,
|
calls,
|
||||||
typeof(WorldLifecycleAutomationController),
|
typeof(WorldLifecycleAutomationController),
|
||||||
|
|
|
||||||
|
|
@ -197,95 +197,6 @@ public sealed class RetailPViewPassExecutorTests
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void DrawInside_production_product_supplies_the_ordered_entity_routes()
|
|
||||||
{
|
|
||||||
// Campaign FW3.2b-2: no cell-static entity here (adjudicated
|
|
||||||
// 2026-08-30) — CellStatic no longer flows through the packed
|
|
||||||
// product at all (WalkFrameDriver owns every cell static now, via
|
|
||||||
// OrderedDrawStream, not this route); a static entity registered on
|
|
||||||
// the LEGACY side with nothing on the packed side would report a
|
|
||||||
// permanent MismatchCount, which is not what this test is proving.
|
|
||||||
// The route-consumption-order assertion below still exercises the
|
|
||||||
// one route that DID survive the cutover (DynamicLast).
|
|
||||||
LoadedCell interior = InteriorWithExit(0xA9B40100u);
|
|
||||||
WorldEntity dynamic = Entity(
|
|
||||||
31u,
|
|
||||||
serverGuid: 0x80000031u,
|
|
||||||
parentCellId: interior.CellId);
|
|
||||||
uint landblockId = interior.CellId & 0xFFFF0000u;
|
|
||||||
var oracle = new CurrentRenderSceneOracle();
|
|
||||||
using var shadow = new RenderSceneShadowRuntime(
|
|
||||||
RenderSceneGeneration.FromRaw(1));
|
|
||||||
RenderProjectionRecord dynamicRecord =
|
|
||||||
RenderProjectionRecordFactory.ProjectEntity(
|
|
||||||
LiveRenderProjectionJournal.ProjectionId(dynamic.Id),
|
|
||||||
RenderProjectionClass.LiveDynamicRoot,
|
|
||||||
RenderOwnerIncarnation.FromRaw(dynamic.Id),
|
|
||||||
landblockId,
|
|
||||||
interior.CellId,
|
|
||||||
dynamic,
|
|
||||||
spatiallyVisible: true);
|
|
||||||
shadow.Journal.Register(in dynamicRecord);
|
|
||||||
shadow.DrainUpdateBoundary();
|
|
||||||
var product = new RenderScenePViewFrameProductController(
|
|
||||||
shadow,
|
|
||||||
oracle);
|
|
||||||
var renderer = new RetailPViewRenderer(oracle, product);
|
|
||||||
using var executor = new RecordingExecutor();
|
|
||||||
|
|
||||||
renderer.DrawInside(
|
|
||||||
Frame(interior, [dynamic]),
|
|
||||||
executor);
|
|
||||||
|
|
||||||
AssertAppearsInOrder(
|
|
||||||
string.Join('|', executor.Operations),
|
|
||||||
"entity-frame-begin",
|
|
||||||
"entity-route:DynamicLast:0:00000000",
|
|
||||||
"entity-frame-complete");
|
|
||||||
Assert.Equal(0, product.Snapshot.MismatchCount);
|
|
||||||
Assert.Equal(
|
|
||||||
product.Snapshot.ExpectedDigest,
|
|
||||||
product.Snapshot.ActualDigest);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void DrawInside_production_product_uses_retained_routes_without_legacy_partition_when_diagnostics_are_disabled()
|
|
||||||
{
|
|
||||||
LoadedCell interior = InteriorWithExit(0xA9B40100u);
|
|
||||||
WorldEntity dynamic = Entity(
|
|
||||||
32u,
|
|
||||||
serverGuid: 0x80000032u,
|
|
||||||
parentCellId: interior.CellId);
|
|
||||||
uint landblockId = interior.CellId & 0xFFFF0000u;
|
|
||||||
using var shadow = new RenderSceneShadowRuntime(
|
|
||||||
RenderSceneGeneration.FromRaw(1));
|
|
||||||
RenderProjectionRecord dynamicRecord =
|
|
||||||
RenderProjectionRecordFactory.ProjectEntity(
|
|
||||||
LiveRenderProjectionJournal.ProjectionId(dynamic.Id),
|
|
||||||
RenderProjectionClass.LiveDynamicRoot,
|
|
||||||
RenderOwnerIncarnation.FromRaw(dynamic.Id),
|
|
||||||
landblockId,
|
|
||||||
interior.CellId,
|
|
||||||
dynamic,
|
|
||||||
spatiallyVisible: true);
|
|
||||||
shadow.Journal.Register(in dynamicRecord);
|
|
||||||
shadow.DrainUpdateBoundary();
|
|
||||||
var product = new RenderScenePViewFrameProductController(shadow);
|
|
||||||
var renderer = new RetailPViewRenderer(null, product);
|
|
||||||
using var executor = new RecordingExecutor();
|
|
||||||
|
|
||||||
renderer.DrawInside(Frame(interior, [dynamic]), executor);
|
|
||||||
|
|
||||||
AssertAppearsInOrder(
|
|
||||||
string.Join('|', executor.Operations),
|
|
||||||
"entity-frame-begin",
|
|
||||||
"entity-route:DynamicLast:0:00000000",
|
|
||||||
"entity-frame-complete");
|
|
||||||
Assert.Equal(0uL, product.Snapshot.ComparisonCount);
|
|
||||||
Assert.Equal(1, product.Snapshot.ActualCandidateCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DrawInside_interior_root_executes_the_nearby_building_look_in_punch()
|
public void DrawInside_interior_root_executes_the_nearby_building_look_in_punch()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue