refactor(render): delete the packed dispatcher
This commit is contained in:
parent
8d8aa715ee
commit
73ba1d3c32
7 changed files with 115 additions and 1659 deletions
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue