Add support for unorm10-10-10-2 vertex format

This CL adds unorm10-10-10-2 vertex format on Metal, D3D12, and Vulkan.
It consists of of one packed 32-bit value with four normalized unsigned
integer values, arranged as 10 bits, 10 bits, 10 bits, and 2 bits.

Spec issue: https://github.com/gpuweb/gpuweb/issues/4275
Bug: dawn:2044

Change-Id: I659ba8ffa1eeee8fdd0e911efc70b01359b0e55b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/150147
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Fr <beaufort.francois@gmail.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/dawn.json b/dawn.json
index fb73083..7a22990 100644
--- a/dawn.json
+++ b/dawn.json
@@ -3620,7 +3620,8 @@
             {"value": 27, "name": "sint32"},
             {"value": 28, "name": "sint32x2"},
             {"value": 29, "name": "sint32x3"},
-            {"value": 30, "name": "sint32x4"}
+            {"value": 30, "name": "sint32x4"},
+            {"value": 31, "name": "unorm 10_10_10_2", "jsrepr": "'unorm10-10-10-2'"}
         ]
     },
     "whole size" : {
diff --git a/src/dawn/native/RenderPipeline.cpp b/src/dawn/native/RenderPipeline.cpp
index ddbf4ed..8401389 100644
--- a/src/dawn/native/RenderPipeline.cpp
+++ b/src/dawn/native/RenderPipeline.cpp
@@ -29,7 +29,7 @@
 
 namespace dawn::native {
 
-static constexpr std::array<VertexFormatInfo, 31> sVertexFormatTable = {{
+static constexpr std::array<VertexFormatInfo, 32> sVertexFormatTable = {{
     //
     {wgpu::VertexFormat::Undefined, 0, 0, VertexFormatBaseType::Float},
 
@@ -65,6 +65,7 @@
     {wgpu::VertexFormat::Sint32x2, 8, 2, VertexFormatBaseType::Sint},
     {wgpu::VertexFormat::Sint32x3, 12, 3, VertexFormatBaseType::Sint},
     {wgpu::VertexFormat::Sint32x4, 16, 4, VertexFormatBaseType::Sint},
+    {wgpu::VertexFormat::Unorm10_10_10_2, 4, 4, VertexFormatBaseType::Float},
     //
 }};
 
diff --git a/src/dawn/native/TintUtils.cpp b/src/dawn/native/TintUtils.cpp
index 36ccb50..182fa46 100644
--- a/src/dawn/native/TintUtils.cpp
+++ b/src/dawn/native/TintUtils.cpp
@@ -104,6 +104,8 @@
             return tint::ast::transform::VertexFormat::kSint32x3;
         case wgpu::VertexFormat::Sint32x4:
             return tint::ast::transform::VertexFormat::kSint32x4;
+        case wgpu::VertexFormat::Unorm10_10_10_2:
+            return tint::ast::transform::VertexFormat::kUnorm10_10_10_2;
 
         case wgpu::VertexFormat::Undefined:
             break;
diff --git a/src/dawn/native/d3d/UtilsD3D.cpp b/src/dawn/native/d3d/UtilsD3D.cpp
index 7282987..07d8e3a 100644
--- a/src/dawn/native/d3d/UtilsD3D.cpp
+++ b/src/dawn/native/d3d/UtilsD3D.cpp
@@ -465,6 +465,8 @@
             return DXGI_FORMAT_R32G32B32_SINT;
         case wgpu::VertexFormat::Sint32x4:
             return DXGI_FORMAT_R32G32B32A32_SINT;
+        case wgpu::VertexFormat::Unorm10_10_10_2:
+            return DXGI_FORMAT_R10G10B10A2_UNORM;
         default:
             DAWN_UNREACHABLE();
     }
diff --git a/src/dawn/native/metal/RenderPipelineMTL.mm b/src/dawn/native/metal/RenderPipelineMTL.mm
index cc620a7..c28103d 100644
--- a/src/dawn/native/metal/RenderPipelineMTL.mm
+++ b/src/dawn/native/metal/RenderPipelineMTL.mm
@@ -88,6 +88,8 @@
             return MTLVertexFormatInt3;
         case wgpu::VertexFormat::Sint32x4:
             return MTLVertexFormatInt4;
+        case wgpu::VertexFormat::Unorm10_10_10_2:
+            return MTLVertexFormatUInt1010102Normalized;
         default:
             DAWN_UNREACHABLE();
     }
diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp
index ceeee66..e7bfb43 100644
--- a/src/dawn/native/opengl/CommandBufferGL.cpp
+++ b/src/dawn/native/opengl/CommandBufferGL.cpp
@@ -96,6 +96,8 @@
         case wgpu::VertexFormat::Sint32x3:
         case wgpu::VertexFormat::Sint32x4:
             return GL_INT;
+        case wgpu::VertexFormat::Unorm10_10_10_2:
+            return GL_UNSIGNED_INT_2_10_10_10_REV;
         default:
             DAWN_UNREACHABLE();
     }
@@ -111,6 +113,7 @@
         case wgpu::VertexFormat::Unorm16x4:
         case wgpu::VertexFormat::Snorm16x2:
         case wgpu::VertexFormat::Snorm16x4:
+        case wgpu::VertexFormat::Unorm10_10_10_2:
             return GL_TRUE;
         default:
             return GL_FALSE;
diff --git a/src/dawn/native/vulkan/RenderPipelineVk.cpp b/src/dawn/native/vulkan/RenderPipelineVk.cpp
index f686bff..d12659d 100644
--- a/src/dawn/native/vulkan/RenderPipelineVk.cpp
+++ b/src/dawn/native/vulkan/RenderPipelineVk.cpp
@@ -108,6 +108,8 @@
             return VK_FORMAT_R32G32B32_SINT;
         case wgpu::VertexFormat::Sint32x4:
             return VK_FORMAT_R32G32B32A32_SINT;
+        case wgpu::VertexFormat::Unorm10_10_10_2:
+            return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
         default:
             DAWN_UNREACHABLE();
     }
diff --git a/src/dawn/node/binding/Converter.cpp b/src/dawn/node/binding/Converter.cpp
index 48c404e..5d80c49 100644
--- a/src/dawn/node/binding/Converter.cpp
+++ b/src/dawn/node/binding/Converter.cpp
@@ -1196,6 +1196,9 @@
         case interop::GPUVertexFormat::kSint32X4:
             out = wgpu::VertexFormat::Sint32x4;
             return true;
+        case interop::GPUVertexFormat::kUnorm1010102:
+            out = wgpu::VertexFormat::Unorm10_10_10_2;
+            return true;
         default:
             break;
     }
diff --git a/src/dawn/node/interop/DawnExtensions.idl b/src/dawn/node/interop/DawnExtensions.idl
index da97b08..ddccef6 100644
--- a/src/dawn/node/interop/DawnExtensions.idl
+++ b/src/dawn/node/interop/DawnExtensions.idl
@@ -20,6 +20,10 @@
     "chromium-experimental-subgroup-uniform-control-flow",
 };
 
+enum GPUVertexFormat {
+    "unorm10-10-10-2",
+};
+
 dictionary GPURequestAdapterOptions {
     boolean compatibilityMode = false;
 };
diff --git a/src/dawn/tests/end2end/VertexFormatTests.cpp b/src/dawn/tests/end2end/VertexFormatTests.cpp
index f4f6429..78300ed 100644
--- a/src/dawn/tests/end2end/VertexFormatTests.cpp
+++ b/src/dawn/tests/end2end/VertexFormatTests.cpp
@@ -69,12 +69,34 @@
             case wgpu::VertexFormat::Unorm16x4:
             case wgpu::VertexFormat::Snorm16x2:
             case wgpu::VertexFormat::Snorm16x4:
+            case wgpu::VertexFormat::Unorm10_10_10_2:
                 return true;
             default:
                 return false;
         }
     }
 
+    uint32_t NormalizationFactor(wgpu::VertexFormat format, uint32_t component) {
+        switch (format) {
+            case wgpu::VertexFormat::Unorm8x2:
+            case wgpu::VertexFormat::Unorm8x4:
+                return 255;
+            case wgpu::VertexFormat::Snorm8x2:
+            case wgpu::VertexFormat::Snorm8x4:
+                return 127;
+            case wgpu::VertexFormat::Unorm16x2:
+            case wgpu::VertexFormat::Unorm16x4:
+                return 65535;
+            case wgpu::VertexFormat::Snorm16x2:
+            case wgpu::VertexFormat::Snorm16x4:
+                return 32767;
+            case wgpu::VertexFormat::Unorm10_10_10_2:
+                return (component == 3) ? 3 : 1023;
+            default:
+                DAWN_UNREACHABLE();
+        }
+    }
+
     bool IsUnsignedFormat(wgpu::VertexFormat format) {
         switch (format) {
             case wgpu::VertexFormat::Uint32:
@@ -89,6 +111,7 @@
             case wgpu::VertexFormat::Unorm8x4:
             case wgpu::VertexFormat::Unorm16x2:
             case wgpu::VertexFormat::Unorm16x4:
+            case wgpu::VertexFormat::Unorm10_10_10_2:
                 return true;
             default:
                 return false;
@@ -119,41 +142,44 @@
         }
     }
 
-    uint32_t BytesPerComponents(wgpu::VertexFormat format) {
+    uint32_t ByteSize(wgpu::VertexFormat format) {
         switch (format) {
-            case wgpu::VertexFormat::Uint8x2:
-            case wgpu::VertexFormat::Uint8x4:
             case wgpu::VertexFormat::Sint8x2:
-            case wgpu::VertexFormat::Sint8x4:
-            case wgpu::VertexFormat::Unorm8x2:
-            case wgpu::VertexFormat::Unorm8x4:
             case wgpu::VertexFormat::Snorm8x2:
-            case wgpu::VertexFormat::Snorm8x4:
-                return 1;
-            case wgpu::VertexFormat::Uint16x2:
-            case wgpu::VertexFormat::Uint16x4:
-            case wgpu::VertexFormat::Unorm16x2:
-            case wgpu::VertexFormat::Unorm16x4:
-            case wgpu::VertexFormat::Sint16x2:
-            case wgpu::VertexFormat::Sint16x4:
-            case wgpu::VertexFormat::Snorm16x2:
-            case wgpu::VertexFormat::Snorm16x4:
-            case wgpu::VertexFormat::Float16x2:
-            case wgpu::VertexFormat::Float16x4:
+            case wgpu::VertexFormat::Uint8x2:
+            case wgpu::VertexFormat::Unorm8x2:
                 return 2;
+            case wgpu::VertexFormat::Float16x2:
             case wgpu::VertexFormat::Float32:
-            case wgpu::VertexFormat::Float32x2:
-            case wgpu::VertexFormat::Float32x3:
-            case wgpu::VertexFormat::Float32x4:
-            case wgpu::VertexFormat::Uint32:
-            case wgpu::VertexFormat::Uint32x2:
-            case wgpu::VertexFormat::Uint32x3:
-            case wgpu::VertexFormat::Uint32x4:
+            case wgpu::VertexFormat::Unorm10_10_10_2:
+            case wgpu::VertexFormat::Sint16x2:
             case wgpu::VertexFormat::Sint32:
-            case wgpu::VertexFormat::Sint32x2:
-            case wgpu::VertexFormat::Sint32x3:
-            case wgpu::VertexFormat::Sint32x4:
+            case wgpu::VertexFormat::Sint8x4:
+            case wgpu::VertexFormat::Snorm16x2:
+            case wgpu::VertexFormat::Snorm8x4:
+            case wgpu::VertexFormat::Uint16x2:
+            case wgpu::VertexFormat::Uint32:
+            case wgpu::VertexFormat::Uint8x4:
+            case wgpu::VertexFormat::Unorm16x2:
+            case wgpu::VertexFormat::Unorm8x4:
                 return 4;
+            case wgpu::VertexFormat::Float16x4:
+            case wgpu::VertexFormat::Float32x2:
+            case wgpu::VertexFormat::Sint16x4:
+            case wgpu::VertexFormat::Sint32x2:
+            case wgpu::VertexFormat::Snorm16x4:
+            case wgpu::VertexFormat::Uint16x4:
+            case wgpu::VertexFormat::Uint32x2:
+            case wgpu::VertexFormat::Unorm16x4:
+                return 8;
+            case wgpu::VertexFormat::Float32x3:
+            case wgpu::VertexFormat::Sint32x3:
+            case wgpu::VertexFormat::Uint32x3:
+                return 12;
+            case wgpu::VertexFormat::Float32x4:
+            case wgpu::VertexFormat::Sint32x4:
+            case wgpu::VertexFormat::Uint32x4:
+                return 16;
             default:
                 DAWN_UNREACHABLE();
         }
@@ -194,6 +220,7 @@
             case wgpu::VertexFormat::Float32x4:
             case wgpu::VertexFormat::Uint32x4:
             case wgpu::VertexFormat::Sint32x4:
+            case wgpu::VertexFormat::Unorm10_10_10_2:
                 return 4;
             default:
                 DAWN_UNREACHABLE();
@@ -320,8 +347,7 @@
                     // Move normalize operation into shader because of CPU and GPU precision
                     // different on float math.
                     vs << "max(f32(" << std::to_string(expectedData[i * componentCount + j])
-                       << ") / " << std::to_string(std::numeric_limits<T>::max())
-                       << ".0 , -1.0));\n";
+                       << ") / " << std::to_string(NormalizationFactor(format, j)) << ", -1.0));\n";
                 } else if (isHalf) {
                     // Because Vulkan and D3D12 handle -0.0f through bitcast have different
                     // result (Vulkan take -0.0f as -0.0 but D3D12 take -0.0f as 0), add workaround
@@ -389,12 +415,7 @@
                     return 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);
-        }
+        uint32_t strideBytes = Align(ByteSize(format), 4);
 
         utils::ComboRenderPipelineDescriptor descriptor;
         descriptor.vertex.module = vsModule;
@@ -849,6 +870,30 @@
     DoVertexFormatTest(wgpu::VertexFormat::Sint32x4, vertexData, vertexData);
 }
 
+TEST_P(VertexFormatTest, Unorm10_10_10_2) {
+    auto MakeRGB10A2 = [](uint32_t r, uint32_t g, uint32_t b, uint32_t a) -> uint32_t {
+        DAWN_ASSERT((r & 0x3FF) == r);
+        DAWN_ASSERT((g & 0x3FF) == g);
+        DAWN_ASSERT((b & 0x3FF) == b);
+        DAWN_ASSERT((a & 0x3) == a);
+        return r | g << 10 | b << 20 | a << 30;
+    };
+
+    std::vector<uint32_t> vertexData = {
+        MakeRGB10A2(0, 0, 0, 0),
+        MakeRGB10A2(1023, 1023, 1023, 3),
+        MakeRGB10A2(243, 567, 765, 2),
+    };
+
+    std::vector<uint32_t> expectedData = {
+        0,    0,    0,    0,  //
+        1023, 1023, 1023, 3,  //
+        243,  567,  765,  2,
+    };
+
+    DoVertexFormatTest(wgpu::VertexFormat::Unorm10_10_10_2, vertexData, expectedData);
+}
+
 DAWN_INSTANTIATE_TEST(VertexFormatTest,
                       D3D11Backend(),
                       D3D12Backend(),
diff --git a/src/dawn/tests/unittests/validation/VertexStateValidationTests.cpp b/src/dawn/tests/unittests/validation/VertexStateValidationTests.cpp
index c52d775..ad83431 100644
--- a/src/dawn/tests/unittests/validation/VertexStateValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/VertexStateValidationTests.cpp
@@ -388,6 +388,11 @@
     DoTest(wgpu::VertexFormat::Unorm16x2, "i32", false);
     DoTest(wgpu::VertexFormat::Unorm16x2, "u32", false);
 
+    // Test that unorm10-10-10-2 format is compatible only with f32.
+    DoTest(wgpu::VertexFormat::Unorm10_10_10_2, "f32", true);
+    DoTest(wgpu::VertexFormat::Unorm10_10_10_2, "i32", false);
+    DoTest(wgpu::VertexFormat::Unorm10_10_10_2, "u32", false);
+
     // Test that an snorm format is compatible only with f32.
     DoTest(wgpu::VertexFormat::Snorm16x4, "f32", true);
     DoTest(wgpu::VertexFormat::Snorm16x4, "i32", false);
diff --git a/src/dawn/utils/TestUtils.cpp b/src/dawn/utils/TestUtils.cpp
index 4bff85d..0376d08 100644
--- a/src/dawn/utils/TestUtils.cpp
+++ b/src/dawn/utils/TestUtils.cpp
@@ -169,6 +169,7 @@
         case wgpu::VertexFormat::Float32:
         case wgpu::VertexFormat::Uint32:
         case wgpu::VertexFormat::Sint32:
+        case wgpu::VertexFormat::Unorm10_10_10_2:
             return 4;
         case wgpu::VertexFormat::Uint16x4:
         case wgpu::VertexFormat::Sint16x4:
diff --git a/src/tint/lang/wgsl/ast/transform/vertex_pulling.cc b/src/tint/lang/wgsl/ast/transform/vertex_pulling.cc
index cc747fb..944ecf4 100644
--- a/src/tint/lang/wgsl/ast/transform/vertex_pulling.cc
+++ b/src/tint/lang/wgsl/ast/transform/vertex_pulling.cc
@@ -126,6 +126,8 @@
             return out << "sint32x3";
         case VertexFormat::kSint32x4:
             return out << "sint32x4";
+        case VertexFormat::kUnorm10_10_10_2:
+            return out << "unorm10-10-10-2";
     }
     return out << "<unknown>";
 }
@@ -223,6 +225,7 @@
         case VertexFormat::kSnorm16x4:
         case VertexFormat::kFloat16x4:
         case VertexFormat::kFloat32x4:
+        case VertexFormat::kUnorm10_10_10_2:
             return {VertexDataType::kFloat, 4};
     }
     return {VertexDataType::kInvalid, 0};
@@ -667,6 +670,15 @@
             case VertexFormat::kFloat16x4:
                 return b.Call<vec4<f32>>(b.Call("unpack2x16float", load_u32()),
                                          b.Call("unpack2x16float", load_next_u32()));
+            case VertexFormat::kUnorm10_10_10_2:
+                auto* u32s = b.Call<vec4<u32>>(load_u32());
+                // shr = u32s >> vec4u(0, 10, 20, 30);
+                auto* shr = b.Shr(u32s, b.Call<vec4<u32>>(0_u, 10_u, 20_u, 30_u));
+                // mask = shr & vec4u(0x3FF, 0x3FF, 0x3FF, 0x3);
+                auto* mask = b.And(shr, b.Call<vec4<u32>>(0x3FF_u, 0x3FF_u, 0x3FF_u, 0x3_u));
+                // return vec4f(mask) / vec4f(1023, 1023, 1023, 3);
+                return b.Div(b.Call<vec4<f32>>(mask),
+                             b.Call<vec4<f32>>(1023_f, 1023_f, 1023_f, 3_f));
         }
 
         TINT_UNREACHABLE() << "format " << static_cast<int>(format);
diff --git a/src/tint/lang/wgsl/ast/transform/vertex_pulling.h b/src/tint/lang/wgsl/ast/transform/vertex_pulling.h
index deaed5a..e5084ab 100644
--- a/src/tint/lang/wgsl/ast/transform/vertex_pulling.h
+++ b/src/tint/lang/wgsl/ast/transform/vertex_pulling.h
@@ -27,36 +27,37 @@
 
 /// Describes the format of data in a vertex buffer
 enum class VertexFormat {
-    kUint8x2,    // uint8x2
-    kUint8x4,    // uint8x4
-    kSint8x2,    // sint8x2
-    kSint8x4,    // sint8x4
-    kUnorm8x2,   // unorm8x2
-    kUnorm8x4,   // unorm8x4
-    kSnorm8x2,   // snorm8x2
-    kSnorm8x4,   // snorm8x4
-    kUint16x2,   // uint16x2
-    kUint16x4,   // uint16x4
-    kSint16x2,   // sint16x2
-    kSint16x4,   // sint16x4
-    kUnorm16x2,  // unorm16x2
-    kUnorm16x4,  // unorm16x4
-    kSnorm16x2,  // snorm16x2
-    kSnorm16x4,  // snorm16x4
-    kFloat16x2,  // float16x2
-    kFloat16x4,  // float16x4
-    kFloat32,    // float32
-    kFloat32x2,  // float32x2
-    kFloat32x3,  // float32x3
-    kFloat32x4,  // float32x4
-    kUint32,     // uint32
-    kUint32x2,   // uint32x2
-    kUint32x3,   // uint32x3
-    kUint32x4,   // uint32x4
-    kSint32,     // sint32
-    kSint32x2,   // sint32x2
-    kSint32x3,   // sint32x3
-    kSint32x4,   // sint32x4
+    kUint8x2,          // uint8x2
+    kUint8x4,          // uint8x4
+    kSint8x2,          // sint8x2
+    kSint8x4,          // sint8x4
+    kUnorm8x2,         // unorm8x2
+    kUnorm8x4,         // unorm8x4
+    kSnorm8x2,         // snorm8x2
+    kSnorm8x4,         // snorm8x4
+    kUint16x2,         // uint16x2
+    kUint16x4,         // uint16x4
+    kSint16x2,         // sint16x2
+    kSint16x4,         // sint16x4
+    kUnorm16x2,        // unorm16x2
+    kUnorm16x4,        // unorm16x4
+    kSnorm16x2,        // snorm16x2
+    kSnorm16x4,        // snorm16x4
+    kFloat16x2,        // float16x2
+    kFloat16x4,        // float16x4
+    kFloat32,          // float32
+    kFloat32x2,        // float32x2
+    kFloat32x3,        // float32x3
+    kFloat32x4,        // float32x4
+    kUint32,           // uint32
+    kUint32x2,         // uint32x2
+    kUint32x3,         // uint32x3
+    kUint32x4,         // uint32x4
+    kSint32,           // sint32
+    kSint32x2,         // sint32x2
+    kSint32x3,         // sint32x3
+    kSint32x4,         // sint32x4
+    kUnorm10_10_10_2,  // unorm10-10-10-2
 
     kLastEntry = kSint32x4,
 };
diff --git a/src/tint/lang/wgsl/ast/transform/vertex_pulling_test.cc b/src/tint/lang/wgsl/ast/transform/vertex_pulling_test.cc
index 5bb7c59..df3f1c8 100644
--- a/src/tint/lang/wgsl/ast/transform/vertex_pulling_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/vertex_pulling_test.cc
@@ -1000,6 +1000,7 @@
     @location(11) float32x2 : vec2<f32>,
     @location(12) float32x3 : vec3<f32>,
     @location(13) float32x4 : vec4<f32>,
+    @location(14) unorm10_10_10_2 : vec4<f32>,
   ) -> @builtin(position) vec4<f32> {
   return vec4<f32>(0.0, 0.0, 0.0, 1.0);
 }
@@ -1028,6 +1029,7 @@
   var float32x2 : vec2<f32>;
   var float32x3 : vec3<f32>;
   var float32x4 : vec4<f32>;
+  var unorm10_10_10_2 : vec4<f32>;
   {
     let buffer_array_base_0 = (tint_pulling_vertex_index * 64u);
     unorm8x2 = unpack4x8unorm((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)] & 65535u)).xy;
@@ -1044,6 +1046,7 @@
     float32x2 = vec2<f32>(bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)]));
     float32x3 = vec3<f32>(bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 18u)]));
     float32x4 = vec4<f32>(bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 18u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 19u)]));
+    unorm10_10_10_2 = (vec4<f32>(((vec4<u32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)]) >> vec4<u32>(0u, 10u, 20u, 30u)) & vec4<u32>(1023u, 1023u, 1023u, 3u))) / vec4<f32>(1023.0f, 1023.0f, 1023.0f, 3.0f));
   }
   return vec4<f32>(0.0, 0.0, 0.0, 1.0);
 }
@@ -1067,6 +1070,7 @@
                               {VertexFormat::kFloat32x2, 64, 11},
                               {VertexFormat::kFloat32x3, 64, 12},
                               {VertexFormat::kFloat32x4, 64, 13},
+                              {VertexFormat::kUnorm10_10_10_2, 64, 14},
                           }}}};
 
     DataMap data;
@@ -1332,6 +1336,7 @@
     @location(11) float32x2 : vec2<f32>,
     @location(12) float32x3 : vec3<f32>,
     @location(13) float32x4 : vec4<f32>,
+    @location(14) unorm10_10_10_2 : vec4<f32>,
   ) -> @builtin(position) vec4<f32> {
   return vec4<f32>(0.0, 0.0, 0.0, 1.0);
 }
@@ -1360,6 +1365,7 @@
   var float32x2 : vec2<f32>;
   var float32x3 : vec3<f32>;
   var float32x4 : vec4<f32>;
+  var unorm10_10_10_2 : vec4<f32>;
   {
     let buffer_array_base_0 = (tint_pulling_vertex_index * 64u);
     unorm8x2 = unpack4x8unorm((((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)] << 8u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 15u)] >> 24u)) & 65535u)).xy;
@@ -1376,6 +1382,7 @@
     float32x2 = vec2<f32>(bitcast<f32>(((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 15u)] >> 24u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)] << 8u))), bitcast<f32>(((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)] >> 24u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)] << 8u))));
     float32x3 = vec3<f32>(bitcast<f32>(((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 15u)] >> 24u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)] << 8u))), bitcast<f32>(((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)] >> 24u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)] << 8u))), bitcast<f32>(((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)] >> 24u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 18u)] << 8u))));
     float32x4 = vec4<f32>(bitcast<f32>(((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 15u)] >> 24u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)] << 8u))), bitcast<f32>(((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)] >> 24u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)] << 8u))), bitcast<f32>(((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)] >> 24u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 18u)] << 8u))), bitcast<f32>(((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 18u)] >> 24u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 19u)] << 8u))));
+    unorm10_10_10_2 = (vec4<f32>(((vec4<u32>(((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 15u)] >> 24u) | (tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)] << 8u))) >> vec4<u32>(0u, 10u, 20u, 30u)) & vec4<u32>(1023u, 1023u, 1023u, 3u))) / vec4<f32>(1023.0f, 1023.0f, 1023.0f, 3.0f));
   }
   return vec4<f32>(0.0, 0.0, 0.0, 1.0);
 }
@@ -1399,6 +1406,7 @@
                               {VertexFormat::kFloat32x2, 63, 11},
                               {VertexFormat::kFloat32x3, 63, 12},
                               {VertexFormat::kFloat32x4, 63, 13},
+                              {VertexFormat::kUnorm10_10_10_2, 63, 14},
                           }}}};
 
     DataMap data;
@@ -2088,6 +2096,9 @@
     @location(23) sclr_float32x4 :      f32 ,
     @location(24) vec2_float32x4 : vec2<f32>,
     @location(25) vec3_float32x4 : vec3<f32>,
+    @location(26) sclr_unorm10_10_10_2   :      f32 ,
+    @location(27) vec2_unorm10_10_10_2   : vec2<f32>,
+    @location(28) vec3_unorm10_10_10_2   : vec3<f32>,
   ) -> @builtin(position) vec4<f32> {
   return vec4<f32>(0.0, 0.0, 0.0, 1.0);
 }
@@ -2128,6 +2139,9 @@
   var sclr_float32x4 : f32;
   var vec2_float32x4 : vec2<f32>;
   var vec3_float32x4 : vec3<f32>;
+  var sclr_unorm10_10_10_2 : f32;
+  var vec2_unorm10_10_10_2 : vec2<f32>;
+  var vec3_unorm10_10_10_2 : vec3<f32>;
   {
     let buffer_array_base_0 = (tint_pulling_vertex_index * 64u);
     sclr_unorm8x2 = unpack4x8unorm((tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)] & 65535u)).xy.x;
@@ -2156,6 +2170,9 @@
     sclr_float32x4 = vec4<f32>(bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 18u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 19u)])).x;
     vec2_float32x4 = vec4<f32>(bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 18u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 19u)])).xy;
     vec3_float32x4 = vec4<f32>(bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 17u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 18u)]), bitcast<f32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 19u)])).xyz;
+    sclr_unorm10_10_10_2 = ((vec4<f32>(((vec4<u32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)]) >> vec4<u32>(0u, 10u, 20u, 30u)) & vec4<u32>(1023u, 1023u, 1023u, 3u))) / vec4<f32>(1023.0f, 1023.0f, 1023.0f, 3.0f))).x;
+    vec2_unorm10_10_10_2 = ((vec4<f32>(((vec4<u32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)]) >> vec4<u32>(0u, 10u, 20u, 30u)) & vec4<u32>(1023u, 1023u, 1023u, 3u))) / vec4<f32>(1023.0f, 1023.0f, 1023.0f, 3.0f))).xy;
+    vec3_unorm10_10_10_2 = ((vec4<f32>(((vec4<u32>(tint_pulling_vertex_buffer_0.tint_vertex_data[(buffer_array_base_0 + 16u)]) >> vec4<u32>(0u, 10u, 20u, 30u)) & vec4<u32>(1023u, 1023u, 1023u, 3u))) / vec4<f32>(1023.0f, 1023.0f, 1023.0f, 3.0f))).xyz;
   }
   return vec4<f32>(0.0, 0.0, 0.0, 1.0);
 }
@@ -2166,19 +2183,21 @@
         {{256,
           VertexStepMode::kVertex,
           {
-              {VertexFormat::kUnorm8x2, 64, 0},   {VertexFormat::kUnorm8x4, 64, 1},
-              {VertexFormat::kUnorm8x4, 64, 2},   {VertexFormat::kUnorm8x4, 64, 3},
-              {VertexFormat::kSnorm8x2, 64, 4},   {VertexFormat::kSnorm8x4, 64, 5},
-              {VertexFormat::kSnorm8x4, 64, 6},   {VertexFormat::kSnorm8x4, 64, 7},
-              {VertexFormat::kUnorm16x2, 64, 8},  {VertexFormat::kUnorm16x4, 64, 9},
-              {VertexFormat::kUnorm16x4, 64, 10}, {VertexFormat::kUnorm16x4, 64, 11},
-              {VertexFormat::kSnorm16x2, 64, 12}, {VertexFormat::kSnorm16x4, 64, 13},
-              {VertexFormat::kSnorm16x4, 64, 14}, {VertexFormat::kSnorm16x4, 64, 15},
-              {VertexFormat::kFloat16x2, 64, 16}, {VertexFormat::kFloat16x4, 64, 17},
-              {VertexFormat::kFloat16x4, 64, 18}, {VertexFormat::kFloat16x4, 64, 19},
-              {VertexFormat::kFloat32x2, 64, 20}, {VertexFormat::kFloat32x3, 64, 21},
-              {VertexFormat::kFloat32x3, 64, 22}, {VertexFormat::kFloat32x4, 64, 23},
-              {VertexFormat::kFloat32x4, 64, 24}, {VertexFormat::kFloat32x4, 64, 25},
+              {VertexFormat::kUnorm8x2, 64, 0},         {VertexFormat::kUnorm8x4, 64, 1},
+              {VertexFormat::kUnorm8x4, 64, 2},         {VertexFormat::kUnorm8x4, 64, 3},
+              {VertexFormat::kSnorm8x2, 64, 4},         {VertexFormat::kSnorm8x4, 64, 5},
+              {VertexFormat::kSnorm8x4, 64, 6},         {VertexFormat::kSnorm8x4, 64, 7},
+              {VertexFormat::kUnorm16x2, 64, 8},        {VertexFormat::kUnorm16x4, 64, 9},
+              {VertexFormat::kUnorm16x4, 64, 10},       {VertexFormat::kUnorm16x4, 64, 11},
+              {VertexFormat::kSnorm16x2, 64, 12},       {VertexFormat::kSnorm16x4, 64, 13},
+              {VertexFormat::kSnorm16x4, 64, 14},       {VertexFormat::kSnorm16x4, 64, 15},
+              {VertexFormat::kFloat16x2, 64, 16},       {VertexFormat::kFloat16x4, 64, 17},
+              {VertexFormat::kFloat16x4, 64, 18},       {VertexFormat::kFloat16x4, 64, 19},
+              {VertexFormat::kFloat32x2, 64, 20},       {VertexFormat::kFloat32x3, 64, 21},
+              {VertexFormat::kFloat32x3, 64, 22},       {VertexFormat::kFloat32x4, 64, 23},
+              {VertexFormat::kFloat32x4, 64, 24},       {VertexFormat::kFloat32x4, 64, 25},
+              {VertexFormat::kUnorm10_10_10_2, 64, 26}, {VertexFormat::kUnorm10_10_10_2, 64, 27},
+              {VertexFormat::kUnorm10_10_10_2, 64, 28},
           }}}};
 
     DataMap data;