fix(rendering): reconcile shadow roots from live runtime

Recover ordinary live roots from LiveEntityRuntime's canonical active spatial workset instead of relying on already-retained derived records. Keep attached children callback-authoritative, cache immutable selection geometry fingerprints, and pin zero-allocation retained update paths with regression tests.

Release gate: 3,733 App tests / 3 skips; 8,217 complete-solution tests / 5 skips.

Co-Authored-By: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-24 22:45:14 +02:00
parent 056bbd4efd
commit 91463db551
7 changed files with 272 additions and 15 deletions

View file

@ -211,6 +211,8 @@ internal sealed class CurrentRenderSceneOracle :
new(ReferenceEqualityComparer.Instance);
private readonly List<CurrentRenderSelectionFingerprint>
_selectionParts = [];
private readonly Dictionary<uint, SelectionGeometryCacheEntry>
_selectionGeometryByGfxObj = [];
private ulong _frameSequence;
private int _abortedFrames;
private int _outdoorStaticCount;
@ -637,16 +639,15 @@ internal sealed class CurrentRenderSceneOracle :
}
ArgumentNullException.ThrowIfNull(mesh);
StableRenderHash128 geometry = StableRenderHash128.Create();
geometry.Add(mesh.SphereCenter);
geometry.Add(mesh.SphereRadius);
geometry.Add(mesh.Polygons.Count);
foreach (RetailSelectionPolygon polygon in mesh.Polygons)
if (!_selectionGeometryByGfxObj.TryGetValue(
gfxObjId,
out SelectionGeometryCacheEntry cachedGeometry)
|| !ReferenceEquals(cachedGeometry.Mesh, mesh))
{
geometry.Add(polygon.SingleSided);
geometry.Add(polygon.Vertices.Count);
foreach (Vector3 vertex in polygon.Vertices)
geometry.Add(vertex);
cachedGeometry = new SelectionGeometryCacheEntry(
mesh,
FingerprintSelectionGeometry(mesh));
_selectionGeometryByGfxObj[gfxObjId] = cachedGeometry;
}
var fingerprint = new CurrentRenderSelectionFingerprint(
@ -656,7 +657,7 @@ internal sealed class CurrentRenderSceneOracle :
PartIndex: partIndex,
GfxObjId: gfxObjId,
LocalToWorld: localToWorld,
Geometry: geometry.Finish());
Geometry: cachedGeometry.Fingerprint);
_selectionParts.Add(fingerprint);
_selectionHash.Add(fingerprint.Sequence);
_selectionHash.Add(fingerprint.ServerGuid);
@ -722,8 +723,11 @@ internal sealed class CurrentRenderSceneOracle :
StableRenderHash128 geometry = StableRenderHash128.Create();
geometry.Add(entity.MeshRefs.Count);
foreach (MeshRef mesh in entity.MeshRefs)
for (int meshIndex = 0;
meshIndex < entity.MeshRefs.Count;
meshIndex++)
{
MeshRef mesh = entity.MeshRefs[meshIndex];
geometry.Add(mesh.GfxObjId);
geometry.Add(mesh.PartTransform);
}
@ -734,8 +738,12 @@ internal sealed class CurrentRenderSceneOracle :
appearance.Add(true);
appearance.Add(palette.BasePaletteId);
appearance.Add(palette.SubPalettes.Count);
foreach (PaletteOverride.SubPaletteRange range in palette.SubPalettes)
for (int rangeIndex = 0;
rangeIndex < palette.SubPalettes.Count;
rangeIndex++)
{
PaletteOverride.SubPaletteRange range =
palette.SubPalettes[rangeIndex];
appearance.Add(range.SubPaletteId);
appearance.Add(range.Offset);
appearance.Add(range.Length);
@ -747,8 +755,11 @@ internal sealed class CurrentRenderSceneOracle :
}
appearance.Add(entity.PartOverrides.Count);
foreach (PartOverride part in entity.PartOverrides)
for (int partIndex = 0;
partIndex < entity.PartOverrides.Count;
partIndex++)
{
PartOverride part = entity.PartOverrides[partIndex];
appearance.Add(part.PartIndex);
appearance.Add(part.GfxObjId);
}
@ -784,6 +795,31 @@ internal sealed class CurrentRenderSceneOracle :
Appearance: appearance.Finish());
}
private static RenderSceneHash128 FingerprintSelectionGeometry(
RetailSelectionMesh mesh)
{
StableRenderHash128 geometry = StableRenderHash128.Create();
geometry.Add(mesh.SphereCenter);
geometry.Add(mesh.SphereRadius);
geometry.Add(mesh.Polygons.Count);
for (int polygonIndex = 0;
polygonIndex < mesh.Polygons.Count;
polygonIndex++)
{
RetailSelectionPolygon polygon = mesh.Polygons[polygonIndex];
geometry.Add(polygon.SingleSided);
geometry.Add(polygon.Vertices.Count);
for (int vertexIndex = 0;
vertexIndex < polygon.Vertices.Count;
vertexIndex++)
{
geometry.Add(polygon.Vertices[vertexIndex]);
}
}
return geometry.Finish();
}
private static InteriorEntityPartition.ProjectionClass ProjectionClassOf(
WorldEntity entity) =>
entity.ServerGuid != 0
@ -792,6 +828,10 @@ internal sealed class CurrentRenderSceneOracle :
? InteriorEntityPartition.ProjectionClass.CellStatic
: InteriorEntityPartition.ProjectionClass.OutdoorStatic;
private readonly record struct SelectionGeometryCacheEntry(
RetailSelectionMesh Mesh,
RenderSceneHash128 Fingerprint);
private static void AddPViewCandidate(
ref StableRenderHash128 hash,
in CurrentRenderPViewCandidateFingerprint candidate)

View file

@ -25,6 +25,7 @@ internal sealed class LiveRenderProjectionJournal : ILiveRenderProjectionSink
private readonly LiveEntityRuntime _runtime;
private readonly RenderProjectionJournal _journal;
private readonly Dictionary<uint, TrackedProjection> _byLocalId = [];
private readonly List<LiveEntityRecord> _activeRootScratch = [];
private readonly List<TrackedProjection> _activeScratch = [];
public LiveRenderProjectionJournal(
@ -107,10 +108,34 @@ internal sealed class LiveRenderProjectionJournal : ILiveRenderProjectionSink
/// </summary>
public void SynchronizeActiveSources()
{
// The canonical live-runtime workset is the source of truth for world
// roots. Walking only this journal's already-tracked values would make
// a missed/reordered derived callback permanent and prevent shadow
// comparison from converging after a same-location revisit.
_runtime.CopySpatialRootObjectRecordsTo(_activeRootScratch);
for (int i = 0; i < _activeRootScratch.Count; i++)
{
LiveEntityRecord record = _activeRootScratch[i];
if (!record.ResourcesRegistered
|| record.WorldEntity is not { } entity
|| !_runtime.IsCurrentSpatialRootObject(record))
{
continue;
}
Upsert(record, entity, record.IsSpatiallyVisible);
}
// Attached children are not members of the ordinary root workset.
// Their exact EntityReady/pose/removal callbacks remain authoritative;
// synchronization only refreshes children whose attachment projection
// is still retained by this derived scene.
_activeScratch.Clear();
foreach (TrackedProjection tracked in _byLocalId.Values)
{
if (tracked.Record.IsSpatiallyProjected)
if (tracked.Record.IsSpatiallyProjected
&& tracked.Record.ProjectionKind is
LiveEntityProjectionKind.Attached)
_activeScratch.Add(tracked);
}
@ -137,6 +162,7 @@ internal sealed class LiveRenderProjectionJournal : ILiveRenderProjectionSink
public void ResetTracking()
{
_byLocalId.Clear();
_activeRootScratch.Clear();
_activeScratch.Clear();
}