Enable the optional feature bgra8unorm-storage on Metal

This patch adds the support of the optional feature "bgra8unorm-storage"
and enable it on  Metal.

Bug: dawn:1641
Test: dawn_end2end_tests
Change-Id: Id58cefd8735f46b8d1807376ebcfada10df2890e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/111380
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/dawn.json b/dawn.json
index 86dc7dc..f4ec78c 100644
--- a/dawn.json
+++ b/dawn.json
@@ -1450,6 +1450,7 @@
             {"value": 8, "name": "indirect first instance"},
             {"value": 9, "name": "shader f16"},
             {"value": 10, "name": "RG11B10 ufloat renderable"},
+            {"value": 11, "name": "BGRA8 unorm storage"},
             {"value": 1001, "name": "dawn shader float 16", "tags": ["dawn"]},
             {"value": 1002, "name": "dawn internal usages", "tags": ["dawn"]},
             {"value": 1003, "name": "dawn multi planar formats", "tags": ["dawn"]},
diff --git a/src/dawn/native/Features.cpp b/src/dawn/native/Features.cpp
index a94c6b4..f1098bc 100644
--- a/src/dawn/native/Features.cpp
+++ b/src/dawn/native/Features.cpp
@@ -79,6 +79,9 @@
       "Allows the RENDER_ATTACHMENT usage on textures with format \"rg11b10ufloat\", and also "
       "allows textures of that format to be multisampled.",
       "https://bugs.chromium.org/p/dawn/issues/detail?id=1518", FeatureInfo::FeatureState::Stable}},
+    {Feature::BGRA8UnormStorage,
+     {"bgra8unorm-storage", "Allows the STORAGE usage on textures with format \"bgra8unorm\".",
+      "https://bugs.chromium.org/p/dawn/issues/detail?id=1591", FeatureInfo::FeatureState::Stable}},
     {Feature::DawnInternalUsages,
      {"dawn-internal-usages",
       "Add internal usages to resources to affect how the texture is allocated, but not "
@@ -134,6 +137,8 @@
             return Feature::ShaderF16;
         case wgpu::FeatureName::RG11B10UfloatRenderable:
             return Feature::RG11B10UfloatRenderable;
+        case wgpu::FeatureName::BGRA8UnormStorage:
+            return Feature::BGRA8UnormStorage;
     }
     return Feature::InvalidEnum;
 }
@@ -170,6 +175,8 @@
             return wgpu::FeatureName::ShaderF16;
         case Feature::RG11B10UfloatRenderable:
             return wgpu::FeatureName::RG11B10UfloatRenderable;
+        case Feature::BGRA8UnormStorage:
+            return wgpu::FeatureName::BGRA8UnormStorage;
 
         case Feature::EnumCount:
             break;
diff --git a/src/dawn/native/Features.h b/src/dawn/native/Features.h
index 2a35963..064d55d 100644
--- a/src/dawn/native/Features.h
+++ b/src/dawn/native/Features.h
@@ -39,6 +39,7 @@
     IndirectFirstInstance,
     ShaderF16,
     RG11B10UfloatRenderable,
+    BGRA8UnormStorage,
 
     // Dawn-specific
     DawnInternalUsages,
diff --git a/src/dawn/native/Format.cpp b/src/dawn/native/Format.cpp
index bfffd97..6f905ee 100644
--- a/src/dawn/native/Format.cpp
+++ b/src/dawn/native/Format.cpp
@@ -368,7 +368,9 @@
         AddColorFormat(wgpu::TextureFormat::RGBA8Snorm, false, true, false, false, 4, kAnyFloat, 4);
         AddColorFormat(wgpu::TextureFormat::RGBA8Uint, true, true, true, false, 4, SampleTypeBit::Uint, 4, 4, 1);
         AddColorFormat(wgpu::TextureFormat::RGBA8Sint, true, true, true, false, 4, SampleTypeBit::Sint, 4, 4, 1);
-        AddColorFormat(wgpu::TextureFormat::BGRA8Unorm, true, false, true, true, 4, kAnyFloat, 4, 8, 1);
+
+        bool BGRA8UnormSupportsStorageUsage = device->HasFeature(Feature::BGRA8UnormStorage);
+        AddColorFormat(wgpu::TextureFormat::BGRA8Unorm, true, BGRA8UnormSupportsStorageUsage, true, true, 4, kAnyFloat, 4, 8, 1);
         AddColorFormat(wgpu::TextureFormat::BGRA8UnormSrgb, true, false, true, true, 4, kAnyFloat, 4, 8, 1, wgpu::TextureFormat::BGRA8Unorm);
         AddColorFormat(wgpu::TextureFormat::RGB10A2Unorm, true, false, true, true, 4, kAnyFloat, 4, 8, 4);
 
diff --git a/src/dawn/native/ShaderModule.cpp b/src/dawn/native/ShaderModule.cpp
index 7db19a2..8b1fadf 100644
--- a/src/dawn/native/ShaderModule.cpp
+++ b/src/dawn/native/ShaderModule.cpp
@@ -730,8 +730,10 @@
                 info.storageTexture.viewDimension =
                     TintTextureDimensionToTextureViewDimension(resource.dim);
 
-                DAWN_INVALID_IF(info.storageTexture.format == wgpu::TextureFormat::BGRA8Unorm,
-                                "BGRA8Unorm storage textures are not yet supported.");
+                DAWN_INVALID_IF(info.storageTexture.format == wgpu::TextureFormat::BGRA8Unorm &&
+                                    !device->HasFeature(Feature::BGRA8UnormStorage),
+                                "BGRA8Unorm storage textures are not supported if optional feature "
+                                "bgra8unorm-storage is not supported.");
 
                 break;
             case BindingInfoType::ExternalTexture:
diff --git a/src/dawn/native/metal/BackendMTL.mm b/src/dawn/native/metal/BackendMTL.mm
index 5339731..c2811c1 100644
--- a/src/dawn/native/metal/BackendMTL.mm
+++ b/src/dawn/native/metal/BackendMTL.mm
@@ -512,6 +512,7 @@
         mSupportedFeatures.EnableFeature(Feature::IndirectFirstInstance);
         mSupportedFeatures.EnableFeature(Feature::ShaderF16);
         mSupportedFeatures.EnableFeature(Feature::RG11B10UfloatRenderable);
+        mSupportedFeatures.EnableFeature(Feature::BGRA8UnormStorage);
     }
 
     void InitializeVendorArchitectureImpl() override {
diff --git a/src/dawn/node/binding/Converter.cpp b/src/dawn/node/binding/Converter.cpp
index 524fdca..e645c44 100644
--- a/src/dawn/node/binding/Converter.cpp
+++ b/src/dawn/node/binding/Converter.cpp
@@ -1652,8 +1652,8 @@
             out = wgpu::FeatureName::RG11B10UfloatRenderable;
             return true;
         case interop::GPUFeatureName::kBgra8UnormStorage:
-            // TODO(dawn:1123) Add support for these extensions when possible.
-            return false;
+            out = wgpu::FeatureName::BGRA8UnormStorage;
+            return true;
     }
     return false;
 }
@@ -1687,6 +1687,9 @@
         case wgpu::FeatureName::RG11B10UfloatRenderable:
             out = interop::GPUFeatureName::kRg11B10UfloatRenderable;
             return true;
+        case wgpu::FeatureName::BGRA8UnormStorage:
+            out = interop::GPUFeatureName::kBgra8UnormStorage;
+            return true;
 
         case wgpu::FeatureName::PipelineStatisticsQuery:
         case wgpu::FeatureName::DawnShaderFloat16:
diff --git a/src/dawn/tests/end2end/StorageTextureTests.cpp b/src/dawn/tests/end2end/StorageTextureTests.cpp
index 648ddb5..0403632 100644
--- a/src/dawn/tests/end2end/StorageTextureTests.cpp
+++ b/src/dawn/tests/end2end/StorageTextureTests.cpp
@@ -148,6 +148,13 @@
                 break;
             }
 
+            case wgpu::TextureFormat::BGRA8Unorm: {
+                utils::RGBA8* valuePtr = static_cast<utils::RGBA8*>(pixelValuePtr);
+                *valuePtr =
+                    utils::RGBA8(pixelValue * 3, pixelValue * 2, pixelValue, pixelValue * 4);
+                break;
+            }
+
             case wgpu::TextureFormat::RGBA8Snorm:
             case wgpu::TextureFormat::RGBA8Sint: {
                 int8_t* valuePtr = static_cast<int8_t*>(pixelValuePtr);
@@ -237,6 +244,7 @@
 
             // normalized signed/unsigned integer formats
             case wgpu::TextureFormat::RGBA8Unorm:
+            case wgpu::TextureFormat::BGRA8Unorm:
                 return "vec4f(f32(value) / 255.0, f32(value) / 255.0 * 2.0, "
                        "f32(value) / 255.0 * 3.0, f32(value) / 255.0 * 4.0)";
 
@@ -865,6 +873,70 @@
                       OpenGLESBackend(),
                       VulkanBackend());
 
+class BGRA8UnormStorageTextureTests : public StorageTextureTests {
+  public:
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        if (SupportsFeatures({wgpu::FeatureName::BGRA8UnormStorage})) {
+            mIsBGRA8UnormStorageSupported = true;
+            return {wgpu::FeatureName::BGRA8UnormStorage};
+        } else {
+            mIsBGRA8UnormStorageSupported = false;
+            return {};
+        }
+    }
+
+    bool IsBGRA8UnormStorageSupported() { return mIsBGRA8UnormStorageSupported; }
+
+  private:
+    bool mIsBGRA8UnormStorageSupported = false;
+};
+
+// Test that BGRA8Unorm is supported to be used as storage texture in compute shaders when the
+// optional feature 'bgra8unorm-storage' is supported.
+TEST_P(BGRA8UnormStorageTextureTests, WriteonlyStorageTextureInComputeShader) {
+    DAWN_TEST_UNSUPPORTED_IF(!IsBGRA8UnormStorageSupported());
+
+    constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::BGRA8Unorm;
+    wgpu::Texture writeonlyStorageTexture =
+        CreateTexture(kFormat, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc,
+                      {kWidth, kHeight});
+
+    // Write the expected pixel values into the write-only storage texture.
+    const std::string computeShader = CommonWriteOnlyTestCode("compute", kFormat);
+    WriteIntoStorageTextureInComputePass(writeonlyStorageTexture, computeShader.c_str());
+
+    // Verify the pixel data in the write-only storage texture is expected.
+    CheckOutputStorageTexture(writeonlyStorageTexture, kFormat, {kWidth, kHeight});
+}
+
+// Test that BGRA8Unorm is supported to be used as storage texture in fragment shaders when the
+// optional feature 'bgra8unorm-storage' is supported.
+TEST_P(BGRA8UnormStorageTextureTests, WriteonlyStorageTextureInFragmentShader) {
+    DAWN_TEST_UNSUPPORTED_IF(!IsBGRA8UnormStorageSupported());
+
+    constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::BGRA8Unorm;
+
+    // Prepare the write-only storage texture.
+    wgpu::Texture writeonlyStorageTexture =
+        CreateTexture(kFormat, wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc,
+                      {kWidth, kHeight});
+
+    // Write the expected pixel values into the write-only storage texture.
+    const std::string fragmentShader = CommonWriteOnlyTestCode("fragment", kFormat);
+    WriteIntoStorageTextureInRenderPass(writeonlyStorageTexture, kSimpleVertexShader,
+                                        fragmentShader.c_str());
+
+    // Verify the pixel data in the write-only storage texture is expected.
+    CheckOutputStorageTexture(writeonlyStorageTexture, kFormat, {kWidth, kHeight});
+}
+
+DAWN_INSTANTIATE_TEST(BGRA8UnormStorageTextureTests,
+                      D3D12Backend(),
+                      MetalBackend(),
+                      OpenGLBackend(),
+                      OpenGLESBackend(),
+                      VulkanBackend());
+
 class StorageTextureZeroInitTests : public StorageTextureTests {
   public:
     static std::vector<uint8_t> GetExpectedData() {
diff --git a/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp b/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
index fee7da0..19fda30 100644
--- a/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/StorageTextureValidationTests.cpp
@@ -233,9 +233,9 @@
 // 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, BGRA8Unorm, BGRA8UnormSrgb because they are not related to any
-    // SPIR-V Image Formats.
-    constexpr std::array<wgpu::TextureFormat, 32> kWGPUTextureFormatSupportedAsSPIRVImageFormats = {
+    // Not include RGBA8UnormSrgb, BGRA8UnormSrgb because they are neither related to any SPIR-V
+    // Image Formats nor WGSL texture formats.
+    constexpr std::array<wgpu::TextureFormat, 33> kWGPUTextureFormatSupportedAsSPIRVImageFormats = {
         wgpu::TextureFormat::R32Uint,      wgpu::TextureFormat::R32Sint,
         wgpu::TextureFormat::R32Float,     wgpu::TextureFormat::RGBA8Unorm,
         wgpu::TextureFormat::RGBA8Snorm,   wgpu::TextureFormat::RGBA8Uint,
@@ -251,7 +251,8 @@
         wgpu::TextureFormat::RG8Snorm,     wgpu::TextureFormat::RG8Uint,
         wgpu::TextureFormat::RG8Sint,      wgpu::TextureFormat::RG16Uint,
         wgpu::TextureFormat::RG16Sint,     wgpu::TextureFormat::RG16Float,
-        wgpu::TextureFormat::RGB10A2Unorm, wgpu::TextureFormat::RG11B10Ufloat};
+        wgpu::TextureFormat::RGB10A2Unorm, wgpu::TextureFormat::RG11B10Ufloat,
+        wgpu::TextureFormat::BGRA8Unorm};
 
     for (wgpu::StorageTextureAccess storageTextureBindingType : kSupportedStorageTextureAccess) {
         for (wgpu::TextureFormat format : kWGPUTextureFormatSupportedAsSPIRVImageFormats) {
@@ -266,6 +267,26 @@
     }
 }
 
+class BGRA8UnormStorageTextureInShaderValidationTests : public StorageTextureValidationTests {
+  protected:
+    WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter) override {
+        wgpu::DeviceDescriptor descriptor;
+        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::BGRA8UnormStorage};
+        descriptor.requiredFeatures = requiredFeatures;
+        descriptor.requiredFeaturesCount = 1;
+        return dawnAdapter.CreateDevice(&descriptor);
+    }
+};
+
+// Test that 'bgra8unorm' is a valid storage texture format if 'bgra8unorm-storage' is enabled.
+TEST_F(BGRA8UnormStorageTextureInShaderValidationTests, BGRA8UnormAsStorageInShader) {
+    for (wgpu::StorageTextureAccess storageTextureBindingType : kSupportedStorageTextureAccess) {
+        std::string computeShader = CreateComputeShaderWithStorageTexture(
+            storageTextureBindingType, wgpu::TextureFormat::BGRA8Unorm);
+        utils::CreateShaderModule(device, computeShader.c_str());
+    }
+}
+
 // Verify that declaring a storage texture format that is not supported in WebGPU causes validation
 // error.
 TEST_F(StorageTextureValidationTests, UnsupportedWGSLStorageTextureFormat) {
@@ -404,6 +425,32 @@
     }
 }
 
+class BGRA8UnormStorageBindGroupLayoutTest : public StorageTextureValidationTests {
+  protected:
+    WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter) override {
+        wgpu::DeviceDescriptor descriptor;
+        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::BGRA8UnormStorage};
+        descriptor.requiredFeatures = requiredFeatures;
+        descriptor.requiredFeaturesCount = 1;
+        return dawnAdapter.CreateDevice(&descriptor);
+    }
+};
+
+// Test that creating a bind group layout with BGRA8Unorm as storage texture format is valid when
+// The optional feature bgra8unorm-storage is supported.
+TEST_F(BGRA8UnormStorageBindGroupLayoutTest, BGRA8UnormAsStorage) {
+    wgpu::BindGroupLayoutEntry bindGroupLayoutBinding;
+    bindGroupLayoutBinding.binding = 0;
+    bindGroupLayoutBinding.visibility = wgpu::ShaderStage::Compute;
+    bindGroupLayoutBinding.storageTexture.format = wgpu::TextureFormat::BGRA8Unorm;
+    bindGroupLayoutBinding.storageTexture.viewDimension = wgpu::TextureViewDimension::e2D;
+
+    for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
+        bindGroupLayoutBinding.storageTexture.access = bindingType;
+        utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding});
+    }
+}
+
 // Verify the storage texture format in the bind group layout must match the declaration in shader.
 TEST_F(StorageTextureValidationTests, BindGroupLayoutStorageTextureFormatMatchesShaderDeclaration) {
     for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
diff --git a/src/dawn/tests/unittests/validation/TextureValidationTests.cpp b/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
index 7665fc3..5cc3d9c 100644
--- a/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/TextureValidationTests.cpp
@@ -908,6 +908,27 @@
     device.CreateTexture(&descriptor);
 }
 
+class BGRA8UnormTextureFormatsValidationTests : public TextureValidationTest {
+  protected:
+    WGPUDevice CreateTestDevice(dawn::native::Adapter dawnAdapter) override {
+        wgpu::DeviceDescriptor descriptor;
+        wgpu::FeatureName requiredFeatures[1] = {wgpu::FeatureName::BGRA8UnormStorage};
+        descriptor.requiredFeatures = requiredFeatures;
+        descriptor.requiredFeaturesCount = 1;
+        return dawnAdapter.CreateDevice(&descriptor);
+    }
+};
+
+// Test that BGRA8Unorm format is valid as storage texture if 'bgra8unorm-storage' is enabled.
+TEST_F(BGRA8UnormTextureFormatsValidationTests, StorageFeature) {
+    wgpu::TextureDescriptor descriptor;
+    descriptor.size = {1, 1, 1};
+    descriptor.usage = wgpu::TextureUsage::StorageBinding;
+
+    descriptor.format = wgpu::TextureFormat::BGRA8Unorm;
+    device.CreateTexture(&descriptor);
+}
+
 static void CheckTextureMatchesDescriptor(const wgpu::Texture& tex,
                                           const wgpu::TextureDescriptor& desc) {
     EXPECT_EQ(desc.size.width, tex.GetWidth());
diff --git a/src/dawn/utils/TextureUtils.cpp b/src/dawn/utils/TextureUtils.cpp
index b630a0c..b4e60fc 100644
--- a/src/dawn/utils/TextureUtils.cpp
+++ b/src/dawn/utils/TextureUtils.cpp
@@ -656,6 +656,8 @@
             return "rgba8uint";
         case wgpu::TextureFormat::RGBA8Sint:
             return "rgba8sint";
+        case wgpu::TextureFormat::BGRA8Unorm:
+            return "bgra8unorm";
         case wgpu::TextureFormat::RGBA16Uint:
             return "rgba16uint";
         case wgpu::TextureFormat::RGBA16Sint:
diff --git a/src/dawn/wire/SupportedFeatures.cpp b/src/dawn/wire/SupportedFeatures.cpp
index 1003dcd..d3b0849 100644
--- a/src/dawn/wire/SupportedFeatures.cpp
+++ b/src/dawn/wire/SupportedFeatures.cpp
@@ -39,6 +39,7 @@
         case WGPUFeatureName_ChromiumExperimentalDp4a:
         case WGPUFeatureName_ShaderF16:
         case WGPUFeatureName_RG11B10UfloatRenderable:
+        case WGPUFeatureName_BGRA8UnormStorage:
             return true;
     }
 
diff --git a/webgpu-cts/expectations.txt b/webgpu-cts/expectations.txt
index ae85de9..1e606bb 100644
--- a/webgpu-cts/expectations.txt
+++ b/webgpu-cts/expectations.txt
@@ -326,6 +326,15 @@
 crbug.com/dawn/1599 [ webgpu-adapter-swiftshader ] webgpu:web_platform,copyToTexture,canvas:color_space_conversion:* [ Failure ]
 
 ################################################################################
+# Failures about the validation on the storage usage of bgra8unorm
+################################################################################
+crbug.com/dawn/1641 webgpu:api,validation,createBindGroupLayout:storage_texture,formats:format="bgra8unorm" [ Failure ]
+crbug.com/dawn/1641 webgpu:api,validation,createTexture:texture_usage:dimension="1d";format="bgra8unorm" [ Failure ]
+crbug.com/dawn/1641 webgpu:api,validation,createTexture:texture_usage:dimension="2d";format="bgra8unorm" [ Failure ]
+crbug.com/dawn/1641 webgpu:api,validation,createTexture:texture_usage:dimension="3d";format="bgra8unorm" [ Failure ]
+crbug.com/dawn/1641 webgpu:api,validation,createTexture:texture_usage:dimension="_undef_";format="bgra8unorm" [ Failure ]
+
+################################################################################
 # untriaged failures
 ################################################################################
 crbug.com/dawn/0000 [ dawn-backend-validation win10 ] webgpu:api,operation,render_pass,resolve:* [ Failure ]