diff --git a/src/AcDream.App/Composition/FrameRootComposition.cs b/src/AcDream.App/Composition/FrameRootComposition.cs
index 1bdb3105..07cba262 100644
--- a/src/AcDream.App/Composition/FrameRootComposition.cs
+++ b/src/AcDream.App/Composition/FrameRootComposition.cs
@@ -354,7 +354,6 @@ internal sealed class FrameRootCompositionPhase
// contract cleanup later in this campaign.
CurrentRenderSceneOracle? currentRenderSceneOracle = null;
RenderSceneShadowComparisonController? renderSceneShadowComparison = null;
- RenderScenePViewFrameProductController? renderFrameProduct = null;
{
// Campaign V slice V6j: the world scene is composed on BOTH arms.
// Every renderer below now exists on Vulkan too; what forks is one
@@ -607,8 +606,7 @@ internal sealed class FrameRootCompositionPhase
?? throw new InvalidOperationException(
"Lifecycle automation requires the canonical physics cache."),
currentRenderSceneOracle,
- renderSceneShadowComparison,
- renderFrameProduct);
+ renderSceneShadowComparison);
lifecycleAutomation =
new WorldLifecycleAutomationController(
() => session.WorldReveal.Snapshot,
diff --git a/src/AcDream.App/Diagnostics/WorldLifecycleResourceSnapshotSource.cs b/src/AcDream.App/Diagnostics/WorldLifecycleResourceSnapshotSource.cs
index 7af0f706..b38145c3 100644
--- a/src/AcDream.App/Diagnostics/WorldLifecycleResourceSnapshotSource.cs
+++ b/src/AcDream.App/Diagnostics/WorldLifecycleResourceSnapshotSource.cs
@@ -47,8 +47,6 @@ internal sealed class WorldLifecycleResourceSnapshotSource
_renderSceneOracle;
private readonly IRenderSceneShadowSnapshotSource?
_renderSceneShadow;
- private readonly IRenderFrameProductSnapshotSource?
- _renderFrameProduct;
public WorldLifecycleResourceSnapshotSource(
GpuWorldState world,
@@ -69,8 +67,7 @@ internal sealed class WorldLifecycleResourceSnapshotSource
ResidencyManager residency,
PhysicsDataCache physics,
ICurrentRenderSceneOracleSnapshotSource? renderSceneOracle = null,
- IRenderSceneShadowSnapshotSource? renderSceneShadow = null,
- IRenderFrameProductSnapshotSource? renderFrameProduct = null)
+ IRenderSceneShadowSnapshotSource? renderSceneShadow = null)
{
_world = world ?? throw new ArgumentNullException(nameof(world));
_animations = animations
@@ -99,7 +96,6 @@ internal sealed class WorldLifecycleResourceSnapshotSource
_physics = physics ?? throw new ArgumentNullException(nameof(physics));
_renderSceneOracle = renderSceneOracle;
_renderSceneShadow = renderSceneShadow;
- _renderFrameProduct = renderFrameProduct;
}
public WorldLifecycleResourceSnapshot Capture(RenderFrameOutcome outcome)
@@ -151,9 +147,7 @@ internal sealed class WorldLifecycleResourceSnapshotSource
RenderSceneShadow:
_renderSceneShadow?.CaptureCheckpointSnapshot()
?? RenderSceneShadowComparisonSnapshot.Disabled,
- RenderFrameProduct:
- _renderFrameProduct?.Snapshot
- ?? RenderFrameProductComparisonSnapshot.Disabled,
+ RenderFrameProduct: RenderFrameProductComparisonSnapshot.Disabled,
PendingLiveTeardowns: _liveEntities.PendingTeardownCount,
PendingLandblockRetirements: _streaming.PendingRetirementCount,
ParticleEmitters: _particles.ActiveEmitterCount,
diff --git a/src/AcDream.App/Rendering/Scene/RenderFrameProductComparisonSnapshot.cs b/src/AcDream.App/Rendering/Scene/RenderFrameProductComparisonSnapshot.cs
new file mode 100644
index 00000000..0179ba2d
--- /dev/null
+++ b/src/AcDream.App/Rendering/Scene/RenderFrameProductComparisonSnapshot.cs
@@ -0,0 +1,98 @@
+namespace AcDream.App.Rendering.Scene;
+
+///
+/// Retained lifecycle-automation schema for releases that still deserialize
+/// the former packed-frame comparison block. Campaign FW4 removed that
+/// producer; new snapshots always publish .
+///
+internal readonly record struct RenderFrameProductComparisonSnapshot(
+ bool Enabled,
+ ulong ProductFrameSequence,
+ ulong CurrentPViewFrameSequence,
+ ulong ComparisonCount,
+ ulong SuccessfulComparisonCount,
+ int ExpectedCandidateCount,
+ int ActualCandidateCount,
+ long MismatchCount,
+ string? FirstMismatch,
+ RenderSceneHash128 ExpectedDigest,
+ RenderSceneHash128 ActualDigest,
+ ulong PackedInputComparisonCount,
+ ulong PackedInputSuccessfulComparisonCount,
+ int PackedInputExpectedCount,
+ int PackedInputActualCount,
+ long PackedInputMismatchCount,
+ string? PackedInputFirstMismatch,
+ RenderSceneHash128 PackedInputExpectedDigest,
+ RenderSceneHash128 PackedInputActualDigest,
+ ulong ClassifiedOutputComparisonCount,
+ ulong ClassifiedOutputSuccessfulComparisonCount,
+ int ClassifiedOutputExpectedDrawCount,
+ int ClassifiedOutputActualDrawCount,
+ long ClassifiedOutputMismatchCount,
+ string? ClassifiedOutputFirstMismatch,
+ RenderSceneHash128 ClassifiedOutputExpectedDigest,
+ RenderSceneHash128 ClassifiedOutputActualDigest,
+ ulong SelectionOutputComparisonCount,
+ ulong SelectionOutputSuccessfulComparisonCount,
+ int SelectionOutputExpectedPartCount,
+ int SelectionOutputActualPartCount,
+ long SelectionOutputMismatchCount,
+ string? SelectionOutputFirstMismatch,
+ RenderSceneHash128 SelectionOutputExpectedDigest,
+ RenderSceneHash128 SelectionOutputActualDigest,
+ int PackedClassificationProjectionCount,
+ int PackedClassificationStaticRebuildCount,
+ int PackedClassificationSameFrameReuseCount,
+ int PackedClassificationCrossFrameReuseCount,
+ int PackedClassificationAnimatedCount,
+ int PackedClassificationRetiredCount,
+ long PackedClassificationRetainedPayloadBytes,
+ RenderFrameDiagnosticCounts ProductCounts)
+{
+ public static RenderFrameProductComparisonSnapshot Disabled { get; } =
+ new(
+ Enabled: false,
+ ProductFrameSequence: 0,
+ CurrentPViewFrameSequence: 0,
+ ComparisonCount: 0,
+ SuccessfulComparisonCount: 0,
+ ExpectedCandidateCount: 0,
+ ActualCandidateCount: 0,
+ MismatchCount: 0,
+ FirstMismatch: null,
+ ExpectedDigest: RenderSceneHash128.Empty,
+ ActualDigest: RenderSceneHash128.Empty,
+ PackedInputComparisonCount: 0,
+ PackedInputSuccessfulComparisonCount: 0,
+ PackedInputExpectedCount: 0,
+ PackedInputActualCount: 0,
+ PackedInputMismatchCount: 0,
+ PackedInputFirstMismatch: null,
+ PackedInputExpectedDigest: RenderSceneHash128.Empty,
+ PackedInputActualDigest: RenderSceneHash128.Empty,
+ ClassifiedOutputComparisonCount: 0,
+ ClassifiedOutputSuccessfulComparisonCount: 0,
+ ClassifiedOutputExpectedDrawCount: 0,
+ ClassifiedOutputActualDrawCount: 0,
+ ClassifiedOutputMismatchCount: 0,
+ ClassifiedOutputFirstMismatch: null,
+ ClassifiedOutputExpectedDigest: RenderSceneHash128.Empty,
+ ClassifiedOutputActualDigest: RenderSceneHash128.Empty,
+ SelectionOutputComparisonCount: 0,
+ SelectionOutputSuccessfulComparisonCount: 0,
+ SelectionOutputExpectedPartCount: 0,
+ SelectionOutputActualPartCount: 0,
+ SelectionOutputMismatchCount: 0,
+ SelectionOutputFirstMismatch: null,
+ SelectionOutputExpectedDigest: RenderSceneHash128.Empty,
+ SelectionOutputActualDigest: RenderSceneHash128.Empty,
+ PackedClassificationProjectionCount: 0,
+ PackedClassificationStaticRebuildCount: 0,
+ PackedClassificationSameFrameReuseCount: 0,
+ PackedClassificationCrossFrameReuseCount: 0,
+ PackedClassificationAnimatedCount: 0,
+ PackedClassificationRetiredCount: 0,
+ PackedClassificationRetainedPayloadBytes: 0,
+ ProductCounts: default);
+}
diff --git a/src/AcDream.App/Rendering/Scene/RenderScenePViewFrameProduct.cs b/src/AcDream.App/Rendering/Scene/RenderScenePViewFrameProduct.cs
deleted file mode 100644
index 1a27aa51..00000000
--- a/src/AcDream.App/Rendering/Scene/RenderScenePViewFrameProduct.cs
+++ /dev/null
@@ -1,1645 +0,0 @@
-using System.Numerics;
-using AcDream.App.Rendering.Wb;
-using AcDream.App.Rendering.Walk;
-
-namespace AcDream.App.Rendering.Scene;
-
-internal readonly record struct RenderScenePViewBuildInput(
- RenderSceneQuery Scene,
- RenderSceneDigest SourceDigest,
- PortalVisibilityFrame PortalFrame,
- ClipFrameAssembly ClipAssembly,
- ViewconeCuller Viewcone,
- IReadOnlyList LookInCellTurns,
- IWalkLookInViewSource? WalkLookInViews,
- HashSet DrawableCells,
- IRetailPViewCellSource Cells,
- HashSet? AnimatedEntityIds,
- bool RootIsOutdoor,
- IReadOnlySet RootFloodCells);
-
-internal readonly record struct RenderFrameProductComparisonSnapshot(
- bool Enabled,
- ulong ProductFrameSequence,
- ulong CurrentPViewFrameSequence,
- ulong ComparisonCount,
- ulong SuccessfulComparisonCount,
- int ExpectedCandidateCount,
- int ActualCandidateCount,
- long MismatchCount,
- string? FirstMismatch,
- RenderSceneHash128 ExpectedDigest,
- RenderSceneHash128 ActualDigest,
- ulong PackedInputComparisonCount,
- ulong PackedInputSuccessfulComparisonCount,
- int PackedInputExpectedCount,
- int PackedInputActualCount,
- long PackedInputMismatchCount,
- string? PackedInputFirstMismatch,
- RenderSceneHash128 PackedInputExpectedDigest,
- RenderSceneHash128 PackedInputActualDigest,
- ulong ClassifiedOutputComparisonCount,
- ulong ClassifiedOutputSuccessfulComparisonCount,
- int ClassifiedOutputExpectedDrawCount,
- int ClassifiedOutputActualDrawCount,
- long ClassifiedOutputMismatchCount,
- string? ClassifiedOutputFirstMismatch,
- RenderSceneHash128 ClassifiedOutputExpectedDigest,
- RenderSceneHash128 ClassifiedOutputActualDigest,
- ulong SelectionOutputComparisonCount,
- ulong SelectionOutputSuccessfulComparisonCount,
- int SelectionOutputExpectedPartCount,
- int SelectionOutputActualPartCount,
- long SelectionOutputMismatchCount,
- string? SelectionOutputFirstMismatch,
- RenderSceneHash128 SelectionOutputExpectedDigest,
- RenderSceneHash128 SelectionOutputActualDigest,
- int PackedClassificationProjectionCount,
- int PackedClassificationStaticRebuildCount,
- int PackedClassificationSameFrameReuseCount,
- int PackedClassificationCrossFrameReuseCount,
- int PackedClassificationAnimatedCount,
- int PackedClassificationRetiredCount,
- long PackedClassificationRetainedPayloadBytes,
- RenderFrameDiagnosticCounts ProductCounts)
-{
- public static RenderFrameProductComparisonSnapshot Disabled { get; } =
- new(
- Enabled: false,
- ProductFrameSequence: 0,
- CurrentPViewFrameSequence: 0,
- ComparisonCount: 0,
- SuccessfulComparisonCount: 0,
- ExpectedCandidateCount: 0,
- ActualCandidateCount: 0,
- MismatchCount: 0,
- FirstMismatch: null,
- ExpectedDigest: RenderSceneHash128.Empty,
- ActualDigest: RenderSceneHash128.Empty,
- PackedInputComparisonCount: 0,
- PackedInputSuccessfulComparisonCount: 0,
- PackedInputExpectedCount: 0,
- PackedInputActualCount: 0,
- PackedInputMismatchCount: 0,
- PackedInputFirstMismatch: null,
- PackedInputExpectedDigest: RenderSceneHash128.Empty,
- PackedInputActualDigest: RenderSceneHash128.Empty,
- ClassifiedOutputComparisonCount: 0,
- ClassifiedOutputSuccessfulComparisonCount: 0,
- ClassifiedOutputExpectedDrawCount: 0,
- ClassifiedOutputActualDrawCount: 0,
- ClassifiedOutputMismatchCount: 0,
- ClassifiedOutputFirstMismatch: null,
- ClassifiedOutputExpectedDigest: RenderSceneHash128.Empty,
- ClassifiedOutputActualDigest: RenderSceneHash128.Empty,
- SelectionOutputComparisonCount: 0,
- SelectionOutputSuccessfulComparisonCount: 0,
- SelectionOutputExpectedPartCount: 0,
- SelectionOutputActualPartCount: 0,
- SelectionOutputMismatchCount: 0,
- SelectionOutputFirstMismatch: null,
- SelectionOutputExpectedDigest: RenderSceneHash128.Empty,
- SelectionOutputActualDigest: RenderSceneHash128.Empty,
- PackedClassificationProjectionCount: 0,
- PackedClassificationStaticRebuildCount: 0,
- PackedClassificationSameFrameReuseCount: 0,
- PackedClassificationCrossFrameReuseCount: 0,
- PackedClassificationAnimatedCount: 0,
- PackedClassificationRetiredCount: 0,
- PackedClassificationRetainedPayloadBytes: 0,
- ProductCounts: default);
-}
-
-internal interface IRenderFrameProductSnapshotSource
-{
- RenderFrameProductComparisonSnapshot Snapshot { get; }
-}
-
-///
-/// Owns the same-frame PView product. Diagnostic composition may compare it
-/// with the retired current-path observer; production borrows the exact same
-/// arena and supplies its ordered ranges directly to the dispatcher.
-///
-internal sealed class RenderScenePViewFrameProductController :
- IRenderFrameProductSnapshotSource
-{
- private readonly RenderSceneShadowRuntime _shadow;
- private readonly CurrentRenderSceneOracle? _current;
-
- /// Campaign FW3.2b-2: the frame's retained-scene read view for
- /// the walk's world-data provider (the same query
- /// reads its routes from).
- internal RenderSceneQuery SceneQuery => _shadow.Query;
- private readonly WbDrawDispatcher? _dispatcher;
- private readonly RenderScenePViewFrameBuilder _builder = new();
- private readonly RenderFrameExchange _exchange = new();
- private readonly List _expected = [];
- private readonly List _actual = [];
- private readonly List _packedExpected = [];
- private readonly List _packedActual = [];
- private readonly List _selectionExpected = [];
- private readonly List _selectionActual = [];
- private readonly Action? _log;
- private ulong _productFrameSequence;
- private ulong _comparisonCount;
- private ulong _successfulComparisonCount;
- private long _mismatchCount;
- private string? _firstMismatch;
- private ulong _packedInputComparisonCount;
- private ulong _packedInputSuccessfulComparisonCount;
- private long _packedInputMismatchCount;
- private string? _packedInputFirstMismatch;
- private ulong _classifiedOutputComparisonCount;
- private ulong _classifiedOutputSuccessfulComparisonCount;
- private long _classifiedOutputMismatchCount;
- private string? _classifiedOutputFirstMismatch;
- private ulong _selectionOutputComparisonCount;
- private ulong _selectionOutputSuccessfulComparisonCount;
- private long _selectionOutputMismatchCount;
- private string? _selectionOutputFirstMismatch;
- private PackedClassificationCacheSnapshot
- _packedClassification;
-
- public RenderScenePViewFrameProductController(
- RenderSceneShadowRuntime shadow,
- CurrentRenderSceneOracle? current = null,
- Action? log = null,
- WbDrawDispatcher? dispatcher = null)
- {
- _shadow = shadow ?? throw new ArgumentNullException(nameof(shadow));
- _current = current;
- _log = log;
- _dispatcher = dispatcher;
- Snapshot = RenderFrameProductComparisonSnapshot.Disabled with
- {
- Enabled = true,
- };
- }
-
- public RenderFrameProductComparisonSnapshot Snapshot { get; private set; }
-
- private CurrentRenderSceneOracle Current =>
- _current
- ?? throw new InvalidOperationException(
- "Current-path comparison was requested without a referee.");
-
- public RenderFrameView BuildAndBorrow(
- PortalVisibilityFrame portalFrame,
- ClipFrameAssembly clipAssembly,
- ViewconeCuller viewcone,
- IReadOnlyList lookInCellTurns,
- IWalkLookInViewSource? walkLookInViews,
- HashSet drawableCells,
- IRetailPViewCellSource cells,
- HashSet? animatedEntityIds,
- bool rootIsOutdoor,
- IReadOnlySet rootFloodCells)
- {
- ArgumentNullException.ThrowIfNull(portalFrame);
- ArgumentNullException.ThrowIfNull(clipAssembly);
- ArgumentNullException.ThrowIfNull(viewcone);
- ArgumentNullException.ThrowIfNull(lookInCellTurns);
- ArgumentNullException.ThrowIfNull(drawableCells);
- ArgumentNullException.ThrowIfNull(cells);
-
- ulong frameSequence = checked(++_productFrameSequence);
- RenderSceneQuery scene = _shadow.Query;
- RenderSceneDigest sourceDigest = _current is null
- ? new RenderSceneDigest(
- scene.Generation,
- scene.Counts,
- default)
- : _shadow.BuildDigest();
- var input = new RenderScenePViewBuildInput(
- scene,
- sourceDigest,
- portalFrame,
- clipAssembly,
- viewcone,
- lookInCellTurns,
- walkLookInViews,
- drawableCells,
- cells,
- animatedEntityIds,
- rootIsOutdoor,
- rootFloodCells);
- _builder.Build(_exchange, frameSequence, in input);
- return _exchange.BorrowLatest(
- scene.Generation,
- frameSequence);
- }
-
- public void Release(in RenderFrameView view) =>
- _exchange.Release(in view);
-
- public void BuildAndCompare(
- PortalVisibilityFrame portalFrame,
- ClipFrameAssembly clipAssembly,
- ViewconeCuller viewcone,
- IReadOnlyList lookInFrames,
- HashSet drawableCells,
- IRetailPViewCellSource cells,
- HashSet? animatedEntityIds,
- uint tupleLandblockId,
- bool rootIsOutdoor,
- Vector3 cameraWorldPosition = default,
- IReadOnlySet? 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(
- portalFrame,
- clipAssembly,
- viewcone,
- FlattenLegacyLookInCells(lookInFrames),
- walkLookInViews: null,
- drawableCells,
- cells,
- animatedEntityIds,
- rootIsOutdoor,
- rootFloodCells ?? drawableCells);
- try
- {
- Compare(
- in view,
- tupleLandblockId,
- cameraWorldPosition);
- }
- finally
- {
- Release(in view);
- }
- }
-
- private static uint[] FlattenLegacyLookInCells(
- IReadOnlyList lookInFrames)
- {
- var cells = new List();
- for (int frameIndex = 0; frameIndex < lookInFrames.Count; frameIndex++)
- {
- PortalVisibilityFrame frame = lookInFrames[frameIndex];
- for (int index = frame.OrderedVisibleCells.Count - 1; index >= 0; index--)
- cells.Add(frame.OrderedVisibleCells[index]);
- }
- return cells.ToArray();
- }
-
- public void CompleteProduction(in RenderFrameView view)
- {
- _packedClassification =
- _dispatcher?.PackedClassificationSnapshot ?? default;
- if (_current is null)
- {
- _shadow.ClearDirty();
- Snapshot = Snapshot with
- {
- Enabled = true,
- ProductFrameSequence = view.FrameSequence,
- ActualCandidateCount = view.RouteCandidates.Length,
- PackedClassificationProjectionCount =
- _packedClassification.ProjectionCount,
- PackedClassificationStaticRebuildCount =
- _packedClassification.StaticRebuildCount,
- PackedClassificationSameFrameReuseCount =
- _packedClassification.SameFrameReuseCount,
- PackedClassificationCrossFrameReuseCount =
- _packedClassification.CrossFrameReuseCount,
- PackedClassificationAnimatedCount =
- _packedClassification.AnimatedClassificationCount,
- PackedClassificationRetiredCount =
- _packedClassification.RetiredProjectionCount,
- PackedClassificationRetainedPayloadBytes =
- _packedClassification.RetainedPayloadBytes,
- ProductCounts = view.DiagnosticCounts,
- };
- return;
- }
-
- CandidateComparison candidates =
- CompareCandidateMembership(in view);
- if (candidates.Successful)
- _shadow.ClearDirty();
- Snapshot = Snapshot with
- {
- Enabled = true,
- ProductFrameSequence = view.FrameSequence,
- CurrentPViewFrameSequence =
- Current.Snapshot.CompletedPViewFrameSequence,
- ComparisonCount = _comparisonCount,
- SuccessfulComparisonCount = _successfulComparisonCount,
- ExpectedCandidateCount = _expected.Count,
- ActualCandidateCount = _actual.Count,
- MismatchCount = _mismatchCount,
- FirstMismatch = _firstMismatch,
- ExpectedDigest = candidates.ExpectedDigest,
- ActualDigest = candidates.ActualDigest,
- PackedClassificationProjectionCount =
- _packedClassification.ProjectionCount,
- PackedClassificationStaticRebuildCount =
- _packedClassification.StaticRebuildCount,
- PackedClassificationSameFrameReuseCount =
- _packedClassification.SameFrameReuseCount,
- PackedClassificationCrossFrameReuseCount =
- _packedClassification.CrossFrameReuseCount,
- PackedClassificationAnimatedCount =
- _packedClassification.AnimatedClassificationCount,
- PackedClassificationRetiredCount =
- _packedClassification.RetiredProjectionCount,
- PackedClassificationRetainedPayloadBytes =
- _packedClassification.RetainedPayloadBytes,
- ProductCounts = view.DiagnosticCounts,
- };
- }
-
- private CandidateComparison CompareCandidateMembership(
- in RenderFrameView view)
- {
- _comparisonCount = checked(_comparisonCount + 1);
- _expected.Clear();
- _actual.Clear();
-
- IReadOnlyList expected =
- Current.PViewCandidates;
- if (_expected.Capacity < expected.Count)
- _expected.Capacity = expected.Count;
- for (int index = 0; index < expected.Count; index++)
- {
- CurrentRenderPViewCandidateFingerprint candidate =
- expected[index];
- CurrentRenderProjectionFingerprint projection =
- candidate.Projection;
- _expected.Add(new CandidateKey(
- Map(candidate.Route),
- candidate.RouteIndex,
- candidate.CellId,
- ExpectedId(in projection)));
- }
-
- ReadOnlySpan ranges =
- view.RouteRanges;
- ReadOnlySpan records =
- view.RouteCandidates;
- if (_actual.Capacity < records.Length)
- _actual.Capacity = records.Length;
- for (int rangeIndex = 0;
- rangeIndex < ranges.Length;
- rangeIndex++)
- {
- RenderFrameCandidateRange range = ranges[rangeIndex];
- int end = checked(range.Offset + range.Count);
- if ((uint)range.Offset > (uint)records.Length
- || (uint)end > (uint)records.Length)
- {
- throw new InvalidOperationException(
- $"Render-frame route range {rangeIndex} exceeds its candidate storage.");
- }
-
- for (int index = range.Offset; index < end; index++)
- {
- _actual.Add(new CandidateKey(
- range.Route,
- range.RouteIndex,
- range.CellId,
- records[index].Id));
- }
- }
-
- RenderSceneHash128 expectedDigest = BuildDigest(_expected);
- RenderSceneHash128 actualDigest = BuildDigest(_actual);
- string? mismatch = CompareKeys();
- if (mismatch is null)
- {
- _successfulComparisonCount =
- checked(_successfulComparisonCount + 1);
- }
- else
- {
- _mismatchCount = checked(_mismatchCount + 1);
- if (_firstMismatch is null)
- {
- _firstMismatch = mismatch;
- _log?.Invoke(
- "[render-frame-product] first mismatch: "
- + mismatch);
- }
- }
-
- return new CandidateComparison(
- mismatch is null,
- expectedDigest,
- actualDigest);
- }
-
- private void Compare(
- in RenderFrameView view,
- uint tupleLandblockId,
- Vector3 cameraWorldPosition)
- {
- CandidateComparison candidates =
- CompareCandidateMembership(in view);
- RenderSceneHash128 expectedDigest =
- candidates.ExpectedDigest;
- RenderSceneHash128 actualDigest =
- candidates.ActualDigest;
- string? mismatch = candidates.Successful
- ? null
- : _firstMismatch;
-
- PackedInputComparison packed =
- ComparePackedInput(in view, tupleLandblockId);
- ClassifiedOutputComparison classified =
- CompareClassifiedOutput(
- in view,
- tupleLandblockId,
- cameraWorldPosition);
- if (_dispatcher is not null
- && mismatch is null
- && packed.Successful
- && classified.Successful)
- {
- // The frame product and retained classifier have consumed every
- // dirty fact and matched the production oracle. A later update
- // starts the next dirty epoch; a mismatch or exception above
- // deliberately leaves dirtiness intact for the next rebuild.
- _shadow.ClearDirty();
- }
-
- Snapshot = new RenderFrameProductComparisonSnapshot(
- Enabled: true,
- ProductFrameSequence: view.FrameSequence,
- CurrentPViewFrameSequence:
- Current.Snapshot.CompletedPViewFrameSequence,
- ComparisonCount: _comparisonCount,
- SuccessfulComparisonCount: _successfulComparisonCount,
- ExpectedCandidateCount: _expected.Count,
- ActualCandidateCount: _actual.Count,
- MismatchCount: _mismatchCount,
- FirstMismatch: _firstMismatch,
- ExpectedDigest: expectedDigest,
- ActualDigest: actualDigest,
- PackedInputComparisonCount: _packedInputComparisonCount,
- PackedInputSuccessfulComparisonCount:
- _packedInputSuccessfulComparisonCount,
- PackedInputExpectedCount: _packedExpected.Count,
- PackedInputActualCount: _packedActual.Count,
- PackedInputMismatchCount: _packedInputMismatchCount,
- PackedInputFirstMismatch: _packedInputFirstMismatch,
- PackedInputExpectedDigest: packed.ExpectedDigest,
- PackedInputActualDigest: packed.ActualDigest,
- ClassifiedOutputComparisonCount:
- _classifiedOutputComparisonCount,
- ClassifiedOutputSuccessfulComparisonCount:
- _classifiedOutputSuccessfulComparisonCount,
- ClassifiedOutputExpectedDrawCount:
- classified.ExpectedDrawCount,
- ClassifiedOutputActualDrawCount:
- classified.ActualDrawCount,
- ClassifiedOutputMismatchCount:
- _classifiedOutputMismatchCount,
- ClassifiedOutputFirstMismatch:
- _classifiedOutputFirstMismatch,
- ClassifiedOutputExpectedDigest:
- classified.ExpectedDigest,
- ClassifiedOutputActualDigest:
- classified.ActualDigest,
- SelectionOutputComparisonCount:
- _selectionOutputComparisonCount,
- SelectionOutputSuccessfulComparisonCount:
- _selectionOutputSuccessfulComparisonCount,
- SelectionOutputExpectedPartCount:
- classified.ExpectedSelectionCount,
- SelectionOutputActualPartCount:
- classified.ActualSelectionCount,
- SelectionOutputMismatchCount:
- _selectionOutputMismatchCount,
- SelectionOutputFirstMismatch:
- _selectionOutputFirstMismatch,
- SelectionOutputExpectedDigest:
- classified.ExpectedSelectionDigest,
- SelectionOutputActualDigest:
- classified.ActualSelectionDigest,
- PackedClassificationProjectionCount:
- _packedClassification.ProjectionCount,
- PackedClassificationStaticRebuildCount:
- _packedClassification.StaticRebuildCount,
- PackedClassificationSameFrameReuseCount:
- _packedClassification.SameFrameReuseCount,
- PackedClassificationCrossFrameReuseCount:
- _packedClassification.CrossFrameReuseCount,
- PackedClassificationAnimatedCount:
- _packedClassification.AnimatedClassificationCount,
- PackedClassificationRetiredCount:
- _packedClassification.RetiredProjectionCount,
- PackedClassificationRetainedPayloadBytes:
- _packedClassification.RetainedPayloadBytes,
- ProductCounts: view.DiagnosticCounts);
- }
-
- private ClassifiedOutputComparison CompareClassifiedOutput(
- in RenderFrameView view,
- uint tupleLandblockId,
- Vector3 cameraWorldPosition)
- {
- WbDrawDispatcher? dispatcher = _dispatcher;
- if (dispatcher is null)
- return default;
-
- dispatcher.BuildPackedDispatcherOracle(
- in view,
- tupleLandblockId,
- cameraWorldPosition);
- _packedClassification =
- dispatcher.PackedClassificationSnapshot;
- IReadOnlyList expected =
- Current.DispatcherSubmissions;
- IReadOnlyList actual =
- dispatcher.PackedDispatcherSubmissions;
- RenderSceneHash128 expectedDigest =
- BuildSubmissionDigest(expected);
- RenderSceneHash128 actualDigest =
- BuildSubmissionDigest(actual);
- _classifiedOutputComparisonCount =
- checked(_classifiedOutputComparisonCount + 1);
- string? mismatch = CompareSubmissions(expected, actual);
- if (mismatch is null)
- {
- _classifiedOutputSuccessfulComparisonCount =
- checked(
- _classifiedOutputSuccessfulComparisonCount + 1);
- }
- else
- {
- _classifiedOutputMismatchCount =
- checked(_classifiedOutputMismatchCount + 1);
- if (_classifiedOutputFirstMismatch is null)
- {
- _classifiedOutputFirstMismatch = mismatch;
- _log?.Invoke(
- "[render-classified-output] first mismatch: "
- + mismatch
- + $" classification={_packedClassification}");
- }
- }
-
- IReadOnlyList
- expectedSelection = Current.SelectionParts;
- IReadOnlyList
- actualSelection = dispatcher.PackedSelectionParts;
- BuildSelectionKeys(
- expectedSelection,
- _selectionExpected);
- BuildSelectionKeys(
- actualSelection,
- _selectionActual);
- RenderSceneHash128 expectedSelectionDigest =
- BuildSelectionDigest(_selectionExpected);
- RenderSceneHash128 actualSelectionDigest =
- BuildSelectionDigest(_selectionActual);
- _selectionOutputComparisonCount =
- checked(_selectionOutputComparisonCount + 1);
- string? selectionMismatch = CompareSelection(
- _selectionExpected,
- _selectionActual);
- if (selectionMismatch is null)
- {
- _selectionOutputSuccessfulComparisonCount =
- checked(
- _selectionOutputSuccessfulComparisonCount + 1);
- }
- else
- {
- _selectionOutputMismatchCount =
- checked(_selectionOutputMismatchCount + 1);
- if (_selectionOutputFirstMismatch is null)
- {
- _selectionOutputFirstMismatch = selectionMismatch;
- _log?.Invoke(
- "[render-selection-output] first mismatch: "
- + selectionMismatch
- + $" classification={_packedClassification}");
- }
- }
-
- return new ClassifiedOutputComparison(
- Successful: mismatch is null && selectionMismatch is null,
- expected.Count,
- actual.Count,
- expectedDigest,
- actualDigest,
- expectedSelection.Count,
- actualSelection.Count,
- expectedSelectionDigest,
- actualSelectionDigest);
- }
-
- private static string? CompareSubmissions(
- IReadOnlyList expected,
- IReadOnlyList actual)
- {
- if (expected.Count != actual.Count)
- {
- int shared = Math.Min(expected.Count, actual.Count);
- for (int index = 0; index < shared; index++)
- {
- if (expected[index] != actual[index])
- {
- return $"field=draw index={index} "
- + $"expected={expected[index]} "
- + $"actual={actual[index]}";
- }
- }
-
- string extra = expected.Count > actual.Count
- ? $"firstExtraExpected={expected[shared]}"
- : $"firstExtraActual={actual[shared]}";
- return $"field=drawCount expected={expected.Count} "
- + $"actual={actual.Count} {extra}";
- }
-
- for (int index = 0; index < expected.Count; index++)
- {
- if (expected[index] != actual[index])
- {
- return $"field=draw index={index} "
- + $"expected={expected[index]} "
- + $"actual={actual[index]}";
- }
- }
-
- return null;
- }
-
- private static string? CompareSelection(
- IReadOnlyList expected,
- IReadOnlyList actual)
- {
- if (expected.Count != actual.Count)
- {
- int shared = Math.Min(expected.Count, actual.Count);
- for (int index = 0; index < shared; index++)
- {
- if (expected[index] != actual[index])
- {
- return $"field=part index={index} "
- + $"expected={expected[index]} "
- + $"actual={actual[index]}";
- }
- }
-
- string extra = expected.Count > actual.Count
- ? $"firstExtraExpected={expected[shared]}"
- : $"firstExtraActual={actual[shared]}";
- return $"field=partCount expected={expected.Count} "
- + $"actual={actual.Count} {extra}";
- }
-
- for (int index = 0; index < expected.Count; index++)
- {
- if (expected[index] != actual[index])
- {
- return $"field=part index={index} "
- + $"expected={expected[index]} "
- + $"actual={actual[index]}";
- }
- }
-
- return null;
- }
-
- private static RenderSceneHash128 BuildSubmissionDigest(
- IReadOnlyList submissions)
- {
- StableRenderHash128 hash = StableRenderHash128.Create();
- hash.Add(submissions.Count);
- for (int index = 0; index < submissions.Count; index++)
- {
- CurrentRenderDispatcherSubmission submission =
- submissions[index];
- hash.Add(submission.VisibleInstanceCount);
- hash.Add(submission.ImmediateInstanceCount);
- hash.Add(submission.OpaqueGroupCount);
- hash.Add(submission.TransparentGroupCount);
- hash.Add(submission.TransparentDeferred);
- hash.Add(submission.OpaqueDigest.Low);
- hash.Add(submission.OpaqueDigest.High);
- hash.Add(submission.TransparentDigest.Low);
- hash.Add(submission.TransparentDigest.High);
- hash.Add(submission.TransparentSetDigest.Low);
- hash.Add(submission.TransparentSetDigest.High);
- hash.Add(submission.Digest.Low);
- hash.Add(submission.Digest.High);
- }
-
- return hash.Finish();
- }
-
- private static void BuildSelectionKeys(
- IReadOnlyList source,
- List destination)
- {
- destination.Clear();
- if (destination.Capacity < source.Count)
- destination.Capacity = source.Count;
- for (int index = 0; index < source.Count; index++)
- {
- CurrentRenderSelectionFingerprint part = source[index];
- destination.Add(new SelectionKey(
- part.ServerGuid,
- part.LocalEntityId,
- part.PartIndex,
- part.GfxObjId,
- part.LocalToWorld,
- part.Geometry));
- }
- destination.Sort(SelectionKeyComparer.Instance);
- }
-
- private static RenderSceneHash128 BuildSelectionDigest(
- IReadOnlyList parts)
- {
- StableRenderHash128 hash = StableRenderHash128.Create();
- hash.Add(parts.Count);
- for (int index = 0; index < parts.Count; index++)
- {
- SelectionKey part = parts[index];
- hash.Add(part.ServerGuid);
- hash.Add(part.LocalEntityId);
- hash.Add(part.PartIndex);
- hash.Add(part.GfxObjId);
- hash.Add(part.LocalToWorld);
- hash.Add(part.Geometry.Low);
- hash.Add(part.Geometry.High);
- }
-
- return hash.Finish();
- }
-
- private PackedInputComparison ComparePackedInput(
- in RenderFrameView view,
- uint tupleLandblockId)
- {
- _packedInputComparisonCount =
- checked(_packedInputComparisonCount + 1);
- _packedExpected.Clear();
- _packedActual.Clear();
-
- IReadOnlyList expected =
- Current.DispatcherCandidates;
- if (_packedExpected.Capacity < expected.Count)
- _packedExpected.Capacity = expected.Count;
- for (int index = 0; index < expected.Count; index++)
- {
- CurrentRenderDispatcherFingerprint candidate = expected[index];
- CurrentRenderProjectionFingerprint projection =
- candidate.Projection;
- _packedExpected.Add(new PackedInputKey(
- candidate.DrawSequence,
- candidate.Set,
- ExpectedId(in projection),
- candidate.MeshRefIndex,
- candidate.TupleLandblockId,
- candidate.CacheLandblockId,
- projection.EntityId,
- projection.ServerGuid,
- projection.SourceId,
- projection.ParentCellId,
- projection.EffectCellId,
- projection.Flags,
- projection.Transform,
- projection.Geometry,
- projection.Appearance,
- candidate.GfxObjId,
- candidate.PartTransform,
- candidate.SurfaceOverrides));
- }
-
- ReadOnlySpan ranges = view.RouteRanges;
- ReadOnlySpan routeCandidates =
- view.RouteCandidates;
- if (_packedActual.Capacity < view.MeshParts.Length)
- _packedActual.Capacity = view.MeshParts.Length;
- for (int drawSequence = 0;
- drawSequence < ranges.Length;
- drawSequence++)
- {
- RenderFrameCandidateRange range = ranges[drawSequence];
- int end = checked(range.Offset + range.Count);
- for (int candidateIndex = range.Offset;
- candidateIndex < end;
- candidateIndex++)
- {
- RenderProjectionRecord projection =
- routeCandidates[candidateIndex];
- if ((projection.Flags & RenderProjectionFlags.Draw) == 0)
- continue;
-
- IReadOnlyList? meshRefs =
- projection.EntityPayload.MeshRefs;
- int meshCount = meshRefs?.Count ?? 0;
- for (int partIndex = 0;
- partIndex < meshCount;
- partIndex++)
- {
- AcDream.Core.World.MeshRef meshRef =
- meshRefs![partIndex];
- _packedActual.Add(new PackedInputKey(
- drawSequence,
- WbDrawDispatcher.EntitySet.All,
- projection.Id,
- partIndex,
- tupleLandblockId,
- DispatcherCacheLandblock(
- in projection,
- tupleLandblockId),
- projection.Source.LocalEntityId,
- projection.Source.ServerGuid,
- projection.Source.SourceId,
- projection.Source.ParentCellId,
- projection.Source.EffectCellId,
- projection.Source.CurrentProjectionFlags,
- projection.Source.TransformFingerprint,
- projection.Source.GeometryFingerprint,
- projection.Source.AppearanceFingerprint,
- meshRef.GfxObjId,
- meshRef.PartTransform,
- CurrentRenderSceneOracle
- .CreateSurfaceOverrideFingerprint(
- meshRef.SurfaceOverrides)));
- }
- }
- }
-
- RenderSceneHash128 expectedDigest =
- BuildPackedInputDigest(_packedExpected);
- RenderSceneHash128 actualDigest =
- BuildPackedInputDigest(_packedActual);
- string? mismatch = ComparePackedInputKeys(ranges.Length);
- if (mismatch is null)
- {
- _packedInputSuccessfulComparisonCount =
- checked(_packedInputSuccessfulComparisonCount + 1);
- }
- else
- {
- _packedInputMismatchCount =
- checked(_packedInputMismatchCount + 1);
- if (_packedInputFirstMismatch is null)
- {
- _packedInputFirstMismatch = mismatch;
- _log?.Invoke(
- "[render-packed-input] first mismatch: " + mismatch);
- }
- }
-
- return new PackedInputComparison(
- Successful: mismatch is null,
- expectedDigest,
- actualDigest);
- }
-
- private static uint DispatcherCacheLandblock(
- in RenderProjectionRecord projection,
- uint tupleLandblockId) =>
- projection.Source.ParentCellId != 0
- ? (projection.Source.ParentCellId & 0xFFFF0000u) | 0xFFFFu
- : tupleLandblockId;
-
- private string? ComparePackedInputKeys(int actualDrawCount)
- {
- if (Current.Snapshot.DispatcherDrawCount != actualDrawCount)
- {
- return $"field=drawCount expected="
- + $"{Current.Snapshot.DispatcherDrawCount} "
- + $"actual={actualDrawCount}";
- }
- if (_packedExpected.Count != _packedActual.Count)
- {
- int shared = Math.Min(
- _packedExpected.Count,
- _packedActual.Count);
- for (int index = 0; index < shared; index++)
- {
- if (_packedExpected[index] != _packedActual[index])
- {
- return $"field=meshPart index={index} "
- + $"expected={_packedExpected[index]} "
- + $"actual={_packedActual[index]}";
- }
- }
-
- string extra = _packedExpected.Count > _packedActual.Count
- ? $"firstExtraExpected={_packedExpected[shared]}"
- : $"firstExtraActual={_packedActual[shared]}";
- return $"field=count expected={_packedExpected.Count} "
- + $"actual={_packedActual.Count} {extra}";
- }
-
- for (int index = 0; index < _packedExpected.Count; index++)
- {
- if (_packedExpected[index] != _packedActual[index])
- {
- return $"field=meshPart index={index} "
- + $"expected={_packedExpected[index]} "
- + $"actual={_packedActual[index]}";
- }
- }
-
- return null;
- }
-
- private static RenderSceneHash128 BuildPackedInputDigest(
- List candidates)
- {
- StableRenderHash128 hash = StableRenderHash128.Create();
- hash.Add(candidates.Count);
- for (int index = 0; index < candidates.Count; index++)
- candidates[index].AddTo(ref hash);
- return hash.Finish();
- }
-
- private string? CompareKeys()
- {
- if (_expected.Count != _actual.Count)
- {
- int shared = Math.Min(_expected.Count, _actual.Count);
- for (int index = 0; index < shared; index++)
- {
- if (_expected[index] != _actual[index])
- {
- return $"field=candidate index={index} "
- + $"expected={_expected[index]} "
- + $"actual={_actual[index]}";
- }
- }
-
- string extra = _expected.Count > _actual.Count
- ? $"firstExtraExpected={_expected[shared]}"
- : $"firstExtraActual={_actual[shared]}";
- return $"field=count expected={_expected.Count} "
- + $"actual={_actual.Count} {extra}";
- }
-
- for (int i = 0; i < _expected.Count; i++)
- {
- if (_expected[i] != _actual[i])
- {
- return $"field=candidate index={i} "
- + $"expected={_expected[i]} actual={_actual[i]}";
- }
- }
-
- return null;
- }
-
- private static RenderSceneHash128 BuildDigest(
- List candidates)
- {
- StableRenderHash128 hash = StableRenderHash128.Create();
- hash.Add(candidates.Count);
- for (int i = 0; i < candidates.Count; i++)
- {
- CandidateKey candidate = candidates[i];
- hash.Add((byte)candidate.Route);
- hash.Add(candidate.RouteIndex);
- hash.Add(candidate.CellId);
- candidate.ProjectionId.AddTo(ref hash);
- }
- return hash.Finish();
- }
-
- private static RenderProjectionId ExpectedId(
- in CurrentRenderProjectionFingerprint projection) =>
- projection.ServerGuid == 0
- ? StaticRenderProjectionJournal.StaticEntityId(
- projection.LandblockId,
- projection.EntityId)
- : LiveRenderProjectionJournal.ProjectionId(
- projection.EntityId);
-
- private static RenderFrameCandidateRoute Map(
- CurrentRenderPViewRoute route) =>
- route switch
- {
- CurrentRenderPViewRoute.LandscapeOutdoorStatic =>
- RenderFrameCandidateRoute.LandscapeOutdoorStatic,
- CurrentRenderPViewRoute.LandscapeBuildingShell =>
- RenderFrameCandidateRoute.LandscapeBuildingShell,
- CurrentRenderPViewRoute.LandscapeOutsideDynamic =>
- RenderFrameCandidateRoute.LandscapeOutsideDynamic,
- CurrentRenderPViewRoute.LookInObject =>
- RenderFrameCandidateRoute.LookInObject,
- CurrentRenderPViewRoute.CellStatic =>
- RenderFrameCandidateRoute.CellStatic,
- CurrentRenderPViewRoute.DynamicLast =>
- RenderFrameCandidateRoute.DynamicLast,
- _ => throw new ArgumentOutOfRangeException(
- nameof(route),
- route,
- null),
- };
-
- private readonly record struct CandidateKey(
- RenderFrameCandidateRoute Route,
- int RouteIndex,
- uint CellId,
- RenderProjectionId ProjectionId);
-
- private readonly record struct PackedInputComparison(
- bool Successful,
- RenderSceneHash128 ExpectedDigest,
- RenderSceneHash128 ActualDigest);
-
- private readonly record struct CandidateComparison(
- bool Successful,
- RenderSceneHash128 ExpectedDigest,
- RenderSceneHash128 ActualDigest);
-
- private readonly record struct ClassifiedOutputComparison(
- bool Successful,
- int ExpectedDrawCount,
- int ActualDrawCount,
- RenderSceneHash128 ExpectedDigest,
- RenderSceneHash128 ActualDigest,
- int ExpectedSelectionCount,
- int ActualSelectionCount,
- RenderSceneHash128 ExpectedSelectionDigest,
- RenderSceneHash128 ActualSelectionDigest);
-
- private readonly record struct SelectionKey(
- uint ServerGuid,
- uint LocalEntityId,
- int PartIndex,
- uint GfxObjId,
- Matrix4x4 LocalToWorld,
- RenderSceneHash128 Geometry);
-
- private sealed class SelectionKeyComparer : IComparer
- {
- public static SelectionKeyComparer Instance { get; } = new();
-
- private SelectionKeyComparer()
- {
- }
-
- public int Compare(SelectionKey left, SelectionKey right)
- {
- int value = left.LocalEntityId.CompareTo(
- right.LocalEntityId);
- if (value != 0) return value;
- value = left.PartIndex.CompareTo(right.PartIndex);
- if (value != 0) return value;
- value = left.GfxObjId.CompareTo(right.GfxObjId);
- if (value != 0) return value;
- return left.ServerGuid.CompareTo(right.ServerGuid);
- }
- }
-
- private readonly record struct PackedInputKey(
- int DrawSequence,
- WbDrawDispatcher.EntitySet Set,
- RenderProjectionId ProjectionId,
- int MeshRefIndex,
- uint TupleLandblockId,
- uint CacheLandblockId,
- uint LocalEntityId,
- uint ServerGuid,
- uint SourceId,
- uint ParentCellId,
- uint EffectCellId,
- uint SourceFlags,
- RenderSceneHash128 Transform,
- RenderSceneHash128 Geometry,
- RenderSceneHash128 Appearance,
- uint GfxObjId,
- Matrix4x4 PartTransform,
- RenderSceneHash128 SurfaceOverrides)
- {
- public void AddTo(ref StableRenderHash128 hash)
- {
- hash.Add(DrawSequence);
- hash.Add((int)Set);
- ProjectionId.AddTo(ref hash);
- hash.Add(MeshRefIndex);
- hash.Add(TupleLandblockId);
- hash.Add(CacheLandblockId);
- hash.Add(LocalEntityId);
- hash.Add(ServerGuid);
- hash.Add(SourceId);
- hash.Add(ParentCellId);
- hash.Add(EffectCellId);
- hash.Add(SourceFlags);
- hash.Add(Transform.Low);
- hash.Add(Transform.High);
- hash.Add(Geometry.Low);
- hash.Add(Geometry.High);
- hash.Add(Appearance.Low);
- hash.Add(Appearance.High);
- hash.Add(GfxObjId);
- hash.Add(PartTransform);
- hash.Add(SurfaceOverrides.Low);
- hash.Add(SurfaceOverrides.High);
- }
- }
-
-}
-
-///
-/// Allocation-free scene-query projection for one accepted PView frame.
-/// It applies the existing viewcone and outside-stage predicates verbatim,
-/// changing only the source of candidate records.
-///
-internal sealed class RenderScenePViewFrameBuilder
-{
- private const byte EnvCellProjectionDomain = 2;
-
- private readonly HashSet _projectionIds = [];
- private RenderProjectionRecord[] _dynamics = [];
- private RenderProjectionRecord[] _cell = [];
- private RenderProjectionRecord[] _dirty = [];
- private RenderProjectionRecord[] _survivors = [];
-
- // Cells drawn as building look-ins this frame — the DynamicLast route
- // honors retail's drawn-once contract (DrawMeshInternal @0x0059F360
- // marks every non-player part): an object whose cell drew with a look-in
- // must not enter the final dynamics route again.
- private readonly HashSet _lookInCellScratch = new();
- private readonly Dictionary
- _dynamicPositions = [];
- private int _dynamicCount;
-
- // ACDREAM_PROBE_WALK_ROOT companion (throwaway): rate limiter for the
- // [walk-dyn] outside-stage classification dump, plus the [dyn-route]
- // per-entity on-change admission cache.
- private uint _probeDynFrameCounter;
- private readonly Dictionary _probeDynRouteStates = new();
- private int _dirtyCount;
- private RenderSceneGeneration _indexGeneration;
- private ulong _indexRevision;
-
- public void Build(
- RenderFrameExchange exchange,
- ulong frameSequence,
- in RenderScenePViewBuildInput input)
- {
- ArgumentNullException.ThrowIfNull(exchange);
- RenderFrameWriter writer =
- exchange.BeginBuild(input.Scene.Generation, frameSequence);
- try
- {
- writer.SetBorrowedProducts(
- input.PortalFrame,
- input.ClipAssembly,
- input.WalkLookInViews);
- RenderSceneDigest digest = input.SourceDigest;
- writer.SetSourceDigest(in digest);
- _projectionIds.Clear();
- LoadSceneIndices(input.Scene);
-
- // The walk owns every look-in cell's complete object-list turn.
- // Keep one set for both downstream dynamic routes: those objects
- // belong to LookInObject exactly once, never to the generic
- // landscape-outside route and never to DynamicLast. The former
- // outside-route duplicate was especially damaging across an
- // interior root's depth clear: its color survived the clear even
- // when the later look-in draw was correctly occluded by a wall.
- _lookInCellScratch.Clear();
- for (int index = 0; index < input.LookInCellTurns.Count; index++)
- _lookInCellScratch.Add(input.LookInCellTurns[index]);
-
- // FW4: the walk now owns the complete look-in object-list turn —
- // statics, live dynamics, fades, picking, and particles. Do not
- // advertise a packed LookInObject range the dispatcher cannot and
- // must not consume a second time.
- // FW4 cutover: once a walk view source is present, every live
- // object is emitted at its retail cell turn (landscape, root
- // flood, or building look-in). The packed dynamic ranges exist
- // only for non-walk diagnostic callers during this atomic slice;
- // advertising either range in production would draw the same
- // object a second time after its owner cell.
- if (input.WalkLookInViews is null)
- {
- BuildOutsideDynamicRoutes(writer, in input);
- BuildDynamicLastRoute(writer, in input);
- }
- writer.Publish();
- AcknowledgeCachedDirtyRecords();
- }
- catch
- {
- writer.Abort();
- throw;
- }
- }
-
- private void LoadSceneIndices(RenderSceneQuery scene)
- {
- RenderSceneIndexCounts counts = scene.IndexCounts;
- ulong revision = scene.IndexRevision;
- if (scene.Generation != _indexGeneration
- || revision != _indexRevision)
- {
- EnsureCapacity(ref _dynamics, counts.Dynamic);
- _dynamicCount = scene.CopyIndexTo(
- RenderSceneIndex.Dynamic,
- _dynamics);
- _dynamicCount = CompactAndSort(
- _dynamics,
- _dynamicCount);
- BuildPositionIndex(
- _dynamics,
- _dynamicCount,
- _dynamicPositions);
-
- _indexGeneration = scene.Generation;
- _indexRevision = revision;
- }
-
- EnsureCapacity(ref _dirty, counts.Dirty);
- _dirtyCount = scene.CopyIndexTo(
- RenderSceneIndex.Dirty,
- _dirty);
- for (int index = 0; index < _dirtyCount; index++)
- {
- RenderProjectionRecord record = _dirty[index];
- if (_dynamicPositions.TryGetValue(
- record.Id,
- out int dynamicPosition))
- {
- _dynamics[dynamicPosition] = record;
- }
- }
- }
-
- private void AcknowledgeCachedDirtyRecords()
- {
- for (int index = 0; index < _dirtyCount; index++)
- {
- RenderProjectionId id = _dirty[index].Id;
- if (_dynamicPositions.TryGetValue(
- id,
- out int dynamicPosition))
- {
- _dynamics[dynamicPosition] =
- _dynamics[dynamicPosition] with
- {
- DirtyMask = RenderDirtyMask.None,
- };
- }
- }
- }
-
- private static void BuildPositionIndex(
- RenderProjectionRecord[] records,
- int count,
- Dictionary positions)
- {
- positions.Clear();
- positions.EnsureCapacity(count);
- for (int index = 0; index < count; index++)
- positions.Add(records[index].Id, index);
- }
-
- /// Campaign FW3.2b-2: LookInObject now carries DYNAMIC
- /// candidates only — the walk draws every look-in cell's STATIC content
- /// directly (WalkFrameDriver's LookInStatic turn, via
- /// WalkProductionWorldData.GetCellStatics), so loading this
- /// route's cell contents with includeStatics: false is what keeps
- /// the two draws from doubling a look-in room's furniture (plan §FW3
- /// item 3).
- private void BuildLookInRoutes(
- RenderFrameWriter writer,
- in RenderScenePViewBuildInput input)
- {
- for (int routeIndex = 0; routeIndex < input.LookInCellTurns.Count; routeIndex++)
- {
- uint cellId = input.LookInCellTurns[routeIndex];
- int count = LoadCell(
- input.Scene,
- cellId,
- includeStatics: false,
- includeDynamics: true);
- if (count == 0)
- continue;
-
- int survivorCount = 0;
- EnsureCapacity(ref _survivors, count);
- for (int index = 0; index < count; index++)
- {
- RenderProjectionRecord record = _cell[index];
- _survivors[survivorCount++] = record;
- AddProjection(
- writer,
- in record,
- input.AnimatedEntityIds);
- }
- if (survivorCount == 0)
- continue;
- writer.AddRouteRange(
- RenderFrameCandidateRoute.LookInObject,
- routeIndex,
- cellId,
- _survivors.AsSpan(0, survivorCount));
- }
- }
-
- private void BuildOutsideDynamicRoutes(
- RenderFrameWriter writer,
- in RenderScenePViewBuildInput input)
- {
- if (input.RootIsOutdoor)
- return;
-
- int sliceCount = input.ClipAssembly.OutsideViewSlices.Length;
- bool probeDynamics =
- AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled
- && ++_probeDynFrameCounter % 120 == 0;
- for (int sliceIndex = 0; sliceIndex < sliceCount; sliceIndex++)
- {
- int count = 0;
- EnsureCapacity(ref _survivors, _dynamicCount);
- for (int i = 0; i < _dynamicCount; i++)
- {
- RenderProjectionRecord record = _dynamics[i];
- uint? parentCellId = ParentCell(in record);
- if (parentCellId is uint parentCell
- && (_lookInCellScratch.Contains(parentCell)
- || (input.WalkLookInViews is not null
- && input.RootFloodCells.Contains(parentCell))))
- continue;
- Sphere(in record, out Vector3 center, out float radius);
- bool outside = RetailPViewRenderer.DynamicDrawsInOutsideStage(
- ParentCell(in record),
- center,
- radius,
- input.DrawableCells,
- input.Cells);
- bool cone = input.Viewcone.SphereVisibleInOutsideSlice(
- sliceIndex, in center, radius);
- if (probeDynamics)
- {
- Console.WriteLine(
- $"[walk-dyn] slice={sliceIndex} id={record.Source.LocalEntityId:x} "
- + $"parent={ParentCell(in record)?.ToString("x8") ?? "null"} "
- + $"r={radius:F1} outside={(outside ? 1 : 0)} cone={(cone ? 1 : 0)}");
- }
- if (!outside || !cone)
- continue;
-
- _survivors[count++] = record;
- writer.AddDynamic(in record);
- AddProjection(
- writer,
- in record,
- input.AnimatedEntityIds);
- }
-
- writer.AddRouteRange(
- RenderFrameCandidateRoute.LandscapeOutsideDynamic,
- sliceIndex,
- 0,
- _survivors.AsSpan(0, count));
- }
- }
-
- private void BuildDynamicLastRoute(
- RenderFrameWriter writer,
- in RenderScenePViewBuildInput input)
- {
- int count = 0;
- EnsureCapacity(ref _survivors, _dynamicCount);
- for (int i = 0; i < _dynamicCount; i++)
- {
- RenderProjectionRecord record = _dynamics[i];
- uint? parentCellId = ParentCell(in record);
- bool indoor = InteriorEntityPartition.IsIndoorCellId(
- parentCellId);
- if (!input.RootIsOutdoor && !indoor)
- continue;
-
- // Drawn-once (retail DrawMeshInternal @0x0059F360): a dynamic
- // whose cell drew as a building look-in already rendered with
- // that cell in the landscape stage; re-entering the final route
- // would draw it after the boundary alpha drain and overpaint
- // nearer flames (the Holtburg candle-behind-door class).
- if (indoor && _lookInCellScratch.Contains(parentCellId!.Value))
- {
- if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled)
- {
- string state = $"parent={parentCellId.Value:x8} lookin-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;
- }
-
- // FW4: an active walk draws every root-flood cell's complete
- // object list at that cell's own PView::DrawCells turn. The
- // packed last pass must not duplicate those live objects after
- // the walk has already established their correct wall depth.
- if (indoor
- && input.WalkLookInViews is not null
- && input.RootFloodCells.Contains(parentCellId!.Value))
- {
- 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);
- bool visible = indoor
- ? input.Viewcone.SphereVisibleInCell(
- parentCellId!.Value,
- in center,
- radius)
- : input.Viewcone.SphereVisibleOutside(
- in center,
- radius);
- // ACDREAM_PROBE_WALK_ROOT companion (throwaway): the DynamicLast
- // admission trace for the through-wall remote-player report —
- // per entity, ON CHANGE: parent cell, look-in exclusion, the
- // viewcone verdict, and the cell's lifted-plane shape ("0" = a
- // pass-all zero-plane slice — the scissor-fallback trapdoor).
- if (AcDream.Core.Rendering.RenderingDiagnostics.ProbeWalkRootEnabled
- && indoor)
- {
- string state =
- $"parent={parentCellId!.Value:x8} vis={(visible ? 1 : 0)} "
- + $"cone={input.Viewcone.ProbeDescribeCell(parentCellId.Value)}";
- 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} route=DynamicLast");
- }
- }
- if (!visible)
- continue;
-
- _survivors[count++] = record;
- writer.AddDynamic(in record);
- AddProjection(
- writer,
- in record,
- input.AnimatedEntityIds);
- }
-
- writer.AddRouteRange(
- RenderFrameCandidateRoute.DynamicLast,
- 0,
- 0,
- _survivors.AsSpan(0, count));
- }
-
- private int LoadCell(
- RenderSceneQuery scene,
- uint cellId,
- bool includeDynamics,
- bool includeStatics = true)
- {
- int staticCount = includeStatics
- ? scene.GetCellStaticCount(cellId)
- : 0;
- int dynamicCount = includeDynamics
- ? scene.GetCellDynamicCount(cellId)
- : 0;
- EnsureCapacity(
- ref _cell,
- checked(staticCount + dynamicCount));
- int count = 0;
- if (staticCount > 0)
- {
- count = scene.CopyCellStaticsTo(
- cellId,
- _cell.AsSpan(0, staticCount));
- }
- if (dynamicCount > 0)
- {
- count += scene.CopyCellDynamicsTo(
- cellId,
- _cell.AsSpan(count, dynamicCount));
- }
-
- return CompactAndSort(_cell, count);
- }
-
- private void AddProjection(
- RenderFrameWriter writer,
- in RenderProjectionRecord record,
- HashSet? animatedEntityIds)
- {
- if (!_projectionIds.Add(record.Id))
- return;
- writer.AddTransform(new RenderFrameTransformRecord(
- record.Id,
- -1,
- record.Transform.LocalToWorld));
- writer.AddEntityCandidate(
- in record,
- animatedEntityIds?.Contains(record.Source.LocalEntityId) == true);
- }
-
- private static int CompactAndSort(
- RenderProjectionRecord[] records,
- int count)
- {
- int write = 0;
- for (int read = 0; read < count; read++)
- {
- RenderProjectionRecord record = records[read];
- if (record.Id.Domain == EnvCellProjectionDomain
- || record.MeshSet.MeshCount <= 0
- || (record.Flags
- & RenderProjectionFlags.SpatiallyResident) == 0)
- {
- continue;
- }
-
- records[write++] = record;
- }
-
- Array.Sort(
- records,
- 0,
- write,
- RenderProjectionRecordSortComparer.Instance);
- return write;
- }
-
- private static uint? ParentCell(in RenderProjectionRecord record) =>
- record.Source.ParentCellId == 0
- ? null
- : record.Source.ParentCellId;
-
- private static void Sphere(
- in RenderProjectionRecord record,
- out Vector3 center,
- out float radius)
- {
- center = (record.Bounds.Minimum + record.Bounds.Maximum) * 0.5f;
- radius =
- (record.Bounds.Maximum - record.Bounds.Minimum).Length() * 0.5f;
- }
-
- private static void EnsureCapacity(ref T[] values, int required)
- {
- if (values.Length >= required)
- return;
- int capacity = values.Length == 0 ? 4 : values.Length;
- while (capacity < required)
- capacity = checked(capacity * 2);
- Array.Resize(ref values, capacity);
- }
-
- private sealed class RenderProjectionRecordSortComparer :
- IComparer
- {
- public static RenderProjectionRecordSortComparer Instance { get; } =
- new();
-
- private RenderProjectionRecordSortComparer()
- {
- }
-
- public int Compare(
- RenderProjectionRecord left,
- RenderProjectionRecord right)
- {
- int value = left.SortKey.Value.CompareTo(right.SortKey.Value);
- return value != 0
- ? value
- : left.Id.CompareTo(right.Id);
- }
- }
-}
diff --git a/tests/AcDream.App.Tests/Rendering/RenderScenePViewFrameProductTests.cs b/tests/AcDream.App.Tests/Rendering/RenderScenePViewFrameProductTests.cs
deleted file mode 100644
index 64392224..00000000
--- a/tests/AcDream.App.Tests/Rendering/RenderScenePViewFrameProductTests.cs
+++ /dev/null
@@ -1,679 +0,0 @@
-using System.Numerics;
-using AcDream.App.Rendering;
-using AcDream.App.Rendering.Scene;
-using AcDream.App.Rendering.Scene.Arch;
-using AcDream.App.Rendering.Wb;
-using AcDream.App.Rendering.Walk;
-using AcDream.Core.World;
-
-namespace AcDream.App.Tests.Rendering;
-
-public sealed class RenderScenePViewFrameProductTests
-{
- private sealed class RejectAllLookInViews(uint cellId) : IWalkLookInViewSource
- {
- public IReadOnlyList LookInCellTurns { get; } = [cellId];
-
- public bool SphereVisibleInLookInTurn(
- int routeIndex,
- in Vector3 center,
- float radius) => false;
- }
-
- private const uint Landblock = 0xA9B4FFFF;
- private const uint Cell = 0xA9B40170;
- private static readonly RenderSceneGeneration Generation =
- RenderSceneGeneration.FromRaw(1);
-
- // Campaign FW3.2b-2 (adjudicated 2026-08-30): Builder_PreservesPViewRoutesAndExcludesShellsAndWithdrawnOwners,
- // Builder_OrdersMultiSliceBuildingShellsAfterLookIns, and
- // Builder_InterleavesEachLookInWithItsOwnPackedBuildingShellRoutes are
- // DELETED — all three asserted route-range interleaving for
- // LandscapeOutdoorStatic/LandscapeBuildingShell/CellStatic, which
- // RenderScenePViewFrameBuilder no longer emits at all (WalkFrameDriver
- // draws every outdoor static, building shell, and cell static — look-in
- // included — directly through OrderedDrawStream; plan §FW3
- // "FW3.2b-2 — the production rooting", item 3). There is no successor
- // assertion to write: the routes themselves are retired, not relocated.
-
- [Fact]
- public void Builder_ReusesOrderedIndicesWhileRefreshingDirtyRecords()
- {
- // Campaign FW3.2b-2 (adjudicated 2026-08-30): re-expressed against
- // the DYNAMIC index (LiveDynamicRoot / DynamicCandidates) instead of
- // the now-dead OutdoorStatic index — LoadSceneIndices' dirty-record
- // refresh + sort-key reorder mechanism this test proves is SHARED
- // between the two indices; the OutdoorStatic side was deleted along
- // with BuildOutdoorRoutes (its only reader).
- using var scene = new ArchRenderScene(Generation);
- RenderProjectionRecord later = Record(
- 0x0100_0000_0000_0012,
- RenderProjectionClass.LiveDynamicRoot);
- RenderProjectionRecord earlier = Record(
- 0x0100_0000_0000_0011,
- RenderProjectionClass.LiveDynamicRoot);
- scene.Apply(
- [
- RenderProjectionDelta.Register(Generation, 1, later),
- RenderProjectionDelta.Register(Generation, 2, earlier),
- ]);
-
- PortalVisibilityFrame portal = Portal(Cell);
- ClipFrameAssembly clip = FullScreenClip(Cell);
- ViewconeCuller viewcone =
- ViewconeCuller.Build(clip, Matrix4x4.Identity);
- var exchange = new RenderFrameExchange();
- var builder = new RenderScenePViewFrameBuilder();
-
- Build(frameSequence: 1);
- RenderFrameView first = exchange.BorrowLatest(Generation, 1);
- try
- {
- Assert.Equal(
- [earlier.Id, later.Id],
- first.DynamicCandidates.ToArray()
- .Select(static record => record.Id));
- }
- finally
- {
- exchange.Release(in first);
- }
-
- scene.ClearDirty();
- ulong retainedRevision = scene.OpenQuery().IndexRevision;
- Matrix4x4 movedTransform =
- Matrix4x4.CreateTranslation(0.25f, 0, 0);
- RenderProjectionRecord moved = later with
- {
- Transform = new RenderTransform(movedTransform),
- PreviousTransform =
- new PreviousRenderTransform(
- later.Transform.LocalToWorld),
- Bounds = new RenderWorldBounds(
- new Vector3(-0.75f, -1, -1),
- new Vector3(1.25f, 1, 1)),
- };
- scene.Apply(
- [
- RenderProjectionDelta.Update(
- RenderProjectionDeltaKind.UpdateTransform,
- Generation,
- 3,
- moved),
- ]);
- Assert.Equal(
- retainedRevision,
- scene.OpenQuery().IndexRevision);
-
- Build(frameSequence: 2);
- RenderFrameView second = exchange.BorrowLatest(Generation, 2);
- try
- {
- RenderProjectionRecord projected =
- second.EntityCandidates.ToArray()
- .Single(candidate =>
- candidate.Projection.Id == later.Id)
- .Projection;
- Assert.Equal(
- movedTransform,
- projected.Transform.LocalToWorld);
- Assert.Equal(
- [earlier.Id, later.Id],
- second.DynamicCandidates.ToArray()
- .Select(static record => record.Id));
- }
- finally
- {
- exchange.Release(in second);
- }
-
- scene.ClearDirty();
- RenderProjectionRecord reordered = moved with
- {
- SortKey = new RenderSortKey(0),
- };
- scene.Apply(
- [
- RenderProjectionDelta.Update(
- RenderProjectionDeltaKind.UpdateTransform,
- Generation,
- 4,
- reordered),
- ]);
- Assert.True(
- scene.OpenQuery().IndexRevision > retainedRevision);
-
- Build(frameSequence: 3);
- RenderFrameView third = exchange.BorrowLatest(Generation, 3);
- try
- {
- Assert.Equal(
- [later.Id, earlier.Id],
- third.DynamicCandidates.ToArray()
- .Select(static record => record.Id));
- }
- finally
- {
- exchange.Release(in third);
- }
-
- void Build(ulong frameSequence)
- {
- RenderSceneQuery query = scene.OpenQuery();
- var input = new RenderScenePViewBuildInput(
- query,
- new RenderSceneDigest(
- query.Generation,
- query.Counts,
- default),
- portal,
- clip,
- viewcone,
- [],
- null,
- [Cell],
- EmptyCellSource.Instance,
- [],
- RootIsOutdoor: true,
- RootFloodCells: new HashSet { Cell });
- builder.Build(exchange, frameSequence, in input);
- }
- }
-
- [Fact]
- public void Builder_KeysLookInDynamicsToWalkTurnsAndExcludesThemFromDynamicLast()
- {
- const uint lookInCell = 0xA9B4_0171;
- using var scene = new ArchRenderScene(Generation);
- RenderProjectionRecord rootDynamic = Record(
- 0x0100_0000_0000_0021,
- RenderProjectionClass.LiveDynamicRoot,
- parentCell: Cell);
- RenderProjectionRecord lookInDynamic = Record(
- 0x0100_0000_0000_0022,
- RenderProjectionClass.LiveDynamicRoot,
- parentCell: lookInCell);
- RenderProjectionRecord outdoorDynamic = Record(
- 0x0100_0000_0000_0023,
- RenderProjectionClass.LiveDynamicRoot);
- scene.Apply(
- [
- RenderProjectionDelta.Register(Generation, 1, rootDynamic),
- RenderProjectionDelta.Register(Generation, 2, lookInDynamic),
- RenderProjectionDelta.Register(Generation, 3, outdoorDynamic),
- ]);
-
- PortalVisibilityFrame portal = Portal(Cell);
- ClipFrameAssembly clip = FullScreenClip(Cell);
- ViewconeCuller viewcone = ViewconeCuller.Build(clip, Matrix4x4.Identity);
- RenderSceneQuery query = scene.OpenQuery();
- var input = new RenderScenePViewBuildInput(
- query,
- new RenderSceneDigest(query.Generation, query.Counts, default),
- portal,
- clip,
- viewcone,
- LookInCellTurns: [lookInCell],
- WalkLookInViews: null,
- DrawableCells: [Cell, lookInCell],
- Cells: EmptyCellSource.Instance,
- AnimatedEntityIds: [],
- RootIsOutdoor: false,
- RootFloodCells: new HashSet { Cell });
- var exchange = new RenderFrameExchange();
- var builder = new RenderScenePViewFrameBuilder();
-
- builder.Build(exchange, frameSequence: 1, in input);
- RenderFrameView frame = exchange.BorrowLatest(Generation, 1);
- try
- {
- Assert.DoesNotContain(
- frame.RouteRanges.ToArray(),
- range => range.Route == RenderFrameCandidateRoute.LookInObject);
-
- RenderFrameCandidateRange outside = Assert.Single(
- frame.RouteRanges.ToArray(),
- range => range.Route
- == RenderFrameCandidateRoute.LandscapeOutsideDynamic);
- RenderProjectionId[] outsideIds = frame.RouteCandidates
- .Slice(outside.Offset, outside.Count)
- .ToArray()
- .Select(static record => record.Id)
- .ToArray();
- Assert.Contains(outdoorDynamic.Id, outsideIds);
- Assert.DoesNotContain(lookInDynamic.Id, outsideIds);
-
- RenderFrameCandidateRange dynamicLast = Assert.Single(
- frame.RouteRanges.ToArray(),
- range => range.Route == RenderFrameCandidateRoute.DynamicLast);
- RenderProjectionId[] lastIds = frame.RouteCandidates
- .Slice(dynamicLast.Offset, dynamicLast.Count)
- .ToArray()
- .Select(static record => record.Id)
- .ToArray();
- Assert.Contains(rootDynamic.Id, lastIds);
- Assert.DoesNotContain(lookInDynamic.Id, lastIds);
- }
- finally
- {
- exchange.Release(in frame);
- }
- }
-
- [Fact]
- public void Builder_CarriesExactWalkConeForPerPartDispatcherAdmission()
- {
- const uint lookInCell = 0xA9B4_0171;
- using var scene = new ArchRenderScene(Generation);
- RenderProjectionRecord lookInDynamic = Record(
- 0x0100_0000_0000_0022,
- RenderProjectionClass.LiveDynamicRoot,
- parentCell: lookInCell);
- RenderProjectionRecord rootDynamic = Record(
- 0x0100_0000_0000_0023,
- RenderProjectionClass.LiveDynamicRoot,
- parentCell: Cell);
- RenderProjectionRecord outdoorDynamic = Record(
- 0x0100_0000_0000_0024,
- RenderProjectionClass.LiveDynamicRoot);
- scene.Apply(
- [
- RenderProjectionDelta.Register(Generation, 1, lookInDynamic),
- RenderProjectionDelta.Register(Generation, 2, rootDynamic),
- RenderProjectionDelta.Register(Generation, 3, outdoorDynamic),
- ]);
-
- PortalVisibilityFrame portal = Portal(Cell);
- ClipFrameAssembly clip = FullScreenClip(Cell);
- ViewconeCuller viewcone = ViewconeCuller.Build(clip, Matrix4x4.Identity);
- RenderSceneQuery query = scene.OpenQuery();
- var input = new RenderScenePViewBuildInput(
- query,
- new RenderSceneDigest(query.Generation, query.Counts, default),
- portal,
- clip,
- viewcone,
- LookInCellTurns: [lookInCell],
- WalkLookInViews: new RejectAllLookInViews(lookInCell),
- DrawableCells: [Cell, lookInCell],
- Cells: EmptyCellSource.Instance,
- AnimatedEntityIds: [],
- RootIsOutdoor: false,
- RootFloodCells: new HashSet { Cell });
- var exchange = new RenderFrameExchange();
- var builder = new RenderScenePViewFrameBuilder();
-
- builder.Build(exchange, frameSequence: 1, in input);
- RenderFrameView frame = exchange.BorrowLatest(Generation, 1);
- try
- {
- Assert.DoesNotContain(
- frame.RouteRanges.ToArray(),
- candidate => candidate.Route == RenderFrameCandidateRoute.LookInObject);
- Assert.DoesNotContain(
- frame.RouteCandidates.ToArray(),
- candidate => candidate.Id == rootDynamic.Id);
- Assert.DoesNotContain(
- frame.RouteCandidates.ToArray(),
- candidate => candidate.Id == outdoorDynamic.Id);
- Assert.NotNull(frame.WalkLookInViews);
- Assert.False(frame.WalkLookInViews!.SphereVisibleInLookInTurn(
- routeIndex: 0,
- center: Vector3.Zero,
- radius: 1f));
- }
- finally
- {
- exchange.Release(in frame);
- }
- }
-
- [Fact]
- public void Controller_MatchesCurrentPViewThenNamesAWithdrawnCandidate()
- {
- // Campaign FW3.2b-2 (adjudicated 2026-08-30): no static entities here
- // — LandscapeOutdoorStatic/CellStatic no longer flow through the
- // packed product at all (WalkFrameDriver owns every static now), so
- // a static observed on the LEGACY oracle side with nothing on the
- // packed side would report a permanent mismatch unrelated to what
- // this test is proving (withdrawal detection on the SURVIVING
- // DynamicLast route).
- WorldEntity outdoorDynamic =
- Entity(1_000_012, 0x8000_0012, parentCell: null);
- WorldEntity cellDynamic =
- Entity(1_000_013, 0x8000_0013, parentCell: Cell);
- WorldEntity[] entities = [outdoorDynamic, cellDynamic];
- var oracle = new CurrentRenderSceneOracle();
- var partition = new InteriorEntityPartition.Result();
- InteriorEntityPartition.Partition(
- partition,
- [Cell],
- [Entry(entities)],
- oracle);
- oracle.BeginPViewFrame();
- oracle.ObservePViewBucket(
- CurrentRenderPViewRoute.DynamicLast,
- 0,
- 0,
- [outdoorDynamic, cellDynamic]);
- oracle.CompletePViewFrame();
- oracle.BeginDispatcherFrame();
- oracle.ObserveDispatcherDraw(
- WbDrawDispatcher.EntitySet.All,
- entitiesWalked: 2,
- [
- (outdoorDynamic, 0, Landblock),
- (cellDynamic, 0, Landblock),
- ]);
-
- using var shadow = new RenderSceneShadowRuntime(Generation);
- foreach (WorldEntity entity in entities)
- {
- RenderProjectionRecord record = Project(entity);
- shadow.Journal.Register(in record);
- }
- shadow.DrainUpdateBoundary();
- var logs = new List();
- var controller = new RenderScenePViewFrameProductController(
- shadow,
- oracle,
- logs.Add);
- PortalVisibilityFrame portal = Portal(Cell);
- ClipFrameAssembly clip = FullScreenClip(Cell);
- ViewconeCuller viewcone =
- ViewconeCuller.Build(clip, Matrix4x4.Identity);
-
- controller.BuildAndCompare(
- portal,
- clip,
- viewcone,
- [],
- [Cell],
- EmptyCellSource.Instance,
- [],
- Landblock,
- rootIsOutdoor: true);
-
- RenderFrameProductComparisonSnapshot matched = controller.Snapshot;
- Assert.True(matched.Enabled);
- Assert.Equal(1uL, matched.ComparisonCount);
- Assert.Equal(1uL, matched.SuccessfulComparisonCount);
- Assert.Equal(2, matched.ExpectedCandidateCount);
- Assert.Equal(2, matched.ActualCandidateCount);
- Assert.Equal(0, matched.MismatchCount);
- Assert.Null(matched.FirstMismatch);
- Assert.Equal(matched.ExpectedDigest, matched.ActualDigest);
- Assert.Equal(1uL, matched.PackedInputComparisonCount);
- Assert.Equal(1uL, matched.PackedInputSuccessfulComparisonCount);
- Assert.Equal(2, matched.PackedInputExpectedCount);
- Assert.Equal(2, matched.PackedInputActualCount);
- Assert.Equal(0, matched.PackedInputMismatchCount);
- Assert.Null(matched.PackedInputFirstMismatch);
- Assert.Equal(
- matched.PackedInputExpectedDigest,
- matched.PackedInputActualDigest);
- Assert.Empty(logs);
-
- RenderProjectionId withdrawnId =
- LiveRenderProjectionJournal.ProjectionId(cellDynamic.Id);
- shadow.Journal.Unregister(
- withdrawnId,
- RenderOwnerIncarnation.FromRaw(cellDynamic.Id));
- shadow.DrainUpdateBoundary();
- controller.BuildAndCompare(
- portal,
- clip,
- viewcone,
- [],
- [Cell],
- EmptyCellSource.Instance,
- [],
- Landblock,
- rootIsOutdoor: true);
-
- RenderFrameProductComparisonSnapshot mismatch = controller.Snapshot;
- Assert.Equal(2uL, mismatch.ComparisonCount);
- Assert.Equal(1uL, mismatch.SuccessfulComparisonCount);
- Assert.Equal(1, mismatch.MismatchCount);
- Assert.Contains("field=count expected=2 actual=1", mismatch.FirstMismatch);
- Assert.Equal(1, mismatch.PackedInputMismatchCount);
- Assert.Contains(
- "field=count expected=2 actual=1",
- mismatch.PackedInputFirstMismatch);
- Assert.Equal(2, logs.Count);
- }
-
- [Fact]
- public void Controller_RejectsEqualMembershipInDifferentTraversalOrder()
- {
- WorldEntity first =
- Entity(1_000_001, 0x8000_0001, parentCell: null);
- WorldEntity second =
- Entity(1_000_002, 0x8000_0002, parentCell: null);
- var oracle = new CurrentRenderSceneOracle();
- var partition = new InteriorEntityPartition.Result();
- InteriorEntityPartition.Partition(
- partition,
- [],
- [Entry([first, second])],
- oracle);
- oracle.BeginPViewFrame();
- oracle.ObservePViewBucket(
- CurrentRenderPViewRoute.DynamicLast,
- 0,
- 0,
- [first, second]);
- oracle.CompletePViewFrame();
- oracle.BeginDispatcherFrame();
- oracle.ObserveDispatcherDraw(
- WbDrawDispatcher.EntitySet.All,
- entitiesWalked: 2,
- [
- (first, 0, Landblock),
- (second, 0, Landblock),
- ]);
-
- using var shadow = new RenderSceneShadowRuntime(Generation);
- RenderProjectionRecord firstRecord = Project(first) with
- {
- SortKey = new RenderSortKey(2),
- };
- RenderProjectionRecord secondRecord = Project(second) with
- {
- SortKey = new RenderSortKey(1),
- };
- shadow.Journal.Register(in firstRecord);
- shadow.Journal.Register(in secondRecord);
- shadow.DrainUpdateBoundary();
- var controller = new RenderScenePViewFrameProductController(
- shadow,
- oracle);
- PortalVisibilityFrame portal = Portal(Cell);
- ClipFrameAssembly clip = FullScreenClip(Cell);
- ViewconeCuller viewcone =
- ViewconeCuller.Build(clip, Matrix4x4.Identity);
-
- controller.BuildAndCompare(
- portal,
- clip,
- viewcone,
- [],
- [],
- EmptyCellSource.Instance,
- [],
- Landblock,
- rootIsOutdoor: true);
-
- RenderFrameProductComparisonSnapshot snapshot =
- controller.Snapshot;
- Assert.Equal(1, snapshot.MismatchCount);
- Assert.Contains(
- $"ProjectionId = {LiveRenderProjectionJournal.ProjectionId(first.Id)}",
- snapshot.FirstMismatch);
- Assert.Contains(
- $"ProjectionId = {LiveRenderProjectionJournal.ProjectionId(second.Id)}",
- snapshot.FirstMismatch);
- Assert.Equal(1, snapshot.PackedInputMismatchCount);
- }
-
- private static RenderProjectionRecord Project(WorldEntity entity)
- {
- bool dynamic = entity.ServerGuid != 0;
- RenderProjectionId id = dynamic
- ? LiveRenderProjectionJournal.ProjectionId(entity.Id)
- : StaticRenderProjectionJournal.StaticEntityId(
- Landblock,
- entity.Id);
- RenderProjectionClass projectionClass = dynamic
- ? RenderProjectionClass.LiveDynamicRoot
- : entity.ParentCellId is uint parentCell
- && InteriorEntityPartition.IsIndoorCellId(parentCell)
- ? RenderProjectionClass.IndoorCellStatic
- : RenderProjectionClass.OutdoorStatic;
- return RenderProjectionRecordFactory.ProjectEntity(
- id,
- projectionClass,
- RenderOwnerIncarnation.FromRaw(entity.Id),
- Landblock,
- entity.ParentCellId ?? Landblock,
- entity,
- spatiallyVisible: true);
- }
-
- private static RenderProjectionRecord Record(
- ulong id,
- RenderProjectionClass projectionClass,
- uint? parentCell = null,
- RenderProjectionFlags flags =
- RenderProjectionFlags.Draw
- | RenderProjectionFlags.SpatiallyResident,
- bool isBuildingShell = false,
- uint buildingShellAnchorCellId = 0)
- {
- uint fullCellId = parentCell ?? Landblock;
- Matrix4x4 transform = Matrix4x4.Identity;
- return new RenderProjectionRecord(
- RenderProjectionId.FromRaw(id),
- projectionClass,
- RenderOwnerIncarnation.FromRaw(id),
- new RenderTransform(transform),
- new PreviousRenderTransform(transform),
- new RenderMeshSet(RenderAssetHandle.FromRaw(id), 1, 1),
- new RenderMaterialVariant(0, 0, 1),
- new RenderSpatialResidency(
- RenderSpatialBucket.FromRaw(fullCellId),
- Landblock,
- fullCellId),
- new RenderWorldBounds(
- new Vector3(-1),
- new Vector3(1)),
- flags,
- default,
- new RenderSortKey(id),
- RenderDirtyMask.All,
- new RenderSourceMetadata(
- LocalEntityId: (uint)id,
- ServerGuid: projectionClass
- is RenderProjectionClass.LiveDynamicRoot ? (uint)id : 0,
- SourceId: 1,
- ParentCellId: parentCell ?? 0,
- EffectCellId: 0,
- BuildingShellAnchorCellId: buildingShellAnchorCellId,
- TransformFingerprint: default,
- GeometryFingerprint: default,
- AppearanceFingerprint: default),
- new RenderEntityPayload(
- [new MeshRef((uint)id, Matrix4x4.Identity)],
- PaletteOverride: null,
- IsBuildingShell: isBuildingShell));
- }
-
- private static WorldEntity Entity(
- uint id,
- uint serverGuid,
- uint? parentCell) =>
- new()
- {
- Id = id,
- ServerGuid = serverGuid,
- SourceGfxObjOrSetupId = 0x0200_0001,
- Position = Vector3.Zero,
- Rotation = Quaternion.Identity,
- MeshRefs =
- [
- new MeshRef(0x0100_0001, Matrix4x4.Identity),
- ],
- ParentCellId = parentCell,
- };
-
- private static PortalVisibilityFrame Portal(uint cellId)
- {
- var frame = new PortalVisibilityFrame();
- frame.OrderedVisibleCells.Add(cellId);
- return frame;
- }
-
- private static ClipFrameAssembly FullScreenClip(uint cellId)
- {
- var slice = new ClipViewSlice(
- Slot: 0,
- NdcAabb: new Vector4(-1, -1, 1, 1),
- Planes: []);
- var assembly = new ClipFrameAssembly();
- assembly.SetOutsideViewSlices([slice]);
- assembly.CellIdToViewSlices[cellId] = [slice];
- return assembly;
- }
-
- private static ClipFrameAssembly TwoSliceClip(uint cellId)
- {
- var first = new ClipViewSlice(
- Slot: 0,
- NdcAabb: new Vector4(-1, -1, 0, 1),
- Planes: []);
- var second = new ClipViewSlice(
- Slot: 1,
- NdcAabb: new Vector4(0, -1, 1, 1),
- Planes: []);
- var assembly = new ClipFrameAssembly();
- assembly.SetOutsideViewSlices([first, second]);
- assembly.CellIdToViewSlices[cellId] = [first];
- return assembly;
- }
-
- private static (
- uint LandblockId,
- Vector3 AabbMin,
- Vector3 AabbMax,
- IReadOnlyList Entities,
- IReadOnlyDictionary? AnimatedById)
- Entry(IReadOnlyList entities) =>
- (
- Landblock,
- Vector3.Zero,
- Vector3.One,
- entities,
- null);
-
- private sealed class EmptyCellSource : IRetailPViewCellSource
- {
- public static EmptyCellSource Instance { get; } = new();
-
- private EmptyCellSource()
- {
- }
-
- public LoadedCell? Find(uint cellId) => null;
- }
-
- private sealed class DictionaryCellSource : IRetailPViewCellSource
- {
- private readonly Dictionary _cells;
-
- public DictionaryCellSource(params LoadedCell[] cells) =>
- _cells = cells.ToDictionary(static cell => cell.CellId);
-
- public LoadedCell? Find(uint cellId) =>
- _cells.TryGetValue(cellId, out LoadedCell? cell) ? cell : null;
- }
-}