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