Add validation code for texture subresource usage tracking

This patch also add validation tests for texture subresource tracking
for render pass. Resource usage tracking for compute is per each
dispatch() call, I will add it in next patch.

BUG=dawn:157

Change-Id: I6c4b932e317d66521fa428311e727876d0adf4ea
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/17661
Commit-Queue: Yunchao He <yunchao.he@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index 22dafa1..82c2fc6 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -588,12 +588,11 @@
                     cmd->colorAttachments[i].clearColor =
                         descriptor->colorAttachments[i].clearColor;
 
-                    usageTracker.TextureUsedAs(view->GetTexture(),
-                                               wgpu::TextureUsage::OutputAttachment);
+                    usageTracker.TextureViewUsedAs(view, wgpu::TextureUsage::OutputAttachment);
 
                     if (resolveTarget != nullptr) {
-                        usageTracker.TextureUsedAs(resolveTarget->GetTexture(),
-                                                   wgpu::TextureUsage::OutputAttachment);
+                        usageTracker.TextureViewUsedAs(resolveTarget,
+                                                       wgpu::TextureUsage::OutputAttachment);
                     }
                 }
 
@@ -614,8 +613,7 @@
                     cmd->depthStencilAttachment.stencilStoreOp =
                         descriptor->depthStencilAttachment->stencilStoreOp;
 
-                    usageTracker.TextureUsedAs(view->GetTexture(),
-                                               wgpu::TextureUsage::OutputAttachment);
+                    usageTracker.TextureViewUsedAs(view, wgpu::TextureUsage::OutputAttachment);
                 }
 
                 cmd->width = width;
diff --git a/src/dawn_native/CommandValidation.cpp b/src/dawn_native/CommandValidation.cpp
index 818b503..55b6cec 100644
--- a/src/dawn_native/CommandValidation.cpp
+++ b/src/dawn_native/CommandValidation.cpp
@@ -306,23 +306,36 @@
         }
 
         // Textures can only be used as single-write or multiple read.
-        // TODO(cwallez@chromium.org): implement per-subresource tracking
         for (size_t i = 0; i < pass.textures.size(); ++i) {
             const TextureBase* texture = pass.textures[i];
-            wgpu::TextureUsage usage = pass.textureUsages[i];
+            const PassTextureUsage& textureUsage = pass.textureUsages[i];
+            wgpu::TextureUsage usage = textureUsage.usage;
 
             if (usage & ~texture->GetUsage()) {
                 return DAWN_VALIDATION_ERROR("Texture missing usage for the pass");
             }
 
+            // TODO (yunchao.he@intel.com): add read/write usage tracking for compute
+
+            // The usage variable for the whole texture is a fast path for texture usage tracking.
+            // Because in most cases a texture (with or without subresources) is used as
+            // single-write or multiple read, then we can skip iterating the subresources' usages.
             bool readOnly = (usage & kReadOnlyTextureUsages) == usage;
             bool singleUse = wgpu::HasZeroOrOneBits(usage);
-            if (pass.passType == PassType::Render && !readOnly && !singleUse) {
-                return DAWN_VALIDATION_ERROR(
-                    "Texture used as writable usage and another usage in render pass");
+            if (pass.passType != PassType::Render || readOnly || singleUse) {
+                continue;
+            }
+            // Inspect the subresources if the usage of the whole texture violates usage validation.
+            // Every single subresource can only be used as single-write or multiple read.
+            for (wgpu::TextureUsage subresourceUsage : textureUsage.subresourceUsages) {
+                bool readOnly = (subresourceUsage & kReadOnlyTextureUsages) == subresourceUsage;
+                bool singleUse = wgpu::HasZeroOrOneBits(subresourceUsage);
+                if (!readOnly && !singleUse) {
+                    return DAWN_VALIDATION_ERROR(
+                        "Texture used as writable usage and another usage in render pass");
+                }
             }
         }
-
         return {};
     }
 
diff --git a/src/dawn_native/PassResourceUsage.h b/src/dawn_native/PassResourceUsage.h
index c9ff10f..470aad5 100644
--- a/src/dawn_native/PassResourceUsage.h
+++ b/src/dawn_native/PassResourceUsage.h
@@ -27,6 +27,15 @@
 
     enum class PassType { Render, Compute };
 
+    // Describe the usage of the whole texture and its subresources.
+    // subresourceUsages vector is used to track every subresource's usage within a texture.
+    // usage variable is used the track the whole texture even though it can be deduced from
+    // subresources' usages. This is designed deliberately to track texture usage in a fast path.
+    struct PassTextureUsage {
+        wgpu::TextureUsage usage;
+        std::vector<wgpu::TextureUsage> subresourceUsages;
+    };
+
     // Which resources are used by pass and how they are used. The command buffer validation
     // pre-computes this information so that backends with explicit barriers don't have to
     // re-compute it.
@@ -36,7 +45,7 @@
         std::vector<wgpu::BufferUsage> bufferUsages;
 
         std::vector<TextureBase*> textures;
-        std::vector<wgpu::TextureUsage> textureUsages;
+        std::vector<PassTextureUsage> textureUsages;
     };
 
     using PerPassUsages = std::vector<PassResourceUsage>;
diff --git a/src/dawn_native/PassResourceUsageTracker.cpp b/src/dawn_native/PassResourceUsageTracker.cpp
index 0773150..1f9c350 100644
--- a/src/dawn_native/PassResourceUsageTracker.cpp
+++ b/src/dawn_native/PassResourceUsageTracker.cpp
@@ -27,10 +27,53 @@
         mBufferUsages[buffer] |= usage;
     }
 
-    void PassResourceUsageTracker::TextureUsedAs(TextureBase* texture, wgpu::TextureUsage usage) {
-        // std::map's operator[] will create the key and return 0 if the key didn't exist
-        // before.
-        mTextureUsages[texture] |= usage;
+    void PassResourceUsageTracker::TextureViewUsedAs(TextureViewBase* view,
+                                                     wgpu::TextureUsage usage) {
+        TextureBase* texture = view->GetTexture();
+        uint32_t baseMipLevel = view->GetBaseMipLevel();
+        uint32_t levelCount = view->GetLevelCount();
+        uint32_t baseArrayLayer = view->GetBaseArrayLayer();
+        uint32_t layerCount = view->GetLayerCount();
+
+        // std::map's operator[] will create the key and return a PassTextureUsage with usage = 0
+        // and an empty vector for subresourceUsages.
+        // TODO (yunchao.he@intel.com): optimize this
+        PassTextureUsage& textureUsage = mTextureUsages[texture];
+
+        // Set usage for the whole texture
+        textureUsage.usage |= usage;
+
+        // Set usages for subresources
+        uint32_t subresourceCount =
+            texture->GetSubresourceIndex(texture->GetNumMipLevels(), texture->GetArrayLayers());
+        if (!textureUsage.subresourceUsages.size()) {
+            textureUsage.subresourceUsages =
+                std::vector<wgpu::TextureUsage>(subresourceCount, wgpu::TextureUsage::None);
+        }
+        for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; ++mipLevel) {
+            for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount;
+                 ++arrayLayer) {
+                uint32_t subresourceIndex = texture->GetSubresourceIndex(mipLevel, arrayLayer);
+                textureUsage.subresourceUsages[subresourceIndex] |= usage;
+            }
+        }
+    }
+
+    void PassResourceUsageTracker::AddTextureUsage(TextureBase* texture,
+                                                   const PassTextureUsage& textureUsage) {
+        PassTextureUsage& passTextureUsage = mTextureUsages[texture];
+        passTextureUsage.usage |= textureUsage.usage;
+
+        uint32_t subresourceCount =
+            texture->GetSubresourceIndex(texture->GetNumMipLevels(), texture->GetArrayLayers());
+        ASSERT(textureUsage.subresourceUsages.size() == subresourceCount);
+        if (!passTextureUsage.subresourceUsages.size()) {
+            passTextureUsage.subresourceUsages = textureUsage.subresourceUsages;
+            return;
+        }
+        for (uint32_t i = 0; i < subresourceCount; ++i) {
+            passTextureUsage.subresourceUsages[i] |= textureUsage.subresourceUsages[i];
+        }
     }
 
     // Returns the per-pass usage for use by backends for APIs with explicit barriers.
@@ -49,7 +92,7 @@
 
         for (auto& it : mTextureUsages) {
             result.textures.push_back(it.first);
-            result.textureUsages.push_back(it.second);
+            result.textureUsages.push_back(std::move(it.second));
         }
 
         mBufferUsages.clear();
diff --git a/src/dawn_native/PassResourceUsageTracker.h b/src/dawn_native/PassResourceUsageTracker.h
index eccb3eb..cfcaa22 100644
--- a/src/dawn_native/PassResourceUsageTracker.h
+++ b/src/dawn_native/PassResourceUsageTracker.h
@@ -34,7 +34,8 @@
       public:
         PassResourceUsageTracker(PassType passType);
         void BufferUsedAs(BufferBase* buffer, wgpu::BufferUsage usage);
-        void TextureUsedAs(TextureBase* texture, wgpu::TextureUsage usage);
+        void TextureViewUsedAs(TextureViewBase* texture, wgpu::TextureUsage usage);
+        void AddTextureUsage(TextureBase* texture, const PassTextureUsage& textureUsage);
 
         // Returns the per-pass usage for use by backends for APIs with explicit barriers.
         PassResourceUsage AcquireResourceUsage();
@@ -42,7 +43,7 @@
       private:
         PassType mPassType;
         std::map<BufferBase*, wgpu::BufferUsage> mBufferUsages;
-        std::map<TextureBase*, wgpu::TextureUsage> mTextureUsages;
+        std::map<TextureBase*, PassTextureUsage> mTextureUsages;
     };
 
 }  // namespace dawn_native
diff --git a/src/dawn_native/ProgrammablePassEncoder.cpp b/src/dawn_native/ProgrammablePassEncoder.cpp
index 93a9692..df96752 100644
--- a/src/dawn_native/ProgrammablePassEncoder.cpp
+++ b/src/dawn_native/ProgrammablePassEncoder.cpp
@@ -47,9 +47,8 @@
                     }
 
                     case wgpu::BindingType::SampledTexture: {
-                        TextureBase* texture =
-                            group->GetBindingAsTextureView(bindingIndex)->GetTexture();
-                        usageTracker->TextureUsedAs(texture, wgpu::TextureUsage::Sampled);
+                        TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex);
+                        usageTracker->TextureViewUsedAs(view, wgpu::TextureUsage::Sampled);
                         break;
                     }
 
@@ -64,16 +63,14 @@
                         break;
 
                     case wgpu::BindingType::ReadonlyStorageTexture: {
-                        TextureBase* texture =
-                            group->GetBindingAsTextureView(bindingIndex)->GetTexture();
-                        usageTracker->TextureUsedAs(texture, kReadonlyStorageTexture);
+                        TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex);
+                        usageTracker->TextureViewUsedAs(view, kReadonlyStorageTexture);
                         break;
                     }
 
                     case wgpu::BindingType::WriteonlyStorageTexture: {
-                        TextureBase* texture =
-                            group->GetBindingAsTextureView(bindingIndex)->GetTexture();
-                        usageTracker->TextureUsedAs(texture, wgpu::TextureUsage::Storage);
+                        TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex);
+                        usageTracker->TextureViewUsedAs(view, wgpu::TextureUsage::Storage);
                         break;
                     }
 
diff --git a/src/dawn_native/RenderPassEncoder.cpp b/src/dawn_native/RenderPassEncoder.cpp
index 9dbb2ee..e2dc4db 100644
--- a/src/dawn_native/RenderPassEncoder.cpp
+++ b/src/dawn_native/RenderPassEncoder.cpp
@@ -153,8 +153,9 @@
                 for (uint32_t i = 0; i < usages.buffers.size(); ++i) {
                     mUsageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]);
                 }
+
                 for (uint32_t i = 0; i < usages.textures.size(); ++i) {
-                    mUsageTracker.TextureUsedAs(usages.textures[i], usages.textureUsages[i]);
+                    mUsageTracker.AddTextureUsage(usages.textures[i], usages.textureUsages[i]);
                 }
             }
 
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 0a45e2e..6f96262 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -475,7 +475,7 @@
                 // Clear textures that are not output attachments. Output attachments will be
                 // cleared during record render pass if the texture subresource has not been
                 // initialized before the render pass.
-                if (!(usages.textureUsages[i] & wgpu::TextureUsage::OutputAttachment)) {
+                if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
                     texture->EnsureSubresourceContentInitialized(commandContext, 0,
                                                                  texture->GetNumMipLevels(), 0,
                                                                  texture->GetArrayLayers());
@@ -488,10 +488,10 @@
                 D3D12_RESOURCE_BARRIER barrier;
                 if (ToBackend(usages.textures[i])
                         ->TrackUsageAndGetResourceBarrier(commandContext, &barrier,
-                                                          usages.textureUsages[i])) {
+                                                          usages.textureUsages[i].usage)) {
                     barriers.push_back(barrier);
                 }
-                textureUsages |= usages.textureUsages[i];
+                textureUsages |= usages.textureUsages[i].usage;
             }
 
             if (barriers.size()) {
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index 186b1ea..f27e7d4 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -691,7 +691,7 @@
                 // Clear textures that are not output attachments. Output attachments will be
                 // cleared in CreateMTLRenderPassDescriptor by setting the loadop to clear when the
                 // texture subresource has not been initialized before the render pass.
-                if (!(usages.textureUsages[i] & wgpu::TextureUsage::OutputAttachment)) {
+                if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
                     texture->EnsureSubresourceContentInitialized(0, texture->GetNumMipLevels(), 0,
                                                                  texture->GetArrayLayers());
                 }
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index a658c6d..9ee4e08 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -421,7 +421,7 @@
                 // Clear textures that are not output attachments. Output attachments will be
                 // cleared in BeginRenderPass by setting the loadop to clear when the
                 // texture subresource has not been initialized before the render pass.
-                if (!(usages.textureUsages[i] & wgpu::TextureUsage::OutputAttachment)) {
+                if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
                     texture->EnsureSubresourceContentInitialized(0, texture->GetNumMipLevels(), 0,
                                                                  texture->GetArrayLayers());
                 }
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index 53f2602..558e576 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -381,12 +381,12 @@
                 // Clear textures that are not output attachments. Output attachments will be
                 // cleared in RecordBeginRenderPass by setting the loadop to clear when the
                 // texture subresource has not been initialized before the render pass.
-                if (!(usages.textureUsages[i] & wgpu::TextureUsage::OutputAttachment)) {
+                if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
                     texture->EnsureSubresourceContentInitialized(recordingContext, 0,
                                                                  texture->GetNumMipLevels(), 0,
                                                                  texture->GetArrayLayers());
                 }
-                texture->TransitionUsageNow(recordingContext, usages.textureUsages[i]);
+                texture->TransitionUsageNow(recordingContext, usages.textureUsages[i].usage);
             }
         };
         const std::vector<PassResourceUsage>& passResourceUsages = GetResourceUsages().perPass;
diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn
index 581f1fc..66e3355 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -193,6 +193,7 @@
     "unittests/validation/SamplerValidationTests.cpp",
     "unittests/validation/ShaderModuleValidationTests.cpp",
     "unittests/validation/StorageTextureValidationTests.cpp",
+    "unittests/validation/TextureSubresourceTests.cpp",
     "unittests/validation/TextureValidationTests.cpp",
     "unittests/validation/TextureViewValidationTests.cpp",
     "unittests/validation/ToggleValidationTests.cpp",
diff --git a/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp b/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp
index bb6c3e5..6daed27 100644
--- a/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp
+++ b/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp
@@ -1121,10 +1121,15 @@
     // TODO (yunchao.he@intel.com):
     // * useless bindings in bind groups. For example, a bind group includes bindings for compute
     // stage, but the bind group is used in render pass.
+    //
     // * more read write tracking tests for texture which need readonly storage texture and
     // writeonly storage texture support
+    //
     // * resource write and read dependency
     //     1) across passes (render + render, compute + compute, compute and render mixed) is valid
     //     2) across draws/dispatches is invalid
+    //
+    //	* Add tests for multiple encoders upon the same resource simultaneously. This situation fits
+    //	some cases like VR, multi-threading, etc.
 
 }  // anonymous namespace
diff --git a/src/tests/unittests/validation/TextureSubresourceTests.cpp b/src/tests/unittests/validation/TextureSubresourceTests.cpp
new file mode 100644
index 0000000..bab0544
--- /dev/null
+++ b/src/tests/unittests/validation/TextureSubresourceTests.cpp
@@ -0,0 +1,167 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "utils/WGPUHelpers.h"
+
+#include "tests/unittests/validation/ValidationTest.h"
+
+namespace {
+
+    class TextureSubresourceTest : public ValidationTest {
+      public:
+        static constexpr uint32_t kSize = 32u;
+        static constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
+
+        wgpu::Texture CreateTexture(uint32_t mipLevelCount,
+                                    uint32_t arrayLayerCount,
+                                    wgpu::TextureUsage usage) {
+            wgpu::TextureDescriptor texDesc;
+            texDesc.dimension = wgpu::TextureDimension::e2D;
+            texDesc.size = {kSize, kSize, 1};
+            texDesc.arrayLayerCount = arrayLayerCount;
+            texDesc.sampleCount = 1;
+            texDesc.mipLevelCount = mipLevelCount;
+            texDesc.usage = usage;
+            texDesc.format = kFormat;
+            return device.CreateTexture(&texDesc);
+        }
+
+        wgpu::TextureView CreateTextureView(wgpu::Texture texture,
+                                            uint32_t baseMipLevel,
+                                            uint32_t baseArrayLayer) {
+            wgpu::TextureViewDescriptor viewDesc;
+            viewDesc.format = kFormat;
+            viewDesc.baseArrayLayer = baseArrayLayer;
+            viewDesc.arrayLayerCount = 1;
+            viewDesc.baseMipLevel = baseMipLevel;
+            viewDesc.mipLevelCount = 1;
+            viewDesc.dimension = wgpu::TextureViewDimension::e2D;
+            return texture.CreateView(&viewDesc);
+        }
+
+        void TestRenderPass(const wgpu::TextureView& renderView,
+                            const wgpu::TextureView& samplerView) {
+            // Create bind group
+            wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
+                device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::SampledTexture}});
+
+            utils::ComboRenderPassDescriptor renderPassDesc({renderView});
+
+            // It is valid to read from and write into different subresources of the same texture
+            {
+                wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, samplerView}});
+                wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+                wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
+                pass.SetBindGroup(0, bindGroup);
+                pass.EndPass();
+                encoder.Finish();
+            }
+
+            // It is valid to has multiple read from a subresource and one single write into another
+            // subresource
+            {
+                wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, samplerView}});
+
+                wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
+                    device,
+                    {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageTexture,
+                      false, false, wgpu::TextureViewDimension::Undefined,
+                      wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float,
+                      kFormat}});
+
+                wgpu::BindGroup bindGroup1 = utils::MakeBindGroup(device, bgl1, {{0, samplerView}});
+
+                wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+                wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
+                pass.SetBindGroup(0, bindGroup);
+                pass.SetBindGroup(1, bindGroup1);
+                pass.EndPass();
+                encoder.Finish();
+            }
+
+            // It is invalid to read and write into the same subresources
+            {
+                wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, renderView}});
+                wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+                wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
+                pass.SetBindGroup(0, bindGroup);
+                pass.EndPass();
+                ASSERT_DEVICE_ERROR(encoder.Finish());
+            }
+
+            // It is valid to write into and then read from the same level of a texture in different
+            // render passes
+            {
+                wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, samplerView}});
+
+                wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(
+                    device,
+                    {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::WriteonlyStorageTexture,
+                      false, false, wgpu::TextureViewDimension::Undefined,
+                      wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float,
+                      kFormat}});
+                wgpu::BindGroup bindGroup1 = utils::MakeBindGroup(device, bgl1, {{0, samplerView}});
+
+                wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+                wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&renderPassDesc);
+                pass1.SetBindGroup(0, bindGroup1);
+                pass1.EndPass();
+
+                wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
+                pass.SetBindGroup(0, bindGroup);
+                pass.EndPass();
+
+                encoder.Finish();
+            }
+        }
+    };
+
+    // Test different mipmap levels
+    TEST_F(TextureSubresourceTest, MipmapLevelsTest) {
+        // Create texture with 2 mipmap levels and 1 layer
+        wgpu::Texture texture =
+            CreateTexture(2, 1,
+                          wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment |
+                              wgpu::TextureUsage::Storage);
+
+        // Create two views on different mipmap levels.
+        wgpu::TextureView samplerView = CreateTextureView(texture, 0, 0);
+        wgpu::TextureView renderView = CreateTextureView(texture, 1, 0);
+        TestRenderPass(samplerView, renderView);
+    }
+
+    // Test different array layers
+    TEST_F(TextureSubresourceTest, ArrayLayersTest) {
+        // Create texture with 1 mipmap level and 2 layers
+        wgpu::Texture texture =
+            CreateTexture(1, 2,
+                          wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment |
+                              wgpu::TextureUsage::Storage);
+
+        // Create two views on different layers.
+        wgpu::TextureView samplerView = CreateTextureView(texture, 0, 0);
+        wgpu::TextureView renderView = CreateTextureView(texture, 0, 1);
+
+        TestRenderPass(samplerView, renderView);
+    }
+
+    // TODO (yunchao.he@intel.com):
+    //	* Add tests for compute, in which texture subresource is traced per dispatch.
+    //
+    //	* Add tests for multiple encoders upon the same resource simultaneously. This situation fits
+    //	some cases like VR, multi-threading, etc.
+    //
+    //	* Add tests for conflicts between usages in two render bundles used in the same pass.
+
+}  // anonymous namespace