Revert "perf(rendering): draw retained frame product"
This reverts commit ef1d263337.
This commit is contained in:
parent
624e1119ca
commit
2c848d4167
13 changed files with 268 additions and 1334 deletions
|
|
@ -1,5 +1,4 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Rendering;
|
||||
using AcDream.App.Rendering.Scene;
|
||||
using AcDream.App.Rendering.Selection;
|
||||
using AcDream.Core.Lighting;
|
||||
|
|
@ -11,10 +10,10 @@ using DatReaderWriter.Enums;
|
|||
namespace AcDream.App.Rendering.Wb;
|
||||
|
||||
/// <summary>
|
||||
/// Scene-frame classification and production submission. The retained group
|
||||
/// storage is also consumed by the G2/G3 compare oracle, while production
|
||||
/// routes reuse the dispatcher's exact mesh, texture, light, translucency,
|
||||
/// selection, upload, and draw owners.
|
||||
/// G2 compare-only classification of the scene-built frame product. This file
|
||||
/// deliberately shares the production dispatcher's exact mesh, texture,
|
||||
/// light, translucency, selection, grouping, and ordering owners while keeping
|
||||
/// its group storage separate. It never uploads or draws.
|
||||
/// </summary>
|
||||
public sealed unsafe partial class WbDrawDispatcher
|
||||
{
|
||||
|
|
@ -36,10 +35,6 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
private long _packedGroupFrame;
|
||||
private long _nextPackedGroupRegistration = 1;
|
||||
private int _nextPackedInstanceSubmissionOrder;
|
||||
private bool _packedProductionFrameOpen;
|
||||
private RenderSceneGeneration _packedProductionGeneration;
|
||||
private ulong _packedProductionFrameSequence;
|
||||
private int _packedProductionNextRange;
|
||||
|
||||
internal IReadOnlyList<CurrentRenderDispatcherSubmission>
|
||||
PackedDispatcherSubmissions => _packedSubmissions;
|
||||
|
|
@ -56,28 +51,104 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
uint tupleLandblockId,
|
||||
Vector3 cameraWorldPosition)
|
||||
{
|
||||
if (_packedProductionFrameOpen)
|
||||
if (_packedGroupFrame == long.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The packed dispatcher oracle cannot replace an active production frame.");
|
||||
"Packed dispatcher frame identity was exhausted.");
|
||||
}
|
||||
|
||||
BeginPackedFrameStorage(in view);
|
||||
_packedGroupFrame++;
|
||||
PruneInstanceGroupsUnusedBeforeFrame(
|
||||
_packedGroups,
|
||||
_packedRetiredGroupKeys,
|
||||
_packedGroupFrame - 1);
|
||||
_packedEntityById.Clear();
|
||||
_packedSubmissions.Clear();
|
||||
_packedSelectionParts.Clear();
|
||||
_packedSelectionKeys.Clear();
|
||||
_packedClassificationCache.BeginFrame(view.Generation);
|
||||
|
||||
ReadOnlySpan<RenderFrameEntityCandidate> entities =
|
||||
view.EntityCandidates;
|
||||
for (int i = 0; i < entities.Length; i++)
|
||||
{
|
||||
if (!_packedEntityById.TryAdd(
|
||||
entities[i].Projection.Id,
|
||||
entities[i]))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Packed dispatcher received duplicate projection "
|
||||
+ $"{entities[i].Projection.Id}.");
|
||||
}
|
||||
}
|
||||
|
||||
ReadOnlySpan<RenderFrameMeshPart> meshParts = view.MeshParts;
|
||||
ReadOnlySpan<RenderProjectionRecord> routeCandidates =
|
||||
view.RouteCandidates;
|
||||
ReadOnlySpan<RenderFrameCandidateRange> ranges =
|
||||
view.RouteRanges;
|
||||
for (int rangeIndex = 0;
|
||||
rangeIndex < ranges.Length;
|
||||
rangeIndex++)
|
||||
{
|
||||
PackedRangeClassification classified =
|
||||
ClassifyPackedRange(
|
||||
in view,
|
||||
rangeIndex,
|
||||
tupleLandblockId,
|
||||
publishSelection: false);
|
||||
_nextPackedInstanceSubmissionOrder = 0;
|
||||
uint anyVao = 0;
|
||||
foreach (InstanceGroup group in _packedGroups.Values)
|
||||
group.ClearPerInstanceData();
|
||||
|
||||
RenderFrameCandidateRange range = ranges[rangeIndex];
|
||||
int end = checked(range.Offset + range.Count);
|
||||
if ((uint)range.Offset > (uint)routeCandidates.Length
|
||||
|| (uint)end > (uint)routeCandidates.Length)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Packed dispatcher route {rangeIndex} exceeds "
|
||||
+ "candidate storage.");
|
||||
}
|
||||
|
||||
for (int routeIndex = range.Offset;
|
||||
routeIndex < end;
|
||||
routeIndex++)
|
||||
{
|
||||
RenderProjectionRecord projection =
|
||||
routeCandidates[routeIndex];
|
||||
if ((projection.Flags & RenderProjectionFlags.Draw) == 0)
|
||||
continue;
|
||||
if (!_packedEntityById.TryGetValue(
|
||||
projection.Id,
|
||||
out RenderFrameEntityCandidate source))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Packed route references absent entity "
|
||||
+ $"{projection.Id}.");
|
||||
}
|
||||
|
||||
int meshEnd = checked(
|
||||
source.MeshPartOffset + source.MeshPartCount);
|
||||
if ((uint)source.MeshPartOffset
|
||||
> (uint)meshParts.Length
|
||||
|| (uint)meshEnd > (uint)meshParts.Length)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Packed entity {projection.Id} exceeds mesh "
|
||||
+ "part storage.");
|
||||
}
|
||||
|
||||
RenderInstanceCandidate candidate =
|
||||
RenderInstanceCandidate.FromFrame(
|
||||
in source,
|
||||
tupleLandblockId);
|
||||
ClassifyPackedEntity(
|
||||
in projection,
|
||||
in candidate,
|
||||
meshParts.Slice(
|
||||
source.MeshPartOffset,
|
||||
source.MeshPartCount),
|
||||
ref anyVao);
|
||||
}
|
||||
|
||||
bool deferTransparent = ShouldDeferPackedTransparent(
|
||||
classified.AnyVao,
|
||||
anyVao,
|
||||
_alphaQueue?.IsCollecting == true);
|
||||
InstanceLayoutCounts counts = PartitionInstanceGroups(
|
||||
_packedGroups.Values,
|
||||
|
|
@ -105,236 +176,11 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
_packedClassificationCache.EndFrame();
|
||||
}
|
||||
|
||||
internal void BeginPackedProductionFrame(
|
||||
in RenderFrameView view)
|
||||
{
|
||||
if (_packedProductionFrameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The packed dispatcher cannot begin a second production frame.");
|
||||
}
|
||||
|
||||
BeginPackedFrameStorage(in view);
|
||||
_packedProductionFrameOpen = true;
|
||||
_packedProductionGeneration = view.Generation;
|
||||
_packedProductionFrameSequence = view.FrameSequence;
|
||||
_packedProductionNextRange = 0;
|
||||
}
|
||||
|
||||
internal bool DrawPackedProductionRoute(
|
||||
ICamera camera,
|
||||
in RenderFrameView view,
|
||||
RenderFrameCandidateRoute route,
|
||||
int routeIndex,
|
||||
uint cellId,
|
||||
uint tupleLandblockId)
|
||||
{
|
||||
ValidatePackedProductionView(in view);
|
||||
ReadOnlySpan<RenderFrameCandidateRange> ranges =
|
||||
view.RouteRanges;
|
||||
if (_packedProductionNextRange >= ranges.Length)
|
||||
return false;
|
||||
|
||||
RenderFrameCandidateRange range =
|
||||
ranges[_packedProductionNextRange];
|
||||
if (range.Route != route
|
||||
|| range.RouteIndex != routeIndex
|
||||
|| range.CellId != cellId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int rangePosition = _packedProductionNextRange++;
|
||||
bool diagnosticsEnabled = BeginEntityDispatch(
|
||||
camera,
|
||||
out Matrix4x4 viewProjection,
|
||||
out Vector3 cameraWorldPosition);
|
||||
PackedRangeClassification classified =
|
||||
ClassifyPackedRange(
|
||||
in view,
|
||||
rangePosition,
|
||||
tupleLandblockId,
|
||||
publishSelection: true);
|
||||
ExecuteClassifiedGroups(
|
||||
viewProjection,
|
||||
cameraWorldPosition,
|
||||
classified.AnyVao,
|
||||
_packedGroups.Values,
|
||||
EntitySet.All,
|
||||
classified.CandidateCount,
|
||||
classified.MeshPartCount,
|
||||
diagnosticsEnabled,
|
||||
observeCurrentPath: false);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void CompletePackedProductionFrame(
|
||||
in RenderFrameView view)
|
||||
{
|
||||
ValidatePackedProductionView(in view);
|
||||
int expected = view.RouteRanges.Length;
|
||||
if (_packedProductionNextRange != expected)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The packed dispatcher did not consume the complete route stream: "
|
||||
+ $"consumed={_packedProductionNextRange} expected={expected}.");
|
||||
}
|
||||
|
||||
_packedClassificationCache.EndFrame();
|
||||
ResetPackedProductionFrame();
|
||||
}
|
||||
|
||||
internal void AbortPackedProductionFrame()
|
||||
{
|
||||
if (!_packedProductionFrameOpen)
|
||||
return;
|
||||
|
||||
_packedClassificationCache.EndFrame();
|
||||
ResetPackedProductionFrame();
|
||||
}
|
||||
|
||||
private void BeginPackedFrameStorage(
|
||||
in RenderFrameView view)
|
||||
{
|
||||
if (_packedGroupFrame == long.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Packed dispatcher frame identity was exhausted.");
|
||||
}
|
||||
|
||||
_packedGroupFrame++;
|
||||
PruneInstanceGroupsUnusedBeforeFrame(
|
||||
_packedGroups,
|
||||
_packedRetiredGroupKeys,
|
||||
_packedGroupFrame - 1);
|
||||
_packedEntityById.Clear();
|
||||
_packedSubmissions.Clear();
|
||||
_packedSelectionParts.Clear();
|
||||
_packedSelectionKeys.Clear();
|
||||
_packedClassificationCache.BeginFrame(view.Generation);
|
||||
|
||||
ReadOnlySpan<RenderFrameEntityCandidate> entities =
|
||||
view.EntityCandidates;
|
||||
for (int index = 0; index < entities.Length; index++)
|
||||
{
|
||||
if (!_packedEntityById.TryAdd(
|
||||
entities[index].Projection.Id,
|
||||
entities[index]))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Packed dispatcher received duplicate projection "
|
||||
+ $"{entities[index].Projection.Id}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PackedRangeClassification ClassifyPackedRange(
|
||||
in RenderFrameView view,
|
||||
int rangeIndex,
|
||||
uint tupleLandblockId,
|
||||
bool publishSelection)
|
||||
{
|
||||
ReadOnlySpan<RenderFrameCandidateRange> ranges =
|
||||
view.RouteRanges;
|
||||
if ((uint)rangeIndex >= (uint)ranges.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(rangeIndex));
|
||||
|
||||
_nextPackedInstanceSubmissionOrder = 0;
|
||||
foreach (InstanceGroup group in _packedGroups.Values)
|
||||
group.ClearPerInstanceData();
|
||||
|
||||
uint anyVao = 0;
|
||||
int meshPartCount = 0;
|
||||
RenderFrameCandidateRange range = ranges[rangeIndex];
|
||||
ReadOnlySpan<RenderProjectionRecord> routeCandidates =
|
||||
view.RouteCandidates;
|
||||
ReadOnlySpan<RenderFrameMeshPart> meshParts = view.MeshParts;
|
||||
int end = checked(range.Offset + range.Count);
|
||||
if ((uint)range.Offset > (uint)routeCandidates.Length
|
||||
|| (uint)end > (uint)routeCandidates.Length)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Packed dispatcher route {rangeIndex} exceeds candidate storage.");
|
||||
}
|
||||
|
||||
for (int candidateIndex = range.Offset;
|
||||
candidateIndex < end;
|
||||
candidateIndex++)
|
||||
{
|
||||
RenderProjectionRecord projection =
|
||||
routeCandidates[candidateIndex];
|
||||
if ((projection.Flags & RenderProjectionFlags.Draw) == 0)
|
||||
continue;
|
||||
if (!_packedEntityById.TryGetValue(
|
||||
projection.Id,
|
||||
out RenderFrameEntityCandidate source))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Packed route references absent entity {projection.Id}.");
|
||||
}
|
||||
|
||||
int meshEnd = checked(
|
||||
source.MeshPartOffset + source.MeshPartCount);
|
||||
if ((uint)source.MeshPartOffset > (uint)meshParts.Length
|
||||
|| (uint)meshEnd > (uint)meshParts.Length)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Packed entity {projection.Id} exceeds mesh part storage.");
|
||||
}
|
||||
|
||||
meshPartCount = checked(
|
||||
meshPartCount + source.MeshPartCount);
|
||||
RenderInstanceCandidate candidate =
|
||||
RenderInstanceCandidate.FromFrame(
|
||||
in source,
|
||||
tupleLandblockId);
|
||||
ClassifyPackedEntity(
|
||||
in projection,
|
||||
in candidate,
|
||||
meshParts.Slice(
|
||||
source.MeshPartOffset,
|
||||
source.MeshPartCount),
|
||||
ref anyVao,
|
||||
publishSelection);
|
||||
}
|
||||
|
||||
return new PackedRangeClassification(
|
||||
anyVao,
|
||||
range.Count,
|
||||
meshPartCount);
|
||||
}
|
||||
|
||||
private void ValidatePackedProductionView(
|
||||
in RenderFrameView view)
|
||||
{
|
||||
if (!_packedProductionFrameOpen
|
||||
|| view.Generation != _packedProductionGeneration
|
||||
|| view.FrameSequence != _packedProductionFrameSequence)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The packed dispatcher received a stale or foreign production frame.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetPackedProductionFrame()
|
||||
{
|
||||
_packedProductionFrameOpen = false;
|
||||
_packedProductionGeneration = default;
|
||||
_packedProductionFrameSequence = 0;
|
||||
_packedProductionNextRange = 0;
|
||||
}
|
||||
|
||||
private readonly record struct PackedRangeClassification(
|
||||
uint AnyVao,
|
||||
int CandidateCount,
|
||||
int MeshPartCount);
|
||||
|
||||
private void ClassifyPackedEntity(
|
||||
in RenderProjectionRecord projection,
|
||||
in RenderInstanceCandidate entity,
|
||||
ReadOnlySpan<RenderFrameMeshPart> meshParts,
|
||||
ref uint anyVao,
|
||||
bool publishSelection)
|
||||
ref uint anyVao)
|
||||
{
|
||||
(uint slot, bool culled) = ResolveSlotForFrame(
|
||||
_clipRoutingActive,
|
||||
|
|
@ -385,8 +231,7 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
slot,
|
||||
lights,
|
||||
indoor,
|
||||
selectionLighting,
|
||||
publishSelection);
|
||||
selectionLighting);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -427,8 +272,6 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
if (renderData is null)
|
||||
{
|
||||
reusableAcrossFrames = false;
|
||||
if (_missRequested.Add(meshRef.GfxObjId))
|
||||
_meshAdapter.EnsureLoaded(meshRef.GfxObjId);
|
||||
continue;
|
||||
}
|
||||
if (anyVao == 0)
|
||||
|
|
@ -448,8 +291,6 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
if (partData is null)
|
||||
{
|
||||
reusableAcrossFrames = false;
|
||||
if (_missRequested.Add(gfxObjId))
|
||||
_meshAdapter.EnsureLoaded(gfxObjId);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -493,8 +334,7 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
in entity,
|
||||
selectionPartIndex,
|
||||
(uint)gfxObjId,
|
||||
model,
|
||||
publishSelection);
|
||||
model);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -534,8 +374,7 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
in entity,
|
||||
partIndex,
|
||||
meshRef.GfxObjId,
|
||||
model,
|
||||
publishSelection);
|
||||
model);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -642,8 +481,7 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
uint slot,
|
||||
InstanceLightSet lights,
|
||||
bool indoor,
|
||||
Vector2 selectionLighting,
|
||||
bool publishSelection)
|
||||
Vector2 selectionLighting)
|
||||
{
|
||||
for (int i = 0; i < entry.Batches.Count; i++)
|
||||
{
|
||||
|
|
@ -666,8 +504,7 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
in entity,
|
||||
part.PartIndex,
|
||||
part.GfxObjId,
|
||||
part.RestPose * entity.RootWorld,
|
||||
publishSelection);
|
||||
part.RestPose * entity.RootWorld);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -756,29 +593,13 @@ public sealed unsafe partial class WbDrawDispatcher
|
|||
in RenderInstanceCandidate entity,
|
||||
int partIndex,
|
||||
uint gfxObjId,
|
||||
Matrix4x4 localToWorld,
|
||||
bool publishSelection)
|
||||
Matrix4x4 localToWorld)
|
||||
{
|
||||
if (!_packedSelectionKeys.Add(new PackedSelectionKey(
|
||||
entity.LocalEntityId,
|
||||
partIndex,
|
||||
gfxObjId)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (publishSelection)
|
||||
{
|
||||
_selectionSink?.AddVisiblePart(
|
||||
entity.ServerGuid,
|
||||
entity.LocalEntityId,
|
||||
partIndex,
|
||||
gfxObjId,
|
||||
localToWorld);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectionSink is not IRetailSelectionRenderOracle oracle
|
||||
|| !_packedSelectionKeys.Add(new PackedSelectionKey(
|
||||
entity.LocalEntityId,
|
||||
partIndex,
|
||||
gfxObjId))
|
||||
|| !oracle.TryCreateVisiblePart(
|
||||
entity.ServerGuid,
|
||||
entity.LocalEntityId,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue