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 <kainino@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
diff --git a/BUILD.gn b/BUILD.gn
index 87f5f20..b5365d3 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -650,6 +650,7 @@
"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 6aec721..d9217c8 100644
--- a/src/common/Math.cpp
+++ b/src/common/Math.cpp
@@ -16,6 +16,8 @@
#include "common/Assert.h"
+#include <algorithm>
+
#if defined(DAWN_COMPILER_MSVC)
# include <intrin.h>
#endif
@@ -77,3 +79,29 @@
uint32_t alignment32 = static_cast<uint32_t>(alignment);
return (value + (alignment32 - 1)) & ~(alignment32 - 1);
}
+
+uint16_t Float32ToFloat16(float fp32) {
+ uint32_t fp32i = BitCast<uint32_t>(fp32);
+ uint32_t sign16 = (fp32i & 0x80000000) >> 16;
+ uint32_t mantissaAndExponent = fp32i & 0x7FFFFFFF;
+
+ if (mantissaAndExponent > 0x47FFEFFF) { // Infinity
+ return static_cast<uint16_t>(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<uint16_t>(
+ sign16 | (mantissaAndExponent + 0x00000FFF + ((mantissaAndExponent >> 13) & 1)) >> 13);
+ } else {
+ return static_cast<uint16_t>(sign16 | (mantissaAndExponent + 0xC8000000 + 0x00000FFF +
+ ((mantissaAndExponent >> 13) & 1)) >>
+ 13);
+ }
+}
diff --git a/src/common/Math.h b/src/common/Math.h
index 359d57a..377f857 100644
--- a/src/common/Math.h
+++ b/src/common/Math.h
@@ -17,6 +17,10 @@
#include <cstddef>
#include <cstdint>
+#include <cstring>
+
+#include <limits>
+#include <type_traits>
// The following are not valid for 0
uint32_t ScanForward(uint32_t bits);
@@ -38,4 +42,14 @@
return reinterpret_cast<const T*>(AlignVoidPtr(const_cast<T*>(ptr), alignment));
}
+template <typename destType, typename sourceType>
+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 b2fecc4..03f2e11 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -105,6 +105,30 @@
}
}
+ 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 @@
GLboolean normalized = VertexFormatIsNormalized(attribute.format);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
- glVertexAttribPointer(
- location, components, formatType, normalized, input.stride,
- reinterpret_cast<void*>(
- static_cast<intptr_t>(offset + attribute.offset)));
+ if (VertexFormatIsInt(attribute.format)) {
+ glVertexAttribIPointer(location, components, formatType, input.stride,
+ reinterpret_cast<void*>(static_cast<intptr_t>(
+ offset + attribute.offset)));
+ } else {
+ glVertexAttribPointer(
+ location, components, formatType, normalized, input.stride,
+ reinterpret_cast<void*>(
+ static_cast<intptr_t>(offset + attribute.offset)));
+ }
}
}
diff --git a/src/tests/end2end/VertexFormatTests.cpp b/src/tests/end2end/VertexFormatTests.cpp
new file mode 100644
index 0000000..df04776
--- /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<uint16_t> Float32ToFloat16(std::vector<float> data) {
+ std::vector<uint16_t> expectedData;
+ for (auto& element : data) {
+ expectedData.push_back(Float32ToFloat16(element));
+ }
+ return expectedData;
+}
+
+template <typename destType, typename srcType>
+std::vector<destType> BitCast(std::vector<srcType> data) {
+ std::vector<destType> 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 <typename T>
+ dawn::RenderPipeline MakeTestPipeline(dawn::VertexFormat format, std::vector<T>& 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<float>(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<T>::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<uint16_t>(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 <typename VertexType, typename ExpectedType>
+ void DoVertexFormatTest(dawn::VertexFormat format,
+ std::vector<VertexType> vertex,
+ std::vector<ExpectedType> 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<uint8_t> vertexData = {
+ std::numeric_limits<uint8_t>::max(),
+ 0,
+ 0, // padding two bytes for stride
+ 0,
+ std::numeric_limits<uint8_t>::min(),
+ 2,
+ 0,
+ 0, // padding two bytes for stride
+ 200,
+ 201,
+ 0,
+ 0 // padding two bytes for buffer copy
+ };
+
+ std::vector<uint8_t> expectedData = {
+ std::numeric_limits<uint8_t>::max(), 0, std::numeric_limits<uint8_t>::min(), 2, 200, 201,
+ };
+
+ DoVertexFormatTest(dawn::VertexFormat::UChar2, vertexData, expectedData);
+}
+
+TEST_P(VertexFormatTest, UChar4) {
+ std::vector<uint8_t> vertexData = {
+ std::numeric_limits<uint8_t>::max(),
+ 0,
+ 1,
+ 2,
+ std::numeric_limits<uint8_t>::min(),
+ 2,
+ 3,
+ 4,
+ 200,
+ 201,
+ 202,
+ 203,
+ };
+
+ DoVertexFormatTest(dawn::VertexFormat::UChar4, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Char2) {
+ std::vector<int8_t> vertexData = {
+ std::numeric_limits<int8_t>::max(),
+ 0,
+ 0, // padding two bytes for stride
+ 0,
+ std::numeric_limits<int8_t>::min(),
+ -2,
+ 0, // padding two bytes for stride
+ 0,
+ 120,
+ -121,
+ 0,
+ 0 // padding two bytes for buffer copy
+ };
+
+ std::vector<int8_t> expectedData = {
+ std::numeric_limits<int8_t>::max(), 0, std::numeric_limits<int8_t>::min(), -2, 120, -121,
+ };
+
+ DoVertexFormatTest(dawn::VertexFormat::Char2, vertexData, expectedData);
+}
+
+TEST_P(VertexFormatTest, Char4) {
+ std::vector<int8_t> vertexData = {
+ std::numeric_limits<int8_t>::max(),
+ 0,
+ -1,
+ 2,
+ std::numeric_limits<int8_t>::min(),
+ -2,
+ 3,
+ 4,
+ 120,
+ -121,
+ 122,
+ -123,
+ };
+
+ DoVertexFormatTest(dawn::VertexFormat::Char4, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, UChar2Norm) {
+ std::vector<uint8_t> vertexData = {
+ std::numeric_limits<uint8_t>::max(),
+ std::numeric_limits<uint8_t>::min(),
+ 0, // padding two bytes for stride
+ 0,
+ std::numeric_limits<uint8_t>::max() / 2,
+ std::numeric_limits<uint8_t>::min() / 2,
+ 0, // padding two bytes for stride
+ 0,
+ 200,
+ 201,
+ 0,
+ 0 // padding two bytes for buffer copy
+ };
+
+ std::vector<uint8_t> expectedData = {std::numeric_limits<uint8_t>::max(),
+ std::numeric_limits<uint8_t>::min(),
+ std::numeric_limits<uint8_t>::max() / 2,
+ std::numeric_limits<uint8_t>::min() / 2,
+ 200,
+ 201};
+
+ DoVertexFormatTest(dawn::VertexFormat::UChar2Norm, vertexData, expectedData);
+}
+
+TEST_P(VertexFormatTest, UChar4Norm) {
+ std::vector<uint8_t> vertexData = {std::numeric_limits<uint8_t>::max(),
+ std::numeric_limits<uint8_t>::min(),
+ 0,
+ 0,
+ std::numeric_limits<uint8_t>::max() / 2,
+ std::numeric_limits<uint8_t>::min() / 2,
+ 0,
+ 0,
+ 200,
+ 201,
+ 202,
+ 203};
+
+ DoVertexFormatTest(dawn::VertexFormat::UChar4Norm, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Char2Norm) {
+ std::vector<int8_t> vertexData = {
+ std::numeric_limits<int8_t>::max(),
+ std::numeric_limits<int8_t>::min(),
+ 0, // padding two bytes for stride
+ 0,
+ std::numeric_limits<int8_t>::max() / 2,
+ std::numeric_limits<int8_t>::min() / 2,
+ 0, // padding two bytes for stride
+ 0,
+ 120,
+ -121,
+ 0,
+ 0 // padding two bytes for buffer copy
+ };
+
+ std::vector<int8_t> expectedData = {
+ std::numeric_limits<int8_t>::max(),
+ std::numeric_limits<int8_t>::min(),
+ std::numeric_limits<int8_t>::max() / 2,
+ std::numeric_limits<int8_t>::min() / 2,
+ 120,
+ -121,
+ };
+
+ DoVertexFormatTest(dawn::VertexFormat::Char2Norm, vertexData, expectedData);
+}
+
+TEST_P(VertexFormatTest, Char4Norm) {
+ std::vector<int8_t> vertexData = {std::numeric_limits<int8_t>::max(),
+ std::numeric_limits<int8_t>::min(),
+ 0,
+ 0,
+ std::numeric_limits<int8_t>::max() / 2,
+ std::numeric_limits<int8_t>::min() / 2,
+ -2,
+ 2,
+ 120,
+ -120,
+ 102,
+ -123};
+
+ DoVertexFormatTest(dawn::VertexFormat::Char4Norm, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, UShort2) {
+ std::vector<uint16_t> vertexData = {std::numeric_limits<uint16_t>::max(),
+ 0,
+ std::numeric_limits<uint16_t>::min(),
+ 2,
+ 65432,
+ 4890};
+
+ DoVertexFormatTest(dawn::VertexFormat::UShort2, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, UShort4) {
+ std::vector<uint16_t> vertexData = {
+ std::numeric_limits<uint16_t>::max(),
+ std::numeric_limits<uint8_t>::max(),
+ 1,
+ 2,
+ std::numeric_limits<uint16_t>::min(),
+ 2,
+ 3,
+ 4,
+ 65520,
+ 65521,
+ 3435,
+ 3467,
+ };
+
+ DoVertexFormatTest(dawn::VertexFormat::UShort4, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Short2) {
+ std::vector<int16_t> vertexData = {std::numeric_limits<int16_t>::max(),
+ 0,
+ std::numeric_limits<int16_t>::min(),
+ -2,
+ 3876,
+ -3948};
+
+ DoVertexFormatTest(dawn::VertexFormat::Short2, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Short4) {
+ std::vector<int16_t> vertexData = {
+ std::numeric_limits<int16_t>::max(),
+ 0,
+ -1,
+ 2,
+ std::numeric_limits<int16_t>::min(),
+ -2,
+ 3,
+ 4,
+ 24567,
+ -23545,
+ 4350,
+ -2987,
+ };
+
+ DoVertexFormatTest(dawn::VertexFormat::Short4, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, UShort2Norm) {
+ std::vector<uint16_t> vertexData = {std::numeric_limits<uint16_t>::max(),
+ std::numeric_limits<uint16_t>::min(),
+ std::numeric_limits<uint16_t>::max() / 2,
+ std::numeric_limits<uint16_t>::min() / 2,
+ 3456,
+ 6543};
+
+ DoVertexFormatTest(dawn::VertexFormat::UShort2Norm, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, UShort4Norm) {
+ std::vector<uint16_t> vertexData = {std::numeric_limits<uint16_t>::max(),
+ std::numeric_limits<uint16_t>::min(),
+ 0,
+ 0,
+ std::numeric_limits<uint16_t>::max() / 2,
+ std::numeric_limits<uint16_t>::min() / 2,
+ 0,
+ 0,
+ 2987,
+ 3055,
+ 2987,
+ 2987};
+
+ DoVertexFormatTest(dawn::VertexFormat::UShort4Norm, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Short2Norm) {
+ std::vector<int16_t> vertexData = {std::numeric_limits<int16_t>::max(),
+ std::numeric_limits<int16_t>::min(),
+ std::numeric_limits<int16_t>::max() / 2,
+ std::numeric_limits<int16_t>::min() / 2,
+ 4987,
+ -6789};
+
+ DoVertexFormatTest(dawn::VertexFormat::Short2Norm, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Short4Norm) {
+ std::vector<int16_t> vertexData = {std::numeric_limits<int16_t>::max(),
+ std::numeric_limits<int16_t>::min(),
+ 0,
+ 0,
+ std::numeric_limits<int16_t>::max() / 2,
+ std::numeric_limits<int16_t>::min() / 2,
+ -2,
+ 2,
+ 2890,
+ -29011,
+ 20432,
+ -2083};
+
+ DoVertexFormatTest(dawn::VertexFormat::Short4Norm, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Half2) {
+ std::vector<uint16_t> vertexData =
+ Float32ToFloat16(std::vector<float>({14.8, -0.0, 22.5, 1.3, +0.0, -24.8}));
+
+ DoVertexFormatTest(dawn::VertexFormat::Half2, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Half4) {
+ std::vector<uint16_t> vertexData = Float32ToFloat16(std::vector<float>(
+ {+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<float> vertexData = {1.3f, +0.0f, -0.0f};
+
+ DoVertexFormatTest(dawn::VertexFormat::Float, vertexData, vertexData);
+
+ vertexData = std::vector<float>{+1.0f, -1.0f, 18.23f};
+
+ DoVertexFormatTest(dawn::VertexFormat::Float, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Float2) {
+ std::vector<float> 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<float> 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<float> 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<uint32_t> vertexData = {std::numeric_limits<uint32_t>::max(),
+ std::numeric_limits<uint16_t>::max(),
+ std::numeric_limits<uint8_t>::max()};
+
+ DoVertexFormatTest(dawn::VertexFormat::UInt, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, UInt2) {
+ std::vector<uint32_t> vertexData = {std::numeric_limits<uint32_t>::max(), 32,
+ std::numeric_limits<uint16_t>::max(), 64,
+ std::numeric_limits<uint8_t>::max(), 128};
+
+ DoVertexFormatTest(dawn::VertexFormat::UInt2, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, UInt3) {
+ std::vector<uint32_t> vertexData = {std::numeric_limits<uint32_t>::max(), 32, 64,
+ std::numeric_limits<uint16_t>::max(), 164, 128,
+ std::numeric_limits<uint8_t>::max(), 1283, 256};
+
+ DoVertexFormatTest(dawn::VertexFormat::UInt3, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, UInt4) {
+ std::vector<uint32_t> vertexData = {std::numeric_limits<uint32_t>::max(), 32, 64, 5460,
+ std::numeric_limits<uint16_t>::max(), 164, 128, 0,
+ std::numeric_limits<uint8_t>::max(), 1283, 256, 4567};
+
+ DoVertexFormatTest(dawn::VertexFormat::UInt4, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Int) {
+ std::vector<int32_t> vertexData = {std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int8_t>::max()};
+
+ DoVertexFormatTest(dawn::VertexFormat::Int, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Int2) {
+ std::vector<int32_t> vertexData = {
+ std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int16_t>::max(), std::numeric_limits<int16_t>::min(),
+ std::numeric_limits<int8_t>::max(), std::numeric_limits<int8_t>::min()};
+
+ DoVertexFormatTest(dawn::VertexFormat::Int2, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Int3) {
+ std::vector<int32_t> vertexData = {
+ std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min(), 64,
+ std::numeric_limits<int16_t>::max(), std::numeric_limits<int16_t>::min(), 128,
+ std::numeric_limits<int8_t>::max(), std::numeric_limits<int8_t>::min(), 256};
+
+ DoVertexFormatTest(dawn::VertexFormat::Int3, vertexData, vertexData);
+}
+
+TEST_P(VertexFormatTest, Int4) {
+ std::vector<int32_t> vertexData = {
+ std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min(), 64, -5460,
+ std::numeric_limits<int16_t>::max(), std::numeric_limits<int16_t>::min(), -128, 0,
+ std::numeric_limits<int8_t>::max(), std::numeric_limits<int8_t>::min(), 256, -4567};
+
+ DoVertexFormatTest(dawn::VertexFormat::Int4, vertexData, vertexData);
+}
+
+DAWN_INSTANTIATE_TEST(VertexFormatTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend);
\ No newline at end of file