d3d11: enable some CopyTests

To make B2B, B2T and T2B testes work, this CL implements several
missed functions:
 - Texture::Clear() is need for initializing texture data
 - Texture::Read() is need for readback data from 2d and 3d textures

Bug: dawn:1740,dawn:1768
Change-Id: Ib9e354c82fcdc8cc4e86b1699fa652cfc4a50721
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/128621
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Peng Huang <penghuang@chromium.org>
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index 6169e4c..6a97f4d 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -750,6 +750,17 @@
     return mSampleCount > 1;
 }
 
+bool TextureBase::CoverFullSubresource(const Extent3D& size) const {
+    switch (GetDimension()) {
+        case wgpu::TextureDimension::e1D:
+            return size.width == GetSize().width;
+        case wgpu::TextureDimension::e2D:
+            return size.width == GetSize().width && size.height == GetSize().height;
+        case wgpu::TextureDimension::e3D:
+            return size == GetSize();
+    }
+}
+
 Extent3D TextureBase::GetMipLevelSingleSubresourceVirtualSize(uint32_t level) const {
     Extent3D extent = {std::max(mSize.width >> level, 1u), 1u, 1u};
     if (mDimension == wgpu::TextureDimension::e1D) {
diff --git a/src/dawn/native/Texture.h b/src/dawn/native/Texture.h
index c3e8163..42edacf 100644
--- a/src/dawn/native/Texture.h
+++ b/src/dawn/native/Texture.h
@@ -79,6 +79,9 @@
 
     bool IsMultisampledTexture() const;
 
+    // Returns true if the size covers the whole subresource.
+    bool CoverFullSubresource(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
diff --git a/src/dawn/native/d3d11/BufferD3D11.cpp b/src/dawn/native/d3d11/BufferD3D11.cpp
index 5afb0b7..b7e894c 100644
--- a/src/dawn/native/d3d11/BufferD3D11.cpp
+++ b/src/dawn/native/d3d11/BufferD3D11.cpp
@@ -401,9 +401,7 @@
                          uint64_t offset,
                          const void* data,
                          size_t size) {
-    if (size == 0) {
-        return {};
-    }
+    DAWN_ASSERT(size != 0);
 
     MarkUsedInPendingCommands();
     // Map the buffer if it is possible, so EnsureDataInitializedAsDestination() and WriteInternal()
diff --git a/src/dawn/native/d3d11/CommandBufferD3D11.cpp b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
index b54ef84..5ba4ee3 100644
--- a/src/dawn/native/d3d11/CommandBufferD3D11.cpp
+++ b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
@@ -232,8 +232,6 @@
 MaybeError CommandBuffer::Execute() {
     CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext();
 
-    ID3D11DeviceContext1* d3d11DeviceContext1 = commandContext->GetD3D11DeviceContext1();
-
     auto LazyClearSyncScope = [commandContext](const SyncScopeResourceUsage& scope) -> MaybeError {
         for (size_t i = 0; i < scope.textures.size(); i++) {
             Texture* texture = ToBackend(scope.textures[i]);
@@ -366,82 +364,31 @@
                 auto& dst = copy->destination;
 
                 SubresourceRange subresources = GetSubresourcesAffectedByCopy(src, copy->copySize);
-                DAWN_TRY(ToBackend(src.texture)
-                             ->EnsureSubresourceContentInitialized(commandContext, subresources));
-
-                TextureDescriptor desc = {};
-                desc.label = "CopyTextureToBufferStaging";
-                desc.dimension = src.texture->GetDimension();
-                desc.size.width = copy->copySize.width;
-                desc.size.height = copy->copySize.height;
-                desc.size.depthOrArrayLayers = copy->copySize.depthOrArrayLayers;
-                desc.format = src.texture->GetFormat().format;
-                desc.mipLevelCount = 1;
-                desc.sampleCount = 1;
-
-                Ref<Texture> stagingTexture;
-                DAWN_TRY_ASSIGN(stagingTexture,
-                                Texture::CreateStaging(ToBackend(GetDevice()), &desc));
-
-                CopyTextureToTextureCmd copyTextureToBufferCmd;
-                copyTextureToBufferCmd.source = src;
-                copyTextureToBufferCmd.destination.texture = stagingTexture.Get();
-                copyTextureToBufferCmd.destination.origin = {0, 0, 0};
-                copyTextureToBufferCmd.destination.mipLevel = 0;
-                copyTextureToBufferCmd.destination.aspect = src.aspect;
-                copyTextureToBufferCmd.copySize = copy->copySize;
-
-                DAWN_TRY(Texture::Copy(commandContext, &copyTextureToBufferCmd));
 
                 Buffer* buffer = ToBackend(dst.buffer.Get());
                 Buffer::ScopedMap scopedDstMap;
                 DAWN_TRY_ASSIGN(scopedDstMap, Buffer::ScopedMap::Create(buffer));
 
-                DAWN_TRY(buffer->EnsureDataInitializedAsDestination(commandContext, copy));
-
-                for (uint32_t z = 0; z < copy->copySize.depthOrArrayLayers; ++z) {
-                    // Copy the staging texture to the buffer.
-                    // The Map() will block until the GPU is done with the texture.
-                    // TODO(dawn:1705): avoid blocking the CPU.
-                    D3D11_MAPPED_SUBRESOURCE mappedResource;
-                    DAWN_TRY(
-                        CheckHRESULT(d3d11DeviceContext1->Map(stagingTexture->GetD3D11Resource(), z,
-                                                              D3D11_MAP_READ, 0, &mappedResource),
-                                     "D3D11 map staging texture"));
-
-                    uint8_t* pSrcData = reinterpret_cast<uint8_t*>(mappedResource.pData);
-                    const TexelBlockInfo& blockInfo =
-                        ToBackend(src.texture)->GetFormat().GetAspectInfo(src.aspect).block;
-                    uint32_t bytesPerRow = blockInfo.byteSize * copy->copySize.width;
-                    if (scopedDstMap.GetMappedData()) {
-                        uint8_t* pDstData = scopedDstMap.GetMappedData() + dst.offset +
-                                            dst.bytesPerRow * dst.rowsPerImage * z;
-                        for (uint32_t y = 0; y < copy->copySize.height; ++y) {
-                            memcpy(pDstData, pSrcData, bytesPerRow);
-                            pDstData += dst.bytesPerRow;
-                            pSrcData += mappedResource.RowPitch;
-                        }
-                    } else {
-                        uint64_t dstOffset = dst.offset + dst.bytesPerRow * dst.rowsPerImage * z;
-                        if (dst.bytesPerRow == bytesPerRow &&
-                            mappedResource.RowPitch == bytesPerRow) {
-                            // If there is no padding in the rows, we can upload the whole image in
-                            // one buffer->Write() call.
-                            DAWN_TRY(buffer->Write(commandContext, dstOffset, pSrcData,
-                                                   dst.bytesPerRow * copy->copySize.height));
-                        } else {
-                            // Otherwise, we need to upload each row separately.
-                            for (uint32_t y = 0; y < copy->copySize.height; ++y) {
-                                DAWN_TRY(buffer->Write(commandContext, dstOffset, pSrcData,
-                                                       bytesPerRow));
-                                dstOffset += dst.bytesPerRow;
-                                pSrcData += mappedResource.RowPitch;
-                            }
-                        }
-                    }
-                    d3d11DeviceContext1->Unmap(stagingTexture->GetD3D11Resource(), z);
+                Texture::ReadCallback callback;
+                if (scopedDstMap.GetMappedData()) {
+                    callback = [&](const uint8_t* data, uint64_t offset,
+                                   uint64_t size) -> MaybeError {
+                        memcpy(scopedDstMap.GetMappedData() + dst.offset + offset, data, size);
+                        return {};
+                    };
+                } else {
+                    callback = [&](const uint8_t* data, uint64_t offset,
+                                   uint64_t size) -> MaybeError {
+                        DAWN_TRY(ToBackend(dst.buffer)
+                                     ->Write(commandContext, dst.offset + offset, data, size));
+                        return {};
+                    };
                 }
 
+                DAWN_TRY(ToBackend(src.texture)
+                             ->Read(commandContext, subresources, src.origin, copy->copySize,
+                                    dst.bytesPerRow, dst.rowsPerImage, callback));
+
                 dst.buffer->MarkUsedInPendingCommands();
                 break;
             }
diff --git a/src/dawn/native/d3d11/QueueD3D11.cpp b/src/dawn/native/d3d11/QueueD3D11.cpp
index 5bfaef0..1fd5bc0 100644
--- a/src/dawn/native/d3d11/QueueD3D11.cpp
+++ b/src/dawn/native/d3d11/QueueD3D11.cpp
@@ -50,6 +50,11 @@
                                   uint64_t bufferOffset,
                                   const void* data,
                                   size_t size) {
+    if (size == 0) {
+        // skip the empty write
+        return {};
+    }
+
     CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext();
     return ToBackend(buffer)->Write(commandContext, bufferOffset, data, size);
 }
diff --git a/src/dawn/native/d3d11/TextureD3D11.cpp b/src/dawn/native/d3d11/TextureD3D11.cpp
index eb3fda3..80c0e82 100644
--- a/src/dawn/native/d3d11/TextureD3D11.cpp
+++ b/src/dawn/native/d3d11/TextureD3D11.cpp
@@ -49,6 +49,18 @@
     return bindFlags;
 }
 
+Aspect D3D11Aspect(Aspect aspect) {
+    // https://learn.microsoft.com/en-us/windows/win32/direct3d12/subresources
+    // Planar formats existed in Direct3D 11, but individual planes could not be addressed
+    // individually.
+    if (IsSubset(aspect, Aspect::Depth | Aspect::Stencil)) {
+        return Aspect::CombinedDepthStencil;
+    }
+
+    ASSERT(HasOneBit(aspect));
+    return aspect;
+}
+
 }  // namespace
 
 // static
@@ -104,9 +116,9 @@
     }
 
     desc.MipLevels = static_cast<UINT16>(GetNumMipLevels());
-    desc.Format = d3d::DXGITextureFormat(GetFormat().format);
+    desc.Format = GetD3D11Format();
     desc.Usage = mIsStaging ? D3D11_USAGE_STAGING : D3D11_USAGE_DEFAULT;
-    desc.BindFlags = D3D11TextureBindFlags(GetUsage(), GetFormat());
+    desc.BindFlags = D3D11TextureBindFlags(GetInternalUsage(), GetFormat());
     constexpr UINT kCPUReadWriteFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
     desc.CPUAccessFlags = mIsStaging ? kCPUReadWriteFlags : 0;
 
@@ -116,6 +128,12 @@
 MaybeError Texture::InitializeAsInternalTexture() {
     Device* device = ToBackend(GetDevice());
 
+    if (GetFormat().isRenderable && !mIsStaging) {
+        // If the texture format is renderable, we need to add the render attachment usage
+        // internally, so the texture can be cleared with GPU.
+        AddInternalUsage(wgpu::TextureUsage::RenderAttachment);
+    }
+
     switch (GetDimension()) {
         case wgpu::TextureDimension::e1D: {
             D3D11_TEXTURE1D_DESC desc = GetD3D11TextureDesc<D3D11_TEXTURE1D_DESC>();
@@ -146,7 +164,8 @@
         }
     }
 
-    if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
+    // Staging texture is used internally, so we don't need to clear it.
+    if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting) && !mIsStaging) {
         CommandRecordingContext* commandContext = device->GetPendingCommandContext();
         DAWN_TRY(Clear(commandContext, GetAllSubresources(), TextureBase::ClearValue::NonZero));
     }
@@ -184,28 +203,6 @@
     return mD3d11Resource.Get();
 }
 
-DXGI_FORMAT Texture::GetD3D11CopyableSubresourceFormat(Aspect aspect) const {
-    // TODO(dawn:1705): share the code with D3D12
-    ASSERT(GetFormat().aspects & aspect);
-
-    switch (GetFormat().format) {
-        case wgpu::TextureFormat::Depth24PlusStencil8:
-        case wgpu::TextureFormat::Depth32FloatStencil8:
-        case wgpu::TextureFormat::Stencil8:
-            switch (aspect) {
-                case Aspect::Depth:
-                    return DXGI_FORMAT_R32_FLOAT;
-                case Aspect::Stencil:
-                    return DXGI_FORMAT_R8_UINT;
-                default:
-                    UNREACHABLE();
-            }
-        default:
-            ASSERT(HasOneBit(GetFormat().aspects));
-            return GetD3D11Format();
-    }
-}
-
 D3D11_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(const Format& format,
                                                         const SubresourceRange& range) const {
     D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
@@ -278,12 +275,16 @@
 MaybeError Texture::Clear(CommandRecordingContext* commandContext,
                           const SubresourceRange& range,
                           TextureBase::ClearValue clearValue) {
-    // TODO(dawn:1740): Clear non-renderable texture.
-    if ((GetUsage() & wgpu::TextureUsage::RenderAttachment) == 0) {
-        return DAWN_UNIMPLEMENTED_ERROR("Clearing non-renderable textures is not implemented");
+    bool isRenderable = GetInternalUsage() & wgpu::TextureUsage::RenderAttachment;
+
+    if (!isRenderable) {
+        return DAWN_UNIMPLEMENTED_ERROR("Clearing non-renderable textures");
     }
 
-    TextureViewDescriptor desc;
+    ID3D11DeviceContext* d3d11DeviceContext = commandContext->GetD3D11DeviceContext();
+
+    TextureViewDescriptor desc = {};
+    desc.label = "ClearTextureView";
     desc.format = GetFormat().format;
     switch (GetDimension()) {
         case wgpu::TextureDimension::e1D:
@@ -296,11 +297,6 @@
             desc.dimension = wgpu::TextureViewDimension::e3D;
             break;
     }
-    // TODO(dawn:1740): support clearing multiple layers.
-    if (range.levelCount != 1 || range.layerCount != 1) {
-        return DAWN_UNIMPLEMENTED_ERROR("Clearing multiple layers is not implemented");
-    }
-
     desc.baseMipLevel = range.baseMipLevel;
     desc.mipLevelCount = range.levelCount;
     desc.baseArrayLayer = range.baseArrayLayer;
@@ -308,14 +304,36 @@
     desc.aspect = wgpu::TextureAspect::All;
 
     Ref<TextureView> view = TextureView::Create(this, &desc);
-    ComPtr<ID3D11RenderTargetView> d3d11RTV;
-    DAWN_TRY_ASSIGN(d3d11RTV, view->CreateD3D11RenderTargetView());
 
-    static constexpr std::array<float, 4> zero = {0.0f, 0.0f, 0.0f, 0.0f};
-    static constexpr std::array<float, 4> nonZero = {1.0f, 1.0f, 1.0f, 1.0f};
+    if (GetFormat().HasDepthOrStencil()) {
+        ComPtr<ID3D11DepthStencilView> d3d11DSV;
+        DAWN_TRY_ASSIGN(d3d11DSV, view->CreateD3D11DepthStencilView(/*depthReadOnly=*/false,
+                                                                    /*stencilReadOnly=*/false));
+        UINT clearFlags = 0;
+        if (GetFormat().HasDepth() && range.aspects & Aspect::Depth) {
+            clearFlags |= D3D11_CLEAR_DEPTH;
+        }
+        if (GetFormat().HasStencil() && range.aspects & Aspect::Stencil) {
+            clearFlags |= D3D11_CLEAR_STENCIL;
+        }
+        d3d11DeviceContext->ClearDepthStencilView(
+            d3d11DSV.Get(), clearFlags, clearValue == TextureBase::ClearValue::Zero ? 0.0f : 1.0f,
+            0);
+    } else {
+        static constexpr std::array<float, 4> kZero = {0.0f, 0.0f, 0.0f, 0.0f};
+        static constexpr std::array<float, 4> kNonZero = {1.0f, 1.0f, 1.0f, 1.0f};
 
-    commandContext->GetD3D11DeviceContext()->ClearRenderTargetView(
-        d3d11RTV.Get(), clearValue == TextureBase::ClearValue::Zero ? zero.data() : nonZero.data());
+        ComPtr<ID3D11RenderTargetView> d3d11RTV;
+        DAWN_TRY_ASSIGN(d3d11RTV, view->CreateD3D11RenderTargetView());
+        d3d11DeviceContext->ClearRenderTargetView(
+            d3d11RTV.Get(),
+            clearValue == TextureBase::ClearValue::Zero ? kZero.data() : kNonZero.data());
+    }
+
+    if (clearValue == TextureBase::ClearValue::Zero) {
+        SetIsSubresourceContentInitialized(true, range);
+        GetDevice()->IncrementLazyClearCountForTesting();
+    }
 
     return {};
 }
@@ -348,11 +366,18 @@
                           const uint8_t* data,
                           uint32_t bytesPerRow,
                           uint32_t rowsPerImage) {
-    DAWN_ASSERT(size.width != 0 && size.height != 0 && size.depthOrArrayLayers != 0);
+    ASSERT(size.width != 0 && size.height != 0 && size.depthOrArrayLayers != 0);
+
+    if (GetFormat().HasDepth() && GetFormat().HasStencil()) {
+        return DAWN_UNIMPLEMENTED_ERROR("Write combined depth/stencil textures");
+    }
 
     if (IsCompleteSubresourceCopiedTo(this, size, subresources.baseMipLevel)) {
         SetIsSubresourceContentInitialized(true, subresources);
     } else {
+        // Dawn validation should have ensured that full subresources write for depth/stencil
+        // textures.
+        ASSERT(!GetFormat().HasDepthOrStencil());
         DAWN_TRY(EnsureSubresourceContentInitialized(commandContext, subresources));
     }
 
@@ -366,17 +391,20 @@
         dstBox.front = origin.z;
         dstBox.back = origin.z + size.depthOrArrayLayers;
         uint32_t subresource =
-            GetSubresourceIndex(subresources.baseMipLevel, origin.z, subresources.aspects);
+            GetSubresourceIndex(subresources.baseMipLevel, 0, D3D11Aspect(subresources.aspects));
         commandContext->GetD3D11DeviceContext1()->UpdateSubresource(GetD3D11Resource(), subresource,
-                                                                    &dstBox, data, bytesPerRow, 0);
+                                                                    &dstBox, data, bytesPerRow,
+                                                                    bytesPerRow * rowsPerImage);
     } else {
         dstBox.front = 0;
         dstBox.back = 1;
-        for (uint32_t z = origin.z; z < size.depthOrArrayLayers; ++z) {
+        for (uint32_t layer = 0; layer < subresources.layerCount; ++layer) {
             uint32_t subresource =
-                GetSubresourceIndex(subresources.baseMipLevel, z, subresources.aspects);
+                GetSubresourceIndex(subresources.baseMipLevel, subresources.baseArrayLayer + layer,
+                                    D3D11Aspect(subresources.aspects));
+            D3D11_BOX* pDstBox = GetFormat().HasDepthOrStencil() ? nullptr : &dstBox;
             commandContext->GetD3D11DeviceContext1()->UpdateSubresource(
-                GetD3D11Resource(), subresource, &dstBox, data, bytesPerRow, 0);
+                GetD3D11Resource(), subresource, pDstBox, data, bytesPerRow, 0);
             data += rowsPerImage * bytesPerRow;
         }
     }
@@ -384,6 +412,124 @@
     return {};
 }
 
+MaybeError Texture::ReadStaging(CommandRecordingContext* commandContext,
+                                const SubresourceRange& subresources,
+                                const Origin3D& origin,
+                                Extent3D size,
+                                uint32_t dstBytesPerRow,
+                                uint32_t dstRowsPerImage,
+                                Texture::ReadCallback callback) {
+    ASSERT(size.width != 0 && size.height != 0 && size.depthOrArrayLayers != 0);
+    ASSERT(mIsStaging);
+    ASSERT(subresources.baseArrayLayer == 0);
+    ASSERT(origin.z == 0);
+
+    ID3D11DeviceContext1* d3d11DeviceContext1 = commandContext->GetD3D11DeviceContext1();
+    const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(subresources.aspects).block;
+
+    if (GetDimension() == wgpu::TextureDimension::e2D) {
+        for (uint32_t layer = 0; layer < subresources.layerCount; ++layer) {
+            // Copy the staging texture to the buffer.
+            // The Map() will block until the GPU is done with the texture.
+            // TODO(dawn:1705): avoid blocking the CPU.
+            D3D11_MAPPED_SUBRESOURCE mappedResource;
+            DAWN_TRY(CheckHRESULT(d3d11DeviceContext1->Map(GetD3D11Resource(), layer,
+                                                           D3D11_MAP_READ, 0, &mappedResource),
+                                  "D3D11 map staging texture"));
+
+            uint8_t* pSrcData = static_cast<uint8_t*>(mappedResource.pData);
+            uint32_t bytesPerRow = blockInfo.byteSize * size.width;
+            uint64_t dstOffset = dstBytesPerRow * dstRowsPerImage * layer;
+            if (dstBytesPerRow == bytesPerRow && mappedResource.RowPitch == bytesPerRow) {
+                // If there is no padding in the rows, we can upload the whole image
+                // in one read.
+                DAWN_TRY(callback(pSrcData, dstOffset, dstBytesPerRow * size.height));
+            } else {
+                // Otherwise, we need to read each row separately.
+                for (uint32_t y = 0; y < size.height; ++y) {
+                    DAWN_TRY(callback(pSrcData, dstOffset, bytesPerRow));
+                    dstOffset += dstBytesPerRow;
+                    pSrcData += mappedResource.RowPitch;
+                }
+            }
+            d3d11DeviceContext1->Unmap(GetD3D11Resource(), layer);
+        }
+        return {};
+    }
+
+    // 3D textures are copied one slice at a time.
+    // Copy the staging texture to the buffer.
+    // The Map() will block until the GPU is done with the texture.
+    // TODO(dawn:1705): avoid blocking the CPU.
+    D3D11_MAPPED_SUBRESOURCE mappedResource;
+    DAWN_TRY(CheckHRESULT(
+        d3d11DeviceContext1->Map(GetD3D11Resource(), 0, D3D11_MAP_READ, 0, &mappedResource),
+        "D3D11 map staging texture"));
+
+    uint32_t bytesPerRow = blockInfo.byteSize * size.width;
+    for (uint32_t z = 0; z < size.depthOrArrayLayers; ++z) {
+        uint64_t dstOffset = dstBytesPerRow * dstRowsPerImage * z;
+        uint8_t* pSrcData =
+            static_cast<uint8_t*>(mappedResource.pData) + z * mappedResource.DepthPitch;
+        if (dstBytesPerRow == bytesPerRow && mappedResource.RowPitch == bytesPerRow) {
+            // If there is no padding in the rows, we can upload the whole image
+            // in one read.
+            DAWN_TRY(callback(pSrcData, dstOffset, bytesPerRow * size.height));
+        } else {
+            // Otherwise, we need to read each row separately.
+            for (uint32_t y = 0; y < size.height; ++y) {
+                DAWN_TRY(callback(pSrcData, dstOffset, bytesPerRow));
+                dstOffset += dstBytesPerRow;
+                pSrcData += mappedResource.RowPitch;
+            }
+        }
+    }
+    d3d11DeviceContext1->Unmap(GetD3D11Resource(), 0);
+
+    return {};
+}
+
+MaybeError Texture::Read(CommandRecordingContext* commandContext,
+                         const SubresourceRange& subresources,
+                         const Origin3D& origin,
+                         Extent3D size,
+                         uint32_t dstBytesPerRow,
+                         uint32_t dstRowsPerImage,
+                         Texture::ReadCallback callback) {
+    ASSERT(size.width != 0 && size.height != 0 && size.depthOrArrayLayers != 0);
+    ASSERT(!mIsStaging);
+
+    DAWN_TRY(EnsureSubresourceContentInitialized(commandContext, subresources));
+    TextureDescriptor desc = {};
+    desc.label = "CopyTextureToBufferStaging";
+    desc.dimension = GetDimension();
+    desc.size = size;
+    desc.format = GetFormat().format;
+    desc.mipLevelCount = subresources.levelCount;
+    desc.sampleCount = GetSampleCount();
+
+    Ref<Texture> stagingTexture;
+    DAWN_TRY_ASSIGN(stagingTexture, CreateStaging(ToBackend(GetDevice()), &desc));
+
+    CopyTextureToTextureCmd copyCmd;
+    copyCmd.source.texture = this;
+    copyCmd.source.origin = origin;
+    copyCmd.source.mipLevel = subresources.baseMipLevel;
+    copyCmd.source.aspect = subresources.aspects;
+    copyCmd.destination.texture = stagingTexture.Get();
+    copyCmd.destination.origin = {0, 0, 0};
+    copyCmd.destination.mipLevel = 0;
+    copyCmd.destination.aspect = subresources.aspects;
+    copyCmd.copySize = size;
+
+    DAWN_TRY(Texture::Copy(commandContext, &copyCmd));
+    SubresourceRange stagingSubresources = SubresourceRange::MakeFull(
+        subresources.aspects, subresources.layerCount, subresources.levelCount);
+
+    return stagingTexture->ReadStaging(commandContext, stagingSubresources, {0, 0, 0}, size,
+                                       dstBytesPerRow, dstRowsPerImage, callback);
+}
+
 // static
 MaybeError Texture::Copy(CommandRecordingContext* commandContext, CopyTextureToTextureCmd* copy) {
     ASSERT(copy->copySize.width != 0 && copy->copySize.height != 0 &&
@@ -392,39 +538,63 @@
     auto& src = copy->source;
     auto& dst = copy->destination;
 
-    SubresourceRange subresources = GetSubresourcesAffectedByCopy(src, copy->copySize);
-    DAWN_TRY(
-        ToBackend(src.texture)->EnsureSubresourceContentInitialized(commandContext, subresources));
+    ASSERT(src.aspect == dst.aspect);
 
-    subresources = GetSubresourcesAffectedByCopy(dst, copy->copySize);
-    if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, dst.mipLevel)) {
-        dst.texture->SetIsSubresourceContentInitialized(true, subresources);
-    } else {
-        // Partial update subresource of a depth/stencil texture is not allowed.
-        DAWN_ASSERT(!dst.texture->GetFormat().HasDepthOrStencil());
-        DAWN_TRY(ToBackend(dst.texture)
-                     ->EnsureSubresourceContentInitialized(commandContext, subresources));
+    // TODO(dawn:1705): support copy between textures with different dimensions.
+    if (src.texture->GetDimension() != dst.texture->GetDimension()) {
+        return DAWN_UNIMPLEMENTED_ERROR("Copy between textures with different dimensions");
     }
 
-    bool isWholeTextureCopy =
-        src.texture->GetSize() == copy->copySize && dst.texture->GetSize() == copy->copySize;
-    // Partial update subresource of a depth/stencil texture is not allowed.
-    DAWN_ASSERT(isWholeTextureCopy || !dst.texture->GetFormat().HasDepthOrStencil());
+    SubresourceRange srcSubresources = GetSubresourcesAffectedByCopy(src, copy->copySize);
+    DAWN_TRY(ToBackend(src.texture)
+                 ->EnsureSubresourceContentInitialized(commandContext, srcSubresources));
+
+    SubresourceRange dstSubresources = GetSubresourcesAffectedByCopy(dst, copy->copySize);
+    if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, dst.mipLevel)) {
+        dst.texture->SetIsSubresourceContentInitialized(true, dstSubresources);
+    } else {
+        // Partial update subresource of a depth/stencil texture is not allowed.
+        ASSERT(!dst.texture->GetFormat().HasDepthOrStencil());
+        DAWN_TRY(ToBackend(dst.texture)
+                     ->EnsureSubresourceContentInitialized(commandContext, dstSubresources));
+    }
 
     D3D11_BOX srcBox;
     srcBox.left = src.origin.x;
     srcBox.right = src.origin.x + copy->copySize.width;
     srcBox.top = src.origin.y;
     srcBox.bottom = src.origin.y + copy->copySize.height;
-    srcBox.front = 0;
-    srcBox.back = 1;
+    switch (src.texture->GetDimension()) {
+        case wgpu::TextureDimension::e2D:
+            srcBox.front = 0;
+            srcBox.back = 1;
+            break;
+        case wgpu::TextureDimension::e3D:
+            srcBox.front = src.origin.z;
+            srcBox.back = src.origin.z + copy->copySize.depthOrArrayLayers;
+            break;
+        default:
+            // TODO(dawn:1705): support 1d texture.
+            UNREACHABLE();
+    }
 
-    uint32_t subresource = src.texture->GetSubresourceIndex(src.mipLevel, src.origin.z, src.aspect);
+    bool isWholeSubresource = src.texture->CoverFullSubresource(copy->copySize);
 
-    commandContext->GetD3D11DeviceContext1()->CopySubresourceRegion(
-        ToBackend(dst.texture)->GetD3D11Resource(), dst.mipLevel, dst.origin.x, dst.origin.y,
-        dst.origin.z, ToBackend(src.texture)->GetD3D11Resource(), subresource,
-        isWholeTextureCopy ? nullptr : &srcBox);
+    // Partial update subresource of a depth/stencil texture is not allowed.
+    ASSERT(isWholeSubresource || !src.texture->GetFormat().HasDepthOrStencil());
+
+    for (uint32_t layer = 0; layer < srcSubresources.layerCount; ++layer) {
+        uint32_t srcSubresource =
+            src.texture->GetSubresourceIndex(src.mipLevel, srcSubresources.baseArrayLayer + layer,
+                                             D3D11Aspect(srcSubresources.aspects));
+        uint32_t dstSubresource =
+            dst.texture->GetSubresourceIndex(dst.mipLevel, dstSubresources.baseArrayLayer + layer,
+                                             D3D11Aspect(dstSubresources.aspects));
+        commandContext->GetD3D11DeviceContext1()->CopySubresourceRegion(
+            ToBackend(dst.texture)->GetD3D11Resource(), dstSubresource, dst.origin.x, dst.origin.y,
+            0, ToBackend(src.texture)->GetD3D11Resource(), srcSubresource,
+            isWholeSubresource ? nullptr : &srcBox);
+    }
 
     return {};
 }
@@ -444,7 +614,7 @@
 ResultOrError<ComPtr<ID3D11ShaderResourceView>> TextureView::CreateD3D11ShaderResourceView() const {
     Device* device = ToBackend(GetDevice());
     D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
-    srvDesc.Format = d3d::DXGITextureFormat(GetFormat().format);
+    srvDesc.Format = GetD3D11Format();
 
     const Format& textureFormat = GetTexture()->GetFormat();
     // TODO(dawn:1705): share below code with D3D12?
diff --git a/src/dawn/native/d3d11/TextureD3D11.h b/src/dawn/native/d3d11/TextureD3D11.h
index 9190a8c..00024fd 100644
--- a/src/dawn/native/d3d11/TextureD3D11.h
+++ b/src/dawn/native/d3d11/TextureD3D11.h
@@ -37,14 +37,10 @@
     static ResultOrError<Ref<Texture>> Create(Device* device,
                                               const TextureDescriptor* descriptor,
                                               ComPtr<ID3D11Resource> d3d11Texture);
-    static ResultOrError<Ref<Texture>> CreateStaging(Device* device,
-                                                     const TextureDescriptor* descriptor);
 
     DXGI_FORMAT GetD3D11Format() const;
     ID3D11Resource* GetD3D11Resource() const;
 
-    DXGI_FORMAT GetD3D11CopyableSubresourceFormat(Aspect aspect) const;
-
     D3D11_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(const Format& format,
                                                    const SubresourceRange& range) const;
     D3D11_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor(const SubresourceRange& range,
@@ -61,9 +57,20 @@
                      const uint8_t* data,
                      uint32_t bytesPerRow,
                      uint32_t rowsPerImage);
+    using ReadCallback = std::function<MaybeError(const uint8_t* data, size_t offset, size_t size)>;
+    MaybeError Read(CommandRecordingContext* commandContext,
+                    const SubresourceRange& subresources,
+                    const Origin3D& origin,
+                    Extent3D size,
+                    uint32_t bytesPerRow,
+                    uint32_t rowsPerImage,
+                    ReadCallback callback);
     static MaybeError Copy(CommandRecordingContext* commandContext, CopyTextureToTextureCmd* copy);
 
   private:
+    static ResultOrError<Ref<Texture>> CreateStaging(Device* device,
+                                                     const TextureDescriptor* descriptor);
+
     Texture(Device* device,
             const TextureDescriptor* descriptor,
             TextureState state,
@@ -85,6 +92,13 @@
     MaybeError Clear(CommandRecordingContext* commandContext,
                      const SubresourceRange& range,
                      TextureBase::ClearValue clearValue);
+    MaybeError ReadStaging(CommandRecordingContext* commandContext,
+                           const SubresourceRange& subresources,
+                           const Origin3D& origin,
+                           Extent3D size,
+                           uint32_t bytesPerRow,
+                           uint32_t rowsPerImage,
+                           ReadCallback callback);
 
     const bool mIsStaging = false;
     ComPtr<ID3D11Resource> mD3d11Resource;
diff --git a/src/dawn/tests/end2end/CopyTests.cpp b/src/dawn/tests/end2end/CopyTests.cpp
index 8aaabc3..012f4b0 100644
--- a/src/dawn/tests/end2end/CopyTests.cpp
+++ b/src/dawn/tests/end2end/CopyTests.cpp
@@ -1353,6 +1353,7 @@
 }
 
 DAWN_INSTANTIATE_TEST(CopyTests_T2B,
+                      D3D11Backend(),
                       D3D12Backend(),
                       MetalBackend(),
                       OpenGLBackend(),
@@ -1946,6 +1947,7 @@
 }
 
 DAWN_INSTANTIATE_TEST(CopyTests_B2T,
+                      D3D11Backend(),
                       D3D12Backend(),
                       MetalBackend(),
                       OpenGLBackend(),
@@ -2444,6 +2446,7 @@
     }
 }
 
+// TODO(dawn:1705): enable this test for D3D11
 DAWN_INSTANTIATE_TEST_P(
     CopyTests_T2T,
     {D3D12Backend(),
@@ -2470,8 +2473,8 @@
 }
 
 DAWN_INSTANTIATE_TEST_P(CopyTests_Formats,
-                        {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
-                         VulkanBackend()},
+                        {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(),
+                         OpenGLESBackend(), VulkanBackend()},
                         {wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8UnormSrgb,
                          wgpu::TextureFormat::BGRA8Unorm, wgpu::TextureFormat::BGRA8UnormSrgb});
 
@@ -2502,6 +2505,7 @@
 }
 
 DAWN_INSTANTIATE_TEST(CopyTests_B2B,
+                      D3D11Backend(),
                       D3D12Backend(),
                       MetalBackend(),
                       OpenGLBackend(),
@@ -2530,6 +2534,7 @@
 }
 
 DAWN_INSTANTIATE_TEST(ClearBufferTests,
+                      D3D11Backend(),
                       D3D12Backend(),
                       MetalBackend(),
                       OpenGLBackend(),
@@ -2712,8 +2717,9 @@
 
 DAWN_INSTANTIATE_TEST_P(
     CopyToDepthStencilTextureAfterDestroyingBigBufferTests,
-    {D3D12Backend(), D3D12Backend({"d3d12_force_clear_copyable_depth_stencil_texture_on_creation"}),
-     MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
+    {D3D11Backend(), D3D12Backend(),
+     D3D12Backend({"d3d12_force_clear_copyable_depth_stencil_texture_on_creation"}), MetalBackend(),
+     OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
     {wgpu::TextureFormat::Depth16Unorm, wgpu::TextureFormat::Stencil8},
     {InitializationMethod::CopyBufferToTexture, InitializationMethod::WriteTexture,
      InitializationMethod::CopyTextureToTexture},