refactor(render): delete the packed dispatcher

This commit is contained in:
Erik 2026-08-31 04:44:22 +02:00
parent 8d8aa715ee
commit 73ba1d3c32
7 changed files with 115 additions and 1659 deletions

View file

@ -1,115 +0,0 @@
using AcDream.App.Rendering.Wb;
using AcDream.App.Rendering.Gpu;
using AcDream.Core.Meshing;
using System.Numerics;
namespace AcDream.App.Tests.Rendering.Wb;
public sealed class PackedDispatcherOracleTests
{
/// <summary>
/// Campaign VM VM6 review fix round 3 (N1): this test does NOT guard
/// the packed classifier itself — CreateGroupFromKey's own test
/// (InstanceGroupClearTests.CreateGroupFromKey_CopiesFoliageFlagsFromTheKey)
/// plus GroupKey.FoliageFlags being a required constructor argument are
/// what actually make F1 (the round-2 blocker: the packed classifier's
/// InstanceGroup silently carried FoliageFlags == 0) impossible to
/// reintroduce. What THIS test proves is the SHARED production step
/// downstream of classification: FoliageWindClassification.Classify,
/// called with the same argument shape ClassifyPackedBatches uses
/// (entity.LocalEntityId, exclusion membership, batch.Translucency,
/// entity-scoped HasCutoutSubset), resolves a real procedural-scenery
/// entity id (0x8…) to CutoutFoliageFlag (0x2); and a group carrying
/// that flags value reaches the literal BatchData.flags word through
/// BuildIndirectArrays — the same already-tested production step both
/// the classic and packed group lists feed into
/// (WbDrawDispatcherIndirectBuilderTests pins bit 0; this pins bits 1/2
/// landing alongside it for a real scenery id).
/// </summary>
[Fact]
public void SceneryCutoutEntityClassificationReachesTheBatchDataFlagsWordAsBit0x2()
{
const uint proceduralSceneryTreeId = 0x80010203u; // top nibble 0x8
uint foliageFlags = FoliageWindClassification.Classify(
proceduralSceneryTreeId,
isExcluded: false,
TranslucencyKind.ClipMap,
meshHasCutoutSubset: true);
Assert.Equal(FoliageWindClassification.CutoutFoliageFlag, foliageFlags);
Assert.Equal(0x2u, foliageFlags);
var groups = new List<WbDrawDispatcher.IndirectGroupInput>
{
new(
IndexCount: 12,
FirstIndex: 0,
BaseVertex: 0,
InstanceCount: 1,
FirstInstance: 0,
TextureIndex: 0x5,
TextureLayer: 0,
Translucency: TranslucencyKind.ClipMap,
FoliageFlags: foliageFlags),
};
var indirect = new DrawElementsIndirectCommand[4];
var batches = new WbDrawDispatcher.BatchDataPublic[4];
WbDrawDispatcher.BuildIndirectArrays(groups, indirect, batches);
// Bit 0 (the #226 built-mesh marker) | bit 1 (cutout foliage) = 0x3;
// isolating bit 1 with a mask proves the foliage classification
// specifically reached the word, not merely that SOME nonzero value
// did.
Assert.Equal(0x2u, batches[0].Flags & 0x2u);
}
[Theory]
[InlineData(false, 0u)]
[InlineData(true, 1u)]
public void PackedInstanceWriter_AppendsDetailCategoryInParallel(
bool buildingDetail,
uint expectedCategory)
{
var group = new WbDrawDispatcher.InstanceGroup();
WbDrawDispatcher.AppendPackedInstance(
group,
Matrix4x4.Identity,
Vector3.One,
submissionOrder: 7,
slot: 3u,
lights: WbDrawDispatcher.InstanceLightSet.Disabled,
indoor: true,
buildingDetail: buildingDetail,
opacity: 0.5f,
selectionLighting: new Vector2(0.25f, 0.75f));
Assert.Single(group.Matrices);
Assert.Single(group.LocalSortCenters);
Assert.Single(group.SubmissionOrders);
Assert.Single(group.Slots);
Assert.Single(group.LightSets);
Assert.Single(group.IndoorFlags);
Assert.Equal(expectedCategory, Assert.Single(group.DetailCategories));
Assert.Single(group.Opacities);
Assert.Single(group.SelectionLighting);
}
[Theory]
[InlineData(0u, false, false)]
[InlineData(0u, true, false)]
[InlineData(7u, false, false)]
[InlineData(7u, true, true)]
public void TransparentDeferral_MatchesProductionNoVaoEarlyReturn(
uint anyVao,
bool alphaQueueCollecting,
bool expected)
{
Assert.Equal(
expected,
WbDrawDispatcher.ShouldDeferPackedTransparent(
anyVao,
alphaQueueCollecting));
}
}

View file

@ -1,215 +0,0 @@
using AcDream.App.Rendering.Scene;
using AcDream.App.Rendering.Wb;
using Xunit;
namespace AcDream.App.Tests.Rendering.Wb;
public sealed class PackedProjectionClassificationCacheTests
{
[Fact]
public void UnchangedProjection_ReusesClassificationAcrossFrames()
{
var cache = new PackedProjectionClassificationCache();
RenderSceneGeneration generation =
RenderSceneGeneration.FromRaw(1);
RenderProjectionId id = RenderProjectionId.FromRaw(0x1234);
PackedClassificationIdentity identity = Identity(7);
cache.BeginFrame(generation);
PackedProjectionClassificationEntry entry =
cache.BeginRebuild(id, in identity);
cache.CompleteRebuild(entry, reusableAcrossFrames: true);
cache.EndFrame();
cache.BeginFrame(generation);
Assert.True(cache.TryGetReusable(
id,
in identity,
RenderDirtyMask.Transform,
out PackedProjectionClassificationEntry? reused));
cache.EndFrame();
Assert.Same(entry, reused);
Assert.Equal(0, cache.Snapshot.StaticRebuildCount);
Assert.Equal(1, cache.Snapshot.CrossFrameReuseCount);
Assert.Equal(1, cache.Snapshot.ProjectionCount);
}
[Fact]
public void AppearanceDirty_RequiresRebuildButTransformDirtyDoesNot()
{
var cache = new PackedProjectionClassificationCache();
RenderSceneGeneration generation =
RenderSceneGeneration.FromRaw(1);
RenderProjectionId id = RenderProjectionId.FromRaw(0x1234);
PackedClassificationIdentity identity = Identity(7);
cache.BeginFrame(generation);
PackedProjectionClassificationEntry entry =
cache.BeginRebuild(id, in identity);
cache.CompleteRebuild(entry, reusableAcrossFrames: true);
cache.EndFrame();
cache.BeginFrame(generation);
Assert.False(cache.TryGetReusable(
id,
in identity,
RenderDirtyMask.Appearance,
out _));
PackedProjectionClassificationEntry rebuilt =
cache.BeginRebuild(id, in identity);
cache.CompleteRebuild(rebuilt, reusableAcrossFrames: true);
Assert.Same(entry, rebuilt);
Assert.Equal(1, cache.Snapshot.StaticRebuildCount);
}
[Fact]
public void IncompleteBuild_CanReplayDuplicateRouteOnlyInSameFrame()
{
var cache = new PackedProjectionClassificationCache();
RenderSceneGeneration generation =
RenderSceneGeneration.FromRaw(1);
RenderProjectionId id = RenderProjectionId.FromRaw(0x1234);
PackedClassificationIdentity identity = Identity(7);
cache.BeginFrame(generation);
PackedProjectionClassificationEntry entry =
cache.BeginRebuild(id, in identity);
cache.CompleteRebuild(entry, reusableAcrossFrames: false);
Assert.True(cache.TryGetReusable(
id,
in identity,
RenderDirtyMask.All,
out _));
Assert.Equal(1, cache.Snapshot.SameFrameReuseCount);
cache.EndFrame();
cache.BeginFrame(generation);
Assert.False(cache.TryGetReusable(
id,
in identity,
RenderDirtyMask.None,
out _));
}
[Fact]
public void IncarnationOrAppearanceChange_CannotReuseRetainedPayload()
{
var cache = new PackedProjectionClassificationCache();
RenderSceneGeneration generation =
RenderSceneGeneration.FromRaw(1);
RenderProjectionId id = RenderProjectionId.FromRaw(0x1234);
PackedClassificationIdentity original = Identity(7);
cache.BeginFrame(generation);
PackedProjectionClassificationEntry entry =
cache.BeginRebuild(id, in original);
cache.CompleteRebuild(entry, reusableAcrossFrames: true);
cache.EndFrame();
cache.BeginFrame(generation);
PackedClassificationIdentity replacement = Identity(17);
Assert.False(cache.TryGetReusable(
id,
in replacement,
RenderDirtyMask.None,
out _));
PackedProjectionClassificationEntry rebuilt =
cache.BeginRebuild(id, in replacement);
cache.CompleteRebuild(rebuilt, reusableAcrossFrames: true);
cache.EndFrame();
Assert.Same(entry, rebuilt);
Assert.Equal(replacement, rebuilt.Identity);
Assert.Equal(1, cache.Snapshot.StaticRebuildCount);
Assert.Equal(0, cache.Snapshot.CrossFrameReuseCount);
}
[Fact]
public void GenerationReplacementAndUnseenProjectionReleaseRetainedEntries()
{
var cache = new PackedProjectionClassificationCache();
RenderProjectionId id = RenderProjectionId.FromRaw(0x1234);
PackedClassificationIdentity identity = Identity(7);
cache.BeginFrame(RenderSceneGeneration.FromRaw(1));
PackedProjectionClassificationEntry entry =
cache.BeginRebuild(id, in identity);
entry.Batches.Capacity = 32;
entry.SelectionParts.Capacity = 16;
cache.CompleteRebuild(entry, reusableAcrossFrames: true);
cache.EndFrame();
Assert.True(cache.Snapshot.RetainedPayloadBytes > 0);
cache.BeginFrame(RenderSceneGeneration.FromRaw(1));
cache.EndFrame();
Assert.Equal(0, cache.Snapshot.ProjectionCount);
Assert.Equal(1, cache.Snapshot.RetiredProjectionCount);
Assert.Equal(0, entry.Batches.Capacity);
Assert.Equal(0, entry.SelectionParts.Capacity);
Assert.Equal(0, cache.Snapshot.RetainedPayloadBytes);
cache.BeginFrame(RenderSceneGeneration.FromRaw(2));
Assert.Equal(0, cache.Snapshot.ProjectionCount);
}
[Fact]
public void WarmUnchangedProjectionCache_AllocatesZeroBytes()
{
var cache = new PackedProjectionClassificationCache();
RenderSceneGeneration generation =
RenderSceneGeneration.FromRaw(1);
RenderProjectionId id = RenderProjectionId.FromRaw(0x1234);
PackedClassificationIdentity identity = Identity(7);
cache.BeginFrame(generation);
PackedProjectionClassificationEntry entry =
cache.BeginRebuild(id, in identity);
cache.CompleteRebuild(entry, reusableAcrossFrames: true);
cache.EndFrame();
// #250: the measured window was a 1,000-iteration loop written inline.
ZeroAllocationProbe.AssertAllocatesNothing(
"PackedProjectionClassificationCache warm cache hit",
() => Hit(cache, generation, id, identity));
}
private static void Hit(
PackedProjectionClassificationCache cache,
RenderSceneGeneration generation,
RenderProjectionId id,
PackedClassificationIdentity identity)
{
cache.BeginFrame(generation);
if (!cache.TryGetReusable(
id,
in identity,
RenderDirtyMask.None,
out _))
{
throw new InvalidOperationException(
"The warmed projection classification was not reusable.");
}
cache.EndFrame();
}
private static PackedClassificationIdentity Identity(ulong seed) =>
new(
RenderOwnerIncarnation.FromRaw(seed),
new RenderMeshSet(
RenderAssetHandle.FromRaw(seed + 1),
MeshCount: 2,
Revision: seed + 2),
new RenderMaterialVariant(
PaletteKey: seed + 3,
TextureReplacementKey: seed + 4,
Opacity: 1f),
new RenderDegradeState(
Level: 0,
Revision: (uint)(seed + 5)),
new RenderSceneHash128(seed + 6, seed + 7),
new RenderSceneHash128(seed + 8, seed + 9));
}