Vulkan: use one barrier if we can for non-pass operations

When we do transition barriers for a texture view outside of a pass
(say copy, clear, initialization), if the texture view can cover all
subresources, and its old usages across all subresources are the
same, then we can use one transition barrier. We don't need to use
separate barrier per each subresource.

This patch can reduce barrier we delivered, and improve performance
for particular situations.

Bug: dawn:441

Change-Id: I2ae9b39793915553cbaaceacaf58bf87c9ba3bc6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/23129
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Yunchao He <yunchao.he@intel.com>
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index 90a6d95..6698ac0 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -807,22 +807,41 @@
         const Format& format = GetFormat();
 
         wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None;
+        uint32_t subresourceCount = GetSubresourceCount();
 
         // This transitions assume it is a 2D texture
         ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
 
-        for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount; ++layer) {
-            for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
-                uint32_t index = GetSubresourceIndex(level, layer);
+        // If the usages transitions can cover all subresources, and old usages of all subresources
+        // are the same, then we can use one barrier to do state transition for all subresources.
+        // Note that if the texture has only one mip level and one array slice, it will fall into
+        // this category.
+        bool isAllSubresourcesCovered = levelCount * layerCount == subresourceCount;
+        if (mSameLastUsagesAcrossSubresources && isAllSubresourcesCovered) {
+            ASSERT(baseMipLevel == 0 && baseArrayLayer == 0);
+            if (CanReuseWithoutBarrier(mSubresourceLastUsages[0], usage)) {
+                return;
+            }
+            barriers.push_back(BuildMemoryBarrier(format, mHandle, mSubresourceLastUsages[0], usage,
+                                                  0, levelCount, 0, layerCount));
+            allLastUsages = mSubresourceLastUsages[0];
+            for (uint32_t i = 0; i < subresourceCount; ++i) {
+                mSubresourceLastUsages[i] = usage;
+            }
+        } else {
+            for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount; ++layer) {
+                for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
+                    uint32_t index = GetSubresourceIndex(level, layer);
 
-                if (CanReuseWithoutBarrier(mSubresourceLastUsages[index], usage)) {
-                    continue;
+                    if (CanReuseWithoutBarrier(mSubresourceLastUsages[index], usage)) {
+                        continue;
+                    }
+
+                    barriers.push_back(BuildMemoryBarrier(
+                        format, mHandle, mSubresourceLastUsages[index], usage, level, 1, layer, 1));
+                    allLastUsages |= mSubresourceLastUsages[index];
+                    mSubresourceLastUsages[index] = usage;
                 }
-
-                barriers.push_back(BuildMemoryBarrier(
-                    format, mHandle, mSubresourceLastUsages[index], usage, level, 1, layer, 1));
-                allLastUsages |= mSubresourceLastUsages[index];
-                mSubresourceLastUsages[index] = usage;
             }
         }
 
@@ -836,9 +855,7 @@
             ->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0,
                                     nullptr, 0, nullptr, barriers.size(), barriers.data());
 
-        // TODO(yunchao.he@intel.com): do the optimization to combine all barriers into a single one
-        // for a texture if possible.
-        mSameLastUsagesAcrossSubresources = levelCount * layerCount == GetSubresourceCount();
+        mSameLastUsagesAcrossSubresources = isAllSubresourcesCovered;
     }
 
     MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext,