using System.Numerics; using AcDream.App.Rendering.Wb; using AcDream.Core.Meshing; using Xunit; namespace AcDream.Core.Tests.Rendering.Wb; /// /// Pure CPU test of . /// Verifies that a synthetic group set lays out into the indirect buffer /// + parallel batch data with opaque section first, transparent second, /// per-group fields propagated correctly. /// public sealed class WbDrawDispatcherIndirectBuilderTests { [Fact] public void TwoOpaqueGroupsAndOneTransparent_LaysOutContiguouslyOpaqueFirst() { // Arrange — three groups: 2 opaque (12+1 instances) + 1 transparent (12 instances) var groups = new List { new(IndexCount: 100, FirstIndex: 0, BaseVertex: 0, InstanceCount: 12, FirstInstance: 0, TextureHandle: 0xAA, TextureLayer: 0, Translucency: TranslucencyKind.Opaque), new(IndexCount: 200, FirstIndex: 100, BaseVertex: 0, InstanceCount: 12, FirstInstance: 12, TextureHandle: 0xBB, TextureLayer: 0, Translucency: TranslucencyKind.AlphaBlend), new(IndexCount: 50, FirstIndex: 300, BaseVertex: 100, InstanceCount: 1, FirstInstance: 24, TextureHandle: 0xCC, TextureLayer: 0, Translucency: TranslucencyKind.Opaque), }; var indirect = new DrawElementsIndirectCommand[16]; var batch = new WbDrawDispatcher.BatchDataPublic[16]; // Act var result = WbDrawDispatcher.BuildIndirectArrays(groups, indirect, batch); // Assert layout Assert.Equal(2, result.OpaqueCount); Assert.Equal(1, result.TransparentCount); Assert.Equal(2 * 20, result.TransparentByteOffset); // sizeof(DEIC) = 20 // Opaque section, in input order (Task 10 callers sort) Assert.Equal(100u, indirect[0].Count); Assert.Equal(0u, indirect[0].FirstIndex); Assert.Equal(0, indirect[0].BaseVertex); Assert.Equal(12u, indirect[0].InstanceCount); Assert.Equal(0u, indirect[0].BaseInstance); Assert.Equal(50u, indirect[1].Count); Assert.Equal(300u, indirect[1].FirstIndex); Assert.Equal(100, indirect[1].BaseVertex); Assert.Equal(1u, indirect[1].InstanceCount); Assert.Equal(24u, indirect[1].BaseInstance); // Transparent section Assert.Equal(200u, indirect[2].Count); Assert.Equal(100u, indirect[2].FirstIndex); Assert.Equal(12u, indirect[2].InstanceCount); Assert.Equal(12u, indirect[2].BaseInstance); // BatchData parallel — same indices as indirect Assert.Equal(0xAAul, batch[0].TextureHandle); Assert.Equal(0xCCul, batch[1].TextureHandle); Assert.Equal(0xBBul, batch[2].TextureHandle); } [Fact] public void EmptyGroupList_ProducesZeroCounts() { var groups = new List(); var indirect = new DrawElementsIndirectCommand[0]; var batch = new WbDrawDispatcher.BatchDataPublic[0]; var result = WbDrawDispatcher.BuildIndirectArrays(groups, indirect, batch); Assert.Equal(0, result.OpaqueCount); Assert.Equal(0, result.TransparentCount); Assert.Equal(0, result.TransparentByteOffset); } [Fact] public void ClipMapTreatedAsOpaque() { // ClipMap surfaces (alpha-cutout) belong with the opaque pass // because the discard handles transparency, not blending. var groups = new List { new(IndexCount: 10, FirstIndex: 0, BaseVertex: 0, InstanceCount: 1, FirstInstance: 0, TextureHandle: 0x1, TextureLayer: 0, Translucency: TranslucencyKind.ClipMap), }; var indirect = new DrawElementsIndirectCommand[4]; var batch = new WbDrawDispatcher.BatchDataPublic[4]; var result = WbDrawDispatcher.BuildIndirectArrays(groups, indirect, batch); Assert.Equal(1, result.OpaqueCount); Assert.Equal(0, result.TransparentCount); } [Fact] public void BatchDataPublic_LayoutMatchesPrivateBatchData() { // Task 10 will use MemoryMarshal.Cast to // expose the dispatcher's per-frame BatchData[] scratch to BuildIndirectArrays // without copying. The cast is only safe if the structs have identical // layout (size, field offsets). Both use [StructLayout(Sequential, Pack=8)]. Assert.Equal(16, System.Runtime.CompilerServices.Unsafe.SizeOf()); Assert.Equal(0, (int)System.Runtime.InteropServices.Marshal.OffsetOf(nameof(WbDrawDispatcher.BatchDataPublic.TextureHandle))); Assert.Equal(8, (int)System.Runtime.InteropServices.Marshal.OffsetOf(nameof(WbDrawDispatcher.BatchDataPublic.TextureLayer))); Assert.Equal(12, (int)System.Runtime.InteropServices.Marshal.OffsetOf(nameof(WbDrawDispatcher.BatchDataPublic.Flags))); } [Fact] public void DrawCommandStride_MatchesStructSize() { Assert.Equal(WbDrawDispatcher.DrawCommandStride, System.Runtime.CompilerServices.Unsafe.SizeOf()); } }