Test TextureComponentType and TextureViewDimension compatibility

The TextureComponentType and TextureViewDimension of resources in
the shader must match those in the bind group layout.

This patch also extracts the texture view dimension from the SPIRV.

Bug: dawn:202
Change-Id: Ie155f17109f4f1b5d9b386d757062ae5ffe5da67
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13861
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp
index dbe2372..2879b51 100644
--- a/src/dawn_native/ShaderModule.cpp
+++ b/src/dawn_native/ShaderModule.cpp
@@ -41,6 +41,31 @@
                     return Format::Other;
             }
         }
+
+        wgpu::TextureViewDimension SpirvDimToTextureViewDimension(spv::Dim dim, bool arrayed) {
+            switch (dim) {
+                case spv::Dim::Dim1D:
+                    return wgpu::TextureViewDimension::e1D;
+                case spv::Dim::Dim2D:
+                    if (arrayed) {
+                        return wgpu::TextureViewDimension::e2DArray;
+                    } else {
+                        return wgpu::TextureViewDimension::e2D;
+                    }
+                case spv::Dim::Dim3D:
+                    return wgpu::TextureViewDimension::e3D;
+                case spv::Dim::DimCube:
+                    if (arrayed) {
+                        return wgpu::TextureViewDimension::CubeArray;
+                    } else {
+                        return wgpu::TextureViewDimension::Cube;
+                    }
+                default:
+                    UNREACHABLE();
+                    return wgpu::TextureViewDimension::Undefined;
+            }
+        }
+
     }  // anonymous namespace
 
     MaybeError ValidateShaderModuleDescriptor(DeviceBase*,
@@ -156,9 +181,14 @@
                 info.base_type_id = resource.base_type_id;
                 switch (bindingType) {
                     case wgpu::BindingType::SampledTexture: {
+                        spirv_cross::SPIRType::ImageType imageType =
+                            compiler.get_type(info.base_type_id).image;
                         spirv_cross::SPIRType::BaseType textureComponentType =
-                            compiler.get_type(compiler.get_type(info.base_type_id).image.type)
-                                .basetype;
+                            compiler.get_type(imageType.type).basetype;
+
+                        info.multisampled = imageType.ms;
+                        info.textureDimension =
+                            SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
                         info.textureComponentType =
                             SpirvCrossBaseTypeToFormatType(textureComponentType);
                         info.type = bindingType;
@@ -322,6 +352,10 @@
                 if (layoutTextureComponentType != moduleInfo.textureComponentType) {
                     return false;
                 }
+
+                if (layoutInfo.textureDimensions[i] != moduleInfo.textureDimension) {
+                    return false;
+                }
             }
         }
 
diff --git a/src/dawn_native/ShaderModule.h b/src/dawn_native/ShaderModule.h
index d0b0d7b..dedde2f 100644
--- a/src/dawn_native/ShaderModule.h
+++ b/src/dawn_native/ShaderModule.h
@@ -51,7 +51,10 @@
             uint32_t id;
             uint32_t base_type_id;
             wgpu::BindingType type;
-            Format::Type textureComponentType;
+            // Match the defaults in BindGroupLayoutDescriptor
+            wgpu::TextureViewDimension textureDimension = wgpu::TextureViewDimension::Undefined;
+            Format::Type textureComponentType = Format::Type::Float;
+            bool multisampled = false;
             bool used = false;
         };
         using ModuleBindingInfo =
diff --git a/src/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/tests/unittests/validation/RenderPipelineValidationTests.cpp
index a215e65..5349a3a 100644
--- a/src/tests/unittests/validation/RenderPipelineValidationTests.cpp
+++ b/src/tests/unittests/validation/RenderPipelineValidationTests.cpp
@@ -360,3 +360,53 @@
         }
     }
 }
+
+// Tests that the texture view dimension in shader must match the bind group layout.
+TEST_F(RenderPipelineValidationTest, TextureViewDimensionCompatibility) {
+    constexpr uint32_t kNumTextureViewDimensions = 6u;
+    std::array<const char*, kNumTextureViewDimensions> kTextureKeywords = {{
+        "texture1D",
+        "texture2D",
+        "texture2DArray",
+        "textureCube",
+        "textureCubeArray",
+        "texture3D",
+    }};
+
+    std::array<wgpu::TextureViewDimension, kNumTextureViewDimensions> kTextureViewDimensions = {{
+        wgpu::TextureViewDimension::e1D,
+        wgpu::TextureViewDimension::e2D,
+        wgpu::TextureViewDimension::e2DArray,
+        wgpu::TextureViewDimension::Cube,
+        wgpu::TextureViewDimension::CubeArray,
+        wgpu::TextureViewDimension::e3D,
+    }};
+
+    for (size_t i = 0; i < kNumTextureViewDimensions; ++i) {
+        for (size_t j = 0; j < kNumTextureViewDimensions; ++j) {
+            utils::ComboRenderPipelineDescriptor descriptor(device);
+            descriptor.vertexStage.module = vsModule;
+
+            std::ostringstream stream;
+            stream << R"(
+                #version 450
+                layout(set = 0, binding = 0) uniform )"
+                   << kTextureKeywords[i] << R"( tex;
+                void main() {
+                })";
+            descriptor.cFragmentStage.module = utils::CreateShaderModule(
+                device, utils::SingleShaderStage::Fragment, stream.str().c_str());
+
+            wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
+                device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false,
+                          false, kTextureViewDimensions[j], wgpu::TextureComponentType::Float}});
+            descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
+
+            if (i == j) {
+                device.CreateRenderPipeline(&descriptor);
+            } else {
+                ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor));
+            }
+        }
+    }
+}