using System; using Blacklight.Core.Cameras; using UnitTestSharp; using GlobalsBlockLayout = Blacklight.WebGL2.WebGL2GlobalsUniformBlock.GlobalsBlockLayout; using GlobalsBlockPacking = Blacklight.WebGL2.WebGL2GlobalsUniformBlock.GlobalsBlockPacking; namespace Blacklight.WebGL2.UnitTests { public class GlobalsBlockLayoutTests : TestFixture { public class FromInteropTests : TestFixture { // Distinct value per (member, field) combination -- a wrong index reads the wrong unique // value and fails loudly instead of accidentally matching. public void ReadsEachMembersOffsetAndStrideFromItsOwnArrayIndex() { double[] offsets = { 101, 104, 107, 110, 113 }; double[] matrixStrides = { 102, 105, 108, 111, 114 }; double[] arrayStrides = { 103, 106, 109, 112, 115 }; var layout = GlobalsBlockLayout.FromInterop(999, offsets, matrixStrides, arrayStrides); CheckEqual(999, layout.BlockSizeInBytes); CheckEqual(101, layout.CameraOffset); CheckEqual(102, layout.CameraMatrixStride); CheckEqual(104, layout.ViewOffset); CheckEqual(105, layout.ViewMatrixStride); CheckEqual(107, layout.ProjectionOffset); CheckEqual(108, layout.ProjectionMatrixStride); CheckEqual(110, layout.ViewportSizeOffset); CheckEqual(113, layout.TextureSizesOffset); CheckEqual(115, layout.TextureSizesArrayStride); } } } public class GlobalsBlockPackingTests : TestFixture { public class PackTests : TestFixture { // Deliberately unaligned/non-round offsets and strides (not the 16-byte std140 values a real // compiled program would report) so a hardcoded-stride bug can't pass by accident. private static readonly GlobalsBlockLayout Layout = new GlobalsBlockLayout( blockSizeInBytes: 512, cameraOffset: 8, cameraMatrixStride: 24, viewOffset: 64, viewMatrixStride: 20, projectionOffset: 112, projectionMatrixStride: 16, viewportSizeOffset: 160, textureSizesOffset: 192, textureSizesArrayStride: 20); private static float ReadFloat(byte[] bytes, int offset) { return BitConverter.ToSingle(bytes, offset); } public void ProducesABufferOfExactlyBlockSizeInBytes() { var viewport = new Viewport(800, 600); var camera = new ScreenSpaceCamera(viewport); byte[] packed = GlobalsBlockPacking.Pack(Layout, camera, viewport, new float[64]); CheckEqual(Layout.BlockSizeInBytes, packed.Length); } public void WritesCameraViewAndProjectionAtTheirOwnOffsetAndRowStride() { var viewport = new Viewport(800, 600); var camera = new ScreenSpaceCamera(viewport) { Magnification = 3 }; byte[] packed = GlobalsBlockPacking.Pack(Layout, camera, viewport, new float[64]); void CheckMatrix(int offset, int matrixStride, Azimuth.AffineMatrix matrix) { CheckEqual((float)matrix.M00, ReadFloat(packed, offset)); CheckEqual((float)matrix.M10, ReadFloat(packed, offset + 4)); CheckEqual((float)matrix.M01, ReadFloat(packed, offset + matrixStride)); CheckEqual((float)matrix.M11, ReadFloat(packed, offset + matrixStride + 4)); CheckEqual((float)matrix.M02, ReadFloat(packed, offset + 2 * matrixStride)); CheckEqual((float)matrix.M12, ReadFloat(packed, offset + 2 * matrixStride + 4)); } CheckMatrix(Layout.CameraOffset, Layout.CameraMatrixStride, camera.WorldToCamera); CheckMatrix(Layout.ViewOffset, Layout.ViewMatrixStride, camera.CameraToProjection); CheckMatrix(Layout.ProjectionOffset, Layout.ProjectionMatrixStride, viewport.ProjectionToClip); } public void WritesViewportWidthAndHeightAsConsecutiveFloats() { var viewport = new Viewport(800, 600); var camera = new ScreenSpaceCamera(viewport); byte[] packed = GlobalsBlockPacking.Pack(Layout, camera, viewport, new float[64]); CheckEqual(800f, ReadFloat(packed, Layout.ViewportSizeOffset)); CheckEqual(600f, ReadFloat(packed, Layout.ViewportSizeOffset + 4)); } public void WritesAllSixteenTextureSizeSlotsAtTheirOwnArrayStride() { var viewport = new Viewport(800, 600); var camera = new ScreenSpaceCamera(viewport); var textureSizes = new float[64]; for (int i = 0; i < textureSizes.Length; ++i) { textureSizes[i] = i * 1.5f; } byte[] packed = GlobalsBlockPacking.Pack(Layout, camera, viewport, textureSizes); for (int slot = 0; slot < 16; ++slot) { int slotOffset = Layout.TextureSizesOffset + slot * Layout.TextureSizesArrayStride; for (int component = 0; component < 4; ++component) { CheckEqual(textureSizes[slot * 4 + component], ReadFloat(packed, slotOffset + component * 4)); } } } } } }