Pass Aspect to TextureBase::GetSize for multiplanar sizes.

Bug: dawn:2099
Change-Id: Ie25ee6686f0ccfcfd33116c1d6316d671c91a36c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/154760
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Austin Eng <enga@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
diff --git a/src/dawn/native/CommandBuffer.cpp b/src/dawn/native/CommandBuffer.cpp
index 06a12f6..9bfc06d 100644
--- a/src/dawn/native/CommandBuffer.cpp
+++ b/src/dawn/native/CommandBuffer.cpp
@@ -91,9 +91,12 @@
 }
 
 bool IsCompleteSubresourceCopiedTo(const TextureBase* texture,
-                                   const Extent3D copySize,
-                                   const uint32_t mipLevel) {
-    Extent3D extent = texture->GetMipLevelSingleSubresourcePhysicalSize(mipLevel);
+                                   const Extent3D& copySize,
+                                   const uint32_t mipLevel,
+                                   Aspect aspect) {
+    DAWN_ASSERT(HasOneBit(aspect) || aspect == (Aspect::Depth | Aspect::Stencil));
+
+    Extent3D extent = texture->GetMipLevelSingleSubresourcePhysicalSize(mipLevel, aspect);
 
     switch (texture->GetDimension()) {
         case wgpu::TextureDimension::e1D:
@@ -108,6 +111,14 @@
     DAWN_UNREACHABLE();
 }
 
+bool IsCompleteSubresourceCopiedTo(const TextureBase* texture,
+                                   const Extent3D& copySize,
+                                   const uint32_t mipLevel,
+                                   wgpu::TextureAspect textureAspect) {
+    auto aspect = SelectFormatAspects(texture->GetFormat(), textureAspect);
+    return IsCompleteSubresourceCopiedTo(texture, copySize, mipLevel, aspect);
+}
+
 SubresourceRange GetSubresourcesAffectedByCopy(const TextureCopy& copy, const Extent3D& copySize) {
     switch (copy.texture->GetDimension()) {
         case wgpu::TextureDimension::e1D:
diff --git a/src/dawn/native/CommandBuffer.h b/src/dawn/native/CommandBuffer.h
index 12a81b3..a64c6da 100644
--- a/src/dawn/native/CommandBuffer.h
+++ b/src/dawn/native/CommandBuffer.h
@@ -64,8 +64,13 @@
 };
 
 bool IsCompleteSubresourceCopiedTo(const TextureBase* texture,
-                                   const Extent3D copySize,
-                                   const uint32_t mipLevel);
+                                   const Extent3D& copySize,
+                                   const uint32_t mipLevel,
+                                   Aspect aspect);
+bool IsCompleteSubresourceCopiedTo(const TextureBase* texture,
+                                   const Extent3D& copySize,
+                                   const uint32_t mipLevel,
+                                   wgpu::TextureAspect textureAspect);
 SubresourceRange GetSubresourcesAffectedByCopy(const TextureCopy& copy, const Extent3D& copySize);
 
 void LazyClearRenderPassAttachments(BeginRenderPassCmd* renderPass);
diff --git a/src/dawn/native/CommandEncoder.cpp b/src/dawn/native/CommandEncoder.cpp
index 7ce6935..ee88515 100644
--- a/src/dawn/native/CommandEncoder.cpp
+++ b/src/dawn/native/CommandEncoder.cpp
@@ -144,14 +144,7 @@
 MaybeError ValidateOrSetAttachmentSize(const TextureViewBase* attachment,
                                        uint32_t* width,
                                        uint32_t* height) {
-    Extent3D attachmentSize = attachment->GetTexture()->GetMipLevelSingleSubresourceVirtualSize(
-        attachment->GetBaseMipLevel());
-    // TODO(dawn:2099): TextureBase::GetWidth/Height should take an Aspect parameter and perform the
-    // necessary computation instead.
-    if (attachment->GetTexture()->GetFormat().IsMultiPlanar()) {
-        attachmentSize = attachment->GetTexture()->GetFormat().GetAspectSize(
-            attachment->GetAspects(), attachmentSize);
-    }
+    Extent3D attachmentSize = attachment->GetSingleSubresourceVirtualSize();
 
     if (*width == 0) {
         DAWN_ASSERT(*height == 0);
@@ -230,12 +223,8 @@
                     "The resolve target %s mip level count (%u) is not 1.", resolveTarget,
                     resolveTarget->GetLevelCount());
 
-    const Extent3D& colorTextureSize =
-        attachment->GetTexture()->GetMipLevelSingleSubresourceVirtualSize(
-            attachment->GetBaseMipLevel());
-    const Extent3D& resolveTextureSize =
-        resolveTarget->GetTexture()->GetMipLevelSingleSubresourceVirtualSize(
-            resolveTarget->GetBaseMipLevel());
+    const Extent3D& colorTextureSize = attachment->GetSingleSubresourceVirtualSize();
+    const Extent3D& resolveTextureSize = resolveTarget->GetSingleSubresourceVirtualSize();
     DAWN_INVALID_IF(colorTextureSize.width != resolveTextureSize.width ||
                         colorTextureSize.height != resolveTextureSize.height,
                     "The Resolve target %s size (width: %u, height: %u) does not match the color "
@@ -260,9 +249,7 @@
 MaybeError ValidateColorAttachmentDepthSlice(const TextureViewBase* attachment,
                                              uint32_t depthSlice) {
     if (attachment->GetDimension() == wgpu::TextureViewDimension::e3D) {
-        const Extent3D& attachmentSize =
-            attachment->GetTexture()->GetMipLevelSingleSubresourceVirtualSize(
-                attachment->GetBaseMipLevel());
+        const Extent3D& attachmentSize = attachment->GetSingleSubresourceVirtualSize();
 
         DAWN_INVALID_IF(depthSlice >= attachmentSize.depthOrArrayLayers,
                         "The depth slice index (%u) of 3D %s used as attachment is >= the "
@@ -1047,7 +1034,7 @@
                     Ref<TextureViewBase> implicitMSAATargetRef;
                     DAWN_TRY_ASSIGN(implicitMSAATargetRef,
                                     device->CreateImplicitMSAARenderTextureViewFor(
-                                        resolveTarget->GetTexture(), implicitSampleCount));
+                                        resolveTarget, implicitSampleCount));
                     colorTarget = implicitMSAATargetRef.Get();
 
                     cmd->colorAttachments[index].view = std::move(implicitMSAATargetRef);
@@ -1314,9 +1301,7 @@
                 descriptor.usage =
                     wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
                 descriptor.format = resolveTarget->GetFormat().format;
-                descriptor.size =
-                    resolveTarget->GetTexture()->GetMipLevelSingleSubresourceVirtualSize(
-                        resolveTarget->GetBaseMipLevel());
+                descriptor.size = resolveTarget->GetSingleSubresourceVirtualSize();
                 descriptor.dimension = wgpu::TextureDimension::e2D;
                 descriptor.mipLevelCount = 1;
 
@@ -1372,7 +1357,7 @@
                         dstImageCopyTexture.origin = {0, 0,
                                                       copyTarget.copyDst->GetBaseArrayLayer()};
 
-                        Extent3D extent3D = copyTarget.copySrc->GetTexture()->GetSize();
+                        Extent3D extent3D = copyTarget.copySrc->GetSingleSubresourceVirtualSize();
 
                         auto internalUsageScope = MakeInternalUsageScope();
                         this->APICopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture,
@@ -1677,6 +1662,10 @@
                 DAWN_TRY(GetDevice()->ValidateObject(source->texture));
                 DAWN_TRY(GetDevice()->ValidateObject(destination->texture));
 
+                DAWN_INVALID_IF(source->texture->GetFormat().IsMultiPlanar() ||
+                                    destination->texture->GetFormat().IsMultiPlanar(),
+                                "Copying between a multiplanar texture and another texture is "
+                                "currently not allowed.");
                 DAWN_TRY_CONTEXT(ValidateImageCopyTexture(GetDevice(), *source, *copySize),
                                  "validating source %s.", source->texture);
                 DAWN_TRY_CONTEXT(ValidateImageCopyTexture(GetDevice(), *destination, *copySize),
diff --git a/src/dawn/native/CommandValidation.cpp b/src/dawn/native/CommandValidation.cpp
index 609d3c8..d3a92ce 100644
--- a/src/dawn/native/CommandValidation.cpp
+++ b/src/dawn/native/CommandValidation.cpp
@@ -337,13 +337,15 @@
                     textureCopy.mipLevel, texture->GetNumMipLevels(), texture);
 
     DAWN_TRY(ValidateTextureAspect(textureCopy.aspect));
-    DAWN_INVALID_IF(SelectFormatAspects(texture->GetFormat(), textureCopy.aspect) == Aspect::None,
+
+    const auto aspect = SelectFormatAspects(texture->GetFormat(), textureCopy.aspect);
+    DAWN_INVALID_IF(aspect == Aspect::None,
                     "%s format (%s) does not have the selected aspect (%s).", texture,
                     texture->GetFormat().format, textureCopy.aspect);
 
     if (texture->GetSampleCount() > 1 || texture->GetFormat().HasDepthOrStencil()) {
         Extent3D subresourceSize =
-            texture->GetMipLevelSingleSubresourcePhysicalSize(textureCopy.mipLevel);
+            texture->GetMipLevelSingleSubresourcePhysicalSize(textureCopy.mipLevel, aspect);
         DAWN_ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
         DAWN_INVALID_IF(
             textureCopy.origin.x != 0 || textureCopy.origin.y != 0 ||
@@ -363,9 +365,14 @@
                                     const ImageCopyTexture& textureCopy,
                                     const Extent3D& copySize) {
     const TextureBase* texture = textureCopy.texture;
+    const Format& format = textureCopy.texture->GetFormat();
+    const Aspect aspect = ConvertAspect(format, textureCopy.aspect);
+
+    DAWN_ASSERT(!format.IsMultiPlanar() || HasOneBit(aspect));
 
     // Validation for the copy being in-bounds:
-    Extent3D mipSize = texture->GetMipLevelSingleSubresourcePhysicalSize(textureCopy.mipLevel);
+    Extent3D mipSize =
+        texture->GetMipLevelSingleSubresourcePhysicalSize(textureCopy.mipLevel, aspect);
     // For 1D/2D textures, include the array layer as depth so it can be checked with other
     // dimensions.
     if (texture->GetDimension() != wgpu::TextureDimension::e3D) {
@@ -386,7 +393,6 @@
         &textureCopy.origin, &copySize, texture, textureCopy.mipLevel, &mipSize);
 
     // Validation for the texel block alignments:
-    const Format& format = textureCopy.texture->GetFormat();
     if (format.isCompressed) {
         const TexelBlockInfo& blockInfo = format.GetAspectInfo(textureCopy.aspect).block;
         DAWN_INVALID_IF(
diff --git a/src/dawn/native/CopyTextureForBrowserHelper.cpp b/src/dawn/native/CopyTextureForBrowserHelper.cpp
index 0ebd828..3c46ff7 100644
--- a/src/dawn/native/CopyTextureForBrowserHelper.cpp
+++ b/src/dawn/native/CopyTextureForBrowserHelper.cpp
@@ -747,7 +747,7 @@
                                    const CopyTextureForBrowserOptions* options) {
     TextureInfo info;
     info.origin = source->origin;
-    info.size = source->texture->GetSize();
+    info.size = source->texture->GetSize(source->aspect);
 
     Ref<TextureViewBase> srcTextureView = nullptr;
     TextureViewDescriptor srcTextureViewDesc = {};
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 7a9bd34c..83cbd18 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -884,14 +884,15 @@
 }
 
 ResultOrError<Ref<TextureViewBase>> DeviceBase::CreateImplicitMSAARenderTextureViewFor(
-    const TextureBase* singleSampledTexture,
+    const TextureViewBase* singleSampledTextureView,
     uint32_t sampleCount) {
     DAWN_ASSERT(IsLockedByCurrentThreadIfNeeded());
 
     TextureDescriptor desc = {};
     desc.dimension = wgpu::TextureDimension::e2D;
-    desc.format = singleSampledTexture->GetFormat().format;
-    desc.size = {singleSampledTexture->GetWidth(), singleSampledTexture->GetHeight(), 1};
+    desc.format = singleSampledTextureView->GetFormat().format;
+    desc.size = {singleSampledTextureView->GetSingleSubresourceVirtualSize().width,
+                 singleSampledTextureView->GetSingleSubresourceVirtualSize().height, 1};
     desc.sampleCount = sampleCount;
     desc.usage = wgpu::TextureUsage::RenderAttachment;
     if (HasFeature(Feature::TransientAttachments)) {
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index a8ea07f..1a4c9dc 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -200,7 +200,7 @@
     PipelineLayoutBase* GetEmptyPipelineLayout();
 
     ResultOrError<Ref<TextureViewBase>> CreateImplicitMSAARenderTextureViewFor(
-        const TextureBase* singleSampledTexture,
+        const TextureViewBase* singleSampledTexture,
         uint32_t sampleCount);
 
     ResultOrError<Ref<TextureViewBase>> GetOrCreatePlaceholderTextureViewForExternalTexture();
diff --git a/src/dawn/native/ExternalTexture.cpp b/src/dawn/native/ExternalTexture.cpp
index be0c85a..1f64fdf 100644
--- a/src/dawn/native/ExternalTexture.cpp
+++ b/src/dawn/native/ExternalTexture.cpp
@@ -101,7 +101,7 @@
     DAWN_INVALID_IF(descriptor->visibleSize.width == 0 || descriptor->visibleSize.height == 0,
                     "VisibleSize %s have 0 on width or height.", &descriptor->visibleSize);
 
-    const Extent3D textureSize = descriptor->plane0->GetTexture()->GetSize();
+    const Extent3D textureSize = descriptor->plane0->GetSingleSubresourceVirtualSize();
     DAWN_INVALID_IF(descriptor->visibleSize.width > textureSize.width ||
                         descriptor->visibleSize.height > textureSize.height,
                     "VisibleSize %s is exceed the texture size, defined by Plane0 size (%u, %u).",
@@ -289,8 +289,8 @@
     // Calculate scale factors and offsets from the specified visibleSize.
     DAWN_ASSERT(descriptor->visibleSize.width > 0);
     DAWN_ASSERT(descriptor->visibleSize.height > 0);
-    uint32_t frameWidth = descriptor->plane0->GetTexture()->GetWidth();
-    uint32_t frameHeight = descriptor->plane0->GetTexture()->GetHeight();
+    uint32_t frameWidth = descriptor->plane0->GetSingleSubresourceVirtualSize().width;
+    uint32_t frameHeight = descriptor->plane0->GetSingleSubresourceVirtualSize().height;
     float xScale =
         static_cast<float>(descriptor->visibleSize.width) / static_cast<float>(frameWidth);
     float yScale =
diff --git a/src/dawn/native/Format.cpp b/src/dawn/native/Format.cpp
index 87d081a..715be6f 100644
--- a/src/dawn/native/Format.cpp
+++ b/src/dawn/native/Format.cpp
@@ -140,41 +140,6 @@
     return aspectInfo[aspectIndex];
 }
 
-Extent3D Format::GetAspectSize(Aspect aspect, const Extent3D& textureSize) const {
-    switch (aspect) {
-        case Aspect::Color:
-        case Aspect::Depth:
-        case Aspect::Stencil:
-        case Aspect::CombinedDepthStencil:
-            return textureSize;
-        case Aspect::Plane0:
-            DAWN_ASSERT(IsMultiPlanar());
-            return textureSize;
-        case Aspect::Plane1: {
-            DAWN_ASSERT(IsMultiPlanar());
-            auto planeSize = textureSize;
-            switch (format) {
-                case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
-                case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
-                    if (planeSize.width > 1) {
-                        planeSize.width >>= 1;
-                    }
-                    if (planeSize.height > 1) {
-                        planeSize.height >>= 1;
-                    }
-                    break;
-                default:
-                    DAWN_UNREACHABLE();
-            }
-            return planeSize;
-        }
-        case Aspect::None:
-            break;
-    }
-
-    DAWN_UNREACHABLE();
-}
-
 FormatIndex Format::GetIndex() const {
     return ComputeFormatIndex(format);
 }
diff --git a/src/dawn/native/Format.h b/src/dawn/native/Format.h
index 1137ff4..8a84465 100644
--- a/src/dawn/native/Format.h
+++ b/src/dawn/native/Format.h
@@ -154,9 +154,6 @@
     // Currently means they differ only in sRGB-ness.
     bool ViewCompatibleWith(const Format& otherFormat) const;
 
-    // Returns the aspect's size given the texture's size.
-    Extent3D GetAspectSize(Aspect aspect, const Extent3D& textureSize) const;
-
   private:
     // Used to store the aspectInfo for one or more planes. For single plane "color" formats,
     // only the first aspect info or aspectInfo[0] is valid. For depth-stencil, the first aspect
diff --git a/src/dawn/native/SwapChain.cpp b/src/dawn/native/SwapChain.cpp
index 9ef69f5..469b616 100644
--- a/src/dawn/native/SwapChain.cpp
+++ b/src/dawn/native/SwapChain.cpp
@@ -200,8 +200,8 @@
     DAWN_ASSERT(mCurrentTexture->GetFormat().format == mFormat);
     DAWN_ASSERT(IsSubset(mUsage, mCurrentTexture->GetUsage()));
     DAWN_ASSERT(mCurrentTexture->GetDimension() == wgpu::TextureDimension::e2D);
-    DAWN_ASSERT(mCurrentTexture->GetWidth() == mWidth);
-    DAWN_ASSERT(mCurrentTexture->GetHeight() == mHeight);
+    DAWN_ASSERT(mCurrentTexture->GetWidth(Aspect::Color) == mWidth);
+    DAWN_ASSERT(mCurrentTexture->GetHeight(Aspect::Color) == mHeight);
     DAWN_ASSERT(mCurrentTexture->GetNumMipLevels() == 1);
     DAWN_ASSERT(mCurrentTexture->GetArrayLayers() == 1);
 
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index 02bbaba..7ec38f4 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -210,11 +210,12 @@
         case wgpu::TextureViewDimension::Cube:
         case wgpu::TextureViewDimension::CubeArray:
             DAWN_INVALID_IF(
-                texture->GetSize().width != texture->GetSize().height,
+                texture->GetSize(descriptor->aspect).width !=
+                    texture->GetSize(descriptor->aspect).height,
                 "A %s texture view is not compatible with %s because the texture's width "
                 "(%u) and height (%u) are not equal.",
-                descriptor->dimension, texture, texture->GetSize().width,
-                texture->GetSize().height);
+                descriptor->dimension, texture, texture->GetSize(descriptor->aspect).width,
+                texture->GetSize(descriptor->aspect).height);
             DAWN_INVALID_IF(descriptor->dimension == wgpu::TextureViewDimension::CubeArray &&
                                 device->IsCompatibilityMode(),
                             "A %s texture view for %s is not supported in compatibility mode",
@@ -516,7 +517,8 @@
     const Format* viewFormat;
     DAWN_TRY_ASSIGN(viewFormat, device->GetInternalFormat(descriptor->format));
 
-    DAWN_INVALID_IF(SelectFormatAspects(format, descriptor->aspect) == Aspect::None,
+    const auto aspect = SelectFormatAspects(format, descriptor->aspect);
+    DAWN_INVALID_IF(aspect == Aspect::None,
                     "Texture format (%s) does not have the texture view's selected aspect (%s).",
                     format.format, descriptor->aspect);
 
@@ -638,7 +640,7 @@
     : ApiObjectBase(device, descriptor->label),
       mDimension(descriptor->dimension),
       mFormat(device->GetValidInternalFormat(descriptor->format)),
-      mSize(descriptor->size),
+      mBaseSize(descriptor->size),
       mMipLevelCount(descriptor->mipLevelCount),
       mSampleCount(descriptor->sampleCount),
       mUsage(descriptor->usage),
@@ -699,7 +701,7 @@
     : ApiObjectBase(device, tag, descriptor->label),
       mDimension(descriptor->dimension),
       mFormat(kUnusedFormat),
-      mSize(descriptor->size),
+      mBaseSize(descriptor->size),
       mMipLevelCount(descriptor->mipLevelCount),
       mSampleCount(descriptor->sampleCount),
       mUsage(descriptor->usage),
@@ -734,29 +736,74 @@
     DAWN_ASSERT(!IsError());
     return mViewFormats;
 }
-const Extent3D& TextureBase::GetSize() const {
+
+const Extent3D& TextureBase::GetBaseSize() const {
     DAWN_ASSERT(!IsError());
-    return mSize;
+    return mBaseSize;
 }
-uint32_t TextureBase::GetWidth() const {
+
+Extent3D TextureBase::GetSize(Aspect aspect) const {
     DAWN_ASSERT(!IsError());
-    return mSize.width;
+    switch (aspect) {
+        case Aspect::Color:
+        case Aspect::Depth:
+        case Aspect::Stencil:
+        case Aspect::CombinedDepthStencil:
+            return mBaseSize;
+        case Aspect::Plane0:
+            DAWN_ASSERT(GetFormat().IsMultiPlanar());
+            return mBaseSize;
+        case Aspect::Plane1: {
+            DAWN_ASSERT(GetFormat().IsMultiPlanar());
+            auto planeSize = mBaseSize;
+            switch (GetFormat().format) {
+                case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
+                case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
+                    if (planeSize.width > 1) {
+                        planeSize.width >>= 1;
+                    }
+                    if (planeSize.height > 1) {
+                        planeSize.height >>= 1;
+                    }
+                    break;
+                default:
+                    DAWN_UNREACHABLE();
+            }
+            return planeSize;
+        }
+        case Aspect::None:
+            break;
+    }
+
+    if (aspect == (Aspect::Depth | Aspect::Stencil)) {
+        return mBaseSize;
+    }
+
+    DAWN_UNREACHABLE();
 }
-uint32_t TextureBase::GetHeight() const {
+Extent3D TextureBase::GetSize(wgpu::TextureAspect textureAspect) const {
+    const auto aspect = SelectFormatAspects(GetFormat(), textureAspect);
+    return GetSize(aspect);
+}
+uint32_t TextureBase::GetWidth(Aspect aspect) const {
     DAWN_ASSERT(!IsError());
-    return mSize.height;
+    return GetSize(aspect).width;
 }
-uint32_t TextureBase::GetDepth() const {
+uint32_t TextureBase::GetHeight(Aspect aspect) const {
+    DAWN_ASSERT(!IsError());
+    return GetSize(aspect).height;
+}
+uint32_t TextureBase::GetDepth(Aspect aspect) const {
     DAWN_ASSERT(!IsError());
     DAWN_ASSERT(mDimension == wgpu::TextureDimension::e3D);
-    return mSize.depthOrArrayLayers;
+    return GetSize(aspect).depthOrArrayLayers;
 }
 uint32_t TextureBase::GetArrayLayers() const {
     DAWN_ASSERT(!IsError());
     if (mDimension == wgpu::TextureDimension::e3D) {
         return 1;
     }
-    return mSize.depthOrArrayLayers;
+    return mBaseSize.depthOrArrayLayers;
 }
 uint32_t TextureBase::GetNumMipLevels() const {
     DAWN_ASSERT(!IsError());
@@ -866,8 +913,10 @@
     return mSampleCount > 1;
 }
 
-bool TextureBase::CoverFullSubresource(uint32_t mipLevel, const Extent3D& size) const {
-    Extent3D levelSize = GetMipLevelSingleSubresourcePhysicalSize(mipLevel);
+bool TextureBase::CoversFullSubresource(uint32_t mipLevel,
+                                        Aspect aspect,
+                                        const Extent3D& size) const {
+    Extent3D levelSize = GetMipLevelSingleSubresourcePhysicalSize(mipLevel, aspect);
     switch (GetDimension()) {
         case wgpu::TextureDimension::e1D:
             return size.width == levelSize.width;
@@ -879,23 +928,25 @@
     DAWN_UNREACHABLE();
 }
 
-Extent3D TextureBase::GetMipLevelSingleSubresourceVirtualSize(uint32_t level) const {
-    Extent3D extent = {std::max(mSize.width >> level, 1u), 1u, 1u};
+Extent3D TextureBase::GetMipLevelSingleSubresourceVirtualSize(uint32_t level, Aspect aspect) const {
+    Extent3D aspectSize = GetSize(aspect);
+    Extent3D extent = {std::max(aspectSize.width >> level, 1u), 1u, 1u};
     if (mDimension == wgpu::TextureDimension::e1D) {
         return extent;
     }
 
-    extent.height = std::max(mSize.height >> level, 1u);
+    extent.height = std::max(aspectSize.height >> level, 1u);
     if (mDimension == wgpu::TextureDimension::e2D) {
         return extent;
     }
 
-    extent.depthOrArrayLayers = std::max(mSize.depthOrArrayLayers >> level, 1u);
+    extent.depthOrArrayLayers = std::max(aspectSize.depthOrArrayLayers >> level, 1u);
     return extent;
 }
 
-Extent3D TextureBase::GetMipLevelSingleSubresourcePhysicalSize(uint32_t level) const {
-    Extent3D extent = GetMipLevelSingleSubresourceVirtualSize(level);
+Extent3D TextureBase::GetMipLevelSingleSubresourcePhysicalSize(uint32_t level,
+                                                               Aspect aspect) const {
+    Extent3D extent = GetMipLevelSingleSubresourceVirtualSize(level, aspect);
 
     // Compressed Textures will have paddings if their width or height is not a multiple of
     // 4 at non-zero mipmap levels.
@@ -913,9 +964,10 @@
 }
 
 Extent3D TextureBase::ClampToMipLevelVirtualSize(uint32_t level,
+                                                 Aspect aspect,
                                                  const Origin3D& origin,
                                                  const Extent3D& extent) const {
-    const Extent3D virtualSizeAtLevel = GetMipLevelSingleSubresourceVirtualSize(level);
+    const Extent3D virtualSizeAtLevel = GetMipLevelSingleSubresourceVirtualSize(level, aspect);
     DAWN_ASSERT(origin.x <= virtualSizeAtLevel.width);
     DAWN_ASSERT(origin.y <= virtualSizeAtLevel.height);
     uint32_t clampedCopyExtentWidth = (extent.width > virtualSizeAtLevel.width - origin.x)
@@ -927,10 +979,10 @@
     return {clampedCopyExtentWidth, clampedCopyExtentHeight, extent.depthOrArrayLayers};
 }
 
-Extent3D TextureBase::GetMipLevelSubresourceVirtualSize(uint32_t level) const {
-    Extent3D extent = GetMipLevelSingleSubresourceVirtualSize(level);
+Extent3D TextureBase::GetMipLevelSubresourceVirtualSize(uint32_t level, Aspect aspect) const {
+    Extent3D extent = GetMipLevelSingleSubresourceVirtualSize(level, aspect);
     if (mDimension == wgpu::TextureDimension::e2D) {
-        extent.depthOrArrayLayers = mSize.depthOrArrayLayers;
+        extent.depthOrArrayLayers = mBaseSize.depthOrArrayLayers;
     }
     return extent;
 }
@@ -968,14 +1020,14 @@
 }
 
 uint32_t TextureBase::APIGetWidth() const {
-    return mSize.width;
+    return mBaseSize.width;
 }
 
 uint32_t TextureBase::APIGetHeight() const {
-    return mSize.height;
+    return mBaseSize.height;
 }
 uint32_t TextureBase::APIGetDepthOrArrayLayers() const {
-    return mSize.depthOrArrayLayers;
+    return mBaseSize.depthOrArrayLayers;
 }
 
 uint32_t TextureBase::APIGetMipLevelCount() const {
@@ -1095,6 +1147,11 @@
     return mRange;
 }
 
+Extent3D TextureViewBase::GetSingleSubresourceVirtualSize() const {
+    DAWN_ASSERT(!IsError());
+    return GetTexture()->GetMipLevelSingleSubresourceVirtualSize(GetBaseMipLevel(), GetAspects());
+}
+
 ApiObjectList* TextureViewBase::GetObjectTrackingList() {
     DAWN_ASSERT(!IsError());
     return mTexture->GetViewTrackingList();
diff --git a/src/dawn/native/Texture.h b/src/dawn/native/Texture.h
index 505895c..bc030bd 100644
--- a/src/dawn/native/Texture.h
+++ b/src/dawn/native/Texture.h
@@ -70,10 +70,15 @@
     wgpu::TextureDimension GetDimension() const;
     const Format& GetFormat() const;
     const FormatSet& GetViewFormats() const;
-    const Extent3D& GetSize() const;
-    uint32_t GetWidth() const;
-    uint32_t GetHeight() const;
-    uint32_t GetDepth() const;
+
+    // For multiplanar textures, base size is the size of plane 0. For other types of textures,
+    // base size is the original size passed via TextureDescriptor.
+    const Extent3D& GetBaseSize() const;
+    Extent3D GetSize(Aspect aspect) const;
+    Extent3D GetSize(wgpu::TextureAspect aspect) const;
+    uint32_t GetWidth(Aspect aspect) const;
+    uint32_t GetHeight(Aspect aspect) const;
+    uint32_t GetDepth(Aspect aspect) const;
     uint32_t GetArrayLayers() const;
     uint32_t GetNumMipLevels() const;
     SubresourceRange GetAllSubresources() const;
@@ -97,21 +102,22 @@
     bool IsMultisampledTexture() const;
 
     // Returns true if the size covers the whole subresource.
-    bool CoverFullSubresource(uint32_t mipLevel, const Extent3D& size) const;
+    bool CoversFullSubresource(uint32_t mipLevel, Aspect aspect, const Extent3D& size) const;
 
     // For a texture with non-block-compressed texture format, its physical size is always equal
     // to its virtual size. For a texture with block compressed texture format, the physical
     // size is the one with paddings if necessary, which is always a multiple of the block size
     // and used in texture copying. The virtual size is the one without paddings, which is not
     // required to be a multiple of the block size and used in texture sampling.
-    Extent3D GetMipLevelSingleSubresourcePhysicalSize(uint32_t level) const;
-    Extent3D GetMipLevelSingleSubresourceVirtualSize(uint32_t level) const;
+    Extent3D GetMipLevelSingleSubresourcePhysicalSize(uint32_t level, Aspect aspect) const;
+    Extent3D GetMipLevelSingleSubresourceVirtualSize(uint32_t level, Aspect aspect) const;
     Extent3D ClampToMipLevelVirtualSize(uint32_t level,
+                                        Aspect aspect,
                                         const Origin3D& origin,
                                         const Extent3D& extent) const;
     // For 2d-array textures, this keeps the array layers in contrast to
     // GetMipLevelSingleSubresourceVirtualSize.
-    Extent3D GetMipLevelSubresourceVirtualSize(uint32_t level) const;
+    Extent3D GetMipLevelSubresourceVirtualSize(uint32_t level, Aspect aspect) const;
 
     ResultOrError<Ref<TextureViewBase>> CreateView(
         const TextureViewDescriptor* descriptor = nullptr);
@@ -158,7 +164,7 @@
     wgpu::TextureDimension mDimension;
     const Format& mFormat;
     FormatSet mViewFormats;
-    Extent3D mSize;
+    Extent3D mBaseSize;
     uint32_t mMipLevelCount;
     uint32_t mSampleCount;
     wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
@@ -196,6 +202,9 @@
     uint32_t GetLayerCount() const;
     const SubresourceRange& GetSubresourceRange() const;
 
+    // Returns the size of the texture's subresource at this view's base mip level and aspect.
+    Extent3D GetSingleSubresourceVirtualSize() const;
+
   protected:
     void DestroyImpl() override;
 
diff --git a/src/dawn/native/d3d11/TextureD3D11.cpp b/src/dawn/native/d3d11/TextureD3D11.cpp
index b0c5032..b56b0f4 100644
--- a/src/dawn/native/d3d11/TextureD3D11.cpp
+++ b/src/dawn/native/d3d11/TextureD3D11.cpp
@@ -241,12 +241,12 @@
     T desc;
 
     if constexpr (std::is_same<T, D3D11_TEXTURE1D_DESC>::value) {
-        desc.Width = GetSize().width;
+        desc.Width = GetBaseSize().width;
         desc.ArraySize = GetArrayLayers();
         desc.MiscFlags = 0;
     } else if constexpr (std::is_same<T, D3D11_TEXTURE2D_DESC>::value) {
-        desc.Width = GetSize().width;
-        desc.Height = GetSize().height;
+        desc.Width = GetBaseSize().width;
+        desc.Height = GetBaseSize().height;
         desc.ArraySize = GetArrayLayers();
         desc.SampleDesc.Count = GetSampleCount();
         desc.SampleDesc.Quality = 0;
@@ -256,9 +256,9 @@
             desc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
         }
     } else if constexpr (std::is_same<T, D3D11_TEXTURE3D_DESC>::value) {
-        desc.Width = GetSize().width;
-        desc.Height = GetSize().height;
-        desc.Depth = GetSize().depthOrArrayLayers;
+        desc.Width = GetBaseSize().width;
+        desc.Height = GetBaseSize().height;
+        desc.Depth = GetBaseSize().depthOrArrayLayers;
         desc.MiscFlags = 0;
     }
 
@@ -537,35 +537,37 @@
     const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(range.aspects).block;
     // TODO(dawn:1705): Use interim texture clear-and-copy as compressed textures do to
     // avoid CPU-to-GPU write.
-    Extent3D writeSize = GetMipLevelSubresourceVirtualSize(range.baseMipLevel);
-    uint32_t bytesPerRow = blockInfo.byteSize * writeSize.width;
+    for (Aspect aspect : IterateEnumMask(range.aspects)) {
+        Extent3D writeSize = GetMipLevelSubresourceVirtualSize(range.baseMipLevel, aspect);
+        uint32_t bytesPerRow = blockInfo.byteSize * writeSize.width;
 
-    uint32_t rowsPerImage = writeSize.height;
-    uint64_t byteLength;
-    DAWN_TRY_ASSIGN(byteLength,
-                    ComputeRequiredBytesInCopy(blockInfo, writeSize, bytesPerRow, rowsPerImage));
+        uint32_t rowsPerImage = writeSize.height;
+        uint64_t byteLength;
+        DAWN_TRY_ASSIGN(byteLength, ComputeRequiredBytesInCopy(blockInfo, writeSize, bytesPerRow,
+                                                               rowsPerImage));
 
-    std::vector<uint8_t> clearData(byteLength, clearValue == ClearValue::Zero ? 0 : 1);
-    SubresourceRange writeRange = range;
-    writeRange.layerCount = 1;
-    writeRange.levelCount = 1;
-    for (uint32_t layer = range.baseArrayLayer; layer < range.baseArrayLayer + range.layerCount;
-         ++layer) {
-        for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
-             ++level) {
-            if (clearValue == TextureBase::ClearValue::Zero &&
-                IsSubresourceContentInitialized(
-                    SubresourceRange::SingleMipAndLayer(level, layer, range.aspects))) {
-                // Skip lazy clears if already initialized.
-                continue;
+        std::vector<uint8_t> clearData(byteLength, clearValue == ClearValue::Zero ? 0 : 1);
+        SubresourceRange writeRange = range;
+        writeRange.layerCount = 1;
+        writeRange.levelCount = 1;
+        for (uint32_t layer = range.baseArrayLayer; layer < range.baseArrayLayer + range.layerCount;
+             ++layer) {
+            for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
+                 ++level) {
+                if (clearValue == TextureBase::ClearValue::Zero &&
+                    IsSubresourceContentInitialized(
+                        SubresourceRange::SingleMipAndLayer(level, layer, aspect))) {
+                    // Skip lazy clears if already initialized.
+                    continue;
+                }
+                writeRange.baseArrayLayer = layer;
+                writeRange.baseMipLevel = level;
+                writeSize = GetMipLevelSubresourceVirtualSize(level, aspect);
+                bytesPerRow = blockInfo.byteSize * writeSize.width;
+                rowsPerImage = writeSize.height;
+                DAWN_TRY(WriteInternal(commandContext, writeRange, {0, 0, 0}, writeSize,
+                                       clearData.data(), bytesPerRow, rowsPerImage));
             }
-            writeRange.baseArrayLayer = layer;
-            writeRange.baseMipLevel = level;
-            writeSize = GetMipLevelSubresourceVirtualSize(level);
-            bytesPerRow = blockInfo.byteSize * writeSize.width;
-            rowsPerImage = writeSize.height;
-            DAWN_TRY(WriteInternal(commandContext, writeRange, {0, 0, 0}, writeSize,
-                                   clearData.data(), bytesPerRow, rowsPerImage));
         }
     }
 
@@ -575,13 +577,14 @@
 MaybeError Texture::ClearCompressed(CommandRecordingContext* commandContext,
                                     const SubresourceRange& range,
                                     TextureBase::ClearValue clearValue) {
+    DAWN_ASSERT(range.aspects == Aspect::Color);
     const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(range.aspects).block;
     // Create an interim texture of renderable format for reinterpretation conversion.
     TextureDescriptor desc = {};
     desc.label = "CopyUncompressedTextureToCompressedTexureInterim";
     desc.dimension = GetDimension();
     DAWN_ASSERT(desc.dimension == wgpu::TextureDimension::e2D);
-    desc.size = {GetSize().width, GetSize().height, 1};
+    desc.size = {GetWidth(Aspect::Color), GetHeight(Aspect::Color), 1};
     desc.format = UncompressedTextureFormat(GetFormat().format);
     desc.mipLevelCount = 1;
     desc.sampleCount = 1;
@@ -631,7 +634,7 @@
                 continue;
             }
             uint32_t dstSubresource = GetSubresourceIndex(level, layer, D3D11Aspect(range.aspects));
-            auto physicalSize = GetMipLevelSingleSubresourcePhysicalSize(level);
+            auto physicalSize = GetMipLevelSingleSubresourcePhysicalSize(level, Aspect::Color);
             // The documentation says D3D11_BOX's coordinates should be in texels for
             // textures. However the validation layer seemingly assumes them to be in
             // blocks. Otherwise it would complain like this:
@@ -680,7 +683,8 @@
                           const uint8_t* data,
                           uint32_t bytesPerRow,
                           uint32_t rowsPerImage) {
-    if (IsCompleteSubresourceCopiedTo(this, size, subresources.baseMipLevel)) {
+    if (IsCompleteSubresourceCopiedTo(this, size, subresources.baseMipLevel,
+                                      subresources.aspects)) {
         SetIsSubresourceContentInitialized(true, subresources);
     } else {
         // Dawn validation should have ensured that full subresources write for depth/stencil
@@ -760,7 +764,8 @@
     DAWN_TRY_ASSIGN(stagingTexture, CreateInternal(ToBackend(GetDevice()), &desc, Kind::Staging));
 
     // Depth-stencil subresources can only be written to completely and not partially.
-    DAWN_ASSERT(IsCompleteSubresourceCopiedTo(this, size, subresources.baseMipLevel));
+    DAWN_ASSERT(
+        IsCompleteSubresourceCopiedTo(this, size, subresources.baseMipLevel, subresources.aspects));
 
     SubresourceRange otherRange = subresources;
     Aspect otherAspects = GetFormat().aspects & ~subresources.aspects;
@@ -993,7 +998,8 @@
                  ->EnsureSubresourceContentInitialized(commandContext, srcSubresources));
 
     SubresourceRange dstSubresources = GetSubresourcesAffectedByCopy(dst, copy->copySize);
-    if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, dst.mipLevel)) {
+    if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, dst.mipLevel,
+                                      dst.aspect)) {
         dst.texture->SetIsSubresourceContentInitialized(true, dstSubresources);
     } else {
         // Partial update subresource of a depth/stencil texture is not allowed.
@@ -1035,8 +1041,9 @@
             DAWN_UNREACHABLE();
     }
 
-    bool isWholeSubresource = src.texture->CoverFullSubresource(src.mipLevel, copy->copySize) &&
-                              dst.texture->CoverFullSubresource(dst.mipLevel, copy->copySize);
+    bool isWholeSubresource =
+        src.texture->CoversFullSubresource(src.mipLevel, src.aspect, copy->copySize) &&
+        dst.texture->CoversFullSubresource(dst.mipLevel, dst.aspect, copy->copySize);
     // Partial update subresource of a depth/stencil texture is not allowed.
     DAWN_ASSERT(isWholeSubresource || !src.texture->GetFormat().HasDepthOrStencil());
 
@@ -1072,7 +1079,7 @@
         TextureDescriptor desc = {};
         desc.label = "InterimStencilTexture";
         desc.dimension = GetDimension();
-        desc.size = GetSize();
+        desc.size = GetSize(Aspect::Stencil);
         desc.format = wgpu::TextureFormat::R8Uint;
         desc.mipLevelCount = GetNumMipLevels();
         desc.sampleCount = GetSampleCount();
@@ -1086,7 +1093,7 @@
     // TODO(dawn:1705): Improve to only sync as few as possible.
     const auto range = view->GetSubresourceRange();
     const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(range.aspects).block;
-    Extent3D size = GetMipLevelSubresourceVirtualSize(range.baseMipLevel);
+    Extent3D size = GetMipLevelSubresourceVirtualSize(range.baseMipLevel, range.aspects);
     uint32_t bytesPerRow = blockInfo.byteSize * size.width;
     uint32_t rowsPerImage = size.height;
     uint64_t byteLength;
@@ -1098,7 +1105,7 @@
          ++layer) {
         for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
              ++level) {
-            size = GetMipLevelSubresourceVirtualSize(level);
+            size = GetMipLevelSubresourceVirtualSize(level, range.aspects);
             bytesPerRow = blockInfo.byteSize * size.width;
             rowsPerImage = size.height;
             auto singleRange = SubresourceRange::MakeSingle(range.aspects, layer, level);
@@ -1339,7 +1346,8 @@
         case wgpu::TextureViewDimension::e3D:
             uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
             uavDesc.Texture3D.FirstWSlice = 0;
-            uavDesc.Texture3D.WSize = std::max(1u, GetTexture()->GetDepth() >> GetBaseMipLevel());
+            uavDesc.Texture3D.WSize =
+                std::max(1u, GetSingleSubresourceVirtualSize().depthOrArrayLayers);
             uavDesc.Texture3D.MipSlice = GetBaseMipLevel();
             break;
         // Cube and Cubemap can't be used as storage texture. So there is no need to create UAV
diff --git a/src/dawn/native/d3d12/CommandBufferD3D12.cpp b/src/dawn/native/d3d12/CommandBufferD3D12.cpp
index 4c2eb2e..b4c49af 100644
--- a/src/dawn/native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn/native/d3d12/CommandBufferD3D12.cpp
@@ -69,8 +69,8 @@
     DAWN_ASSERT(src.texture->GetFormat().CopyCompatibleWith(dst.texture->GetFormat()));
     DAWN_ASSERT(src.aspect == dst.aspect);
 
-    const Extent3D& srcSize = src.texture->GetSize();
-    const Extent3D& dstSize = dst.texture->GetSize();
+    const Extent3D& srcSize = src.texture->GetSize(src.aspect);
+    const Extent3D& dstSize = dst.texture->GetSize(dst.aspect);
 
     // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-copyresource
     // In order to use D3D12's copy resource, the textures must be the same dimensions, and
@@ -839,7 +839,8 @@
                     GetSubresourcesAffectedByCopy(copy->destination, copy->copySize);
 
                 if (IsCompleteSubresourceCopiedTo(texture, copy->copySize,
-                                                  copy->destination.mipLevel)) {
+                                                  copy->destination.mipLevel,
+                                                  copy->destination.aspect)) {
                     texture->SetIsSubresourceContentInitialized(true, subresources);
                 } else {
                     DAWN_TRY(
@@ -913,7 +914,8 @@
 
                 DAWN_TRY(source->EnsureSubresourceContentInitialized(commandContext, srcRange));
                 if (IsCompleteSubresourceCopiedTo(destination, copy->copySize,
-                                                  copy->destination.mipLevel)) {
+                                                  copy->destination.mipLevel,
+                                                  copy->destination.aspect)) {
                     destination->SetIsSubresourceContentInitialized(true, dstRange);
                 } else {
                     DAWN_TRY(
diff --git a/src/dawn/native/d3d12/DeviceD3D12.cpp b/src/dawn/native/d3d12/DeviceD3D12.cpp
index a460199..af836c4 100644
--- a/src/dawn/native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn/native/d3d12/DeviceD3D12.cpp
@@ -550,7 +550,7 @@
 
     SubresourceRange range = GetSubresourcesAffectedByCopy(dst, copySizePixels);
 
-    if (IsCompleteSubresourceCopiedTo(texture, copySizePixels, dst.mipLevel)) {
+    if (IsCompleteSubresourceCopiedTo(texture, copySizePixels, dst.mipLevel, dst.aspect)) {
         texture->SetIsSubresourceContentInitialized(true, range);
     } else {
         DAWN_TRY(texture->EnsureSubresourceContentInitialized(commandContext, range));
diff --git a/src/dawn/native/d3d12/TextureD3D12.cpp b/src/dawn/native/d3d12/TextureD3D12.cpp
index 5c19073..9ce8b1f 100644
--- a/src/dawn/native/d3d12/TextureD3D12.cpp
+++ b/src/dawn/native/d3d12/TextureD3D12.cpp
@@ -246,7 +246,7 @@
     resourceDescriptor.Dimension = D3D12TextureDimension(GetDimension());
     resourceDescriptor.Alignment = 0;
 
-    const Extent3D& size = GetSize();
+    const Extent3D& size = GetBaseSize();
     resourceDescriptor.Width = size.width;
     resourceDescriptor.Height = size.height;
     resourceDescriptor.DepthOrArraySize = size.depthOrArrayLayers;
@@ -812,7 +812,7 @@
                 uint32_t sliceCount = 1;
                 if (GetDimension() == wgpu::TextureDimension::e3D) {
                     baseSlice = 0;
-                    sliceCount = std::max(GetDepth() >> level, 1u);
+                    sliceCount = std::max(GetDepth(Aspect::Color) >> level, 1u);
                 }
                 D3D12_RENDER_TARGET_VIEW_DESC rtvDesc =
                     GetRTVDescriptor(GetFormat(), level, baseSlice, sliceCount);
@@ -830,7 +830,8 @@
         for (Aspect aspect : IterateEnumMask(range.aspects)) {
             const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(aspect).block;
 
-            Extent3D largestMipSize = GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel);
+            Extent3D largestMipSize =
+                GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel, aspect);
 
             uint32_t bytesPerRow =
                 Align((largestMipSize.width / blockInfo.width) * blockInfo.byteSize,
@@ -847,7 +848,7 @@
             for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
                  ++level) {
                 // compute d3d12 texture copy locations for texture and buffer
-                Extent3D copySize = GetMipLevelSingleSubresourcePhysicalSize(level);
+                Extent3D copySize = GetMipLevelSingleSubresourcePhysicalSize(level, aspect);
 
                 for (uint32_t layer = range.baseArrayLayer;
                      layer < range.baseArrayLayer + range.layerCount; ++layer) {
@@ -1116,7 +1117,8 @@
         case wgpu::TextureViewDimension::e3D:
             uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
             uavDesc.Texture3D.FirstWSlice = 0;
-            uavDesc.Texture3D.WSize = std::max(1u, GetTexture()->GetDepth() >> GetBaseMipLevel());
+            uavDesc.Texture3D.WSize =
+                std::max(1u, GetSingleSubresourceVirtualSize().depthOrArrayLayers);
             uavDesc.Texture3D.MipSlice = GetBaseMipLevel();
             break;
         // Cube and Cubemap can't be used as storage texture. So there is no need to create UAV
diff --git a/src/dawn/native/metal/TextureMTL.mm b/src/dawn/native/metal/TextureMTL.mm
index 9180ad8..18be29e 100644
--- a/src/dawn/native/metal/TextureMTL.mm
+++ b/src/dawn/native/metal/TextureMTL.mm
@@ -679,7 +679,8 @@
     NSRef<MTLTextureDescriptor> mtlDescRef = AcquireNSRef([MTLTextureDescriptor new]);
     MTLTextureDescriptor* mtlDesc = mtlDescRef.Get();
 
-    mtlDesc.width = GetWidth();
+    DAWN_ASSERT(!GetFormat().IsMultiPlanar());
+    mtlDesc.width = GetBaseSize().width;
     mtlDesc.sampleCount = GetSampleCount();
     // Metal only allows format reinterpretation to happen on swizzle pattern or conversion
     // between linear space and sRGB. For example, creating bgra8Unorm texture view on
@@ -715,7 +716,7 @@
             break;
 
         case wgpu::TextureDimension::e2D:
-            mtlDesc.height = GetHeight();
+            mtlDesc.height = GetBaseSize().height;
             mtlDesc.arrayLength = GetArrayLayers();
             mtlDesc.depth = 1;
             if (mtlDesc.arrayLength > 1) {
@@ -728,8 +729,8 @@
             }
             break;
         case wgpu::TextureDimension::e3D:
-            mtlDesc.height = GetHeight();
-            mtlDesc.depth = GetDepth();
+            mtlDesc.height = GetBaseSize().height;
+            mtlDesc.depth = GetBaseSize().depthOrArrayLayers;
             mtlDesc.arrayLength = 1;
             DAWN_ASSERT(mtlDesc.sampleCount == 1);
             mtlDesc.textureType = MTLTextureType3D;
@@ -993,9 +994,9 @@
                         }
                     }
 
-                    DAWN_TRY(
-                        EncodeEmptyMetalRenderPass(device, commandContext, descriptor,
-                                                   GetMipLevelSingleSubresourceVirtualSize(level)));
+                    DAWN_TRY(EncodeEmptyMetalRenderPass(
+                        device, commandContext, descriptor,
+                        GetMipLevelSingleSubresourceVirtualSize(level, range.aspects)));
                 }
             }
         } else {
@@ -1008,7 +1009,8 @@
                 NSRef<MTLRenderPassDescriptor> descriptor;
                 uint32_t attachment = 0;
 
-                uint32_t depth = GetMipLevelSingleSubresourceVirtualSize(level).depthOrArrayLayers;
+                uint32_t depth = GetMipLevelSingleSubresourceVirtualSize(level, Aspect::Color)
+                                     .depthOrArrayLayers;
 
                 for (uint32_t arrayLayer = range.baseArrayLayer;
                      arrayLayer < range.baseArrayLayer + range.layerCount; arrayLayer++) {
@@ -1042,16 +1044,16 @@
                             attachment = 0;
                             DAWN_TRY(EncodeEmptyMetalRenderPass(
                                 device, commandContext, descriptor.Get(),
-                                GetMipLevelSingleSubresourceVirtualSize(level)));
+                                GetMipLevelSingleSubresourceVirtualSize(level, Aspect::Color)));
                             descriptor = nullptr;
                         }
                     }
                 }
 
                 if (descriptor != nullptr) {
-                    DAWN_TRY(
-                        EncodeEmptyMetalRenderPass(device, commandContext, descriptor.Get(),
-                                                   GetMipLevelSingleSubresourceVirtualSize(level)));
+                    DAWN_TRY(EncodeEmptyMetalRenderPass(
+                        device, commandContext, descriptor.Get(),
+                        GetMipLevelSingleSubresourceVirtualSize(level, Aspect::Color)));
                 }
             }
         }
@@ -1065,7 +1067,8 @@
 
             // Computations for the bytes per row / image height are done using the physical size
             // so that enough data is reserved for compressed textures.
-            Extent3D largestMipSize = GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel);
+            Extent3D largestMipSize =
+                GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel, aspect);
             uint32_t largestMipBytesPerRow =
                 (largestMipSize.width / blockInfo.width) * blockInfo.byteSize;
             uint64_t largestMipBytesPerImage = static_cast<uint64_t>(largestMipBytesPerRow) *
@@ -1087,7 +1090,7 @@
 
             for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
                  ++level) {
-                Extent3D virtualSize = GetMipLevelSingleSubresourceVirtualSize(level);
+                Extent3D virtualSize = GetMipLevelSingleSubresourceVirtualSize(level, aspect);
 
                 for (uint32_t arrayLayer = range.baseArrayLayer;
                      arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) {
diff --git a/src/dawn/native/metal/UtilsMetal.mm b/src/dawn/native/metal/UtilsMetal.mm
index 4ec1fa2..b63475c 100644
--- a/src/dawn/native/metal/UtilsMetal.mm
+++ b/src/dawn/native/metal/UtilsMetal.mm
@@ -262,7 +262,7 @@
     // clamped to the edge of the texture if the block extends outside the bounds of a
     // texture.
     const Extent3D clampedCopyExtent =
-        texture->ClampToMipLevelVirtualSize(mipLevel, origin, copyExtent);
+        texture->ClampToMipLevelVirtualSize(mipLevel, aspect, origin, copyExtent);
 
     // Note: all current GPUs have a 3D texture size limit of 2048 and otherwise 16348
     // for non-3D textures except for Apple2 GPUs (iPhone6) which has a non-3D texture
@@ -329,7 +329,7 @@
     uint32_t copyBlockRowCount = copyExtent.height / blockInfo.height;
     if (copyBlockRowCount > 1) {
         DAWN_ASSERT(copyExtent.height - blockInfo.height <
-                    texture->GetMipLevelSingleSubresourceVirtualSize(mipLevel).height);
+                    texture->GetMipLevelSingleSubresourceVirtualSize(mipLevel, aspect).height);
         const uint32_t localBytesPerImage = 0;  // workaround case 3
         copy.push_back(TextureBufferCopySplit::CopyInfo(
             currentOffset, bytesPerRow, localBytesPerImage,
@@ -363,7 +363,7 @@
                                                const Extent3D& size) {
     DAWN_ASSERT(texture == dst.texture.Get());
     SubresourceRange range = GetSubresourcesAffectedByCopy(dst, size);
-    if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), size, dst.mipLevel)) {
+    if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), size, dst.mipLevel, dst.aspect)) {
         texture->SetIsSubresourceContentInitialized(true, range);
     } else {
         DAWN_TRY(texture->EnsureSubresourceContentInitialized(commandContext, range));
diff --git a/src/dawn/native/opengl/CommandBufferGL.cpp b/src/dawn/native/opengl/CommandBufferGL.cpp
index e7bfb43..d410bcd 100644
--- a/src/dawn/native/opengl/CommandBufferGL.cpp
+++ b/src/dawn/native/opengl/CommandBufferGL.cpp
@@ -513,7 +513,7 @@
     Extent3D validTextureCopyExtent = copySize;
     const TextureBase* texture = textureCopy.texture.Get();
     Extent3D virtualSizeAtLevel =
-        texture->GetMipLevelSingleSubresourceVirtualSize(textureCopy.mipLevel);
+        texture->GetMipLevelSingleSubresourceVirtualSize(textureCopy.mipLevel, textureCopy.aspect);
     DAWN_ASSERT(textureCopy.origin.x <= virtualSizeAtLevel.width);
     DAWN_ASSERT(textureCopy.origin.y <= virtualSizeAtLevel.height);
     if (copySize.width > virtualSizeAtLevel.width - textureCopy.origin.x) {
@@ -628,8 +628,8 @@
 
                 buffer->EnsureDataInitialized();
                 SubresourceRange range = GetSubresourcesAffectedByCopy(dst, copy->copySize);
-                if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
-                                                  dst.mipLevel)) {
+                if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, dst.mipLevel,
+                                                  dst.aspect)) {
                     dst.texture->SetIsSubresourceContentInitialized(true, range);
                 } else {
                     DAWN_TRY(ToBackend(dst.texture)->EnsureSubresourceContentInitialized(range));
@@ -776,7 +776,7 @@
                 SubresourceRange dstRange = GetSubresourcesAffectedByCopy(dst, copy->copySize);
 
                 DAWN_TRY(srcTexture->EnsureSubresourceContentInitialized(srcRange));
-                if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.mipLevel)) {
+                if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.mipLevel, dst.aspect)) {
                     dstTexture->SetIsSubresourceContentInitialized(true, dstRange);
                 } else {
                     DAWN_TRY(dstTexture->EnsureSubresourceContentInitialized(dstRange));
@@ -1342,7 +1342,8 @@
     uint32_t z = destination.origin.z;
     if (texture->GetFormat().isCompressed) {
         size_t rowSize = copySize.width / blockInfo.width * blockInfo.byteSize;
-        Extent3D virtSize = texture->GetMipLevelSingleSubresourceVirtualSize(destination.mipLevel);
+        Extent3D virtSize = texture->GetMipLevelSingleSubresourceVirtualSize(destination.mipLevel,
+                                                                             destination.aspect);
         uint32_t width = std::min(copySize.width, virtSize.width - x);
 
         // In GLES glPixelStorei() doesn't affect CompressedTexSubImage*D() and
diff --git a/src/dawn/native/opengl/QueueGL.cpp b/src/dawn/native/opengl/QueueGL.cpp
index 3b0f79b..706e17e 100644
--- a/src/dawn/native/opengl/QueueGL.cpp
+++ b/src/dawn/native/opengl/QueueGL.cpp
@@ -63,7 +63,8 @@
     textureCopy.aspect = SelectFormatAspects(destination.texture->GetFormat(), destination.aspect);
 
     SubresourceRange range = GetSubresourcesAffectedByCopy(textureCopy, writeSizePixel);
-    if (IsCompleteSubresourceCopiedTo(destination.texture, writeSizePixel, destination.mipLevel)) {
+    if (IsCompleteSubresourceCopiedTo(destination.texture, writeSizePixel, destination.mipLevel,
+                                      destination.aspect)) {
         destination.texture->SetIsSubresourceContentInitialized(true, range);
     } else {
         DAWN_TRY(ToBackend(destination.texture)->EnsureSubresourceContentInitialized(range));
diff --git a/src/dawn/native/opengl/TextureGL.cpp b/src/dawn/native/opengl/TextureGL.cpp
index 99ac041..b9cbca6 100644
--- a/src/dawn/native/opengl/TextureGL.cpp
+++ b/src/dawn/native/opengl/TextureGL.cpp
@@ -193,7 +193,7 @@
 
     gl.BindTexture(mTarget, mHandle);
 
-    AllocateTexture(gl, mTarget, GetSampleCount(), levels, glFormat.internalFormat, GetSize());
+    AllocateTexture(gl, mTarget, GetSampleCount(), levels, glFormat.internalFormat, GetBaseSize());
 
     // The texture is not complete if it uses mipmapping and not all levels up to
     // MAX_LEVEL have been defined.
@@ -375,7 +375,7 @@
             const GLFormat& glFormat = GetGLFormat();
             for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
                  ++level) {
-                Extent3D mipSize = GetMipLevelSingleSubresourcePhysicalSize(level);
+                Extent3D mipSize = GetMipLevelSingleSubresourcePhysicalSize(level, Aspect::Color);
                 for (uint32_t layer = range.baseArrayLayer;
                      layer < range.baseArrayLayer + range.layerCount; ++layer) {
                     if (clearValue == TextureBase::ClearValue::Zero &&
@@ -441,8 +441,9 @@
                                 DoClear();
                                 break;
                             case wgpu::TextureDimension::e3D:
-                                uint32_t depth = GetMipLevelSingleSubresourceVirtualSize(level)
-                                                     .depthOrArrayLayers;
+                                uint32_t depth =
+                                    GetMipLevelSingleSubresourceVirtualSize(level, Aspect::Color)
+                                        .depthOrArrayLayers;
                                 for (GLint z = 0; z < static_cast<GLint>(depth); ++z) {
                                     gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment,
                                                                GetHandle(), level, z);
@@ -471,7 +472,8 @@
         const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(Aspect::Color).block;
         DAWN_ASSERT(kTextureBytesPerRowAlignment % blockInfo.byteSize == 0);
 
-        Extent3D largestMipSize = GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel);
+        Extent3D largestMipSize =
+            GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel, Aspect::Color);
         uint32_t bytesPerRow =
             Align((largestMipSize.width / blockInfo.width) * blockInfo.byteSize, 4);
 
@@ -515,7 +517,7 @@
             dataLayout.bytesPerRow = bytesPerRow;
             dataLayout.rowsPerImage = largestMipSize.height;
 
-            Extent3D mipSize = GetMipLevelSingleSubresourcePhysicalSize(level);
+            Extent3D mipSize = GetMipLevelSingleSubresourcePhysicalSize(level, Aspect::Color);
 
             for (uint32_t layer = range.baseArrayLayer;
                  layer < range.baseArrayLayer + range.layerCount; ++layer) {
@@ -647,8 +649,8 @@
     uint32_t srcLevel = GetBaseMipLevel();
     uint32_t numLevels = GetLevelCount();
 
-    uint32_t width = texture->GetWidth() >> srcLevel;
-    uint32_t height = texture->GetHeight() >> srcLevel;
+    uint32_t width = GetSingleSubresourceVirtualSize().width;
+    uint32_t height = GetSingleSubresourceVirtualSize().height;
     Extent3D size{width, height, GetLayerCount()};
 
     if (mHandle == 0) {
diff --git a/src/dawn/native/vulkan/CommandBufferVk.cpp b/src/dawn/native/vulkan/CommandBufferVk.cpp
index 78d3dd8..2faf05e 100644
--- a/src/dawn/native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn/native/vulkan/CommandBufferVk.cpp
@@ -579,7 +579,7 @@
                     GetSubresourcesAffectedByCopy(copy->destination, copy->copySize);
 
                 if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
-                                                  subresource.mipLevel)) {
+                                                  subresource.mipLevel, dst.aspect)) {
                     // Since texture has been overwritten, it has been "initialized"
                     dst.texture->SetIsSubresourceContentInitialized(true, range);
                 } else {
@@ -647,8 +647,8 @@
 
                 DAWN_TRY(ToBackend(src.texture)
                              ->EnsureSubresourceContentInitialized(recordingContext, srcRange));
-                if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize,
-                                                  dst.mipLevel)) {
+                if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, dst.mipLevel,
+                                                  dst.aspect)) {
                     // Since destination texture has been overwritten, it has been "initialized"
                     dst.texture->SetIsSubresourceContentInitialized(true, dstRange);
                 } else {
diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp
index 63ac944..455add0 100644
--- a/src/dawn/native/vulkan/DeviceVk.cpp
+++ b/src/dawn/native/vulkan/DeviceVk.cpp
@@ -801,7 +801,8 @@
 
     SubresourceRange range = GetSubresourcesAffectedByCopy(dst, copySizePixels);
 
-    if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copySizePixels, subresource.mipLevel)) {
+    if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copySizePixels, subresource.mipLevel,
+                                      dst.aspect)) {
         // Since texture has been overwritten, it has been "initialized"
         dst.texture->SetIsSubresourceContentInitialized(true, range);
     } else {
diff --git a/src/dawn/native/vulkan/SwapChainVk.cpp b/src/dawn/native/vulkan/SwapChainVk.cpp
index 0fe1bd2..d7ca5c0 100644
--- a/src/dawn/native/vulkan/SwapChainVk.cpp
+++ b/src/dawn/native/vulkan/SwapChainVk.cpp
@@ -551,13 +551,13 @@
         region.srcSubresource.baseArrayLayer = 0;
         region.srcSubresource.layerCount = 1;
         region.srcOffsets[0] = {0, 0, 0};
-        region.srcOffsets[1] = {static_cast<int32_t>(mBlitTexture->GetWidth()),
-                                static_cast<int32_t>(mBlitTexture->GetHeight()), 1};
+        region.srcOffsets[1] = {static_cast<int32_t>(mBlitTexture->GetWidth(Aspect::Color)),
+                                static_cast<int32_t>(mBlitTexture->GetHeight(Aspect::Color)), 1};
 
         region.dstSubresource = region.srcSubresource;
         region.dstOffsets[0] = {0, 0, 0};
-        region.dstOffsets[1] = {static_cast<int32_t>(mTexture->GetWidth()),
-                                static_cast<int32_t>(mTexture->GetHeight()), 1};
+        region.dstOffsets[1] = {static_cast<int32_t>(mTexture->GetWidth(Aspect::Color)),
+                                static_cast<int32_t>(mTexture->GetHeight(Aspect::Color)), 1};
 
         device->fn.CmdBlitImage(recordingContext->commandBuffer, mBlitTexture->GetHandle(),
                                 mBlitTexture->GetCurrentLayoutForSwapChain(), mTexture->GetHandle(),
diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp
index ac52da5..d71df30 100644
--- a/src/dawn/native/vulkan/TextureVk.cpp
+++ b/src/dawn/native/vulkan/TextureVk.cpp
@@ -190,7 +190,7 @@
 }
 
 void FillVulkanCreateInfoSizesAndType(const Texture& texture, VkImageCreateInfo* info) {
-    const Extent3D& size = texture.GetSize();
+    const Extent3D& size = texture.GetBaseSize();
 
     info->mipLevels = texture.GetNumMipLevels();
     info->samples = VulkanSampleCount(texture.GetSampleCount());
@@ -746,7 +746,7 @@
 
     DAWN_ASSERT(IsSampleCountSupported(device, createInfo));
 
-    if (GetArrayLayers() >= 6 && GetWidth() == GetHeight()) {
+    if (GetArrayLayers() >= 6 && GetBaseSize().width == GetBaseSize().height) {
         createInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
     }
 
@@ -794,7 +794,7 @@
             format == wgpu::TextureFormat::RGBA32Float;
         textureIsBuggy &= GetNumMipLevels() > 1;
         textureIsBuggy &= GetDimension() == wgpu::TextureDimension::e2D;
-        textureIsBuggy &= IsPowerOfTwo(GetWidth()) && IsPowerOfTwo(GetHeight());
+        textureIsBuggy &= IsPowerOfTwo(GetBaseSize().width) && IsPowerOfTwo(GetBaseSize().height);
         if (textureIsBuggy) {
             DAWN_TRY(ClearTexture(ToBackend(GetDevice())->GetPendingRecordingContext(),
                                   GetAllSubresources(), TextureBase::ClearValue::Zero));
@@ -1290,8 +1290,8 @@
                     range.aspects == Aspect::Plane1);
         const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(range.aspects).block;
 
-        Extent3D largestMipSize = GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel);
-        largestMipSize = GetFormat().GetAspectSize(range.aspects, largestMipSize);
+        Extent3D largestMipSize =
+            GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel, range.aspects);
 
         uint32_t bytesPerRow = Align((largestMipSize.width / blockInfo.width) * blockInfo.byteSize,
                                      device->GetOptimalBytesPerRowAlignment());
@@ -1307,8 +1307,7 @@
         std::vector<VkBufferImageCopy> regions;
         for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
              ++level) {
-            Extent3D copySize = GetMipLevelSingleSubresourcePhysicalSize(level);
-            copySize = GetFormat().GetAspectSize(range.aspects, copySize);
+            Extent3D copySize = GetMipLevelSingleSubresourcePhysicalSize(level, range.aspects);
             imageRange.baseMipLevel = level;
             for (uint32_t layer = range.baseArrayLayer;
                  layer < range.baseArrayLayer + range.layerCount; ++layer) {
diff --git a/src/dawn/native/vulkan/UtilsVulkan.cpp b/src/dawn/native/vulkan/UtilsVulkan.cpp
index 7080e11..0d651ef 100644
--- a/src/dawn/native/vulkan/UtilsVulkan.cpp
+++ b/src/dawn/native/vulkan/UtilsVulkan.cpp
@@ -115,7 +115,7 @@
     Extent3D validTextureCopyExtent = copySize;
     const TextureBase* texture = textureCopy.texture.Get();
     Extent3D virtualSizeAtLevel =
-        texture->GetMipLevelSingleSubresourceVirtualSize(textureCopy.mipLevel);
+        texture->GetMipLevelSingleSubresourceVirtualSize(textureCopy.mipLevel, textureCopy.aspect);
     DAWN_ASSERT(textureCopy.origin.x <= virtualSizeAtLevel.width);
     DAWN_ASSERT(textureCopy.origin.y <= virtualSizeAtLevel.height);
     if (copySize.width > virtualSizeAtLevel.width - textureCopy.origin.x) {
diff --git a/src/dawn/tests/end2end/VideoViewsTests.cpp b/src/dawn/tests/end2end/VideoViewsTests.cpp
index 77c36e5..161214f 100644
--- a/src/dawn/tests/end2end/VideoViewsTests.cpp
+++ b/src/dawn/tests/end2end/VideoViewsTests.cpp
@@ -773,13 +773,13 @@
     wgpu::Texture srcTexture = platformTexture->wgpuTexture;
 
     wgpu::BufferDescriptor bufferDescriptor;
-    bufferDescriptor.size = 1;
+    bufferDescriptor.size = 256;
     bufferDescriptor.usage = wgpu::BufferUsage::CopyDst;
     wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor);
 
     wgpu::ImageCopyTexture copySrc = utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0});
 
-    wgpu::ImageCopyBuffer copyDst = utils::CreateImageCopyBuffer(dstBuffer, 0, 4);
+    wgpu::ImageCopyBuffer copyDst = utils::CreateImageCopyBuffer(dstBuffer, 0, 256);
 
     wgpu::Extent3D copySize = {1, 1, 1};
 
@@ -799,14 +799,14 @@
     wgpu::Texture srcTexture = platformTexture->wgpuTexture;
 
     wgpu::BufferDescriptor bufferDescriptor;
-    bufferDescriptor.size = 1;
+    bufferDescriptor.size = 256;
     bufferDescriptor.usage = wgpu::BufferUsage::CopyDst;
     wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor);
 
     wgpu::ImageCopyTexture copySrc =
         utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0}, wgpu::TextureAspect::Plane0Only);
 
-    wgpu::ImageCopyBuffer copyDst = utils::CreateImageCopyBuffer(dstBuffer, 0, 4);
+    wgpu::ImageCopyBuffer copyDst = utils::CreateImageCopyBuffer(dstBuffer, 0, 256);
 
     wgpu::Extent3D copySize = {1, 1, 1};
 
@@ -1433,6 +1433,62 @@
     }
 }
 
+// Test copying from a multi-planar format to a buffer fails if texture aspect is not single plane.
+TEST_P(VideoViewsExtendedUsagesTests, T2BCopyAllAspectsFails) {
+    DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("skip_validation"));
+
+    auto srcTexture =
+        CreateMultiPlanarTexture(wgpu::TextureFormat::R8BG8Biplanar420Unorm,
+                                 wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc,
+                                 /*isCheckerboard*/ false,
+                                 /*initialized*/ true);
+
+    wgpu::BufferDescriptor bufferDescriptor;
+    bufferDescriptor.size = 256;
+    bufferDescriptor.usage = wgpu::BufferUsage::CopyDst;
+    wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor);
+
+    wgpu::ImageCopyTexture copySrc =
+        utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0}, wgpu::TextureAspect::All);
+
+    wgpu::ImageCopyBuffer copyDst = utils::CreateImageCopyBuffer(dstBuffer, 0, 256);
+
+    wgpu::Extent3D copySize = {1, 1, 1};
+
+    wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+    encoder.CopyTextureToBuffer(&copySrc, &copyDst, &copySize);
+    ASSERT_DEVICE_ERROR_MSG(encoder.Finish(), testing::HasSubstr("More than a single aspect"));
+}
+
+// Test copying from one multi-planar formatted texture into another fails even if
+// MultiPlanarFormatExtendedUsages is enabled.
+TEST_P(VideoViewsExtendedUsagesTests, T2TCopyPlaneAspectFails) {
+    DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("skip_validation"));
+
+    auto srcTexture =
+        CreateMultiPlanarTexture(wgpu::TextureFormat::R8BG8Biplanar420Unorm,
+                                 wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc,
+                                 /*isCheckerboard*/ false,
+                                 /*initialized*/ true);
+    auto dstTexture =
+        CreateMultiPlanarTexture(wgpu::TextureFormat::R8BG8Biplanar420Unorm,
+                                 wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst,
+                                 /*isCheckerboard*/ false,
+                                 /*initialized*/ true);
+
+    wgpu::ImageCopyTexture copySrc =
+        utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0}, wgpu::TextureAspect::Plane0Only);
+
+    wgpu::ImageCopyTexture copyDst =
+        utils::CreateImageCopyTexture(dstTexture, 0, {0, 0, 0}, wgpu::TextureAspect::Plane0Only);
+
+    wgpu::Extent3D copySize = {1, 1, 1};
+
+    wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+    encoder.CopyTextureToTexture(&copySrc, &copyDst, &copySize);
+    ASSERT_DEVICE_ERROR_MSG(encoder.Finish(), testing::HasSubstr("not allowed"));
+}
+
 DAWN_INSTANTIATE_TEST_B(VideoViewsTests,
                         VideoViewsTestBackend::Backends(),
                         VideoViewsTestBackend::Formats());