feat(rendering): index incremental scene queries
Maintain packed render worksets as lifecycle deltas arrive and synchronize active animated statics without scanning the resident world. This keeps Slice F non-drawing while preparing exact spatial and feature queries for shadow comparison. Release: 8,206 passed, 5 skipped.
This commit is contained in:
parent
58e7c2eb99
commit
ff5d86175f
8 changed files with 745 additions and 16 deletions
|
|
@ -28,6 +28,12 @@ internal sealed class StaticRenderProjectionJournal :
|
|||
private readonly RenderProjectionJournal _journal;
|
||||
private readonly Dictionary<uint, Dictionary<RenderProjectionId, TrackedProjection>>
|
||||
_byLandblock = [];
|
||||
private readonly Dictionary<RenderProjectionId, TrackedProjection>
|
||||
_trackedById = [];
|
||||
private readonly Dictionary<WorldEntity, RenderProjectionId> _idByEntity =
|
||||
new(ReferenceEqualityComparer.Instance);
|
||||
private readonly Dictionary<uint, List<WorldEntity>> _entitiesByLandblock =
|
||||
[];
|
||||
private readonly List<RenderProjectionRecord> _candidates = [];
|
||||
private readonly List<RenderProjectionId> _removed = [];
|
||||
private ulong _nextIncarnation = 1;
|
||||
|
|
@ -87,7 +93,10 @@ internal sealed class StaticRenderProjectionJournal :
|
|||
for (int i = 0; i < _candidates.Count; i++)
|
||||
{
|
||||
RenderProjectionRecord candidate = _candidates[i];
|
||||
if (current.TryGetValue(candidate.Id, out TrackedProjection retained))
|
||||
if (current.TryGetValue(
|
||||
candidate.Id,
|
||||
out TrackedProjection? retained)
|
||||
&& retained is not null)
|
||||
{
|
||||
_removed.Remove(candidate.Id);
|
||||
RenderProjectionRecord accepted = candidate with
|
||||
|
|
@ -107,14 +116,14 @@ internal sealed class StaticRenderProjectionJournal :
|
|||
OwnerIncarnation = NextIncarnation(),
|
||||
};
|
||||
_journal.Register(in accepted);
|
||||
current[candidate.Id] = new TrackedProjection(accepted);
|
||||
retained.Record = accepted;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (accepted != retained.Record)
|
||||
{
|
||||
_journal.AppendDifference(retained.Record, accepted);
|
||||
current[candidate.Id] = new TrackedProjection(accepted);
|
||||
retained.Record = accepted;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -124,7 +133,9 @@ internal sealed class StaticRenderProjectionJournal :
|
|||
OwnerIncarnation = NextIncarnation(),
|
||||
};
|
||||
_journal.Register(in registered);
|
||||
current.Add(registered.Id, new TrackedProjection(registered));
|
||||
var tracked = new TrackedProjection(registered);
|
||||
current.Add(registered.Id, tracked);
|
||||
_trackedById.Add(registered.Id, tracked);
|
||||
ProjectionCount++;
|
||||
}
|
||||
|
||||
|
|
@ -135,11 +146,13 @@ internal sealed class StaticRenderProjectionJournal :
|
|||
TrackedProjection omitted = current[id];
|
||||
_journal.Unregister(id, omitted.Record.OwnerIncarnation);
|
||||
current.Remove(id);
|
||||
_trackedById.Remove(id);
|
||||
ProjectionCount--;
|
||||
}
|
||||
|
||||
if (current.Count == 0)
|
||||
_byLandblock.Remove(landblockId);
|
||||
ReplaceStaticEntityMap(landblockId, publication.Landblock.Entities);
|
||||
}
|
||||
|
||||
public void Retire(GpuLandblockRetirement retirement)
|
||||
|
|
@ -166,17 +179,66 @@ internal sealed class StaticRenderProjectionJournal :
|
|||
}
|
||||
|
||||
ProjectionCount -= current.Count;
|
||||
foreach (RenderProjectionId id in current.Keys)
|
||||
_trackedById.Remove(id);
|
||||
RemoveStaticEntityMap(landblockId);
|
||||
}
|
||||
|
||||
public void Clear(RenderSceneGeneration replacementGeneration)
|
||||
{
|
||||
_byLandblock.Clear();
|
||||
_trackedById.Clear();
|
||||
_idByEntity.Clear();
|
||||
_entitiesByLandblock.Clear();
|
||||
_candidates.Clear();
|
||||
_removed.Clear();
|
||||
ProjectionCount = 0;
|
||||
_journal.Clear(replacementGeneration);
|
||||
}
|
||||
|
||||
public void SynchronizeActiveAnimatedSources(
|
||||
IReadOnlyList<WorldEntity> activeEntities)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(activeEntities);
|
||||
for (int i = 0; i < activeEntities.Count; i++)
|
||||
{
|
||||
WorldEntity entity = activeEntities[i];
|
||||
if (!_idByEntity.TryGetValue(
|
||||
entity,
|
||||
out RenderProjectionId id)
|
||||
|| !_trackedById.TryGetValue(
|
||||
id,
|
||||
out TrackedProjection? tracked))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RenderProjectionRecord current =
|
||||
RenderProjectionRecordFactory.ProjectEntity(
|
||||
id,
|
||||
RenderProjectionClass.ActiveAnimatedStatic,
|
||||
tracked.Record.OwnerIncarnation,
|
||||
tracked.Record.Residency.OwnerLandblockId,
|
||||
tracked.Record.Residency.FullCellId,
|
||||
entity,
|
||||
spatiallyVisible: true) with
|
||||
{
|
||||
PreviousTransform = new PreviousRenderTransform(
|
||||
tracked.Record.Transform.LocalToWorld),
|
||||
};
|
||||
if (tracked.Record.ProjectionClass
|
||||
!= RenderProjectionClass.ActiveAnimatedStatic)
|
||||
{
|
||||
_journal.Register(in current);
|
||||
}
|
||||
else
|
||||
{
|
||||
_journal.AppendDifference(tracked.Record, current);
|
||||
}
|
||||
tracked.Record = current;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool TryGet(
|
||||
RenderProjectionId id,
|
||||
out RenderProjectionRecord record)
|
||||
|
|
@ -184,7 +246,10 @@ internal sealed class StaticRenderProjectionJournal :
|
|||
foreach (Dictionary<RenderProjectionId, TrackedProjection> projections
|
||||
in _byLandblock.Values)
|
||||
{
|
||||
if (projections.TryGetValue(id, out TrackedProjection tracked))
|
||||
if (projections.TryGetValue(
|
||||
id,
|
||||
out TrackedProjection? tracked)
|
||||
&& tracked is not null)
|
||||
{
|
||||
record = tracked.Record;
|
||||
return true;
|
||||
|
|
@ -310,8 +375,43 @@ internal sealed class StaticRenderProjectionJournal :
|
|||
private static uint Canonicalize(uint landblockId) =>
|
||||
(landblockId & 0xFFFF0000u) | 0xFFFFu;
|
||||
|
||||
private readonly record struct TrackedProjection(
|
||||
RenderProjectionRecord Record);
|
||||
private void ReplaceStaticEntityMap(
|
||||
uint landblockId,
|
||||
IReadOnlyList<WorldEntity> entities)
|
||||
{
|
||||
RemoveStaticEntityMap(landblockId);
|
||||
var retained = new List<WorldEntity>();
|
||||
for (int i = 0; i < entities.Count; i++)
|
||||
{
|
||||
WorldEntity entity = entities[i];
|
||||
if (entity.ServerGuid != 0)
|
||||
continue;
|
||||
RenderProjectionId id = StaticEntityId(landblockId, entity.Id);
|
||||
if (!_trackedById.ContainsKey(id))
|
||||
continue;
|
||||
_idByEntity.Add(entity, id);
|
||||
retained.Add(entity);
|
||||
}
|
||||
if (retained.Count > 0)
|
||||
_entitiesByLandblock.Add(landblockId, retained);
|
||||
}
|
||||
|
||||
private void RemoveStaticEntityMap(uint landblockId)
|
||||
{
|
||||
if (!_entitiesByLandblock.Remove(
|
||||
landblockId,
|
||||
out List<WorldEntity>? entities))
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < entities.Count; i++)
|
||||
_idByEntity.Remove(entities[i]);
|
||||
}
|
||||
|
||||
private sealed class TrackedProjection(RenderProjectionRecord record)
|
||||
{
|
||||
public RenderProjectionRecord Record { get; set; } = record;
|
||||
}
|
||||
|
||||
private sealed class ProjectionRecordComparer :
|
||||
IComparer<RenderProjectionRecord>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue