Implement the optional feature "bgra8unorm-storage" on Vulkan

This patch implements the optional feature "bgra8unorm-storage" on
Vulkan. As SPIR-V doesn't support 'bgra8' as a valid image format,
we have to create an image view with RGBA8Unorm format on the
BGRA8Unorm texture when we want to use it as a storage texture.

Bug: dawn:1641
Test: dawn_end2end_tests
Change-Id: I4aeea96ae872fe4e6367c535afb6ab896b952453
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/118021
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/vulkan/AdapterVk.cpp b/src/dawn/native/vulkan/AdapterVk.cpp
index ee5c15f..1d7822c 100644
--- a/src/dawn/native/vulkan/AdapterVk.cpp
+++ b/src/dawn/native/vulkan/AdapterVk.cpp
@@ -240,16 +240,23 @@
         mSupportedFeatures.EnableFeature(Feature::DepthClipControl);
     }
 
-    VkFormatProperties properties;
+    VkFormatProperties rg11b10Properties;
     mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
-        mPhysicalDevice, VK_FORMAT_B10G11R11_UFLOAT_PACK32, &properties);
+        mPhysicalDevice, VK_FORMAT_B10G11R11_UFLOAT_PACK32, &rg11b10Properties);
 
     if (IsSubset(static_cast<VkFormatFeatureFlags>(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
                                                    VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT),
-                 properties.optimalTilingFeatures)) {
+                 rg11b10Properties.optimalTilingFeatures)) {
         mSupportedFeatures.EnableFeature(Feature::RG11B10UfloatRenderable);
     }
 
+    VkFormatProperties bgra8unormProperties;
+    mVulkanInstance->GetFunctions().GetPhysicalDeviceFormatProperties(
+        mPhysicalDevice, VK_FORMAT_B8G8R8A8_UNORM, &bgra8unormProperties);
+    if (bgra8unormProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) {
+        mSupportedFeatures.EnableFeature(Feature::BGRA8UnormStorage);
+    }
+
 #if defined(DAWN_USE_SYNC_FDS)
     // TODO(chromium:1258986): Precisely enable the feature by querying the device's format
     // features.
diff --git a/src/dawn/native/vulkan/BindGroupVk.cpp b/src/dawn/native/vulkan/BindGroupVk.cpp
index 0eafbdb..e4547fc 100644
--- a/src/dawn/native/vulkan/BindGroupVk.cpp
+++ b/src/dawn/native/vulkan/BindGroupVk.cpp
@@ -113,7 +113,12 @@
             case BindingInfoType::StorageTexture: {
                 TextureView* view = ToBackend(GetBindingAsTextureView(bindingIndex));
 
-                VkImageView handle = view->GetHandle();
+                VkImageView handle = VK_NULL_HANDLE;
+                if (view->GetTexture()->GetFormat().format == wgpu::TextureFormat::BGRA8Unorm) {
+                    handle = view->GetHandleForBGRA8UnormStorage();
+                } else {
+                    handle = view->GetHandle();
+                }
                 if (handle == VK_NULL_HANDLE) {
                     // The Texture was destroyed before the TextureView was created.
                     // Skip this descriptor write since it would be
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index 6bec0a9..13e0b28 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -684,7 +684,17 @@
 
     VkImageFormatListCreateInfo imageFormatListInfo = {};
     std::vector<VkFormat> viewFormats;
-    if (GetViewFormats().any()) {
+    bool requiresCreateMutableFormatBit = GetViewFormats().any();
+    // As current SPIR-V SPEC doesn't support 'bgra8' as a valid image format, to support the
+    // STORAGE usage of BGRA8Unorm we have to create an RGBA8Unorm image view on the BGRA8Unorm
+    // storage texture and polyfill it as RGBA8Unorm in Tint. See http://crbug.com/dawn/1641 for
+    // more details.
+    if (createInfo.format == VK_FORMAT_B8G8R8A8_UNORM &&
+        createInfo.usage & VK_IMAGE_USAGE_STORAGE_BIT) {
+        viewFormats.push_back(VK_FORMAT_R8G8B8A8_UNORM);
+        requiresCreateMutableFormatBit = true;
+    }
+    if (requiresCreateMutableFormatBit) {
         createInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
         if (device->GetDeviceInfo().HasExt(DeviceExt::ImageFormatList)) {
             createInfoChain.Add(&imageFormatListInfo,
@@ -1423,6 +1433,16 @@
         device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
         "CreateImageView"));
 
+    // We should create an image view with format RGBA8Unorm on the BGRA8Unorm texture when the
+    // texture is used as storage texture. See http://crbug.com/dawn/1641 for more details.
+    if (createInfo.format == VK_FORMAT_B8G8R8A8_UNORM &&
+        (GetTexture()->GetInternalUsage() & wgpu::TextureUsage::StorageBinding)) {
+        createInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
+        DAWN_TRY(CheckVkSuccess(device->fn.CreateImageView(device->GetVkDevice(), &createInfo,
+                                                           nullptr, &*mHandleForBGRA8UnormStorage),
+                                "CreateImageView for BGRA8Unorm storage"));
+    }
+
     SetLabelImpl();
 
     return {};
@@ -1437,12 +1457,21 @@
         device->GetFencedDeleter()->DeleteWhenUnused(mHandle);
         mHandle = VK_NULL_HANDLE;
     }
+
+    if (mHandleForBGRA8UnormStorage != VK_NULL_HANDLE) {
+        device->GetFencedDeleter()->DeleteWhenUnused(mHandleForBGRA8UnormStorage);
+        mHandleForBGRA8UnormStorage = VK_NULL_HANDLE;
+    }
 }
 
 VkImageView TextureView::GetHandle() const {
     return mHandle;
 }
 
+VkImageView TextureView::GetHandleForBGRA8UnormStorage() const {
+    return mHandleForBGRA8UnormStorage;
+}
+
 void TextureView::SetLabelImpl() {
     SetDebugName(ToBackend(GetDevice()), mHandle, "Dawn_TextureView", GetLabel());
 }
diff --git a/src/dawn/native/vulkan/TextureVk.h b/src/dawn/native/vulkan/TextureVk.h
index b0a2c00..48017bf 100644
--- a/src/dawn/native/vulkan/TextureVk.h
+++ b/src/dawn/native/vulkan/TextureVk.h
@@ -193,6 +193,7 @@
     static ResultOrError<Ref<TextureView>> Create(TextureBase* texture,
                                                   const TextureViewDescriptor* descriptor);
     VkImageView GetHandle() const;
+    VkImageView GetHandleForBGRA8UnormStorage() const;
 
   private:
     ~TextureView() override;
@@ -204,6 +205,7 @@
     void SetLabelImpl() override;
 
     VkImageView mHandle = VK_NULL_HANDLE;
+    VkImageView mHandleForBGRA8UnormStorage = VK_NULL_HANDLE;
 };
 
 }  // namespace dawn::native::vulkan