using System.Runtime.CompilerServices; using AcDream.App.Rendering.Gpu; using AcDream.App.Rendering.Sky; using AcDream.Core.Terrain; using Xunit; namespace AcDream.App.Tests.Rendering; /// /// Campaign V slice V6k: the sky's RHI vertex layout must describe the CPU record /// the GL arm uploads, not a record that merely looks like it. /// /// The slice's first Vulkan sky frame drew the dome as a field of noise /// because the layout declared a 32-byte stride — position + normal + texcoord, /// which is exactly what sky.vert reads — while /// is 36 bytes: it carries a fourth member, /// TerrainLayer, that no sky attribute names. The GL arm has always said /// sizeof(Vertex) and was unaffected, and nothing else in the frame looked /// wrong, so only a side-by-side capture caught it. /// /// The assertion is the REQUIREMENT — "the stride is the record's /// footprint" — rather than the number, so adding or removing a member of /// keeps it honest instead of pinning today's answer. /// public class SkyVertexLayoutTests { [Fact] public void SkyVertexLayout_StrideMatchesTheUploadedRecord() { Assert.Equal( (uint)Unsafe.SizeOf(), SkyRenderer.SkyVertexLayout.StrideBytes); } [Fact] public void SkyVertexLayout_DeclaresTheThreeAttributesTheShaderReads() { Assert.Collection( SkyRenderer.SkyVertexLayout.Attributes, position => { Assert.Equal(0u, position.Location); Assert.Equal(GpuVertexFormat.Float3, position.Format); Assert.Equal(0u, position.OffsetBytes); }, normal => { Assert.Equal(1u, normal.Location); Assert.Equal(GpuVertexFormat.Float3, normal.Format); Assert.Equal(12u, normal.OffsetBytes); }, texCoord => { Assert.Equal(2u, texCoord.Location); Assert.Equal(GpuVertexFormat.Float2, texCoord.Format); Assert.Equal(24u, texCoord.OffsetBytes); }); } /// /// Every attribute has to fit inside the stride it is read with. A layout that /// reaches past its own stride is the same defect wearing the other face. /// [Fact] public void SkyVertexLayout_EveryAttributeFitsInsideTheStride() { foreach (GpuVertexAttribute attribute in SkyRenderer.SkyVertexLayout.Attributes) { uint size = attribute.Format switch { GpuVertexFormat.Float1 => 4u, GpuVertexFormat.Float2 => 8u, GpuVertexFormat.Float3 => 12u, GpuVertexFormat.Float4 => 16u, _ => 4u, }; Assert.True( attribute.OffsetBytes + size <= SkyRenderer.SkyVertexLayout.StrideBytes, $"Attribute at location {attribute.Location} reaches past the vertex stride."); } } }