From 9286adcb0f7e46b1cb628315cdfb75b0f6a1afd6 Mon Sep 17 00:00:00 2001 From: "Yan, Shaobo" Date: Fri, 26 Apr 2019 15:25:18 +0000 Subject: [PATCH] Add end2end test for all vertex formats BUG=dawn:41 Change-Id: I37bde37843522a8d7c8b3bea1cb24c0971efd8e2 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/6340 Reviewed-by: Kai Ninomiya Reviewed-by: Corentin Wallez Commit-Queue: Shaobo Yan --- BUILD.gn | 1 + src/common/Math.cpp | 28 + src/common/Math.h | 14 + src/dawn_native/opengl/CommandBufferGL.cpp | 38 +- src/tests/end2end/VertexFormatTests.cpp | 800 +++++++++++++++++++++ 5 files changed, 877 insertions(+), 4 deletions(-) create mode 100644 src/tests/end2end/VertexFormatTests.cpp diff --git a/BUILD.gn b/BUILD.gn index 87f5f20f30..b5365d33db 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -650,6 +650,7 @@ test("dawn_end2end_tests") { "src/tests/end2end/SamplerTests.cpp", "src/tests/end2end/ScissorTests.cpp", "src/tests/end2end/TextureViewTests.cpp", + "src/tests/end2end/VertexFormatTests.cpp", "src/tests/end2end/ViewportOrientationTests.cpp", ] diff --git a/src/common/Math.cpp b/src/common/Math.cpp index 6aec721a1d..d9217c8e44 100644 --- a/src/common/Math.cpp +++ b/src/common/Math.cpp @@ -16,6 +16,8 @@ #include "common/Assert.h" +#include + #if defined(DAWN_COMPILER_MSVC) # include #endif @@ -77,3 +79,29 @@ uint32_t Align(uint32_t value, size_t alignment) { uint32_t alignment32 = static_cast(alignment); return (value + (alignment32 - 1)) & ~(alignment32 - 1); } + +uint16_t Float32ToFloat16(float fp32) { + uint32_t fp32i = BitCast(fp32); + uint32_t sign16 = (fp32i & 0x80000000) >> 16; + uint32_t mantissaAndExponent = fp32i & 0x7FFFFFFF; + + if (mantissaAndExponent > 0x47FFEFFF) { // Infinity + return static_cast(sign16 | 0x7FFF); + } else if (mantissaAndExponent < 0x38800000) { // Denormal + uint32_t mantissa = (mantissaAndExponent & 0x007FFFFF) | 0x00800000; + int32_t exponent = 113 - (mantissaAndExponent >> 23); + + if (exponent < 24) { + mantissaAndExponent = mantissa >> exponent; + } else { + mantissaAndExponent = 0; + } + + return static_cast( + sign16 | (mantissaAndExponent + 0x00000FFF + ((mantissaAndExponent >> 13) & 1)) >> 13); + } else { + return static_cast(sign16 | (mantissaAndExponent + 0xC8000000 + 0x00000FFF + + ((mantissaAndExponent >> 13) & 1)) >> + 13); + } +} diff --git a/src/common/Math.h b/src/common/Math.h index 359d57aa72..377f857be1 100644 --- a/src/common/Math.h +++ b/src/common/Math.h @@ -17,6 +17,10 @@ #include #include +#include + +#include +#include // The following are not valid for 0 uint32_t ScanForward(uint32_t bits); @@ -38,4 +42,14 @@ const T* AlignPtr(const T* ptr, size_t alignment) { return reinterpret_cast(AlignVoidPtr(const_cast(ptr), alignment)); } +template +destType BitCast(const sourceType& source) { + static_assert(sizeof(destType) == sizeof(sourceType), "BitCast: cannot lose precision."); + destType output; + std::memcpy(&output, &source, sizeof(destType)); + return output; +} + +uint16_t Float32ToFloat16(float fp32); + #endif // COMMON_MATH_H_ diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp index b2fecc4b7c..03f2e11df6 100644 --- a/src/dawn_native/opengl/CommandBufferGL.cpp +++ b/src/dawn_native/opengl/CommandBufferGL.cpp @@ -105,6 +105,30 @@ namespace dawn_native { namespace opengl { } } + bool VertexFormatIsInt(dawn::VertexFormat format) { + switch (format) { + case dawn::VertexFormat::UChar2: + case dawn::VertexFormat::UChar4: + case dawn::VertexFormat::Char2: + case dawn::VertexFormat::Char4: + case dawn::VertexFormat::UShort2: + case dawn::VertexFormat::UShort4: + case dawn::VertexFormat::Short2: + case dawn::VertexFormat::Short4: + case dawn::VertexFormat::UInt: + case dawn::VertexFormat::UInt2: + case dawn::VertexFormat::UInt3: + case dawn::VertexFormat::UInt4: + case dawn::VertexFormat::Int: + case dawn::VertexFormat::Int2: + case dawn::VertexFormat::Int3: + case dawn::VertexFormat::Int4: + return true; + default: + return false; + } + } + GLint GetStencilMaskFromStencilFormat(dawn::TextureFormat depthStencilFormat) { switch (depthStencilFormat) { case dawn::TextureFormat::D32FloatS8Uint: @@ -242,10 +266,16 @@ namespace dawn_native { namespace opengl { GLboolean normalized = VertexFormatIsNormalized(attribute.format); glBindBuffer(GL_ARRAY_BUFFER, buffer); - glVertexAttribPointer( - location, components, formatType, normalized, input.stride, - reinterpret_cast( - static_cast(offset + attribute.offset))); + if (VertexFormatIsInt(attribute.format)) { + glVertexAttribIPointer(location, components, formatType, input.stride, + reinterpret_cast(static_cast( + offset + attribute.offset))); + } else { + glVertexAttribPointer( + location, components, formatType, normalized, input.stride, + reinterpret_cast( + static_cast(offset + attribute.offset))); + } } } diff --git a/src/tests/end2end/VertexFormatTests.cpp b/src/tests/end2end/VertexFormatTests.cpp new file mode 100644 index 0000000000..df04776b7b --- /dev/null +++ b/src/tests/end2end/VertexFormatTests.cpp @@ -0,0 +1,800 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Assert.h" +#include "common/Math.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/DawnHelpers.h" + +// Vertex format tests all work the same way: the test will render a triangle. +// Each test will set up a vertex buffer, and the vertex shader will check that +// the vertex content is the same as what we expected. On success it outputs green, +// otherwise red. + +constexpr uint32_t kRTSize = 400; +constexpr uint32_t kVertexNum = 3; + +std::vector Float32ToFloat16(std::vector data) { + std::vector expectedData; + for (auto& element : data) { + expectedData.push_back(Float32ToFloat16(element)); + } + return expectedData; +} + +template +std::vector BitCast(std::vector data) { + std::vector expectedData; + for (auto& element : data) { + expectedData.push_back(BitCast(element)); + } + return expectedData; +} + +class VertexFormatTest : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + + renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + } + + utils::BasicRenderPass renderPass; + + bool IsNormalizedFormat(dawn::VertexFormat format) { + switch (format) { + case dawn::VertexFormat::UChar2Norm: + case dawn::VertexFormat::UChar4Norm: + case dawn::VertexFormat::Char2Norm: + case dawn::VertexFormat::Char4Norm: + case dawn::VertexFormat::UShort2Norm: + case dawn::VertexFormat::UShort4Norm: + case dawn::VertexFormat::Short2Norm: + case dawn::VertexFormat::Short4Norm: + return true; + default: + return false; + } + } + + bool IsUnsignedFormat(dawn::VertexFormat format) { + switch (format) { + case dawn::VertexFormat::UInt: + case dawn::VertexFormat::UChar2: + case dawn::VertexFormat::UChar4: + case dawn::VertexFormat::UShort2: + case dawn::VertexFormat::UShort4: + case dawn::VertexFormat::UInt2: + case dawn::VertexFormat::UInt3: + case dawn::VertexFormat::UInt4: + case dawn::VertexFormat::UChar2Norm: + case dawn::VertexFormat::UChar4Norm: + case dawn::VertexFormat::UShort2Norm: + case dawn::VertexFormat::UShort4Norm: + return true; + default: + return false; + } + } + + bool IsFloatFormat(dawn::VertexFormat format) { + switch (format) { + case dawn::VertexFormat::Half2: + case dawn::VertexFormat::Half4: + case dawn::VertexFormat::Float: + case dawn::VertexFormat::Float2: + case dawn::VertexFormat::Float3: + case dawn::VertexFormat::Float4: + return true; + default: + return false; + } + } + + bool IsHalfFormat(dawn::VertexFormat format) { + switch (format) { + case dawn::VertexFormat::Half2: + case dawn::VertexFormat::Half4: + return true; + default: + return false; + } + } + + uint32_t BytesPerComponents(dawn::VertexFormat format) { + switch (format) { + case dawn::VertexFormat::Char2: + case dawn::VertexFormat::Char4: + case dawn::VertexFormat::UChar2: + case dawn::VertexFormat::UChar4: + case dawn::VertexFormat::UChar2Norm: + case dawn::VertexFormat::UChar4Norm: + case dawn::VertexFormat::Char2Norm: + case dawn::VertexFormat::Char4Norm: + return 1; + case dawn::VertexFormat::UShort2: + case dawn::VertexFormat::UShort4: + case dawn::VertexFormat::Short2: + case dawn::VertexFormat::Short4: + case dawn::VertexFormat::UShort2Norm: + case dawn::VertexFormat::UShort4Norm: + case dawn::VertexFormat::Short2Norm: + case dawn::VertexFormat::Short4Norm: + case dawn::VertexFormat::Half2: + case dawn::VertexFormat::Half4: + return 2; + case dawn::VertexFormat::UInt: + case dawn::VertexFormat::Int: + case dawn::VertexFormat::Float: + case dawn::VertexFormat::UInt2: + case dawn::VertexFormat::UInt3: + case dawn::VertexFormat::UInt4: + case dawn::VertexFormat::Int2: + case dawn::VertexFormat::Int3: + case dawn::VertexFormat::Int4: + case dawn::VertexFormat::Float2: + case dawn::VertexFormat::Float3: + case dawn::VertexFormat::Float4: + return 4; + default: + DAWN_UNREACHABLE(); + } + } + + uint32_t ComponentCount(dawn::VertexFormat format) { + switch (format) { + case dawn::VertexFormat::UInt: + case dawn::VertexFormat::Int: + case dawn::VertexFormat::Float: + return 1; + case dawn::VertexFormat::UChar2: + case dawn::VertexFormat::UShort2: + case dawn::VertexFormat::UInt2: + case dawn::VertexFormat::Char2: + case dawn::VertexFormat::Short2: + case dawn::VertexFormat::Int2: + case dawn::VertexFormat::UChar2Norm: + case dawn::VertexFormat::Char2Norm: + case dawn::VertexFormat::UShort2Norm: + case dawn::VertexFormat::Short2Norm: + case dawn::VertexFormat::Half2: + case dawn::VertexFormat::Float2: + return 2; + case dawn::VertexFormat::Int3: + case dawn::VertexFormat::UInt3: + case dawn::VertexFormat::Float3: + return 3; + case dawn::VertexFormat::UChar4: + case dawn::VertexFormat::UShort4: + case dawn::VertexFormat::UInt4: + case dawn::VertexFormat::Char4: + case dawn::VertexFormat::Short4: + case dawn::VertexFormat::Int4: + case dawn::VertexFormat::UChar4Norm: + case dawn::VertexFormat::Char4Norm: + case dawn::VertexFormat::UShort4Norm: + case dawn::VertexFormat::Short4Norm: + case dawn::VertexFormat::Half4: + case dawn::VertexFormat::Float4: + return 4; + default: + DAWN_UNREACHABLE(); + } + } + + std::string ShaderTypeGenerator(bool isFloat, + bool isNormalized, + bool isUnsigned, + uint32_t componentCount) { + if (componentCount == 1) { + if (isFloat || isNormalized) { + return "float"; + } else if (isUnsigned) { + return "uint"; + } else { + return "int"; + } + } else { + if (isNormalized || isFloat) { + return "vec" + std::to_string(componentCount); + } else if (isUnsigned) { + return "uvec" + std::to_string(componentCount); + } else { + return "ivec" + std::to_string(componentCount); + } + } + } + + // The length of vertexData is fixed to 3, it aligns to triangle vertex number + template + dawn::RenderPipeline MakeTestPipeline(dawn::VertexFormat format, std::vector& expectedData) { + bool isFloat = IsFloatFormat(format); + bool isNormalized = IsNormalizedFormat(format); + bool isUnsigned = IsUnsignedFormat(format); + bool isInputTypeFloat = isFloat || isNormalized; + bool isHalf = IsHalfFormat(format); + const uint16_t kNegativeZeroInHalf = 0x8000; + + uint32_t componentCount = ComponentCount(format); + + std::string variableType = + ShaderTypeGenerator(isFloat, isNormalized, isUnsigned, componentCount); + std::string expectedDataType = ShaderTypeGenerator(isFloat, isNormalized, isUnsigned, 1); + std::ostringstream vs; + vs << "#version 450\n"; + + // layout(location = 0) in float/uint/int/ivecn/vecn/uvecn test; + vs << "layout(location = 0) in " << variableType << " test;\n"; + vs << "layout(location = 0) out vec4 color;\n"; + // Because x86 CPU using "extended + // precision"(https://en.wikipedia.org/wiki/Extended_precision) during float + // math(https://developer.nvidia.com/sites/default/files/akamai/cuda/files/NVIDIA-CUDA-Floating-Point.pdf), + // move normalization and Float16ToFloat32 into shader to generate + // expected value. + vs << "float Float16ToFloat32(uint fp16) {\n"; + vs << " uint magic = (uint(254) - uint(15)) << 23;\n"; + vs << " uint was_inf_nan = (uint(127) + uint(16)) << 23;\n"; + vs << " uint fp32u;\n"; + vs << " float fp32;\n"; + vs << " fp32u = (fp16 & 0x7FFF) << 13;\n"; + vs << " fp32 = uintBitsToFloat(fp32u) * uintBitsToFloat(magic);\n"; + vs << " fp32u = floatBitsToUint(fp32);\n"; + vs << " if (fp32 >= uintBitsToFloat(was_inf_nan)) {\n"; + vs << " fp32u |= uint(255) << 23;\n"; + vs << " }\n"; + vs << " fp32u |= (fp16 & 0x8000) << 16;\n"; + vs << " fp32 = uintBitsToFloat(fp32u);\n"; + vs << " return fp32;\n"; + vs << "}\n"; + + vs << "void main() {\n"; + + // Hard code the triangle in the shader so that we don't have to add a vertex input for it. + vs << " const vec2 pos[3] = vec2[3](vec2(-1.0f, 0.0f), vec2(-1.0f, -1.0f), vec2(0.0f, " + "-1.0f));\n"; + vs << " gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);\n"; + + // Declare expected values. + vs << " " << expectedDataType << " expected[" + std::to_string(kVertexNum) + "]"; + vs << "[" + std::to_string(componentCount) + "];\n"; + // Assign each elements in expected values + // e.g. expected[0][0] = uint(1); + // expected[0][1] = uint(2); + for (uint32_t i = 0; i < kVertexNum; ++i) { + for (uint32_t j = 0; j < componentCount; ++j) { + vs << " expected[" + std::to_string(i) + "][" + std::to_string(j) + "] = " + << expectedDataType << "("; + if (isInputTypeFloat && + std::isnan(static_cast(expectedData[i * componentCount + j]))) { + // Set NaN. + vs << "0.0 / 0.0);\n"; + } else if (isNormalized) { + // Move normalize operation into shader because of CPU and GPU precision + // different on float math. + vs << "max(float(" << std::to_string(expectedData[i * componentCount + j]) + << ") / " << std::to_string(std::numeric_limits::max()) << ", -1.0));\n"; + } else if (isHalf) { + // Becasue Vulkan and D3D12 handle -0.0f through uintBitsToFloat have different + // result (Vulkan take -0.0f as -0.0 but D3D12 take -0.0f as 0), add workaround + // for -0.0f. + if (static_cast(expectedData[i * componentCount + j]) == + kNegativeZeroInHalf) { + vs << "-0.0f);\n"; + } else { + vs << "Float16ToFloat32(" + << std::to_string(expectedData[i * componentCount + j]); + vs << "));\n"; + } + } else { + vs << std::to_string(expectedData[i * componentCount + j]) << ");\n"; + } + } + } + + vs << " bool success = true;\n"; + // Perform the checks by successively ANDing a boolean + for (uint32_t component = 0; component < componentCount; ++component) { + std::string suffix = componentCount == 1 ? "" : "[" + std::to_string(component) + "]"; + std::string testVal = "testVal" + std::to_string(component); + std::string expectedVal = "expectedVal" + std::to_string(component); + vs << " " << expectedDataType << " " << testVal << ";\n"; + vs << " " << expectedDataType << " " << expectedVal << ";\n"; + vs << " " << testVal << " = test" << suffix << ";\n"; + vs << " " << expectedVal << " = expected[gl_VertexIndex]" + << "[" << component << "];\n"; + if (!isInputTypeFloat) { // Integer / unsigned integer need to match exactly. + vs << " success = success && (" << testVal << " == " << expectedVal << ");\n"; + } else { + // TODO(shaobo.yan@intel.com) : a difference of 8 ULPs is allowed in this test + // because it is required on MacbookPro 11.5,AMD Radeon HD 8870M(on macOS 10.13.6), + // but that it might be possible to tighten. + vs << " if (isnan(" << expectedVal << ")) {\n"; + vs << " success = success && isnan(" << testVal << ");\n"; + vs << " } else {\n"; + vs << " uint testValFloatToUint = floatBitsToUint(" << testVal << ");\n"; + vs << " uint expectedValFloatToUint = floatBitsToUint(" << expectedVal + << ");\n"; + vs << " success = success && max(testValFloatToUint, " + "expectedValFloatToUint)"; + vs << " - min(testValFloatToUint, expectedValFloatToUint) < uint(8);\n"; + vs << " }\n"; + } + } + vs << " if (success) {\n"; + vs << " color = vec4(0.0f, 1.0f, 0.0f, 1.0f);\n"; + vs << " } else {\n"; + vs << " color = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"; + vs << " }\n"; + vs << "}\n"; + + dawn::ShaderModule vsModule = + utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, vs.str().c_str()); + + dawn::ShaderModule fsModule = + utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + #version 450 + layout(location = 0) in vec4 color; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = color; + })"); + + uint32_t bytesPerComponents = BytesPerComponents(format); + uint32_t strideBytes = bytesPerComponents * componentCount; + // Stride size must be multiple of 4 bytes. + if (strideBytes % 4 != 0) { + strideBytes += (4 - strideBytes % 4); + } + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.cVertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.cInputState.numInputs = 1; + descriptor.cInputState.cInputs[0].stride = strideBytes; + descriptor.cInputState.numAttributes = 1; + descriptor.cInputState.cAttributes[0].format = format; + descriptor.cColorStates[0]->format = renderPass.colorFormat; + + return device.CreateRenderPipeline(&descriptor); + } + + template + void DoVertexFormatTest(dawn::VertexFormat format, + std::vector vertex, + std::vector expectedData) { + dawn::RenderPipeline pipeline = MakeTestPipeline(format, expectedData); + dawn::Buffer vertexBuffer = + utils::CreateBufferFromData(device, vertex.data(), vertex.size() * sizeof(VertexType), + dawn::BufferUsageBit::Vertex); + uint64_t zeroOffset = 0; + dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + { + dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); + pass.Draw(3, 1, 0, 0); + pass.EndPass(); + } + + dawn::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 0, 0); + } +}; + +TEST_P(VertexFormatTest, UChar2) { + std::vector vertexData = { + std::numeric_limits::max(), + 0, + 0, // padding two bytes for stride + 0, + std::numeric_limits::min(), + 2, + 0, + 0, // padding two bytes for stride + 200, + 201, + 0, + 0 // padding two bytes for buffer copy + }; + + std::vector expectedData = { + std::numeric_limits::max(), 0, std::numeric_limits::min(), 2, 200, 201, + }; + + DoVertexFormatTest(dawn::VertexFormat::UChar2, vertexData, expectedData); +} + +TEST_P(VertexFormatTest, UChar4) { + std::vector vertexData = { + std::numeric_limits::max(), + 0, + 1, + 2, + std::numeric_limits::min(), + 2, + 3, + 4, + 200, + 201, + 202, + 203, + }; + + DoVertexFormatTest(dawn::VertexFormat::UChar4, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Char2) { + std::vector vertexData = { + std::numeric_limits::max(), + 0, + 0, // padding two bytes for stride + 0, + std::numeric_limits::min(), + -2, + 0, // padding two bytes for stride + 0, + 120, + -121, + 0, + 0 // padding two bytes for buffer copy + }; + + std::vector expectedData = { + std::numeric_limits::max(), 0, std::numeric_limits::min(), -2, 120, -121, + }; + + DoVertexFormatTest(dawn::VertexFormat::Char2, vertexData, expectedData); +} + +TEST_P(VertexFormatTest, Char4) { + std::vector vertexData = { + std::numeric_limits::max(), + 0, + -1, + 2, + std::numeric_limits::min(), + -2, + 3, + 4, + 120, + -121, + 122, + -123, + }; + + DoVertexFormatTest(dawn::VertexFormat::Char4, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, UChar2Norm) { + std::vector vertexData = { + std::numeric_limits::max(), + std::numeric_limits::min(), + 0, // padding two bytes for stride + 0, + std::numeric_limits::max() / 2, + std::numeric_limits::min() / 2, + 0, // padding two bytes for stride + 0, + 200, + 201, + 0, + 0 // padding two bytes for buffer copy + }; + + std::vector expectedData = {std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::max() / 2, + std::numeric_limits::min() / 2, + 200, + 201}; + + DoVertexFormatTest(dawn::VertexFormat::UChar2Norm, vertexData, expectedData); +} + +TEST_P(VertexFormatTest, UChar4Norm) { + std::vector vertexData = {std::numeric_limits::max(), + std::numeric_limits::min(), + 0, + 0, + std::numeric_limits::max() / 2, + std::numeric_limits::min() / 2, + 0, + 0, + 200, + 201, + 202, + 203}; + + DoVertexFormatTest(dawn::VertexFormat::UChar4Norm, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Char2Norm) { + std::vector vertexData = { + std::numeric_limits::max(), + std::numeric_limits::min(), + 0, // padding two bytes for stride + 0, + std::numeric_limits::max() / 2, + std::numeric_limits::min() / 2, + 0, // padding two bytes for stride + 0, + 120, + -121, + 0, + 0 // padding two bytes for buffer copy + }; + + std::vector expectedData = { + std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::max() / 2, + std::numeric_limits::min() / 2, + 120, + -121, + }; + + DoVertexFormatTest(dawn::VertexFormat::Char2Norm, vertexData, expectedData); +} + +TEST_P(VertexFormatTest, Char4Norm) { + std::vector vertexData = {std::numeric_limits::max(), + std::numeric_limits::min(), + 0, + 0, + std::numeric_limits::max() / 2, + std::numeric_limits::min() / 2, + -2, + 2, + 120, + -120, + 102, + -123}; + + DoVertexFormatTest(dawn::VertexFormat::Char4Norm, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, UShort2) { + std::vector vertexData = {std::numeric_limits::max(), + 0, + std::numeric_limits::min(), + 2, + 65432, + 4890}; + + DoVertexFormatTest(dawn::VertexFormat::UShort2, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, UShort4) { + std::vector vertexData = { + std::numeric_limits::max(), + std::numeric_limits::max(), + 1, + 2, + std::numeric_limits::min(), + 2, + 3, + 4, + 65520, + 65521, + 3435, + 3467, + }; + + DoVertexFormatTest(dawn::VertexFormat::UShort4, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Short2) { + std::vector vertexData = {std::numeric_limits::max(), + 0, + std::numeric_limits::min(), + -2, + 3876, + -3948}; + + DoVertexFormatTest(dawn::VertexFormat::Short2, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Short4) { + std::vector vertexData = { + std::numeric_limits::max(), + 0, + -1, + 2, + std::numeric_limits::min(), + -2, + 3, + 4, + 24567, + -23545, + 4350, + -2987, + }; + + DoVertexFormatTest(dawn::VertexFormat::Short4, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, UShort2Norm) { + std::vector vertexData = {std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::max() / 2, + std::numeric_limits::min() / 2, + 3456, + 6543}; + + DoVertexFormatTest(dawn::VertexFormat::UShort2Norm, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, UShort4Norm) { + std::vector vertexData = {std::numeric_limits::max(), + std::numeric_limits::min(), + 0, + 0, + std::numeric_limits::max() / 2, + std::numeric_limits::min() / 2, + 0, + 0, + 2987, + 3055, + 2987, + 2987}; + + DoVertexFormatTest(dawn::VertexFormat::UShort4Norm, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Short2Norm) { + std::vector vertexData = {std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::max() / 2, + std::numeric_limits::min() / 2, + 4987, + -6789}; + + DoVertexFormatTest(dawn::VertexFormat::Short2Norm, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Short4Norm) { + std::vector vertexData = {std::numeric_limits::max(), + std::numeric_limits::min(), + 0, + 0, + std::numeric_limits::max() / 2, + std::numeric_limits::min() / 2, + -2, + 2, + 2890, + -29011, + 20432, + -2083}; + + DoVertexFormatTest(dawn::VertexFormat::Short4Norm, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Half2) { + std::vector vertexData = + Float32ToFloat16(std::vector({14.8, -0.0, 22.5, 1.3, +0.0, -24.8})); + + DoVertexFormatTest(dawn::VertexFormat::Half2, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Half4) { + std::vector vertexData = Float32ToFloat16(std::vector( + {+0.0, -16.8, 18.2, -0.0, 12.5, 1.3, 14.8, -12.4, 22.5, -48.8, 47.4, -24.8})); + + DoVertexFormatTest(dawn::VertexFormat::Half4, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Float) { + std::vector vertexData = {1.3f, +0.0f, -0.0f}; + + DoVertexFormatTest(dawn::VertexFormat::Float, vertexData, vertexData); + + vertexData = std::vector{+1.0f, -1.0f, 18.23f}; + + DoVertexFormatTest(dawn::VertexFormat::Float, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Float2) { + std::vector vertexData = {18.23f, -0.0f, +0.0f, +1.0f, 1.3f, -1.0f}; + + DoVertexFormatTest(dawn::VertexFormat::Float2, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Float3) { + std::vector vertexData = { + +0.0f, -1.0f, -0.0f, 1.0f, 1.3f, 99.45f, 23.6f, -81.2f, 55.0f, + }; + + DoVertexFormatTest(dawn::VertexFormat::Float3, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Float4) { + std::vector vertexData = { + 19.2f, -19.3f, +0.0f, 1.0f, -0.0f, 1.0f, 1.3f, -1.0f, 13.078f, 21.1965f, -1.1f, -1.2f, + }; + + DoVertexFormatTest(dawn::VertexFormat::Float4, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, UInt) { + std::vector vertexData = {std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()}; + + DoVertexFormatTest(dawn::VertexFormat::UInt, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, UInt2) { + std::vector vertexData = {std::numeric_limits::max(), 32, + std::numeric_limits::max(), 64, + std::numeric_limits::max(), 128}; + + DoVertexFormatTest(dawn::VertexFormat::UInt2, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, UInt3) { + std::vector vertexData = {std::numeric_limits::max(), 32, 64, + std::numeric_limits::max(), 164, 128, + std::numeric_limits::max(), 1283, 256}; + + DoVertexFormatTest(dawn::VertexFormat::UInt3, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, UInt4) { + std::vector vertexData = {std::numeric_limits::max(), 32, 64, 5460, + std::numeric_limits::max(), 164, 128, 0, + std::numeric_limits::max(), 1283, 256, 4567}; + + DoVertexFormatTest(dawn::VertexFormat::UInt4, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Int) { + std::vector vertexData = {std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::max()}; + + DoVertexFormatTest(dawn::VertexFormat::Int, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Int2) { + std::vector vertexData = { + std::numeric_limits::max(), std::numeric_limits::min(), + std::numeric_limits::max(), std::numeric_limits::min(), + std::numeric_limits::max(), std::numeric_limits::min()}; + + DoVertexFormatTest(dawn::VertexFormat::Int2, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Int3) { + std::vector vertexData = { + std::numeric_limits::max(), std::numeric_limits::min(), 64, + std::numeric_limits::max(), std::numeric_limits::min(), 128, + std::numeric_limits::max(), std::numeric_limits::min(), 256}; + + DoVertexFormatTest(dawn::VertexFormat::Int3, vertexData, vertexData); +} + +TEST_P(VertexFormatTest, Int4) { + std::vector vertexData = { + std::numeric_limits::max(), std::numeric_limits::min(), 64, -5460, + std::numeric_limits::max(), std::numeric_limits::min(), -128, 0, + std::numeric_limits::max(), std::numeric_limits::min(), 256, -4567}; + + DoVertexFormatTest(dawn::VertexFormat::Int4, vertexData, vertexData); +} + +DAWN_INSTANTIATE_TEST(VertexFormatTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); \ No newline at end of file