perf(rendering): retain packed classification
Retain appearance classification per projection while refreshing transforms, clip slots, lights, selection lighting, and group instance payloads each frame. Incomplete resources and active animation remain on the live classification path, and only an exact full packed comparison acknowledges scene dirtiness.
This commit is contained in:
parent
f10dec58ba
commit
54d17eb446
7 changed files with 731 additions and 30 deletions
|
|
@ -30,6 +30,8 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
private readonly List<CurrentRenderSelectionFingerprint>
|
||||
_packedSelectionParts = [];
|
||||
private readonly HashSet<PackedSelectionKey> _packedSelectionKeys = [];
|
||||
private readonly PackedProjectionClassificationCache
|
||||
_packedClassificationCache = new();
|
||||
private long _packedGroupFrame;
|
||||
private long _nextPackedGroupRegistration = 1;
|
||||
private int _nextPackedInstanceSubmissionOrder;
|
||||
|
|
@ -40,6 +42,10 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
internal IReadOnlyList<CurrentRenderSelectionFingerprint>
|
||||
PackedSelectionParts => _packedSelectionParts;
|
||||
|
||||
internal PackedClassificationCacheSnapshot
|
||||
PackedClassificationSnapshot =>
|
||||
_packedClassificationCache.Snapshot;
|
||||
|
||||
internal void BuildPackedDispatcherOracle(
|
||||
in RenderFrameView view,
|
||||
uint tupleLandblockId,
|
||||
|
|
@ -60,6 +66,7 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
_packedSubmissions.Clear();
|
||||
_packedSelectionParts.Clear();
|
||||
_packedSelectionKeys.Clear();
|
||||
_packedClassificationCache.BeginFrame(view.Generation);
|
||||
|
||||
ReadOnlySpan<RenderFrameEntityCandidate> entities =
|
||||
view.EntityCandidates;
|
||||
|
|
@ -132,6 +139,7 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
in source,
|
||||
tupleLandblockId);
|
||||
ClassifyPackedEntity(
|
||||
in projection,
|
||||
in candidate,
|
||||
meshParts.Slice(
|
||||
source.MeshPartOffset,
|
||||
|
|
@ -164,9 +172,12 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
cameraWorldPosition,
|
||||
_packedAlphaFingerprintScratch));
|
||||
}
|
||||
|
||||
_packedClassificationCache.EndFrame();
|
||||
}
|
||||
|
||||
private void ClassifyPackedEntity(
|
||||
in RenderProjectionRecord projection,
|
||||
in RenderInstanceCandidate entity,
|
||||
ReadOnlySpan<RenderFrameMeshPart> meshParts,
|
||||
ref uint anyVao)
|
||||
|
|
@ -194,6 +205,45 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
lighting.Luminosity,
|
||||
lighting.Diffuse)
|
||||
: new Vector2(0f, 1f);
|
||||
|
||||
PackedProjectionClassificationEntry? cacheEntry = null;
|
||||
if (!entity.Animated)
|
||||
{
|
||||
PackedClassificationIdentity identity =
|
||||
PackedClassificationIdentity.From(in projection);
|
||||
if (_packedClassificationCache.TryGetReusable(
|
||||
entity.ProjectionId,
|
||||
in identity,
|
||||
projection.DirtyMask,
|
||||
out cacheEntry))
|
||||
{
|
||||
if (anyVao == 0 && !meshParts.IsEmpty)
|
||||
{
|
||||
ObjectRenderData? firstRenderData =
|
||||
_meshAdapter.TryGetRenderData(
|
||||
meshParts[0].MeshRef.GfxObjId);
|
||||
if (firstRenderData is not null)
|
||||
anyVao = firstRenderData.VAO;
|
||||
}
|
||||
ReplayPackedClassification(
|
||||
cacheEntry!,
|
||||
in entity,
|
||||
slot,
|
||||
lights,
|
||||
indoor,
|
||||
selectionLighting);
|
||||
return;
|
||||
}
|
||||
|
||||
cacheEntry = _packedClassificationCache.BeginRebuild(
|
||||
entity.ProjectionId,
|
||||
in identity);
|
||||
}
|
||||
else
|
||||
{
|
||||
_packedClassificationCache.RecordAnimatedClassification();
|
||||
}
|
||||
|
||||
PaletteCompositeIdentity paletteIdentity = default;
|
||||
if (entity.PaletteOverride is not null)
|
||||
{
|
||||
|
|
@ -201,6 +251,7 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
entity.PaletteOverride);
|
||||
}
|
||||
|
||||
bool reusableAcrossFrames = !entity.Animated;
|
||||
for (int meshIndex = 0;
|
||||
meshIndex < meshParts.Length;
|
||||
meshIndex++)
|
||||
|
|
@ -219,7 +270,10 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
ObjectRenderData? renderData =
|
||||
_meshAdapter.TryGetRenderData(meshRef.GfxObjId);
|
||||
if (renderData is null)
|
||||
{
|
||||
reusableAcrossFrames = false;
|
||||
continue;
|
||||
}
|
||||
if (anyVao == 0)
|
||||
anyVao = renderData.VAO;
|
||||
|
||||
|
|
@ -235,20 +289,26 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
ObjectRenderData? partData =
|
||||
_meshAdapter.TryGetRenderData(gfxObjId);
|
||||
if (partData is null)
|
||||
{
|
||||
reusableAcrossFrames = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
float opacity = PackedPartOpacity(
|
||||
entity.LocalEntityId,
|
||||
(uint)setupPartIndex);
|
||||
if (opacity < 1f)
|
||||
reusableAcrossFrames = false;
|
||||
if (opacity <= 0f)
|
||||
continue;
|
||||
|
||||
Matrix4x4 model = ComposePartWorldMatrix(
|
||||
entity.RootWorld,
|
||||
meshRef.PartTransform,
|
||||
partTransform);
|
||||
ClassifyPackedBatches(
|
||||
Matrix4x4 restPose =
|
||||
partTransform * meshRef.PartTransform;
|
||||
Matrix4x4 model =
|
||||
restPose * entity.RootWorld;
|
||||
if (!ClassifyPackedBatches(
|
||||
partData,
|
||||
restPose,
|
||||
model,
|
||||
in entity,
|
||||
meshRef,
|
||||
|
|
@ -257,12 +317,22 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
lights,
|
||||
indoor,
|
||||
selectionLighting,
|
||||
opacity);
|
||||
opacity,
|
||||
cacheEntry))
|
||||
{
|
||||
reusableAcrossFrames = false;
|
||||
}
|
||||
int selectionPartIndex = unchecked(
|
||||
(partIndex << 16)
|
||||
| (setupPartIndex & 0xFFFF));
|
||||
cacheEntry?.SelectionParts.Add(
|
||||
new PackedClassifiedSelectionPart(
|
||||
selectionPartIndex,
|
||||
(uint)gfxObjId,
|
||||
restPose));
|
||||
AddPackedSelectionPart(
|
||||
in entity,
|
||||
unchecked(
|
||||
(partIndex << 16)
|
||||
| (setupPartIndex & 0xFFFF)),
|
||||
selectionPartIndex,
|
||||
(uint)gfxObjId,
|
||||
model);
|
||||
}
|
||||
|
|
@ -272,13 +342,16 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
float opacity = PackedPartOpacity(
|
||||
entity.LocalEntityId,
|
||||
0u);
|
||||
if (opacity < 1f)
|
||||
reusableAcrossFrames = false;
|
||||
if (opacity <= 0f)
|
||||
continue;
|
||||
|
||||
Matrix4x4 model =
|
||||
meshRef.PartTransform * entity.RootWorld;
|
||||
ClassifyPackedBatches(
|
||||
Matrix4x4 restPose = meshRef.PartTransform;
|
||||
Matrix4x4 model = restPose * entity.RootWorld;
|
||||
if (!ClassifyPackedBatches(
|
||||
renderData,
|
||||
restPose,
|
||||
model,
|
||||
in entity,
|
||||
meshRef,
|
||||
|
|
@ -287,7 +360,16 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
lights,
|
||||
indoor,
|
||||
selectionLighting,
|
||||
opacity);
|
||||
opacity,
|
||||
cacheEntry))
|
||||
{
|
||||
reusableAcrossFrames = false;
|
||||
}
|
||||
cacheEntry?.SelectionParts.Add(
|
||||
new PackedClassifiedSelectionPart(
|
||||
partIndex,
|
||||
meshRef.GfxObjId,
|
||||
restPose));
|
||||
AddPackedSelectionPart(
|
||||
in entity,
|
||||
partIndex,
|
||||
|
|
@ -295,6 +377,13 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
model);
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheEntry is not null)
|
||||
{
|
||||
_packedClassificationCache.CompleteRebuild(
|
||||
cacheEntry,
|
||||
reusableAcrossFrames);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -324,8 +413,9 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
: 1f - translucency;
|
||||
}
|
||||
|
||||
private void ClassifyPackedBatches(
|
||||
private bool ClassifyPackedBatches(
|
||||
ObjectRenderData renderData,
|
||||
Matrix4x4 restPose,
|
||||
Matrix4x4 model,
|
||||
in RenderInstanceCandidate entity,
|
||||
MeshRef meshRef,
|
||||
|
|
@ -334,8 +424,10 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
InstanceLightSet lights,
|
||||
bool indoor,
|
||||
Vector2 selectionLighting,
|
||||
float opacity)
|
||||
float opacity,
|
||||
PackedProjectionClassificationEntry? cacheEntry)
|
||||
{
|
||||
bool reusableAcrossFrames = true;
|
||||
for (int batchIndex = 0;
|
||||
batchIndex < renderData.Batches.Count;
|
||||
batchIndex++)
|
||||
|
|
@ -351,7 +443,9 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
meshRef,
|
||||
batch,
|
||||
paletteIdentity,
|
||||
out _);
|
||||
out bool compositePending);
|
||||
if (compositePending)
|
||||
reusableAcrossFrames = false;
|
||||
if (texture.Handle == 0)
|
||||
continue;
|
||||
|
||||
|
|
@ -363,17 +457,78 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
texture.Layer,
|
||||
translucency,
|
||||
batch.CullMode);
|
||||
InstanceGroup group = GetOrCreatePackedGroup(key);
|
||||
group.Matrices.Add(model);
|
||||
group.LocalSortCenters.Add(renderData.SortCenter);
|
||||
group.SubmissionOrders.Add(
|
||||
_nextPackedInstanceSubmissionOrder++);
|
||||
group.Slots.Add(slot);
|
||||
group.LightSets.Add(lights);
|
||||
group.IndoorFlags.Add(indoor ? 1u : 0u);
|
||||
group.Opacities.Add(opacity);
|
||||
group.SelectionLighting.Add(selectionLighting);
|
||||
var classified = new PackedClassifiedBatch(
|
||||
key,
|
||||
restPose,
|
||||
renderData.SortCenter);
|
||||
cacheEntry?.Batches.Add(classified);
|
||||
AppendPackedClassification(
|
||||
in classified,
|
||||
model,
|
||||
slot,
|
||||
lights,
|
||||
indoor,
|
||||
opacity,
|
||||
selectionLighting);
|
||||
}
|
||||
|
||||
return reusableAcrossFrames;
|
||||
}
|
||||
|
||||
private void ReplayPackedClassification(
|
||||
PackedProjectionClassificationEntry entry,
|
||||
in RenderInstanceCandidate entity,
|
||||
uint slot,
|
||||
InstanceLightSet lights,
|
||||
bool indoor,
|
||||
Vector2 selectionLighting)
|
||||
{
|
||||
for (int i = 0; i < entry.Batches.Count; i++)
|
||||
{
|
||||
PackedClassifiedBatch classified = entry.Batches[i];
|
||||
AppendPackedClassification(
|
||||
in classified,
|
||||
classified.RestPose * entity.RootWorld,
|
||||
slot,
|
||||
lights,
|
||||
indoor,
|
||||
opacity: 1f,
|
||||
selectionLighting);
|
||||
}
|
||||
|
||||
for (int i = 0; i < entry.SelectionParts.Count; i++)
|
||||
{
|
||||
PackedClassifiedSelectionPart part =
|
||||
entry.SelectionParts[i];
|
||||
AddPackedSelectionPart(
|
||||
in entity,
|
||||
part.PartIndex,
|
||||
part.GfxObjId,
|
||||
part.RestPose * entity.RootWorld);
|
||||
}
|
||||
}
|
||||
|
||||
private void AppendPackedClassification(
|
||||
in PackedClassifiedBatch classified,
|
||||
Matrix4x4 model,
|
||||
uint slot,
|
||||
InstanceLightSet lights,
|
||||
bool indoor,
|
||||
float opacity,
|
||||
Vector2 selectionLighting)
|
||||
{
|
||||
InstanceGroup group =
|
||||
GetOrCreatePackedGroup(classified.Key);
|
||||
group.Matrices.Add(model);
|
||||
group.LocalSortCenters.Add(
|
||||
classified.LocalSortCenter);
|
||||
group.SubmissionOrders.Add(
|
||||
_nextPackedInstanceSubmissionOrder++);
|
||||
group.Slots.Add(slot);
|
||||
group.LightSets.Add(lights);
|
||||
group.IndoorFlags.Add(indoor ? 1u : 0u);
|
||||
group.Opacities.Add(opacity);
|
||||
group.SelectionLighting.Add(selectionLighting);
|
||||
}
|
||||
|
||||
private InstanceGroup GetOrCreatePackedGroup(GroupKey key)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue