Revert "perf(rendering): draw retained frame product"

This reverts commit ef1d263337.
This commit is contained in:
Erik 2026-07-25 06:28:31 +02:00
parent 624e1119ca
commit 2c848d4167
13 changed files with 268 additions and 1334 deletions

View file

@ -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,