feat(rendering): compare scene-built PView candidates

Build a same-frame candidate product from incremental render-scene indices and compare its exact PView routes against the accepted current path without changing the production draw source.
This commit is contained in:
Erik 2026-07-25 00:12:53 +02:00
parent 8c638654be
commit e346f8bbaf
13 changed files with 1187 additions and 9 deletions

View file

@ -9,6 +9,22 @@ internal enum RenderFrameBlendClass : byte
Alpha,
}
internal enum RenderFrameCandidateRoute : byte
{
LandscapeOutdoorStatic,
LandscapeOutsideDynamic,
LookInObject,
CellStatic,
DynamicLast,
}
internal readonly record struct RenderFrameCandidateRange(
RenderFrameCandidateRoute Route,
int RouteIndex,
uint CellId,
int Offset,
int Count);
internal readonly record struct RenderFrameCellRange(
uint CellId,
int PViewOrder,
@ -60,7 +76,8 @@ internal readonly record struct RenderFrameDiagnosticCounts(
int OpaqueClassificationCount,
int AlphaClassificationCount,
int LightSetCount,
int SelectionPartCount);
int SelectionPartCount,
int RouteCandidateCount);
/// <summary>
/// A validated borrowed view over one reusable frame arena. Copies are safe:
@ -125,6 +142,12 @@ internal readonly struct RenderFrameView
public ReadOnlySpan<RenderFrameSelectionPart> SelectionParts =>
Arena.SelectionParts;
public ReadOnlySpan<RenderProjectionRecord> RouteCandidates =>
Arena.RouteCandidates;
public ReadOnlySpan<RenderFrameCandidateRange> RouteRanges =>
Arena.RouteRanges;
public PortalVisibilityFrame? PortalFrame => Arena.PortalFrame;
public ClipFrameAssembly? ClipAssembly => Arena.ClipAssembly;
@ -172,6 +195,8 @@ internal sealed class RenderFrameArena
private RenderFrameClassificationRecord[] _classifications = [];
private RenderFrameObjectLightSet[] _lightSets = [];
private RenderFrameSelectionPart[] _selectionParts = [];
private RenderProjectionRecord[] _routeCandidates = [];
private RenderFrameCandidateRange[] _routeRanges = [];
private int _outdoorCount;
private int _cellStaticCount;
private int _cellRangeCount;
@ -180,6 +205,8 @@ internal sealed class RenderFrameArena
private int _classificationCount;
private int _lightSetCount;
private int _selectionPartCount;
private int _routeCandidateCount;
private int _routeRangeCount;
private int _opaqueClassificationCount;
private int _alphaClassificationCount;
private ulong _epoch;
@ -206,7 +233,8 @@ internal sealed class RenderFrameArena
_opaqueClassificationCount,
_alphaClassificationCount,
_lightSetCount,
_selectionPartCount);
_selectionPartCount,
_routeCandidateCount);
internal ReadOnlySpan<RenderProjectionRecord> OutdoorStaticCandidates =>
_outdoor.AsSpan(0, _outdoorCount);
@ -232,6 +260,12 @@ internal sealed class RenderFrameArena
internal ReadOnlySpan<RenderFrameSelectionPart> SelectionParts =>
_selectionParts.AsSpan(0, _selectionPartCount);
internal ReadOnlySpan<RenderProjectionRecord> RouteCandidates =>
_routeCandidates.AsSpan(0, _routeCandidateCount);
internal ReadOnlySpan<RenderFrameCandidateRange> RouteRanges =>
_routeRanges.AsSpan(0, _routeRangeCount);
internal ulong BeginWrite(
RenderSceneGeneration generation,
ulong frameSequence)
@ -253,6 +287,8 @@ internal sealed class RenderFrameArena
_classificationCount = 0;
_lightSetCount = 0;
_selectionPartCount = 0;
_routeCandidateCount = 0;
_routeRangeCount = 0;
_opaqueClassificationCount = 0;
_alphaClassificationCount = 0;
PortalFrame = null;
@ -393,6 +429,34 @@ internal sealed class RenderFrameArena
_selectionParts[_selectionPartCount++] = part;
}
internal void AddRouteRange(
ulong epoch,
RenderFrameCandidateRoute route,
int routeIndex,
uint cellId,
ReadOnlySpan<RenderProjectionRecord> records)
{
EnsureBuilding(epoch);
if (routeIndex < 0)
throw new ArgumentOutOfRangeException(nameof(routeIndex));
if (records.Length == 0)
return;
int offset = _routeCandidateCount;
EnsureCapacity(
ref _routeCandidates,
checked(offset + records.Length));
records.CopyTo(_routeCandidates.AsSpan(offset));
_routeCandidateCount += records.Length;
EnsureCapacity(ref _routeRanges, _routeRangeCount + 1);
_routeRanges[_routeRangeCount++] = new RenderFrameCandidateRange(
route,
routeIndex,
cellId,
offset,
records.Length);
}
internal void Publish(ulong epoch)
{
EnsureBuilding(epoch);
@ -549,6 +613,18 @@ internal readonly struct RenderFrameWriter
public void AddSelectionPart(in RenderFrameSelectionPart part) =>
Arena.AddSelectionPart(_epoch, in part);
public void AddRouteRange(
RenderFrameCandidateRoute route,
int routeIndex,
uint cellId,
ReadOnlySpan<RenderProjectionRecord> records) =>
Arena.AddRouteRange(
_epoch,
route,
routeIndex,
cellId,
records);
public void Publish() =>
Owner.Publish(_arenaIndex, _epoch);