Refactor subresource related variables to a struct

This patch put subresource related variables like baseMipLevel,
levelCount, baseArrayLayer, layerCount into a single struct at
front-end. We have a lot more at backend too, a following patch
will do that.

Bug: dawn:157

Change-Id: Iab5633a4246b6ae89b80c39f5672dbb31d7a3e78
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/22704
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Yunchao He <yunchao.he@intel.com>
diff --git a/src/dawn_native/CommandBuffer.cpp b/src/dawn_native/CommandBuffer.cpp
index e02cff1..3feebb4 100644
--- a/src/dawn_native/CommandBuffer.cpp
+++ b/src/dawn_native/CommandBuffer.cpp
@@ -59,11 +59,11 @@
 
             ASSERT(view->GetLayerCount() == 1);
             ASSERT(view->GetLevelCount() == 1);
+            SubresourceRange range = view->GetSubresourceRange();
 
             // If the loadOp is Load, but the subresource is not initialized, use Clear instead.
             if (attachmentInfo.loadOp == wgpu::LoadOp::Load &&
-                !view->GetTexture()->IsSubresourceContentInitialized(
-                    view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1)) {
+                !view->GetTexture()->IsSubresourceContentInitialized(range)) {
                 attachmentInfo.loadOp = wgpu::LoadOp::Clear;
                 attachmentInfo.clearColor = {0.f, 0.f, 0.f, 0.f};
             }
@@ -73,20 +73,19 @@
                 // cleared later in the pipeline. The texture will be resolved from the
                 // source color attachment, which will be correctly initialized.
                 TextureViewBase* resolveView = attachmentInfo.resolveTarget.Get();
+                ASSERT(resolveView->GetLayerCount() == 1);
+                ASSERT(resolveView->GetLevelCount() == 1);
                 resolveView->GetTexture()->SetIsSubresourceContentInitialized(
-                    true, resolveView->GetBaseMipLevel(), resolveView->GetLevelCount(),
-                    resolveView->GetBaseArrayLayer(), resolveView->GetLayerCount());
+                    true, resolveView->GetSubresourceRange());
             }
 
             switch (attachmentInfo.storeOp) {
                 case wgpu::StoreOp::Store:
-                    view->GetTexture()->SetIsSubresourceContentInitialized(
-                        true, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
+                    view->GetTexture()->SetIsSubresourceContentInitialized(true, range);
                     break;
 
                 case wgpu::StoreOp::Clear:
-                    view->GetTexture()->SetIsSubresourceContentInitialized(
-                        false, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
+                    view->GetTexture()->SetIsSubresourceContentInitialized(false, range);
                     break;
 
                 default:
@@ -98,12 +97,13 @@
         if (renderPass->attachmentState->HasDepthStencilAttachment()) {
             auto& attachmentInfo = renderPass->depthStencilAttachment;
             TextureViewBase* view = attachmentInfo.view.Get();
+            ASSERT(view->GetLayerCount() == 1);
+            ASSERT(view->GetLevelCount() == 1);
+            SubresourceRange range = view->GetSubresourceRange();
 
             // If the depth stencil texture has not been initialized, we want to use loadop
             // clear to init the contents to 0's
-            if (!view->GetTexture()->IsSubresourceContentInitialized(
-                    view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(),
-                    view->GetLayerCount())) {
+            if (!view->GetTexture()->IsSubresourceContentInitialized(range)) {
                 if (view->GetTexture()->GetFormat().HasDepth() &&
                     attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) {
                     attachmentInfo.clearDepth = 0.0f;
@@ -125,15 +125,11 @@
 
             if (attachmentInfo.depthStoreOp == wgpu::StoreOp::Store &&
                 attachmentInfo.stencilStoreOp == wgpu::StoreOp::Store) {
-                view->GetTexture()->SetIsSubresourceContentInitialized(
-                    true, view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(),
-                    view->GetLayerCount());
+                view->GetTexture()->SetIsSubresourceContentInitialized(true, range);
             } else {
                 ASSERT(attachmentInfo.depthStoreOp == wgpu::StoreOp::Clear &&
                        attachmentInfo.stencilStoreOp == wgpu::StoreOp::Clear);
-                view->GetTexture()->SetIsSubresourceContentInitialized(
-                    false, view->GetBaseMipLevel(), view->GetLevelCount(),
-                    view->GetBaseArrayLayer(), view->GetLayerCount());
+                view->GetTexture()->SetIsSubresourceContentInitialized(false, range);
             }
         }
     }
diff --git a/src/dawn_native/DawnNative.cpp b/src/dawn_native/DawnNative.cpp
index 14ae72a..3206115 100644
--- a/src/dawn_native/DawnNative.cpp
+++ b/src/dawn_native/DawnNative.cpp
@@ -178,8 +178,8 @@
                                          uint32_t layerCount) {
         dawn_native::TextureBase* textureBase =
             reinterpret_cast<dawn_native::TextureBase*>(texture);
-        return textureBase->IsSubresourceContentInitialized(baseMipLevel, levelCount,
-                                                            baseArrayLayer, layerCount);
+        SubresourceRange range = {baseMipLevel, levelCount, baseArrayLayer, layerCount};
+        return textureBase->IsSubresourceContentInitialized(range);
     }
 
     std::vector<const char*> GetProcMapNamesForTestingInternal();
diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp
index bc33ec7..11dbae3 100644
--- a/src/dawn_native/Texture.cpp
+++ b/src/dawn_native/Texture.cpp
@@ -346,8 +346,7 @@
           mDimension(descriptor->dimension),
           mFormat(device->GetValidInternalFormat(descriptor->format)),
           mSize(descriptor->size),
-          mArrayLayerCount(descriptor->arrayLayerCount),
-          mMipLevelCount(descriptor->mipLevelCount),
+          mRange({0, descriptor->mipLevelCount, 0, descriptor->arrayLayerCount}),
           mSampleCount(descriptor->sampleCount),
           mUsage(descriptor->usage),
           mState(state) {
@@ -388,11 +387,15 @@
     }
     uint32_t TextureBase::GetArrayLayers() const {
         ASSERT(!IsError());
-        return mArrayLayerCount;
+        return mRange.layerCount;
     }
     uint32_t TextureBase::GetNumMipLevels() const {
         ASSERT(!IsError());
-        return mMipLevelCount;
+        return mRange.levelCount;
+    }
+    const SubresourceRange& TextureBase::GetAllSubresources() const {
+        ASSERT(!IsError());
+        return mRange;
     }
     uint32_t TextureBase::GetSampleCount() const {
         ASSERT(!IsError());
@@ -400,7 +403,7 @@
     }
     uint32_t TextureBase::GetSubresourceCount() const {
         ASSERT(!IsError());
-        return mMipLevelCount * mArrayLayerCount;
+        return mRange.levelCount * mRange.layerCount;
     }
     wgpu::TextureUsage TextureBase::GetUsage() const {
         ASSERT(!IsError());
@@ -421,15 +424,12 @@
         return GetNumMipLevels() * arraySlice + mipLevel;
     }
 
-    bool TextureBase::IsSubresourceContentInitialized(uint32_t baseMipLevel,
-                                                      uint32_t levelCount,
-                                                      uint32_t baseArrayLayer,
-                                                      uint32_t layerCount) const {
+    bool TextureBase::IsSubresourceContentInitialized(const SubresourceRange& range) const {
         ASSERT(!IsError());
-        for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount;
-             ++arrayLayer) {
-            for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount;
-                 ++mipLevel) {
+        for (uint32_t arrayLayer = range.baseArrayLayer;
+             arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) {
+            for (uint32_t mipLevel = range.baseMipLevel;
+                 mipLevel < range.baseMipLevel + range.levelCount; ++mipLevel) {
                 uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer);
                 ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size());
                 if (!mIsSubresourceContentInitializedAtIndex[subresourceIndex]) {
@@ -441,15 +441,12 @@
     }
 
     void TextureBase::SetIsSubresourceContentInitialized(bool isInitialized,
-                                                         uint32_t baseMipLevel,
-                                                         uint32_t levelCount,
-                                                         uint32_t baseArrayLayer,
-                                                         uint32_t layerCount) {
+                                                         const SubresourceRange& range) {
         ASSERT(!IsError());
-        for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount;
-             ++arrayLayer) {
-            for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount;
-                 ++mipLevel) {
+        for (uint32_t arrayLayer = range.baseArrayLayer;
+             arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) {
+            for (uint32_t mipLevel = range.baseMipLevel;
+                 mipLevel < range.baseMipLevel + range.levelCount; ++mipLevel) {
                 uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer);
                 ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size());
                 mIsSubresourceContentInitializedAtIndex[subresourceIndex] = isInitialized;
@@ -526,10 +523,8 @@
           mTexture(texture),
           mFormat(GetDevice()->GetValidInternalFormat(descriptor->format)),
           mDimension(descriptor->dimension),
-          mBaseMipLevel(descriptor->baseMipLevel),
-          mMipLevelCount(descriptor->mipLevelCount),
-          mBaseArrayLayer(descriptor->baseArrayLayer),
-          mArrayLayerCount(descriptor->arrayLayerCount) {
+          mRange({descriptor->baseMipLevel, descriptor->mipLevelCount, descriptor->baseArrayLayer,
+                  descriptor->arrayLayerCount}) {
     }
 
     TextureViewBase::TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag)
@@ -563,21 +558,26 @@
 
     uint32_t TextureViewBase::GetBaseMipLevel() const {
         ASSERT(!IsError());
-        return mBaseMipLevel;
+        return mRange.baseMipLevel;
     }
 
     uint32_t TextureViewBase::GetLevelCount() const {
         ASSERT(!IsError());
-        return mMipLevelCount;
+        return mRange.levelCount;
     }
 
     uint32_t TextureViewBase::GetBaseArrayLayer() const {
         ASSERT(!IsError());
-        return mBaseArrayLayer;
+        return mRange.baseArrayLayer;
     }
 
     uint32_t TextureViewBase::GetLayerCount() const {
         ASSERT(!IsError());
-        return mArrayLayerCount;
+        return mRange.layerCount;
+    }
+
+    const SubresourceRange& TextureViewBase::GetSubresourceRange() const {
+        ASSERT(!IsError());
+        return mRange;
     }
 }  // namespace dawn_native
diff --git a/src/dawn_native/Texture.h b/src/dawn_native/Texture.h
index 7211839..4765c1d 100644
--- a/src/dawn_native/Texture.h
+++ b/src/dawn_native/Texture.h
@@ -41,6 +41,17 @@
         wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Storage |
         wgpu::TextureUsage::OutputAttachment;
 
+    struct SubresourceRange {
+        uint32_t baseMipLevel;
+        uint32_t levelCount;
+        uint32_t baseArrayLayer;
+        uint32_t layerCount;
+
+        static SubresourceRange SingleSubresource(uint32_t baseMipLevel, uint32_t baseArrayLayer) {
+            return {baseMipLevel, 1, baseArrayLayer, 1};
+        }
+    };
+
     class TextureBase : public ObjectBase {
       public:
         enum class TextureState { OwnedInternal, OwnedExternal, Destroyed };
@@ -54,20 +65,14 @@
         const Extent3D& GetSize() const;
         uint32_t GetArrayLayers() const;
         uint32_t GetNumMipLevels() const;
+        const SubresourceRange& GetAllSubresources() const;
         uint32_t GetSampleCount() const;
         uint32_t GetSubresourceCount() const;
         wgpu::TextureUsage GetUsage() const;
         TextureState GetTextureState() const;
         uint32_t GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice) const;
-        bool IsSubresourceContentInitialized(uint32_t baseMipLevel,
-                                             uint32_t levelCount,
-                                             uint32_t baseArrayLayer,
-                                             uint32_t layerCount) const;
-        void SetIsSubresourceContentInitialized(bool isInitialized,
-                                                uint32_t baseMipLevel,
-                                                uint32_t levelCount,
-                                                uint32_t baseArrayLayer,
-                                                uint32_t layerCount);
+        bool IsSubresourceContentInitialized(const SubresourceRange& range) const;
+        void SetIsSubresourceContentInitialized(bool isInitialized, const SubresourceRange& range);
 
         MaybeError ValidateCanUseInSubmitNow() const;
 
@@ -97,8 +102,7 @@
         // TODO(cwallez@chromium.org): This should be deduplicated in the Device
         const Format& mFormat;
         Extent3D mSize;
-        uint32_t mArrayLayerCount;
-        uint32_t mMipLevelCount;
+        SubresourceRange mRange;
         uint32_t mSampleCount;
         wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
         TextureState mState;
@@ -122,6 +126,7 @@
         uint32_t GetLevelCount() const;
         uint32_t GetBaseArrayLayer() const;
         uint32_t GetLayerCount() const;
+        const SubresourceRange& GetSubresourceRange() const;
 
       private:
         TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag);
@@ -131,10 +136,7 @@
         // TODO(cwallez@chromium.org): This should be deduplicated in the Device
         const Format& mFormat;
         wgpu::TextureViewDimension mDimension;
-        uint32_t mBaseMipLevel;
-        uint32_t mMipLevelCount;
-        uint32_t mBaseArrayLayer;
-        uint32_t mArrayLayerCount;
+        SubresourceRange mRange;
     };
 
 }  // namespace dawn_native
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 9c57c29..239415e 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -512,9 +512,8 @@
                 // cleared during record render pass if the texture subresource has not been
                 // initialized before the render pass.
                 if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) {
-                    texture->EnsureSubresourceContentInitialized(commandContext, 0,
-                                                                 texture->GetNumMipLevels(), 0,
-                                                                 texture->GetArrayLayers());
+                    texture->EnsureSubresourceContentInitialized(commandContext,
+                                                                 texture->GetAllSubresources());
                 }
             }
 
@@ -590,14 +589,15 @@
                     Buffer* buffer = ToBackend(copy->source.buffer.Get());
                     Texture* texture = ToBackend(copy->destination.texture.Get());
 
+                    ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    ASSERT(copy->copySize.depth == 1);
+                    SubresourceRange subresource = SubresourceRange::SingleSubresource(
+                        copy->destination.mipLevel, copy->destination.arrayLayer);
                     if (IsCompleteSubresourceCopiedTo(texture, copy->copySize,
                                                       copy->destination.mipLevel)) {
-                        texture->SetIsSubresourceContentInitialized(
-                            true, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1);
+                        texture->SetIsSubresourceContentInitialized(true, subresource);
                     } else {
-                        texture->EnsureSubresourceContentInitialized(
-                            commandContext, copy->destination.mipLevel, 1,
-                            copy->destination.arrayLayer, 1);
+                        texture->EnsureSubresourceContentInitialized(commandContext, subresource);
                     }
 
                     buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopySrc);
@@ -635,8 +635,11 @@
                     Texture* texture = ToBackend(copy->source.texture.Get());
                     Buffer* buffer = ToBackend(copy->destination.buffer.Get());
 
+                    ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    ASSERT(copy->copySize.depth == 1);
                     texture->EnsureSubresourceContentInitialized(
-                        commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer, 1);
+                        commandContext, SubresourceRange::SingleSubresource(
+                                            copy->source.mipLevel, copy->source.arrayLayer));
 
                     texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc,
                                                         copy->source.mipLevel, 1,
@@ -676,19 +679,18 @@
 
                     Texture* source = ToBackend(copy->source.texture.Get());
                     Texture* destination = ToBackend(copy->destination.texture.Get());
+                    SubresourceRange srcRange = {copy->source.mipLevel, 1, copy->source.arrayLayer,
+                                                 copy->copySize.depth};
+                    SubresourceRange dstRange = {copy->destination.mipLevel, 1,
+                                                 copy->destination.arrayLayer,
+                                                 copy->copySize.depth};
 
-                    source->EnsureSubresourceContentInitialized(
-                        commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer,
-                        copy->copySize.depth);
+                    source->EnsureSubresourceContentInitialized(commandContext, srcRange);
                     if (IsCompleteSubresourceCopiedTo(destination, copy->copySize,
                                                       copy->destination.mipLevel)) {
-                        destination->SetIsSubresourceContentInitialized(
-                            true, copy->destination.mipLevel, 1, copy->destination.arrayLayer,
-                            copy->copySize.depth);
+                        destination->SetIsSubresourceContentInitialized(true, dstRange);
                     } else {
-                        destination->EnsureSubresourceContentInitialized(
-                            commandContext, copy->destination.mipLevel, 1,
-                            copy->destination.arrayLayer, copy->copySize.depth);
+                        destination->EnsureSubresourceContentInitialized(commandContext, dstRange);
                     }
 
                     if (copy->source.texture.Get() == copy->destination.texture.Get() &&
diff --git a/src/dawn_native/d3d12/TextureD3D12.cpp b/src/dawn_native/d3d12/TextureD3D12.cpp
index 78bf027..0c3f6ee 100644
--- a/src/dawn_native/d3d12/TextureD3D12.cpp
+++ b/src/dawn_native/d3d12/TextureD3D12.cpp
@@ -397,10 +397,8 @@
             AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal));
         DAWN_TRY(dawnTexture->InitializeAsExternalTexture(textureDescriptor, sharedHandle,
                                                           acquireMutexKey, isSwapChainTexture));
-
-        dawnTexture->SetIsSubresourceContentInitialized(descriptor->isCleared, 0,
-                                                        textureDescriptor->mipLevelCount, 0,
-                                                        textureDescriptor->arrayLayerCount);
+        dawnTexture->SetIsSubresourceContentInitialized(descriptor->isCleared,
+                                                        dawnTexture->GetAllSubresources());
         return std::move(dawnTexture);
     }
 
@@ -479,7 +477,7 @@
             CommandRecordingContext* commandContext;
             DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext());
 
-            DAWN_TRY(ClearTexture(commandContext, 0, GetNumMipLevels(), 0, GetArrayLayers(),
+            DAWN_TRY(ClearTexture(commandContext, GetAllSubresources(),
                                   TextureBase::ClearValue::NonZero));
         }
 
@@ -504,8 +502,7 @@
         // memory management.
         mResourceAllocation = {info, 0, std::move(nativeTexture), nullptr};
 
-        SetIsSubresourceContentInitialized(true, 0, descriptor->mipLevelCount, 0,
-                                           descriptor->arrayLayerCount);
+        SetIsSubresourceContentInitialized(true, GetAllSubresources());
     }
 
     Texture::~Texture() {
@@ -786,15 +783,11 @@
     }
 
     MaybeError Texture::ClearTexture(CommandRecordingContext* commandContext,
-                                     uint32_t baseMipLevel,
-                                     uint32_t levelCount,
-                                     uint32_t baseArrayLayer,
-                                     uint32_t layerCount,
+                                     const SubresourceRange& range,
                                      TextureBase::ClearValue clearValue) {
         // TODO(jiawei.shao@intel.com): initialize the textures in compressed formats with copies.
         if (GetFormat().isCompressed) {
-            SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
-                                               layerCount);
+            SetIsSubresourceContentInitialized(true, range);
             return {};
         }
 
@@ -808,15 +801,18 @@
         if (GetFormat().isRenderable) {
             if (GetFormat().HasDepthOrStencil()) {
                 TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE,
-                                           baseMipLevel, levelCount, baseArrayLayer, layerCount);
+                                           range.baseMipLevel, range.levelCount,
+                                           range.baseArrayLayer, range.layerCount);
 
                 D3D12_CLEAR_FLAGS clearFlags = {};
 
-                for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
-                    for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
-                         ++layer) {
+                for (uint32_t level = range.baseMipLevel;
+                     level < range.baseMipLevel + range.levelCount; ++level) {
+                    for (uint32_t layer = range.baseArrayLayer;
+                         layer < range.baseArrayLayer + range.layerCount; ++layer) {
                         if (clearValue == TextureBase::ClearValue::Zero &&
-                            IsSubresourceContentInitialized(level, 1, layer, 1)) {
+                            IsSubresourceContentInitialized(
+                                SubresourceRange::SingleSubresource(level, layer))) {
                             // Skip lazy clears if already initialized.
                             continue;
                         }
@@ -843,16 +839,19 @@
                 }
             } else {
                 TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET,
-                                           baseMipLevel, levelCount, baseArrayLayer, layerCount);
+                                           range.baseMipLevel, range.levelCount,
+                                           range.baseArrayLayer, range.layerCount);
 
                 const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor,
                                                  fClearColor};
 
-                for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
-                    for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
-                         ++layer) {
+                for (uint32_t level = range.baseMipLevel;
+                     level < range.baseMipLevel + range.levelCount; ++level) {
+                    for (uint32_t layer = range.baseArrayLayer;
+                         layer < range.baseArrayLayer + range.layerCount; ++layer) {
                         if (clearValue == TextureBase::ClearValue::Zero &&
-                            IsSubresourceContentInitialized(level, 1, layer, 1)) {
+                            IsSubresourceContentInitialized(
+                                SubresourceRange::SingleSubresource(level, layer))) {
                             // Skip lazy clears if already initialized.
                             continue;
                         }
@@ -886,10 +885,12 @@
                             uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
             memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
 
-            TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST, baseMipLevel,
-                                       levelCount, baseArrayLayer, layerCount);
+            TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST,
+                                       range.baseMipLevel, range.levelCount, range.baseArrayLayer,
+                                       range.layerCount);
 
-            for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
+            for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
+                 ++level) {
                 // compute d3d12 texture copy locations for texture and buffer
                 Extent3D copySize = GetMipLevelVirtualSize(level);
 
@@ -898,10 +899,11 @@
                     ComputeTextureCopySplit({0, 0, 0}, copySize, GetFormat(),
                                             uploadHandle.startOffset, bytesPerRow, rowsPerImage);
 
-                for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
-                     ++layer) {
+                for (uint32_t layer = range.baseArrayLayer;
+                     layer < range.baseArrayLayer + range.layerCount; ++layer) {
                     if (clearValue == TextureBase::ClearValue::Zero &&
-                        IsSubresourceContentInitialized(level, 1, layer, 1)) {
+                        IsSubresourceContentInitialized(
+                            SubresourceRange::SingleSubresource(level, layer))) {
                         // Skip lazy clears if already initialized.
                         continue;
                     }
@@ -927,28 +929,22 @@
             }
         }
         if (clearValue == TextureBase::ClearValue::Zero) {
-            SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
-                                               layerCount);
+            SetIsSubresourceContentInitialized(true, range);
             GetDevice()->IncrementLazyClearCountForTesting();
         }
         return {};
     }
 
     void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
-                                                      uint32_t baseMipLevel,
-                                                      uint32_t levelCount,
-                                                      uint32_t baseArrayLayer,
-                                                      uint32_t layerCount) {
+                                                      const SubresourceRange& range) {
         if (!ToBackend(GetDevice())->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
             return;
         }
-        if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
-                                             layerCount)) {
+        if (!IsSubresourceContentInitialized(range)) {
             // If subresource has not been initialized, clear it to black as it could contain
             // dirty bits from recycled memory
-            GetDevice()->ConsumedError(ClearTexture(commandContext, baseMipLevel, levelCount,
-                                                    baseArrayLayer, layerCount,
-                                                    TextureBase::ClearValue::Zero));
+            GetDevice()->ConsumedError(
+                ClearTexture(commandContext, range, TextureBase::ClearValue::Zero));
         }
     }
 
diff --git a/src/dawn_native/d3d12/TextureD3D12.h b/src/dawn_native/d3d12/TextureD3D12.h
index c8c7f5a..fa15412 100644
--- a/src/dawn_native/d3d12/TextureD3D12.h
+++ b/src/dawn_native/d3d12/TextureD3D12.h
@@ -55,10 +55,7 @@
                                                        uint32_t baseArrayLayer,
                                                        uint32_t layerCount) const;
         void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
-                                                 uint32_t baseMipLevel,
-                                                 uint32_t levelCount,
-                                                 uint32_t baseArrayLayer,
-                                                 uint32_t layerCount);
+                                                 const SubresourceRange& range);
 
         void TrackUsageAndGetResourceBarrierForPass(
             CommandRecordingContext* commandContext,
@@ -95,10 +92,7 @@
         // Dawn API
         void DestroyImpl() override;
         MaybeError ClearTexture(CommandRecordingContext* commandContext,
-                                uint32_t baseMipLevel,
-                                uint32_t levelCount,
-                                uint32_t baseArrayLayer,
-                                uint32_t layerCount,
+                                const SubresourceRange& range,
                                 TextureBase::ClearValue clearValue);
 
         UINT16 GetDepthOrArraySize();
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index 3774b8f..2830e06 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -444,19 +444,18 @@
         void EnsureSourceTextureInitialized(Texture* texture,
                                             const Extent3D& size,
                                             const TextureCopy& src) {
-            texture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer,
-                                                         size.depth);
+            texture->EnsureSubresourceContentInitialized(
+                {src.mipLevel, 1, src.arrayLayer, size.depth});
         }
 
         void EnsureDestinationTextureInitialized(Texture* texture,
                                                  const Extent3D& size,
                                                  const TextureCopy& dst) {
+            SubresourceRange range = {dst.mipLevel, 1, dst.arrayLayer, size.depth};
             if (IsCompleteSubresourceCopiedTo(texture, size, dst.mipLevel)) {
-                texture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1, dst.arrayLayer,
-                                                            size.depth);
+                texture->SetIsSubresourceContentInitialized(true, range);
             } else {
-                texture->EnsureSubresourceContentInitialized(dst.mipLevel, 1, dst.arrayLayer,
-                                                             size.depth);
+                texture->EnsureSubresourceContentInitialized(range);
             }
         }
 
@@ -691,8 +690,7 @@
                 // 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].usage & wgpu::TextureUsage::OutputAttachment)) {
-                    texture->EnsureSubresourceContentInitialized(0, texture->GetNumMipLevels(), 0,
-                                                                 texture->GetArrayLayers());
+                    texture->EnsureSubresourceContentInitialized(texture->GetAllSubresources());
                 }
             }
         };
diff --git a/src/dawn_native/metal/TextureMTL.h b/src/dawn_native/metal/TextureMTL.h
index 8e4bce9..7ace396 100644
--- a/src/dawn_native/metal/TextureMTL.h
+++ b/src/dawn_native/metal/TextureMTL.h
@@ -42,21 +42,14 @@
 
         id<MTLTexture> GetMTLTexture();
 
-        void EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
-                                                 uint32_t levelCount,
-                                                 uint32_t baseArrayLayer,
-                                                 uint32_t layerCount);
+        void EnsureSubresourceContentInitialized(const SubresourceRange& range);
 
       private:
         ~Texture() override;
 
         void DestroyImpl() override;
 
-        MaybeError ClearTexture(uint32_t baseMipLevel,
-                                uint32_t levelCount,
-                                uint32_t baseArrayLayer,
-                                uint32_t layerCount,
-                                TextureBase::ClearValue clearValue);
+        MaybeError ClearTexture(const SubresourceRange& range, TextureBase::ClearValue clearValue);
 
         id<MTLTexture> mMtlTexture = nil;
     };
diff --git a/src/dawn_native/metal/TextureMTL.mm b/src/dawn_native/metal/TextureMTL.mm
index ece0214..3ed3b48 100644
--- a/src/dawn_native/metal/TextureMTL.mm
+++ b/src/dawn_native/metal/TextureMTL.mm
@@ -326,8 +326,8 @@
         [mtlDesc release];
 
         if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
-            device->ConsumedError(ClearTexture(0, GetNumMipLevels(), 0, GetArrayLayers(),
-                                               TextureBase::ClearValue::NonZero));
+            device->ConsumedError(
+                ClearTexture(GetAllSubresources(), TextureBase::ClearValue::NonZero));
         }
     }
 
@@ -351,7 +351,7 @@
                                                                  plane:plane];
         [mtlDesc release];
 
-        SetIsSubresourceContentInitialized(descriptor->isCleared, 0, 1, 0, 1);
+        SetIsSubresourceContentInitialized(descriptor->isCleared, {0, 1, 0, 1});
     }
 
     Texture::~Texture() {
@@ -369,10 +369,7 @@
         return mMtlTexture;
     }
 
-    MaybeError Texture::ClearTexture(uint32_t baseMipLevel,
-                                     uint32_t levelCount,
-                                     uint32_t baseArrayLayer,
-                                     uint32_t layerCount,
+    MaybeError Texture::ClearTexture(const SubresourceRange& range,
                                      TextureBase::ClearValue clearValue) {
         Device* device = ToBackend(GetDevice());
 
@@ -389,11 +386,13 @@
 
             if (GetFormat().HasDepthOrStencil()) {
                 // Create a render pass to clear each subresource.
-                for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
-                    for (uint32_t arrayLayer = baseArrayLayer;
-                         arrayLayer < baseArrayLayer + layerCount; arrayLayer++) {
+                for (uint32_t level = range.baseMipLevel;
+                     level < range.baseMipLevel + range.levelCount; ++level) {
+                    for (uint32_t arrayLayer = range.baseArrayLayer;
+                         arrayLayer < range.baseArrayLayer + range.layerCount; arrayLayer++) {
                         if (clearValue == TextureBase::ClearValue::Zero &&
-                            IsSubresourceContentInitialized(level, 1, arrayLayer, 1)) {
+                            IsSubresourceContentInitialized(
+                                SubresourceRange::SingleSubresource(level, arrayLayer))) {
                             // Skip lazy clears if already initialized.
                             continue;
                         }
@@ -421,17 +420,19 @@
                 }
             } else {
                 ASSERT(GetFormat().IsColor());
-                for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
+                for (uint32_t level = range.baseMipLevel;
+                     level < range.baseMipLevel + range.levelCount; ++level) {
                     // Create multiple render passes with each subresource as a color attachment to
                     // clear them all. Only do this for array layers to ensure all attachments have
                     // the same size.
                     MTLRenderPassDescriptor* descriptor = nil;
                     uint32_t attachment = 0;
 
-                    for (uint32_t arrayLayer = baseArrayLayer;
-                         arrayLayer < baseArrayLayer + layerCount; arrayLayer++) {
+                    for (uint32_t arrayLayer = range.baseArrayLayer;
+                         arrayLayer < range.baseArrayLayer + range.layerCount; arrayLayer++) {
                         if (clearValue == TextureBase::ClearValue::Zero &&
-                            IsSubresourceContentInitialized(level, 1, arrayLayer, 1)) {
+                            IsSubresourceContentInitialized(
+                                SubresourceRange::SingleSubresource(level, arrayLayer))) {
                             // Skip lazy clears if already initialized.
                             continue;
                         }
@@ -466,7 +467,7 @@
             }
         } else {
             // Compute the buffer size big enough to fill the largest mip.
-            Extent3D largestMipSize = GetMipLevelVirtualSize(baseMipLevel);
+            Extent3D largestMipSize = GetMipLevelVirtualSize(range.baseMipLevel);
 
             // Metal validation layers: sourceBytesPerRow must be at least 64.
             uint32_t largestMipBytesPerRow = std::max(
@@ -496,13 +497,15 @@
             id<MTLBuffer> uploadBuffer = ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle();
 
             // Encode a buffer to texture copy to clear each subresource.
-            for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
+            for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
+                 ++level) {
                 Extent3D virtualSize = GetMipLevelVirtualSize(level);
 
-                for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount;
-                     ++arrayLayer) {
+                for (uint32_t arrayLayer = range.baseArrayLayer;
+                     arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) {
                     if (clearValue == TextureBase::ClearValue::Zero &&
-                        IsSubresourceContentInitialized(level, 1, arrayLayer, 1)) {
+                        IsSubresourceContentInitialized(
+                            SubresourceRange::SingleSubresource(level, arrayLayer))) {
                         // Skip lazy clears if already initialized.
                         continue;
                     }
@@ -539,26 +542,20 @@
         }
 
         if (clearValue == TextureBase::ClearValue::Zero) {
-            SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
-                                               layerCount);
+            SetIsSubresourceContentInitialized(true, range);
             device->IncrementLazyClearCountForTesting();
         }
         return {};
     }
 
-    void Texture::EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
-                                                      uint32_t levelCount,
-                                                      uint32_t baseArrayLayer,
-                                                      uint32_t layerCount) {
+    void Texture::EnsureSubresourceContentInitialized(const SubresourceRange& range) {
         if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
             return;
         }
-        if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
-                                             layerCount)) {
+        if (!IsSubresourceContentInitialized(range)) {
             // If subresource has not been initialized, clear it to black as it could
             // contain dirty bits from recycled memory
-            GetDevice()->ConsumedError(ClearTexture(baseMipLevel, levelCount, baseArrayLayer,
-                                                    layerCount, TextureBase::ClearValue::Zero));
+            GetDevice()->ConsumedError(ClearTexture(range, TextureBase::ClearValue::Zero));
         }
     }
 
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index acaafe1..a3c5be3 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -458,8 +458,7 @@
                 // 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].usage & wgpu::TextureUsage::OutputAttachment)) {
-                    texture->EnsureSubresourceContentInitialized(0, texture->GetNumMipLevels(), 0,
-                                                                 texture->GetArrayLayers());
+                    texture->EnsureSubresourceContentInitialized(texture->GetAllSubresources());
                 }
             }
         };
@@ -513,12 +512,15 @@
                     Texture* texture = ToBackend(dst.texture.Get());
                     GLenum target = texture->GetGLTarget();
                     const GLFormat& format = texture->GetGLFormat();
+
+                    ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    ASSERT(copy->copySize.depth == 1);
+                    SubresourceRange subresource =
+                        SubresourceRange::SingleSubresource(dst.mipLevel, dst.arrayLayer);
                     if (IsCompleteSubresourceCopiedTo(texture, copySize, dst.mipLevel)) {
-                        texture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1,
-                                                                    dst.arrayLayer, 1);
+                        texture->SetIsSubresourceContentInitialized(true, subresource);
                     } else {
-                        texture->EnsureSubresourceContentInitialized(dst.mipLevel, 1,
-                                                                     dst.arrayLayer, 1);
+                        texture->EnsureSubresourceContentInitialized(subresource);
                     }
 
                     gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->GetHandle());
@@ -602,8 +604,10 @@
                         UNREACHABLE();
                     }
 
-                    texture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer,
-                                                                 1);
+                    ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    ASSERT(copy->copySize.depth == 1);
+                    texture->EnsureSubresourceContentInitialized(
+                        SubresourceRange::SingleSubresource(src.mipLevel, src.arrayLayer));
                     // The only way to move data from a texture to a buffer in GL is via
                     // glReadPixels with a pack buffer. Create a temporary FBO for the copy.
                     gl.BindTexture(target, texture->GetHandle());
@@ -675,14 +679,16 @@
                     Extent3D copySize = ComputeTextureCopyExtent(dst, copy->copySize);
                     Texture* srcTexture = ToBackend(src.texture.Get());
                     Texture* dstTexture = ToBackend(dst.texture.Get());
-                    srcTexture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer,
-                                                                    1);
+                    SubresourceRange srcRange = {src.mipLevel, 1, src.arrayLayer,
+                                                 copy->copySize.depth};
+                    SubresourceRange dstRange = {dst.mipLevel, 1, dst.arrayLayer,
+                                                 copy->copySize.depth};
+
+                    srcTexture->EnsureSubresourceContentInitialized(srcRange);
                     if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.mipLevel)) {
-                        dstTexture->SetIsSubresourceContentInitialized(
-                            true, dst.mipLevel, 1, dst.arrayLayer, copy->copySize.depth);
+                        dstTexture->SetIsSubresourceContentInitialized(true, dstRange);
                     } else {
-                        dstTexture->EnsureSubresourceContentInitialized(
-                            dst.mipLevel, 1, dst.arrayLayer, copy->copySize.depth);
+                        dstTexture->EnsureSubresourceContentInitialized(dstRange);
                     }
                     gl.CopyImageSubData(srcTexture->GetHandle(), srcTexture->GetGLTarget(),
                                         src.mipLevel, src.origin.x, src.origin.y, src.arrayLayer,
diff --git a/src/dawn_native/opengl/TextureGL.cpp b/src/dawn_native/opengl/TextureGL.cpp
index 9f2c163..7a18130 100644
--- a/src/dawn_native/opengl/TextureGL.cpp
+++ b/src/dawn_native/opengl/TextureGL.cpp
@@ -145,8 +145,8 @@
         gl.TexParameteri(mTarget, GL_TEXTURE_MAX_LEVEL, levels - 1);
 
         if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
-            GetDevice()->ConsumedError(ClearTexture(0, GetNumMipLevels(), 0, GetArrayLayers(),
-                                                    TextureBase::ClearValue::NonZero));
+            GetDevice()->ConsumedError(
+                ClearTexture(GetAllSubresources(), TextureBase::ClearValue::NonZero));
         }
     }
 
@@ -181,10 +181,7 @@
         return ToBackend(GetDevice())->GetGLFormat(GetFormat());
     }
 
-    MaybeError Texture::ClearTexture(GLint baseMipLevel,
-                                     GLint levelCount,
-                                     GLint baseArrayLayer,
-                                     GLint layerCount,
+    MaybeError Texture::ClearTexture(const SubresourceRange& range,
                                      TextureBase::ClearValue clearValue) {
         // TODO(jiawei.shao@intel.com): initialize the textures with compressed formats.
         if (GetFormat().isCompressed) {
@@ -224,31 +221,35 @@
                 gl.GenFramebuffers(1, &framebuffer);
                 gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
 
-                for (GLint level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
+                for (uint32_t level = range.baseMipLevel;
+                     level < range.baseMipLevel + range.levelCount; ++level) {
                     switch (GetDimension()) {
                         case wgpu::TextureDimension::e2D:
                             if (GetArrayLayers() == 1) {
                                 if (clearValue == TextureBase::ClearValue::Zero &&
-                                    IsSubresourceContentInitialized(level, 1, 0, 1)) {
+                                    IsSubresourceContentInitialized(
+                                        SubresourceRange::SingleSubresource(level, 0))) {
                                     // Skip lazy clears if already initialized.
                                     continue;
                                 }
                                 gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
                                                         GL_DEPTH_STENCIL_ATTACHMENT, GetGLTarget(),
-                                                        GetHandle(), level);
+                                                        GetHandle(), static_cast<GLint>(level));
                                 DoClear();
                             } else {
-                                for (GLint layer = baseArrayLayer;
-                                     layer < baseArrayLayer + layerCount; ++layer) {
+                                for (uint32_t layer = range.baseArrayLayer;
+                                     layer < range.baseArrayLayer + range.layerCount; ++layer) {
                                     if (clearValue == TextureBase::ClearValue::Zero &&
-                                        IsSubresourceContentInitialized(level, 1, layer, 1)) {
+                                        IsSubresourceContentInitialized(
+                                            SubresourceRange::SingleSubresource(level, layer))) {
                                         // Skip lazy clears if already initialized.
                                         continue;
                                     }
 
-                                    gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER,
-                                                               GL_DEPTH_STENCIL_ATTACHMENT,
-                                                               GetHandle(), level, layer);
+                                    gl.FramebufferTextureLayer(
+                                        GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+                                        GetHandle(), static_cast<GLint>(level),
+                                        static_cast<GLint>(layer));
                                     DoClear();
                                 }
                             }
@@ -268,16 +269,19 @@
                 clearColorData.fill(clearColor);
 
                 const GLFormat& glFormat = GetGLFormat();
-                for (GLint level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
+                for (uint32_t level = range.baseMipLevel;
+                     level < range.baseMipLevel + range.levelCount; ++level) {
                     Extent3D mipSize = GetMipLevelPhysicalSize(level);
-                    for (GLint layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
-                         ++layer) {
+                    for (uint32_t layer = range.baseArrayLayer;
+                         layer < range.baseArrayLayer + range.layerCount; ++layer) {
                         if (clearValue == TextureBase::ClearValue::Zero &&
-                            IsSubresourceContentInitialized(level, 1, layer, 1)) {
+                            IsSubresourceContentInitialized(
+                                SubresourceRange::SingleSubresource(level, layer))) {
                             // Skip lazy clears if already initialized.
                             continue;
                         }
-                        gl.ClearTexSubImage(mHandle, level, 0, 0, layer, mipSize.width,
+                        gl.ClearTexSubImage(mHandle, static_cast<GLint>(level), 0, 0,
+                                            static_cast<GLint>(layer), mipSize.width,
                                             mipSize.height, 1, glFormat.format, glFormat.type,
                                             clearColorData.data());
                     }
@@ -319,7 +323,8 @@
             gl.PixelStorei(GL_UNPACK_ROW_LENGTH,
                            (bytesPerRow / GetFormat().blockByteSize) * GetFormat().blockWidth);
             gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
-            for (GLint level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
+            for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
+                 ++level) {
                 gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, srcBuffer->GetHandle());
                 gl.ActiveTexture(GL_TEXTURE0);
                 gl.BindTexture(GetGLTarget(), GetHandle());
@@ -329,23 +334,26 @@
                     case wgpu::TextureDimension::e2D:
                         if (GetArrayLayers() == 1) {
                             if (clearValue == TextureBase::ClearValue::Zero &&
-                                IsSubresourceContentInitialized(level, 1, 0, 1)) {
+                                IsSubresourceContentInitialized(
+                                    SubresourceRange::SingleSubresource(level, 0))) {
                                 // Skip lazy clears if already initialized.
                                 continue;
                             }
-                            gl.TexSubImage2D(GetGLTarget(), level, 0, 0, size.width, size.height,
-                                             GetGLFormat().format, GetGLFormat().type, 0);
+                            gl.TexSubImage2D(GetGLTarget(), static_cast<GLint>(level), 0, 0,
+                                             size.width, size.height, GetGLFormat().format,
+                                             GetGLFormat().type, 0);
                         } else {
-                            for (GLint layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
-                                 ++layer) {
+                            for (uint32_t layer = range.baseArrayLayer;
+                                 layer < range.baseArrayLayer + range.layerCount; ++layer) {
                                 if (clearValue == TextureBase::ClearValue::Zero &&
-                                    IsSubresourceContentInitialized(level, 1, layer, 1)) {
+                                    IsSubresourceContentInitialized(
+                                        SubresourceRange::SingleSubresource(level, layer))) {
                                     // Skip lazy clears if already initialized.
                                     continue;
                                 }
-                                gl.TexSubImage3D(GetGLTarget(), level, 0, 0, layer, size.width,
-                                                 size.height, 1, GetGLFormat().format,
-                                                 GetGLFormat().type, 0);
+                                gl.TexSubImage3D(GetGLTarget(), static_cast<GLint>(level), 0, 0,
+                                                 static_cast<GLint>(layer), size.width, size.height,
+                                                 1, GetGLFormat().format, GetGLFormat().type, 0);
                             }
                         }
                         break;
@@ -360,24 +368,18 @@
             gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
         }
         if (clearValue == TextureBase::ClearValue::Zero) {
-            SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
-                                               layerCount);
+            SetIsSubresourceContentInitialized(true, range);
             device->IncrementLazyClearCountForTesting();
         }
         return {};
     }
 
-    void Texture::EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
-                                                      uint32_t levelCount,
-                                                      uint32_t baseArrayLayer,
-                                                      uint32_t layerCount) {
+    void Texture::EnsureSubresourceContentInitialized(const SubresourceRange& range) {
         if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
             return;
         }
-        if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
-                                             layerCount)) {
-            GetDevice()->ConsumedError(ClearTexture(baseMipLevel, levelCount, baseArrayLayer,
-                                                    layerCount, TextureBase::ClearValue::Zero));
+        if (!IsSubresourceContentInitialized(range)) {
+            GetDevice()->ConsumedError(ClearTexture(range, TextureBase::ClearValue::Zero));
         }
     }
 
diff --git a/src/dawn_native/opengl/TextureGL.h b/src/dawn_native/opengl/TextureGL.h
index b48cda3..a01b943 100644
--- a/src/dawn_native/opengl/TextureGL.h
+++ b/src/dawn_native/opengl/TextureGL.h
@@ -36,20 +36,13 @@
         GLenum GetGLTarget() const;
         const GLFormat& GetGLFormat() const;
 
-        void EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
-                                                 uint32_t levelCount,
-                                                 uint32_t baseArrayLayer,
-                                                 uint32_t layerCount);
+        void EnsureSubresourceContentInitialized(const SubresourceRange& range);
 
       private:
         ~Texture() override;
 
         void DestroyImpl() override;
-        MaybeError ClearTexture(GLint baseMipLevel,
-                                GLint levelCount,
-                                GLint baseArrayLayer,
-                                GLint layerCount,
-                                TextureBase::ClearValue clearValue);
+        MaybeError ClearTexture(const SubresourceRange& range, TextureBase::ClearValue clearValue);
 
         GLuint mHandle;
         GLenum mTarget;
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index f1e3e46..460977c 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -395,9 +395,8 @@
                 // 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].usage & wgpu::TextureUsage::OutputAttachment)) {
-                    texture->EnsureSubresourceContentInitialized(recordingContext, 0,
-                                                                 texture->GetNumMipLevels(), 0,
-                                                                 texture->GetArrayLayers());
+                    texture->EnsureSubresourceContentInitialized(recordingContext,
+                                                                 texture->GetAllSubresources());
                 }
                 texture->TransitionUsageForPass(recordingContext, usages.textureUsages[i],
                                                 &imageBarriers, &srcStages, &dstStages);
@@ -446,16 +445,17 @@
                         ComputeBufferImageCopyRegion(src, dst, copy->copySize);
                     VkImageSubresourceLayers subresource = region.imageSubresource;
 
+                    ASSERT(dst.texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    ASSERT(copy->copySize.depth == 1);
+                    SubresourceRange range = SubresourceRange::SingleSubresource(
+                        subresource.mipLevel, subresource.baseArrayLayer);
                     if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
                                                       subresource.mipLevel)) {
                         // Since texture has been overwritten, it has been "initialized"
-                        dst.texture->SetIsSubresourceContentInitialized(
-                            true, subresource.mipLevel, 1, subresource.baseArrayLayer, 1);
+                        dst.texture->SetIsSubresourceContentInitialized(true, range);
                     } else {
                         ToBackend(dst.texture)
-                            ->EnsureSubresourceContentInitialized(recordingContext,
-                                                                  subresource.mipLevel, 1,
-                                                                  subresource.baseArrayLayer, 1);
+                            ->EnsureSubresourceContentInitialized(recordingContext, range);
                     }
                     ToBackend(src.buffer)
                         ->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc);
@@ -483,10 +483,13 @@
                         ComputeBufferImageCopyRegion(dst, src, copy->copySize);
                     VkImageSubresourceLayers subresource = region.imageSubresource;
 
+                    ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D);
+                    ASSERT(copy->copySize.depth == 1);
                     ToBackend(src.texture)
-                        ->EnsureSubresourceContentInitialized(recordingContext,
-                                                              subresource.mipLevel, 1,
-                                                              subresource.baseArrayLayer, 1);
+                        ->EnsureSubresourceContentInitialized(
+                            recordingContext,
+                            SubresourceRange::SingleSubresource(subresource.mipLevel,
+                                                                subresource.baseArrayLayer));
 
                     ToBackend(src.texture)
                         ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc,
@@ -508,20 +511,20 @@
                         mCommands.NextCommand<CopyTextureToTextureCmd>();
                     TextureCopy& src = copy->source;
                     TextureCopy& dst = copy->destination;
+                    SubresourceRange srcRange = {src.mipLevel, 1, src.arrayLayer,
+                                                 copy->copySize.depth};
+                    SubresourceRange dstRange = {dst.mipLevel, 1, dst.arrayLayer,
+                                                 copy->copySize.depth};
 
                     ToBackend(src.texture)
-                        ->EnsureSubresourceContentInitialized(recordingContext, src.mipLevel, 1,
-                                                              src.arrayLayer, 1);
+                        ->EnsureSubresourceContentInitialized(recordingContext, srcRange);
                     if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
                                                       dst.mipLevel)) {
                         // Since destination texture has been overwritten, it has been "initialized"
-                        dst.texture->SetIsSubresourceContentInitialized(
-                            true, dst.mipLevel, 1, dst.arrayLayer, copy->copySize.depth);
+                        dst.texture->SetIsSubresourceContentInitialized(true, dstRange);
                     } else {
                         ToBackend(dst.texture)
-                            ->EnsureSubresourceContentInitialized(recordingContext, dst.mipLevel, 1,
-                                                                  dst.arrayLayer,
-                                                                  copy->copySize.depth);
+                            ->EnsureSubresourceContentInitialized(recordingContext, dstRange);
                     }
 
                     if (src.texture.Get() == dst.texture.Get() && src.mipLevel == dst.mipLevel) {
diff --git a/src/dawn_native/vulkan/TextureVk.cpp b/src/dawn_native/vulkan/TextureVk.cpp
index af3b604..90a6d95 100644
--- a/src/dawn_native/vulkan/TextureVk.cpp
+++ b/src/dawn_native/vulkan/TextureVk.cpp
@@ -539,9 +539,8 @@
             "BindImageMemory"));
 
         if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
-            DAWN_TRY(ClearTexture(ToBackend(GetDevice())->GetPendingRecordingContext(), 0,
-                                  GetNumMipLevels(), 0, GetArrayLayers(),
-                                  TextureBase::ClearValue::NonZero));
+            DAWN_TRY(ClearTexture(ToBackend(GetDevice())->GetPendingRecordingContext(),
+                                  GetAllSubresources(), TextureBase::ClearValue::NonZero));
         }
 
         return {};
@@ -595,7 +594,7 @@
 
         // Don't clear imported texture if already cleared
         if (descriptor->isCleared) {
-            SetIsSubresourceContentInitialized(true, 0, 1, 0, 1);
+            SetIsSubresourceContentInitialized(true, {0, 1, 0, 1});
         }
 
         // Success, acquire all the external objects.
@@ -843,50 +842,49 @@
     }
 
     MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext,
-                                     uint32_t baseMipLevel,
-                                     uint32_t levelCount,
-                                     uint32_t baseArrayLayer,
-                                     uint32_t layerCount,
+                                     const SubresourceRange& range,
                                      TextureBase::ClearValue clearValue) {
         Device* device = ToBackend(GetDevice());
 
         uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
         float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
 
-        TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, baseMipLevel, levelCount,
-                           baseArrayLayer, layerCount);
+        TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, range.baseMipLevel,
+                           range.levelCount, range.baseArrayLayer, range.layerCount);
         if (GetFormat().isRenderable) {
-            VkImageSubresourceRange range = {};
-            range.aspectMask = GetVkAspectMask();
-            range.levelCount = 1;
-            range.layerCount = 1;
+            VkImageSubresourceRange imageRange = {};
+            imageRange.aspectMask = GetVkAspectMask();
+            imageRange.levelCount = 1;
+            imageRange.layerCount = 1;
 
-            for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
-                range.baseMipLevel = level;
-                for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
-                     ++layer) {
+            for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
+                 ++level) {
+                imageRange.baseMipLevel = level;
+                for (uint32_t layer = range.baseArrayLayer;
+                     layer < range.baseArrayLayer + range.layerCount; ++layer) {
                     if (clearValue == TextureBase::ClearValue::Zero &&
-                        IsSubresourceContentInitialized(level, 1, layer, 1)) {
+                        IsSubresourceContentInitialized(
+                            SubresourceRange::SingleSubresource(level, layer))) {
                         // Skip lazy clears if already initialized.
                         continue;
                     }
 
-                    range.baseArrayLayer = layer;
+                    imageRange.baseArrayLayer = layer;
 
                     if (GetFormat().HasDepthOrStencil()) {
                         VkClearDepthStencilValue clearDepthStencilValue[1];
                         clearDepthStencilValue[0].depth = fClearColor;
                         clearDepthStencilValue[0].stencil = clearColor;
-                        device->fn.CmdClearDepthStencilImage(recordingContext->commandBuffer,
-                                                             GetHandle(),
-                                                             VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                                                             clearDepthStencilValue, 1, &range);
+                        device->fn.CmdClearDepthStencilImage(
+                            recordingContext->commandBuffer, GetHandle(),
+                            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clearDepthStencilValue, 1,
+                            &imageRange);
                     } else {
                         VkClearColorValue clearColorValue = {
                             {fClearColor, fClearColor, fClearColor, fClearColor}};
                         device->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(),
                                                       VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                                                      &clearColorValue, 1, &range);
+                                                      &clearColorValue, 1, &imageRange);
                     }
                 }
             }
@@ -913,13 +911,15 @@
             bufferCopy.offset = uploadHandle.startOffset;
             bufferCopy.bytesPerRow = bytesPerRow;
 
-            for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
+            for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
+                 ++level) {
                 Extent3D copySize = GetMipLevelVirtualSize(level);
 
-                for (uint32_t layer = baseArrayLayer; layer < baseArrayLayer + layerCount;
-                     ++layer) {
+                for (uint32_t layer = range.baseArrayLayer;
+                     layer < range.baseArrayLayer + range.layerCount; ++layer) {
                     if (clearValue == TextureBase::ClearValue::Zero &&
-                        IsSubresourceContentInitialized(level, 1, layer, 1)) {
+                        IsSubresourceContentInitialized(
+                            SubresourceRange::SingleSubresource(level, layer))) {
                         // Skip lazy clears if already initialized.
                         continue;
                     }
@@ -942,23 +942,18 @@
             }
         }
         if (clearValue == TextureBase::ClearValue::Zero) {
-            SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
-                                               layerCount);
+            SetIsSubresourceContentInitialized(true, range);
             device->IncrementLazyClearCountForTesting();
         }
         return {};
     }
 
     void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
-                                                      uint32_t baseMipLevel,
-                                                      uint32_t levelCount,
-                                                      uint32_t baseArrayLayer,
-                                                      uint32_t layerCount) {
+                                                      const SubresourceRange& range) {
         if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
             return;
         }
-        if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
-                                             layerCount)) {
+        if (!IsSubresourceContentInitialized(range)) {
             // TODO(jiawei.shao@intel.com): initialize textures in BC formats with Buffer-to-Texture
             // copies.
             if (GetFormat().isCompressed) {
@@ -967,9 +962,8 @@
 
             // If subresource has not been initialized, clear it to black as it could contain dirty
             // bits from recycled memory
-            GetDevice()->ConsumedError(ClearTexture(recordingContext, baseMipLevel, levelCount,
-                                                    baseArrayLayer, layerCount,
-                                                    TextureBase::ClearValue::Zero));
+            GetDevice()->ConsumedError(
+                ClearTexture(recordingContext, range, TextureBase::ClearValue::Zero));
         }
     }
 
diff --git a/src/dawn_native/vulkan/TextureVk.h b/src/dawn_native/vulkan/TextureVk.h
index 4bb9ab6..4da7c48 100644
--- a/src/dawn_native/vulkan/TextureVk.h
+++ b/src/dawn_native/vulkan/TextureVk.h
@@ -80,10 +80,7 @@
                                     VkPipelineStageFlags* dstStages);
 
         void EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext,
-                                                 uint32_t baseMipLevel,
-                                                 uint32_t levelCount,
-                                                 uint32_t baseArrayLayer,
-                                                 uint32_t layerCount);
+                                                 const SubresourceRange& range);
 
         MaybeError SignalAndDestroy(VkSemaphore* outSignalSemaphore);
         // Binds externally allocated memory to the VkImage and on success, takes ownership of
@@ -104,10 +101,7 @@
 
         void DestroyImpl() override;
         MaybeError ClearTexture(CommandRecordingContext* recordingContext,
-                                uint32_t baseMipLevel,
-                                uint32_t levelCount,
-                                uint32_t baseArrayLayer,
-                                uint32_t layerCount,
+                                const SubresourceRange& range,
                                 TextureBase::ClearValue);
 
         void TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext,