Fix BGRA8Unorm storage texture validation

Move this validation from shader module compilation time
to bind group layout validation.

Bug: dawn:2464
Change-Id: I222aba74f4e09a0a946150645003e1c8abb731e5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/178923
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Shrek Shao <shrekshao@google.com>
diff --git a/src/dawn/native/ShaderModule.cpp b/src/dawn/native/ShaderModule.cpp
index f7a4620..4e24341 100644
--- a/src/dawn/native/ShaderModule.cpp
+++ b/src/dawn/native/ShaderModule.cpp
@@ -894,11 +894,6 @@
                 bindingInfo.viewDimension =
                     TintTextureDimensionToTextureViewDimension(resource.dim);
 
-                DAWN_INVALID_IF(bindingInfo.format == wgpu::TextureFormat::BGRA8Unorm &&
-                                    !device->HasFeature(Feature::BGRA8UnormStorage),
-                                "BGRA8Unorm storage textures are not supported if optional feature "
-                                "bgra8unorm-storage is not supported.");
-
                 info.bindingInfo = bindingInfo;
                 break;
             }
diff --git a/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp b/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
index e422e85..39f02a9 100644
--- a/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
@@ -257,61 +257,125 @@
 // Validate it is an error to declare a read-only or write-only storage texture in shaders with any
 // format that doesn't support TextureUsage::StorageBinding texture usages.
 TEST_F(StorageTextureValidationTests, StorageTextureFormatInShaders) {
-    // Not include RGBA8UnormSrgb, BGRA8UnormSrgb because they are neither related to any SPIR-V
-    // Image Formats nor WGSL texture formats.
-    constexpr std::array<wgpu::TextureFormat, 34> kWGPUTextureFormatSupportedAsSPIRVImageFormats = {
-        wgpu::TextureFormat::R32Uint,       wgpu::TextureFormat::R32Sint,
-        wgpu::TextureFormat::R32Float,      wgpu::TextureFormat::RGBA8Unorm,
-        wgpu::TextureFormat::RGBA8Snorm,    wgpu::TextureFormat::RGBA8Uint,
-        wgpu::TextureFormat::RGBA8Sint,     wgpu::TextureFormat::RG32Uint,
-        wgpu::TextureFormat::RG32Sint,      wgpu::TextureFormat::RG32Float,
-        wgpu::TextureFormat::RGBA16Uint,    wgpu::TextureFormat::RGBA16Sint,
-        wgpu::TextureFormat::RGBA16Float,   wgpu::TextureFormat::RGBA32Uint,
-        wgpu::TextureFormat::RGBA32Sint,    wgpu::TextureFormat::RGBA32Float,
-        wgpu::TextureFormat::R8Unorm,       wgpu::TextureFormat::R8Snorm,
-        wgpu::TextureFormat::R8Uint,        wgpu::TextureFormat::R8Sint,
-        wgpu::TextureFormat::R16Uint,       wgpu::TextureFormat::R16Sint,
-        wgpu::TextureFormat::R16Float,      wgpu::TextureFormat::RG8Unorm,
-        wgpu::TextureFormat::RG8Snorm,      wgpu::TextureFormat::RG8Uint,
-        wgpu::TextureFormat::RG8Sint,       wgpu::TextureFormat::RG16Uint,
-        wgpu::TextureFormat::RG16Sint,      wgpu::TextureFormat::RG16Float,
-        wgpu::TextureFormat::RGB10A2Uint,   wgpu::TextureFormat::RGB10A2Unorm,
-        wgpu::TextureFormat::RG11B10Ufloat, wgpu::TextureFormat::BGRA8Unorm};
+    // Only include valid WGSL texel format tokens.
+    constexpr std::array<wgpu::TextureFormat, 17> kWGPUTextureFormatSupportedAsSPIRVImageFormats = {
+        wgpu::TextureFormat::R32Uint, wgpu::TextureFormat::R32Sint, wgpu::TextureFormat::R32Float,
+        wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8Snorm,
+        wgpu::TextureFormat::RGBA8Uint, wgpu::TextureFormat::RGBA8Sint,
+        wgpu::TextureFormat::RG32Uint, wgpu::TextureFormat::RG32Sint,
+        wgpu::TextureFormat::RG32Float, wgpu::TextureFormat::RGBA16Uint,
+        wgpu::TextureFormat::RGBA16Sint, wgpu::TextureFormat::RGBA16Float,
+        wgpu::TextureFormat::RGBA32Uint, wgpu::TextureFormat::RGBA32Sint,
+        wgpu::TextureFormat::RGBA32Float,
+        // Although BGRA8Unorm stoarge capability depends on Feature::BGRA8UnormStorage,
+        // It is always a valid storage format token in WGSL.
+        wgpu::TextureFormat::BGRA8Unorm};
 
     for (wgpu::StorageTextureAccess storageTextureBindingType : kSupportedStorageTextureAccess) {
         for (wgpu::TextureFormat format : kWGPUTextureFormatSupportedAsSPIRVImageFormats) {
             std::string computeShader =
                 CreateComputeShaderWithStorageTexture(storageTextureBindingType, format);
-            if (utils::TextureFormatSupportsStorageTexture(format, device,
-                                                           UseCompatibilityMode())) {
-                utils::CreateShaderModule(device, computeShader.c_str());
-            } else {
-                ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, computeShader.c_str()));
-            }
+            utils::CreateShaderModule(device, computeShader.c_str());
         }
     }
 }
 
-class BGRA8UnormStorageTextureInShaderValidationTests : public StorageTextureValidationTests {
+class BGRA8UnormStorageTextureValidationTests
+    : public StorageTextureValidationTests,
+      // Bool param indicates whether requires the BGRA8UnormStorage feature or not.
+      public ::testing::WithParamInterface<bool> {
   protected:
     WGPUDevice CreateTestDevice(native::Adapter dawnAdapter,
                                 wgpu::DeviceDescriptor descriptor) override {
         wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::BGRA8UnormStorage};
-        descriptor.requiredFeatures = requiredFeatures;
-        descriptor.requiredFeatureCount = 1;
+        if (GetParam()) {
+            descriptor.requiredFeatures = requiredFeatures;
+            descriptor.requiredFeatureCount = 1;
+        }
         return dawnAdapter.CreateDevice(&descriptor);
     }
 };
 
-// Test that 'bgra8unorm' is a valid storage texture format if 'bgra8unorm-storage' is enabled.
-TEST_F(BGRA8UnormStorageTextureInShaderValidationTests, BGRA8UnormAsStorageInShader) {
+// Test that bgra8unorm storage texture at create bind group layout.
+TEST_P(BGRA8UnormStorageTextureValidationTests, CreateBindGroupLayout) {
+    for (wgpu::StorageTextureAccess storageTextureBindingType : kSupportedStorageTextureAccess) {
+        wgpu::BindGroupLayoutEntry entry = utils::BindingLayoutEntryInitializationHelper(
+            0, wgpu::ShaderStage::Compute, storageTextureBindingType,
+            wgpu::TextureFormat::BGRA8Unorm);
+        wgpu::BindGroupLayoutDescriptor descriptor;
+        descriptor.entryCount = 1;
+        descriptor.entries = &entry;
+
+        if (GetParam()) {
+            device.CreateBindGroupLayout(&descriptor);
+        } else {
+            ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&descriptor));
+        }
+    }
+}
+
+// Test that bgra8unorm storage texture at create pipeline with implicit pipeline layout.
+TEST_P(BGRA8UnormStorageTextureValidationTests, CreatePipelineWithImplicitLayout) {
     for (wgpu::StorageTextureAccess storageTextureBindingType : kSupportedStorageTextureAccess) {
         std::string computeShader = CreateComputeShaderWithStorageTexture(
             storageTextureBindingType, wgpu::TextureFormat::BGRA8Unorm);
         utils::CreateShaderModule(device, computeShader.c_str());
+
+        wgpu::ComputePipelineDescriptor descriptor;
+        descriptor.layout = nullptr;
+        descriptor.compute.module = utils::CreateShaderModule(device, computeShader.c_str());
+
+        if (GetParam()) {
+            wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&descriptor);
+            pipeline.GetBindGroupLayout(0);
+        } else {
+            ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&descriptor));
+        }
     }
 }
 
+// Test that bgra8unorm storage texture at create pipeline with explicit pipeline layout.
+TEST_P(BGRA8UnormStorageTextureValidationTests, CreatePipelineWithExplicitLayout) {
+    // Skip if wgpu::FeatureName::BGRA8UnormStorage is not required as it will fail at create bind
+    // group layout.
+    DAWN_SKIP_TEST_IF(!GetParam());
+    for (wgpu::StorageTextureAccess storageTextureBindingType : kSupportedStorageTextureAccess) {
+        wgpu::BindGroupLayout bindGroupLayout;
+        {
+            wgpu::BindGroupLayoutEntry entry = utils::BindingLayoutEntryInitializationHelper(
+                0, wgpu::ShaderStage::Compute, storageTextureBindingType,
+                wgpu::TextureFormat::BGRA8Unorm);
+            wgpu::BindGroupLayoutDescriptor descriptor;
+            descriptor.entryCount = 1;
+            descriptor.entries = &entry;
+            bindGroupLayout = device.CreateBindGroupLayout(&descriptor);
+        }
+
+        wgpu::PipelineLayout pipelineLayout;
+        {
+            wgpu::PipelineLayoutDescriptor descriptor;
+            descriptor.bindGroupLayoutCount = 1;
+            descriptor.bindGroupLayouts = &bindGroupLayout;
+            pipelineLayout = device.CreatePipelineLayout(&descriptor);
+        }
+
+        std::string computeShader = CreateComputeShaderWithStorageTexture(
+            storageTextureBindingType, wgpu::TextureFormat::BGRA8Unorm);
+        wgpu::ComputePipelineDescriptor descriptor;
+        descriptor.layout = pipelineLayout;
+        descriptor.compute.module = utils::CreateShaderModule(device, computeShader.c_str());
+
+        wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&descriptor);
+        pipeline.GetBindGroupLayout(0);
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    BGRA8UnormStorageTextureValidationTests,
+    // Bool param indicates whether requires the BGRA8UnormStorage feature or not.
+    ::testing::ValuesIn({false, true}));
+
 // Verify that declaring a storage texture format that is not supported in WebGPU causes validation
 // error.
 TEST_F(StorageTextureValidationTests, UnsupportedWGSLStorageTextureFormat) {