D3D11: Implement B2T using shaders.

Bug: 348653642
Change-Id: I00d27e9e45b3c85b1176ae647e10e4c740bca90b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/230419
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index 605e881..0ba75d4 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -221,6 +221,8 @@
     "BindingInfo.h",
     "BlitBufferToDepthStencil.cpp",
     "BlitBufferToDepthStencil.h",
+    "BlitBufferToTexture.cpp",
+    "BlitBufferToTexture.h",
     "BlitColorToColorWithDraw.cpp",
     "BlitColorToColorWithDraw.h",
     "BlitDepthToDepth.cpp",
diff --git a/src/dawn/native/BlitBufferToTexture.cpp b/src/dawn/native/BlitBufferToTexture.cpp
new file mode 100644
index 0000000..805690d
--- /dev/null
+++ b/src/dawn/native/BlitBufferToTexture.cpp
@@ -0,0 +1,449 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "dawn/native/BlitBufferToTexture.h"
+
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <utility>
+
+#include "dawn/common/Assert.h"
+#include "dawn/native/BindGroup.h"
+#include "dawn/native/CommandBuffer.h"
+#include "dawn/native/CommandEncoder.h"
+#include "dawn/native/Device.h"
+#include "dawn/native/InternalPipelineStore.h"
+#include "dawn/native/Queue.h"
+#include "dawn/native/RenderPassEncoder.h"
+#include "dawn/native/RenderPipeline.h"
+#include "dawn/native/utils/WGPUHelpers.h"
+
+namespace dawn::native {
+
+namespace {
+
+constexpr std::string_view kShaderCommonSrc = R"(
+@vertex fn vert_fullscreen_quad(
+  @builtin(vertex_index) vertex_index : u32
+) -> @builtin(position) vec4f {
+  const pos = array(
+      vec2f(-1.0, -1.0),
+      vec2f( 3.0, -1.0),
+      vec2f(-1.0,  3.0));
+  return vec4f(pos[vertex_index], 0.0, 1.0);
+}
+
+struct Params {
+  srcOffset : u32,
+  bytesPerRow : u32,
+  dstOrigin : vec2u
+};
+
+@group(0) @binding(0) var<storage, read> src_buf : array<u32>;
+@group(0) @binding(1) var<uniform> params : Params;
+
+fn loadU8AsU32(byteOffset: u32) -> u32 {
+    let uintOffset = byteOffset >> 2;
+    let uintModOffset = byteOffset & 3;
+    let bitShift = uintModOffset * 8;
+    return (src_buf[uintOffset] >> bitShift) & 0xff;
+}
+
+fn loadU16AsU32(byteOffset: u32) -> u32 {
+    let firstHalf = loadU8AsU32(byteOffset);
+    let secondHalf = loadU8AsU32(byteOffset + 1);
+    return firstHalf | (secondHalf << 8);
+}
+
+// byteOffset is expected to be aligned to 4.
+fn loadU32(byteOffset: u32) -> u32 {
+    let uintOffset = byteOffset >> 2;
+    return src_buf[uintOffset];
+}
+
+// byteOffset is expected to be aligned to 4.
+fn loadTwoU32s(byteOffset: u32) -> vec2u {
+    return vec2u(loadU32(byteOffset), loadU32(byteOffset + 4));
+}
+
+@fragment fn blit_buffer_to_texture(
+    @builtin(position) screen_position : vec4f
+) -> @location(0) vec4f {
+    let iposition = vec2u(screen_position.xy) - params.dstOrigin;
+
+    let srcOffset = params.srcOffset + iposition.x * kPixelSize + iposition.y * params.bytesPerRow;
+
+    return unpackData(srcOffset);
+}
+)";
+
+constexpr std::string_view kUnpackR8Unorm = R"(
+fn unpackData(byteOffset: u32) -> vec4f {
+    return unpack4x8unorm(loadU8AsU32(byteOffset));
+}
+)";
+
+constexpr std::string_view kUnpackRG8Unorm = R"(
+fn unpackData(byteOffset: u32) -> vec4f {
+    return unpack4x8unorm(loadU16AsU32(byteOffset));
+}
+)";
+
+constexpr std::string_view kUnpackRGBA8Unorm = R"(
+fn unpackData(byteOffset: u32) -> vec4f {
+    return unpack4x8unorm(loadU32(byteOffset));
+}
+)";
+
+constexpr std::string_view kUnpackBGRA8Unorm = R"(
+fn unpackData(byteOffset: u32) -> vec4f {
+    return unpack4x8unorm(loadU32(byteOffset)).bgra;
+}
+)";
+
+constexpr std::string_view kUnpackR16Float = R"(
+fn unpackData(byteOffset: u32) -> vec4f {
+    return vec4f(unpack2x16float(loadU16AsU32(byteOffset)), 0.0, 1.0);
+}
+)";
+
+constexpr std::string_view kUnpackRG16Float = R"(
+fn unpackData(byteOffset: u32) -> vec4f {
+    return vec4f(unpack2x16float(loadU32(byteOffset)), 0.0, 1.0);
+}
+)";
+
+constexpr std::string_view kUnpackRGBA16Float = R"(
+fn unpackData(byteOffset: u32) -> vec4f {
+    let data = loadTwoU32s(byteOffset);
+    return vec4f(unpack2x16float(data.x), unpack2x16float(data.y));
+}
+)";
+
+constexpr std::string_view kUnpackR32Float = R"(
+fn unpackData(byteOffset: u32) -> vec4f {
+    return vec4f(bitcast<f32>(loadU32(byteOffset)), 0.0, 0.0, 1.0);
+}
+)";
+
+constexpr std::string_view kUnpackRG32Float = R"(
+fn unpackData(byteOffset: u32) -> vec4f {
+    let color = bitcast<vec2f>(loadTwoU32s(byteOffset));
+    return vec4f(color, 0.0, 1.0);
+}
+)";
+
+constexpr std::string_view kUnpackRGBA32Float = R"(
+fn unpackData(byteOffset: u32) -> vec4f {
+    return vec4f(bitcast<vec2f>(loadTwoU32s(byteOffset)),
+                 bitcast<vec2f>(loadTwoU32s(byteOffset + 8)));
+}
+)";
+
+std::string GenerateShaderSource(wgpu::TextureFormat format) {
+    int pixelSize = 0;
+    std::ostringstream ss;
+    switch (format) {
+        case wgpu::TextureFormat::R8Unorm:
+            pixelSize = 1;
+            ss << kUnpackR8Unorm;
+            break;
+        case wgpu::TextureFormat::RG8Unorm:
+            pixelSize = 2;
+            ss << kUnpackRG8Unorm;
+            break;
+        case wgpu::TextureFormat::RGBA8Unorm:
+            pixelSize = 4;
+            ss << kUnpackRGBA8Unorm;
+            break;
+        case wgpu::TextureFormat::BGRA8Unorm:
+            pixelSize = 4;
+            ss << kUnpackBGRA8Unorm;
+            break;
+        case wgpu::TextureFormat::R16Float:
+            pixelSize = 2;
+            ss << kUnpackR16Float;
+            break;
+        case wgpu::TextureFormat::RG16Float:
+            pixelSize = 4;
+            ss << kUnpackRG16Float;
+            break;
+        case wgpu::TextureFormat::RGBA16Float:
+            pixelSize = 8;
+            ss << kUnpackRGBA16Float;
+            break;
+        case wgpu::TextureFormat::R32Float:
+            pixelSize = 4;
+            ss << kUnpackR32Float;
+            break;
+        case wgpu::TextureFormat::RG32Float:
+            pixelSize = 8;
+            ss << kUnpackRG32Float;
+            break;
+        case wgpu::TextureFormat::RGBA32Float:
+            pixelSize = 16;
+            ss << kUnpackRGBA32Float;
+            break;
+        default:
+            DAWN_UNREACHABLE();
+    }
+
+    ss << "const kPixelSize = " << pixelSize << ";\n";
+    ss << kShaderCommonSrc;
+
+    return ss.str();
+}
+
+ResultOrError<Ref<RenderPipelineBase>> GetOrCreatePipeline(DeviceBase* device,
+                                                           wgpu::TextureFormat format) {
+    InternalPipelineStore* store = device->GetInternalPipelineStore();
+    {
+        auto it = store->blitBufferToTexturePipelines.find(format);
+        if (it != store->blitBufferToTexturePipelines.end()) {
+            return it->second;
+        }
+    }
+
+    // vertex shader's source.
+    ShaderSourceWGSL wgslDesc = {};
+    ShaderModuleDescriptor shaderModuleDesc = {};
+    shaderModuleDesc.nextInChain = &wgslDesc;
+
+    // shader's source will depend on format key.
+    std::string shaderCode = GenerateShaderSource(format);
+    wgslDesc.code = shaderCode.c_str();
+    Ref<ShaderModuleBase> shaderModule;
+    DAWN_TRY_ASSIGN(shaderModule, device->CreateShaderModule(&shaderModuleDesc));
+
+    FragmentState fragmentState = {};
+    fragmentState.module = shaderModule.Get();
+    fragmentState.entryPoint = "blit_buffer_to_texture";
+
+    // Color target states.
+    ColorTargetState colorTarget = {};
+    colorTarget.format = format;
+    colorTarget.writeMask = wgpu::ColorWriteMask::All;
+
+    fragmentState.targetCount = 1;
+    fragmentState.targets = &colorTarget;
+
+    RenderPipelineDescriptor renderPipelineDesc = {};
+    renderPipelineDesc.label = "blit_buffer_to_texture";
+    renderPipelineDesc.vertex.module = shaderModule.Get();
+    renderPipelineDesc.vertex.entryPoint = "vert_fullscreen_quad";
+    renderPipelineDesc.fragment = &fragmentState;
+
+    // Bind group layout.
+    Ref<BindGroupLayoutBase> bindGroupLayout;
+    DAWN_TRY_ASSIGN(bindGroupLayout,
+                    utils::MakeBindGroupLayout(
+                        device,
+                        {
+                            {0, wgpu::ShaderStage::Fragment, kInternalReadOnlyStorageBufferBinding},
+                            {1, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform},
+                        },
+                        /* allowInternalBinding */ true));
+
+    Ref<PipelineLayoutBase> pipelineLayout;
+    DAWN_TRY_ASSIGN(pipelineLayout, utils::MakeBasicPipelineLayout(device, bindGroupLayout));
+    renderPipelineDesc.layout = pipelineLayout.Get();
+
+    Ref<RenderPipelineBase> pipeline;
+    DAWN_TRY_ASSIGN(pipeline, device->CreateRenderPipeline(&renderPipelineDesc));
+
+    store->blitBufferToTexturePipelines.emplace(format, pipeline);
+    return pipeline;
+}
+
+}  // anonymous namespace
+
+bool IsFormatSupportedByBufferToTextureBlit(wgpu::TextureFormat format) {
+    // TODO(348653642): Eventually we should support all non-compressed formats. For now, just list
+    // a subset of them that we support.
+    switch (format) {
+        case wgpu::TextureFormat::R8Unorm:
+        case wgpu::TextureFormat::RG8Unorm:
+        case wgpu::TextureFormat::RGBA8Unorm:
+        case wgpu::TextureFormat::BGRA8Unorm:
+        case wgpu::TextureFormat::R16Float:
+        case wgpu::TextureFormat::RG16Float:
+        case wgpu::TextureFormat::RGBA16Float:
+        case wgpu::TextureFormat::R32Float:
+        case wgpu::TextureFormat::RG32Float:
+        case wgpu::TextureFormat::RGBA32Float:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool IsBufferToTextureBlitSupported(BufferBase* buffer,
+                                    const TextureCopy& dst,
+                                    const Extent3D& copyExtent) {
+    if (!(buffer->GetInternalUsage() &
+          (kReadOnlyStorageBuffer | kInternalStorageBuffer | wgpu::BufferUsage::Storage))) {
+        return false;
+    }
+
+    if (!IsFormatSupportedByBufferToTextureBlit(dst.texture->GetFormat().format)) {
+        return false;
+    }
+
+    if (dst.texture->GetDimension() == wgpu::TextureDimension::e1D) {
+        // 1D texture cannot be rendered to so skip it.
+        return false;
+    }
+
+    if (dst.aspect != Aspect::Color) {
+        // Don't support multiplanar copies yet.
+        return false;
+    }
+
+    // Must have non-zero copy size.
+    return copyExtent.width * copyExtent.height * copyExtent.depthOrArrayLayers > 0;
+}
+
+MaybeError BlitBufferToTexture(DeviceBase* device,
+                               CommandEncoder* commandEncoder,
+                               BufferBase* buffer,
+                               const TexelCopyBufferLayout& src,
+                               const TextureCopy& dst,
+                               const Extent3D& copyExtent) {
+    DAWN_ASSERT(device->IsLockedByCurrentThreadIfNeeded());
+
+    // This function assumes bytesPerRow is multiples of 4. Normally it's required that
+    // bytesPerRow is aligned to 256. However some backends might enable
+    // DawnTexelCopyBufferRowAlignment feature to relax the alignment. Currently only D3D11 backend
+    // enables this feature, and the relaxed alignment there is 4.
+    DAWN_ASSERT((src.bytesPerRow % 4) == 0);
+
+    DAWN_ASSERT(buffer->GetInternalUsage() &
+                (kReadOnlyStorageBuffer | kInternalStorageBuffer | wgpu::BufferUsage::Storage));
+
+    DAWN_ASSERT(copyExtent.width > 0 && copyExtent.height > 0 && copyExtent.depthOrArrayLayers > 0);
+
+    // Allow internal usages since we need to use the destination
+    // as a render attachment.
+    auto scope = commandEncoder->MakeInternalUsageScope();
+
+    Ref<RenderPipelineBase> pipeline;
+    DAWN_TRY_ASSIGN(pipeline, GetOrCreatePipeline(device, dst.texture->GetFormat().format));
+
+    Ref<BindGroupLayoutBase> bgl;
+    DAWN_TRY_ASSIGN(bgl, pipeline->GetBindGroupLayout(0));
+
+    const auto ssboAlignment = device->GetLimits().v1.minStorageBufferOffsetAlignment;
+    DAWN_ASSERT(IsPowerOfTwo(ssboAlignment));
+
+    wgpu::TextureViewDimension viewDimension;
+    uint32_t baseDepth = 0;
+    uint32_t baseArray = 0;
+    uint32_t depthStep = 0;
+    uint32_t arrayStep = 0;
+    switch (dst.texture->GetDimension()) {
+        case wgpu::TextureDimension::e1D:
+            DAWN_UNREACHABLE();
+            break;
+        case wgpu::TextureDimension::e3D:
+            viewDimension = wgpu::TextureViewDimension::e3D;
+            baseDepth = dst.origin.z;
+            depthStep = 1;
+            break;
+        default:
+            viewDimension = wgpu::TextureViewDimension::e2D;
+            baseArray = dst.origin.z;
+            arrayStep = 1;
+            break;
+    }
+
+    for (uint32_t z = 0; z < copyExtent.depthOrArrayLayers; ++z) {
+        Ref<TextureViewBase> dstView;
+        {
+            TextureViewDescriptor viewDesc = {};
+            viewDesc.dimension = viewDimension;
+            viewDesc.baseArrayLayer = baseArray + arrayStep * z;
+            viewDesc.arrayLayerCount = 1;
+            viewDesc.baseMipLevel = dst.mipLevel;
+            viewDesc.mipLevelCount = 1;
+            DAWN_TRY_ASSIGN(dstView, dst.texture->CreateView(&viewDesc));
+        }
+
+        const uint64_t srcOffset = src.offset + z * src.rowsPerImage * src.bytesPerRow;
+        const uint64_t srcBufferBindingOffset = AlignDown(srcOffset, ssboAlignment);
+        const uint32_t shaderReadOffset = static_cast<uint32_t>(srcOffset & (ssboAlignment - 1));
+        Ref<BufferBase> paramsBuffer;
+        {
+            DAWN_TRY_ASSIGN(paramsBuffer,
+                            device->GetOrCreateTemporaryUniformBuffer(sizeof(uint32_t) * 4));
+
+            uint32_t params[4];
+            params[0] = shaderReadOffset;
+            params[1] = src.bytesPerRow;
+            params[2] = dst.origin.x;
+            params[3] = dst.origin.y;
+            commandEncoder->APIWriteBuffer(paramsBuffer.Get(), 0,
+                                           reinterpret_cast<const uint8_t*>(&params[0]),
+                                           sizeof(params));
+        }
+
+        Ref<BindGroupBase> bindGroup;
+        DAWN_TRY_ASSIGN(bindGroup, utils::MakeBindGroup(device, bgl,
+                                                        {
+                                                            {0, buffer, srcBufferBindingOffset},
+                                                            {1, paramsBuffer},
+                                                        },
+                                                        UsageValidationMode::Internal));
+
+        RenderPassColorAttachment colorAttachment;
+        colorAttachment.view = dstView.Get();
+        if (depthStep) {
+            colorAttachment.depthSlice = baseDepth + depthStep * z;
+        }
+        colorAttachment.loadOp = wgpu::LoadOp::Load;
+        colorAttachment.storeOp = wgpu::StoreOp::Store;
+
+        RenderPassDescriptor rpDesc = {};
+        rpDesc.colorAttachmentCount = 1;
+        rpDesc.colorAttachments = &colorAttachment;
+
+        Ref<RenderPassEncoder> pass = commandEncoder->BeginRenderPass(&rpDesc);
+        // Bind the resources.
+        pass->APISetBindGroup(0, bindGroup.Get());
+        pass->APISetViewport(dst.origin.x, dst.origin.y, copyExtent.width, copyExtent.height, 0.f,
+                             1.f);
+
+        // Draw to perform the blit.
+        pass->APISetPipeline(pipeline.Get());
+        pass->APIDraw(3, 1, 0, 0);
+
+        pass->End();
+    }
+    return {};
+}
+
+}  // namespace dawn::native
diff --git a/src/dawn/native/BlitBufferToTexture.h b/src/dawn/native/BlitBufferToTexture.h
new file mode 100644
index 0000000..20497a5
--- /dev/null
+++ b/src/dawn/native/BlitBufferToTexture.h
@@ -0,0 +1,53 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_NATIVE_BLITBUFFERTOTEXTURE_H_
+#define SRC_DAWN_NATIVE_BLITBUFFERTOTEXTURE_H_
+
+#include "dawn/native/Error.h"
+
+namespace dawn::native {
+
+struct Format;
+struct TextureCopy;
+struct TexelCopyBufferLayout;
+
+bool IsFormatSupportedByBufferToTextureBlit(wgpu::TextureFormat format);
+bool IsBufferToTextureBlitSupported(BufferBase* buffer,
+                                    const TextureCopy& dst,
+                                    const Extent3D& copyExtent);
+
+MaybeError BlitBufferToTexture(DeviceBase* device,
+                               CommandEncoder* commandEncoder,
+                               BufferBase* buffer,
+                               const TexelCopyBufferLayout& src,
+                               const TextureCopy& dst,
+                               const Extent3D& copyExtent);
+
+}  // namespace dawn::native
+
+#endif  // SRC_DAWN_NATIVE_BLITBUFFERTOTEXTURE_H_
diff --git a/src/dawn/native/Buffer.cpp b/src/dawn/native/Buffer.cpp
index 77b5d39..7e3397f 100644
--- a/src/dawn/native/Buffer.cpp
+++ b/src/dawn/native/Buffer.cpp
@@ -97,7 +97,14 @@
     std::unique_ptr<uint8_t[]> mFakeMappedData;
 };
 
-wgpu::BufferUsage AddInternalUsages(const DeviceBase* device, wgpu::BufferUsage usage) {
+// GetMappedRange on a zero-sized buffer returns a pointer to this value.
+static uint32_t sZeroSizedMappingData = 0xCAFED00D;
+
+}  // anonymous namespace
+
+wgpu::BufferUsage ComputeInternalBufferUsages(const DeviceBase* device,
+                                              wgpu::BufferUsage usage,
+                                              size_t bufferSize) {
     // Add readonly storage usage if the buffer has a storage usage. The validation rules in
     // ValidateSyncScopeResourceUsage will make sure we don't use both at the same time.
     if (usage & wgpu::BufferUsage::Storage) {
@@ -135,11 +142,10 @@
             device->IsToggleEnabled(Toggle::UseBlitForFloat32TextureCopy) ||
             device->IsToggleEnabled(Toggle::UseBlitForT2B);
         if (useComputeForT2B) {
-            if (!(usage & (kMappableBufferUsages | wgpu::BufferUsage::Uniform)) ||
-                !device->PreferNotUsingMappableOrUniformBufferAsStorage()) {
-                // If buffer doesn't have mapping nor Uniform usage, or backend is ok with using
-                // this kind of buffer as storage buffer, we can add Storage usage in order to write
-                // to it in compute shader.
+            if (device->CanAddStorageUsageToBufferWithoutSideEffects(kInternalStorageBuffer, usage,
+                                                                     bufferSize)) {
+                // If the backend is ok with using this kind of buffer as storage buffer, we can add
+                // Storage usage in order to write to it in compute shader.
                 usage |= kInternalStorageBuffer;
             }
 
@@ -151,14 +157,18 @@
         }
     }
 
+    if ((usage & wgpu::BufferUsage::CopySrc) && device->IsToggleEnabled(Toggle::UseBlitForB2T)) {
+        if (device->CanAddStorageUsageToBufferWithoutSideEffects(kReadOnlyStorageBuffer, usage,
+                                                                 bufferSize)) {
+            // If the backend is ok with using this kind of buffer as readonly storage buffer,
+            // we can add Storage usage in order to read from it in pixel shader.
+            usage |= kReadOnlyStorageBuffer;
+        }
+    }
+
     return usage;
 }
 
-// GetMappedRange on a zero-sized buffer returns a pointer to this value.
-static uint32_t sZeroSizedMappingData = 0xCAFED00D;
-
-}  // anonymous namespace
-
 struct BufferBase::MapAsyncEvent final : public EventManager::TrackedEvent {
     // MapAsyncEvent stores a raw pointer to the buffer so that it can update the buffer's map state
     // when it completes. If the map completes early (error, unmap, destroy), then the buffer is no
@@ -339,7 +349,7 @@
     : SharedResource(device, descriptor->label),
       mSize(descriptor->size),
       mUsage(descriptor->usage),
-      mInternalUsage(AddInternalUsages(device, descriptor->usage)),
+      mInternalUsage(ComputeInternalBufferUsages(device, descriptor->usage, descriptor->size)),
       mState(descriptor.Get<BufferHostMappedPointer>() ? BufferState::HostMappedPersistent
                                                        : BufferState::Unmapped) {
     GetObjectTrackingList()->Track(this);
diff --git a/src/dawn/native/Buffer.h b/src/dawn/native/Buffer.h
index da80095..d0439eb 100644
--- a/src/dawn/native/Buffer.h
+++ b/src/dawn/native/Buffer.h
@@ -69,6 +69,12 @@
 static constexpr wgpu::BufferUsage kReadOnlyShaderBufferUsages =
     kShaderBufferUsages & kReadOnlyBufferUsages;
 
+// Return the actual internal buffer usages that will be used to create a buffer.
+// In other words, after being created, buffer.GetInternalUsage() will return this value.
+wgpu::BufferUsage ComputeInternalBufferUsages(const DeviceBase* device,
+                                              wgpu::BufferUsage usage,
+                                              size_t bufferSize);
+
 class BufferBase : public SharedResource {
   public:
     enum class BufferState {
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 155a0f9..d3128bf 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -50,6 +50,7 @@
     "BindGroupTracker.h"
     "BindingInfo.h"
     "BlitBufferToDepthStencil.h"
+    "BlitBufferToTexture.h"
     "BlitColorToColorWithDraw.h"
     "BlitDepthToDepth.h"
     "BlitTextureToBuffer.h"
@@ -167,6 +168,7 @@
     "BindGroupLayoutInternal.cpp"
     "BindingInfo.cpp"
     "BlitBufferToDepthStencil.cpp"
+    "BlitBufferToTexture.cpp"
     "BlitColorToColorWithDraw.cpp"
     "BlitDepthToDepth.cpp"
     "BlitTextureToBuffer.cpp"
diff --git a/src/dawn/native/CommandEncoder.cpp b/src/dawn/native/CommandEncoder.cpp
index c3e8fe8..f89de32 100644
--- a/src/dawn/native/CommandEncoder.cpp
+++ b/src/dawn/native/CommandEncoder.cpp
@@ -40,6 +40,7 @@
 #include "dawn/native/ApplyClearColorValueWithDrawHelper.h"
 #include "dawn/native/BindGroup.h"
 #include "dawn/native/BlitBufferToDepthStencil.h"
+#include "dawn/native/BlitBufferToTexture.h"
 #include "dawn/native/BlitDepthToDepth.h"
 #include "dawn/native/BlitTextureToBuffer.h"
 #include "dawn/native/Buffer.h"
@@ -1617,6 +1618,18 @@
                                  "copying from %s to stencil aspect of %s using blit workaround.",
                                  source->buffer, dst.texture.Get());
                 return {};
+            } else if (GetDevice()->IsToggleEnabled(Toggle::UseBlitForB2T) &&
+                       IsBufferToTextureBlitSupported(source->buffer, dst, *copySize)) {
+                // This function might create new resources. Need to lock the Device.
+                // TODO(crbug.com/dawn/1618): In future, all temp resources should be created at
+                // Command Submit time, so the locking would be removed from here at that point.
+                auto deviceLock(GetDevice()->GetScopedLock());
+                DAWN_TRY_CONTEXT(BlitBufferToTexture(GetDevice(), this, source->buffer, srcLayout,
+                                                     dst, *copySize),
+                                 "copying buffer %s to %s using blit workaround.", source->buffer,
+                                 dst.texture.Get());
+
+                return {};
             }
 
             CopyBufferToTextureCmd* copy =
diff --git a/src/dawn/native/Device.cpp b/src/dawn/native/Device.cpp
index 0038ee4..d79a65e 100644
--- a/src/dawn/native/Device.cpp
+++ b/src/dawn/native/Device.cpp
@@ -402,6 +402,11 @@
     // Handle maxXXXPerStage/maxXXXInStage.
     EnforceLimitSpecInvariants(&mLimits.v1, effectiveFeatureLevel);
 
+    if (mLimits.v1.maxStorageBuffersInFragmentStage < 1) {
+        // If there is no storage buffer in fragment stage, UseBlitForB2T is not possible.
+        mToggles.ForceSet(Toggle::UseBlitForB2T, false);
+    }
+
     mFormatTable = BuildFormatTable(this);
 
     if (!descriptor->label.IsUndefined()) {
@@ -2367,8 +2372,10 @@
     return false;
 }
 
-bool DeviceBase::PreferNotUsingMappableOrUniformBufferAsStorage() const {
-    return false;
+bool DeviceBase::CanAddStorageUsageToBufferWithoutSideEffects(wgpu::BufferUsage storageUsage,
+                                                              wgpu::BufferUsage originalUsage,
+                                                              size_t bufferSize) const {
+    return true;
 }
 
 uint64_t DeviceBase::GetBufferCopyOffsetAlignmentForDepthStencil() const {
diff --git a/src/dawn/native/Device.h b/src/dawn/native/Device.h
index 6212db9..5b17855 100644
--- a/src/dawn/native/Device.h
+++ b/src/dawn/native/Device.h
@@ -257,7 +257,7 @@
     SamplerBase* APICreateSampler(const SamplerDescriptor* descriptor);
     ShaderModuleBase* APICreateShaderModule(const ShaderModuleDescriptor* descriptor);
     ShaderModuleBase* APICreateErrorShaderModule(const ShaderModuleDescriptor* descriptor,
-                                                  StringView errorMessage);
+                                                 StringView errorMessage);
     TextureBase* APICreateTexture(const TextureDescriptor* descriptor);
 
     InternalPipelineStore* GetInternalPipelineStore();
@@ -379,8 +379,12 @@
     // will be resolved into.
     virtual bool CanTextureLoadResolveTargetInTheSameRenderpass() const;
 
-    // Whether the backend prefer not using mappable/uniform buffer as storage buffer.
-    virtual bool PreferNotUsingMappableOrUniformBufferAsStorage() const;
+    // Whether the backend can add internal storage usage to the buffer without side effects.
+    // - storageUsage is the internal storage usage that would be added.
+    // - originalUsage is the original usage of the buffer.
+    virtual bool CanAddStorageUsageToBufferWithoutSideEffects(wgpu::BufferUsage storageUsage,
+                                                              wgpu::BufferUsage originalUsage,
+                                                              size_t bufferSize) const;
 
     bool HasFeature(Feature feature) const;
 
diff --git a/src/dawn/native/InternalPipelineStore.h b/src/dawn/native/InternalPipelineStore.h
index 6d53a83..cce4aac 100644
--- a/src/dawn/native/InternalPipelineStore.h
+++ b/src/dawn/native/InternalPipelineStore.h
@@ -110,6 +110,7 @@
         blitR8ToStencilPipelines;
 
     absl::flat_hash_map<wgpu::TextureFormat, Ref<RenderPipelineBase>> depthBlitPipelines;
+    absl::flat_hash_map<wgpu::TextureFormat, Ref<RenderPipelineBase>> blitBufferToTexturePipelines;
 
     BlitColorToColorWithDrawPipelinesCache expandResolveTexturePipelines;
 
diff --git a/src/dawn/native/Texture.cpp b/src/dawn/native/Texture.cpp
index fc2ee35..e395ab4 100644
--- a/src/dawn/native/Texture.cpp
+++ b/src/dawn/native/Texture.cpp
@@ -35,6 +35,7 @@
 #include "dawn/common/Constants.h"
 #include "dawn/common/Math.h"
 #include "dawn/native/Adapter.h"
+#include "dawn/native/BlitBufferToTexture.h"
 #include "dawn/native/BlitTextureToBuffer.h"
 #include "dawn/native/ChainUtils.h"
 #include "dawn/native/CommandValidation.h"
@@ -524,6 +525,11 @@
         device->IsToggleEnabled(Toggle::UseBlitForBufferToStencilTextureCopy)) {
         return true;
     }
+
+    if (device->IsToggleEnabled(Toggle::UseBlitForB2T) &&
+        IsFormatSupportedByBufferToTextureBlit(format.format)) {
+        return true;
+    }
     return false;
 }
 
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index e771ea0..bcc05a4 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -449,6 +449,15 @@
       "Use a compute based blit instead of a copy command to copy texture with supported format to "
       "a buffer.",
       "https://crbug.com/dawn/348654098", ToggleStage::Device}},
+    {Toggle::UseBlitForB2T,
+     {"use_blit_for_b2t",
+      "Use a shader based blit instead of a copy command to copy a buffer to a texture with "
+      "supported format.",
+      "https://crbug.com/dawn/348653642", ToggleStage::Device}},
+    {Toggle::D3D11DisableCPUUploadBuffers,
+     {"d3d11_disable_cpu_buffers",
+      "Force disabling the usages of CPU upload buffers in the D3D11 backend.",
+      "https://crbug.com/dawn/348653642", ToggleStage::Device}},
     {Toggle::UseT2B2TForSRGBTextureCopy,
      {"use_t2b2t_for_srgb_texture_copy",
       "Use T2B and B2T copies to emulate a T2T copy between sRGB and non-sRGB textures."
diff --git a/src/dawn/native/Toggles.h b/src/dawn/native/Toggles.h
index ae751c0..16b2590 100644
--- a/src/dawn/native/Toggles.h
+++ b/src/dawn/native/Toggles.h
@@ -114,6 +114,8 @@
     UseBlitForFloat16TextureCopy,
     UseBlitForFloat32TextureCopy,
     UseBlitForT2B,
+    UseBlitForB2T,
+    D3D11DisableCPUUploadBuffers,
     UseT2B2TForSRGBTextureCopy,
     D3D12ReplaceAddWithMinusWhenDstFactorIsZeroAndSrcFactorIsDstAlpha,
     D3D12PolyfillReflectVec2F32,
diff --git a/src/dawn/native/d3d11/BufferD3D11.cpp b/src/dawn/native/d3d11/BufferD3D11.cpp
index 804ac65..7af87ca 100644
--- a/src/dawn/native/d3d11/BufferD3D11.cpp
+++ b/src/dawn/native/d3d11/BufferD3D11.cpp
@@ -41,6 +41,7 @@
 #include "dawn/native/DynamicUploader.h"
 #include "dawn/native/d3d/D3DError.h"
 #include "dawn/native/d3d11/DeviceD3D11.h"
+#include "dawn/native/d3d11/PhysicalDeviceD3D11.h"
 #include "dawn/native/d3d11/QueueD3D11.h"
 #include "dawn/native/d3d11/UtilsD3D11.h"
 #include "dawn/platform/DawnPlatform.h"
@@ -52,6 +53,9 @@
 
 namespace {
 
+// Max size for a CPU buffer.
+constexpr uint64_t kMaxCPUUploadBufferSize = 64 * 1024;
+
 constexpr wgpu::BufferUsage kCopyUsages =
     wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst | kInternalCopySrcBuffer;
 
@@ -136,16 +140,23 @@
         return sizeof(float) * 4 * 16;
     }
 
-    if (usage &
-        (wgpu::BufferUsage::Storage | kInternalStorageBuffer | wgpu::BufferUsage::CopyDst)) {
+    if (usage & (wgpu::BufferUsage::Storage | kInternalStorageBuffer | kReadOnlyStorageBuffer |
+                 wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc)) {
         // Unordered access buffers must be 4-byte aligned.
         // We also align 4 bytes for CopyDst buffer since it would be used in T2B compute shader.
         // And that shader needs to write 4-byte chunks.
+        // Similarly, we need to align 4 bytes for CopySrc buffer since it would be used in B2T
+        // shader that reads 4 byte chunks.
         return sizeof(uint32_t);
     }
     return 1;
 }
 
+bool CanUseCPUUploadBuffer(const Device* device, wgpu::BufferUsage usage, size_t bufferSize) {
+    return IsUpload(usage) && bufferSize <= kMaxCPUUploadBufferSize &&
+           !device->IsToggleEnabled(Toggle::D3D11DisableCPUUploadBuffers);
+}
+
 constexpr size_t kConstantBufferUpdateAlignment = 16;
 
 }  // namespace
@@ -347,22 +358,55 @@
     ComPtr<ID3D11Buffer> mD3d11Buffer;
 };
 
+bool CanAddStorageUsageToBufferWithoutSideEffects(const Device* device,
+                                                  wgpu::BufferUsage storageUsage,
+                                                  wgpu::BufferUsage originalUsage,
+                                                  size_t bufferSize) {
+    // Don't support uniform buffers being used as storage buffer. Because D3D11 constant buffers
+    // cannot be bound to SRV or UAV. Allowing them to be used as storage buffer would require some
+    // workarounds including extra copies so it's better we prefer to not do that.
+    if (originalUsage & wgpu::BufferUsage::Uniform) {
+        return false;
+    }
+
+    // If buffer is small, we prefer CPU buffer for uploading so don't allow adding storage usage.
+    if (CanUseCPUUploadBuffer(device, originalUsage, bufferSize)) {
+        return false;
+    }
+
+    const bool requiresUAV = storageUsage & (wgpu::BufferUsage::Storage | kInternalStorageBuffer);
+    // Check supports for writeable storage usage:
+    if (requiresUAV) {
+        // D3D11 mappable buffers cannot be used as UAV natively. So avoid that.
+        return !(originalUsage & kMappableBufferUsages);
+    }
+
+    return true;
+}
+
 // static
 ResultOrError<Ref<Buffer>> Buffer::Create(Device* device,
                                           const UnpackedPtr<BufferDescriptor>& descriptor,
                                           const ScopedCommandRecordingContext* commandContext,
                                           bool allowUploadBufferEmulation) {
+    const auto actualUsage =
+        ComputeInternalBufferUsages(device, descriptor->usage, descriptor->size);
     bool useUploadBuffer = allowUploadBufferEmulation;
-    useUploadBuffer &= IsUpload(descriptor->usage);
-    constexpr uint64_t kMaxUploadBufferSize = 4 * 1024 * 1024;
-    useUploadBuffer &= descriptor->size <= kMaxUploadBufferSize;
+    useUploadBuffer &= CanUseCPUUploadBuffer(device, actualUsage, descriptor->size);
     Ref<Buffer> buffer;
     if (useUploadBuffer) {
         buffer = AcquireRef(new UploadBuffer(device, descriptor));
-    } else if (IsStaging(descriptor->usage)) {
+    } else if (IsStaging(actualUsage)) {
         buffer = AcquireRef(new StagingBuffer(device, descriptor));
     } else {
-        buffer = AcquireRef(new GPUUsableBuffer(device, descriptor));
+        const auto& devInfo = ToBackend(device->GetPhysicalDevice())->GetDeviceInfo();
+        // Use D3D11_MAP_WRITE_NO_OVERWRITE when possible to guarantee driver that we don't
+        // overwrite data in use by GPU. MapAsync() already ensures that any GPU commands using this
+        // buffer already finish. In return driver won't try to stall CPU for mapping access.
+        D3D11_MAP mapWriteMode = devInfo.supportsMapNoOverwriteDynamicBuffers
+                                     ? D3D11_MAP_WRITE_NO_OVERWRITE
+                                     : D3D11_MAP_WRITE;
+        buffer = AcquireRef(new GPUUsableBuffer(device, descriptor, mapWriteMode));
     }
     DAWN_TRY(buffer->Initialize(descriptor->mappedAtCreation, commandContext));
     return buffer;
@@ -775,10 +819,12 @@
 
 // GPUUsableBuffer
 GPUUsableBuffer::GPUUsableBuffer(DeviceBase* device,
-                                 const UnpackedPtr<BufferDescriptor>& descriptor)
+                                 const UnpackedPtr<BufferDescriptor>& descriptor,
+                                 D3D11_MAP mapWriteMode)
     : Buffer(device,
              descriptor,
-             /*internalMappableFlags=*/descriptor->usage & kMappableBufferUsages) {}
+             /*internalMappableFlags=*/descriptor->usage & kMappableBufferUsages),
+      mD3DMapWriteMode(mapWriteMode) {}
 
 GPUUsableBuffer::~GPUUsableBuffer() = default;
 
@@ -1071,10 +1117,7 @@
     Storage* storage;
     if (mode == wgpu::MapMode::Write) {
         DAWN_ASSERT(!mCPUWritableStorage->IsStaging());
-        // Use D3D11_MAP_WRITE_NO_OVERWRITE to guarantee driver that we don't overwrite data in
-        // use by GPU. MapAsync() already ensures that any GPU commands using this buffer
-        // already finish. In return driver won't try to stall CPU for mapping access.
-        mapType = D3D11_MAP_WRITE_NO_OVERWRITE;
+        mapType = mD3DMapWriteMode;
         storage = mCPUWritableStorage;
     } else {
         // Always map buffer with D3D11_MAP_READ_WRITE if possible even for mapping
@@ -1193,9 +1236,10 @@
 ResultOrError<ComPtr<ID3D11ShaderResourceView>>
 GPUUsableBuffer::CreateD3D11ShaderResourceViewFromD3DBuffer(ID3D11Buffer* d3d11Buffer,
                                                             uint64_t offset,
-                                                            uint64_t size) {
+                                                            uint64_t originalSize) {
+    uint64_t size = Align(originalSize, 4);
     DAWN_ASSERT(IsAligned(offset, 4u));
-    DAWN_ASSERT(IsAligned(size, 4u));
+    DAWN_ASSERT(size <= GetAllocatedSize());
     UINT firstElement = static_cast<UINT>(offset / 4);
     UINT numElements = static_cast<UINT>(size / 4);
 
@@ -1217,9 +1261,10 @@
 ResultOrError<ComPtr<ID3D11UnorderedAccessView1>>
 GPUUsableBuffer::CreateD3D11UnorderedAccessViewFromD3DBuffer(ID3D11Buffer* d3d11Buffer,
                                                              uint64_t offset,
-                                                             uint64_t size) {
+                                                             uint64_t originalSize) {
+    uint64_t size = Align(originalSize, 4);
     DAWN_ASSERT(IsAligned(offset, 4u));
-    DAWN_ASSERT(IsAligned(size, 4u));
+    DAWN_ASSERT(size <= GetAllocatedSize());
 
     UINT firstElement = static_cast<UINT>(offset / 4);
     UINT numElements = static_cast<UINT>(size / 4);
diff --git a/src/dawn/native/d3d11/BufferD3D11.h b/src/dawn/native/d3d11/BufferD3D11.h
index d44782c..bf09d4d 100644
--- a/src/dawn/native/d3d11/BufferD3D11.h
+++ b/src/dawn/native/d3d11/BufferD3D11.h
@@ -46,6 +46,11 @@
 class ScopedCommandRecordingContext;
 class ScopedSwapStateCommandRecordingContext;
 
+bool CanAddStorageUsageToBufferWithoutSideEffects(const Device* device,
+                                                  wgpu::BufferUsage storageUsage,
+                                                  wgpu::BufferUsage originalUsage,
+                                                  size_t bufferSize);
+
 class Buffer : public BufferBase {
   public:
     static ResultOrError<Ref<Buffer>> Create(Device* device,
@@ -195,7 +200,9 @@
 // TODO(349848481): Consider making this the only Buffer class since it could cover all use cases.
 class GPUUsableBuffer final : public Buffer {
   public:
-    GPUUsableBuffer(DeviceBase* device, const UnpackedPtr<BufferDescriptor>& descriptor);
+    GPUUsableBuffer(DeviceBase* device,
+                    const UnpackedPtr<BufferDescriptor>& descriptor,
+                    D3D11_MAP mapWriteMode);
     ~GPUUsableBuffer() override;
 
     ResultOrError<ID3D11Buffer*> GetD3D11ConstantBuffer(
@@ -331,6 +338,8 @@
     using BufferViewKey = std::tuple<ID3D11Buffer*, uint64_t, uint64_t>;
     absl::flat_hash_map<BufferViewKey, ComPtr<ID3D11ShaderResourceView>> mSRVCache;
     absl::flat_hash_map<BufferViewKey, ComPtr<ID3D11UnorderedAccessView1>> mUAVCache;
+
+    const D3D11_MAP mD3DMapWriteMode = D3D11_MAP_WRITE;
 };
 
 static inline GPUUsableBuffer* ToGPUUsableBuffer(BufferBase* buffer) {
diff --git a/src/dawn/native/d3d11/DeviceD3D11.cpp b/src/dawn/native/d3d11/DeviceD3D11.cpp
index ae05559..12a15c8 100644
--- a/src/dawn/native/d3d11/DeviceD3D11.cpp
+++ b/src/dawn/native/d3d11/DeviceD3D11.cpp
@@ -462,11 +462,11 @@
     return true;
 }
 
-bool Device::PreferNotUsingMappableOrUniformBufferAsStorage() const {
-    // D3D11 constant buffer or mappable buffer cannot be used as UAV. Allowing them to be used as
-    // storage buffer would require some workarounds including extra copies so it's better we
-    // prefer to not do that.
-    return true;
+bool Device::CanAddStorageUsageToBufferWithoutSideEffects(wgpu::BufferUsage storageUsage,
+                                                          wgpu::BufferUsage originalUsage,
+                                                          size_t bufferSize) const {
+    return d3d11::CanAddStorageUsageToBufferWithoutSideEffects(this, storageUsage, originalUsage,
+                                                               bufferSize);
 }
 
 uint32_t Device::GetUAVSlotCount() const {
diff --git a/src/dawn/native/d3d11/DeviceD3D11.h b/src/dawn/native/d3d11/DeviceD3D11.h
index dcb16d0..935f4ac 100644
--- a/src/dawn/native/d3d11/DeviceD3D11.h
+++ b/src/dawn/native/d3d11/DeviceD3D11.h
@@ -77,7 +77,9 @@
     bool MayRequireDuplicationOfIndirectParameters() const override;
     uint64_t GetBufferCopyOffsetAlignmentForDepthStencil() const override;
     bool CanTextureLoadResolveTargetInTheSameRenderpass() const override;
-    bool PreferNotUsingMappableOrUniformBufferAsStorage() const override;
+    bool CanAddStorageUsageToBufferWithoutSideEffects(wgpu::BufferUsage storageUsage,
+                                                      wgpu::BufferUsage originalUsage,
+                                                      size_t bufferSize) const override;
     void SetLabelImpl() override;
 
     void DisposeKeyedMutex(ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex) override;
diff --git a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
index f6e45e2..29f3b73 100644
--- a/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
+++ b/src/dawn/native/d3d11/PhysicalDeviceD3D11.cpp
@@ -318,6 +318,7 @@
         deviceToggles->ForceSet(Toggle::D3D11DisableFence, !mDeviceInfo.supportsNonMonitoredFence);
     }
     deviceToggles->Default(Toggle::UseBlitForT2B, true);
+    deviceToggles->Default(Toggle::UseBlitForB2T, true);
 
     auto deviceId = GetDeviceId();
     auto vendorId = GetVendorId();
diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp
index 576f657..d601510 100644
--- a/src/dawn/native/vulkan/DeviceVk.cpp
+++ b/src/dawn/native/vulkan/DeviceVk.cpp
@@ -30,6 +30,7 @@
 #include <algorithm>
 
 #include "dawn/common/Log.h"
+#include "dawn/common/Math.h"
 #include "dawn/common/NonCopyable.h"
 #include "dawn/common/Platform.h"
 #include "dawn/common/Version_autogen.h"
@@ -1075,9 +1076,15 @@
     }
 }
 
-bool Device::PreferNotUsingMappableOrUniformBufferAsStorage() const {
-    // Return true when the backend doesn't support mappable storage buffer
-    return !mSupportsMappableStorageBuffer;
+bool Device::CanAddStorageUsageToBufferWithoutSideEffects(wgpu::BufferUsage storageUsage,
+                                                          wgpu::BufferUsage originalUsage,
+                                                          size_t bufferSize) const {
+    DAWN_ASSERT(IsSubset(storageUsage, wgpu::BufferUsage::Storage | kInternalStorageBuffer |
+                                           kReadOnlyStorageBuffer));
+    if (originalUsage & kMappableBufferUsages) {
+        return mSupportsMappableStorageBuffer;
+    }
+    return true;
 }
 
 }  // namespace dawn::native::vulkan
diff --git a/src/dawn/native/vulkan/DeviceVk.h b/src/dawn/native/vulkan/DeviceVk.h
index d5ed961..056ebe2 100644
--- a/src/dawn/native/vulkan/DeviceVk.h
+++ b/src/dawn/native/vulkan/DeviceVk.h
@@ -126,7 +126,9 @@
     // Used to associate this device with validation layer messages.
     const char* GetDebugPrefix() { return mDebugPrefix.c_str(); }
 
-    bool PreferNotUsingMappableOrUniformBufferAsStorage() const override;
+    bool CanAddStorageUsageToBufferWithoutSideEffects(wgpu::BufferUsage storageUsage,
+                                                      wgpu::BufferUsage originalUsage,
+                                                      size_t bufferSize) const override;
 
   private:
     Device(AdapterBase* adapter,
diff --git a/src/dawn/tests/end2end/CopyTests.cpp b/src/dawn/tests/end2end/CopyTests.cpp
index 87823d1..f0b4a59 100644
--- a/src/dawn/tests/end2end/CopyTests.cpp
+++ b/src/dawn/tests/end2end/CopyTests.cpp
@@ -27,7 +27,10 @@
 
 #include <algorithm>
 #include <array>
+#include <ostream>
 #include <sstream>
+#include <string>
+#include <type_traits>
 #include <vector>
 
 #include "dawn/common/Constants.h"
@@ -50,6 +53,119 @@
            format == wgpu::TextureFormat::R8Snorm;
 }
 
+template <typename T, size_t NumComponents>
+struct Color {
+  public:
+    static constexpr size_t kNumComponents = NumComponents;
+    static constexpr size_t kDataSize = sizeof(T) * NumComponents;
+
+    Color() = default;
+    explicit Color(T value) { std::fill(components, components + NumComponents, value); }
+
+    bool Equals(const Color& other, const Color& tolerance) const {
+        for (size_t i = 0; i < NumComponents; ++i) {
+            if (Diff(components[i], other.components[i]) > tolerance.components[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    std::string ToString() const {
+        std::ostringstream ss;
+        for (size_t i = 0; i < NumComponents; ++i) {
+            Print(ss, components[i]);
+
+            if (i < NumComponents - 1) {
+                ss << " ";
+            }
+        }
+
+        return ss.str();
+    }
+
+  protected:
+    static T Diff(T lhs, T rhs) {
+        if constexpr (std::is_integral_v<T>) {
+            return std::abs(static_cast<int64_t>(lhs) - static_cast<int64_t>(rhs));
+        }
+        return std::abs(lhs - rhs);
+    }
+
+    static std::ostream& Print(std::ostream& stream, T component) {
+        if constexpr (std::is_same_v<T, uint8_t>) {
+            return stream << static_cast<int>(component);
+        }
+        return stream << component;
+    }
+
+    T components[NumComponents] = {};
+};
+
+template <size_t NumComponents>
+struct ColorF16 : public Color<uint16_t, NumComponents> {
+  public:
+    using Base = Color<uint16_t, NumComponents>;
+    using Base::Base;
+
+    explicit ColorF16(float value) : Base(Float32ToFloat16(value)) {}
+
+    bool Equals(const ColorF16& other, const ColorF16& tolerance) const {
+        for (size_t i = 0; i < NumComponents; ++i) {
+            if (abs(Float16ToFloat32(this->components[i]) - Float16ToFloat32(other.components[i])) >
+                Float16ToFloat32(tolerance.components[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    std::string ToString() const {
+        std::ostringstream ss;
+        for (size_t i = 0; i < NumComponents; ++i) {
+            ss << Float16ToFloat32(this->components[i]);
+            if (i < NumComponents - 1) {
+                ss << " ";
+            }
+        }
+
+        return ss.str();
+    }
+};
+
+static_assert(sizeof(Color<uint8_t, 1>) == 1, "Unexpected padding");
+static_assert(sizeof(Color<uint8_t, 2>) == 2, "Unexpected padding");
+static_assert(sizeof(Color<uint16_t, 1>) == 2, "Unexpected padding");
+
+template <typename ColorType>
+class ColorExpectation : public detail::CustomTextureExpectation {
+  public:
+    ColorExpectation(const ColorType* expected, size_t count, ColorType tolerance)
+        : mTolerance(tolerance) {
+        mExpected.assign(expected, expected + count);
+    }
+
+    uint32_t DataSize() override { return ColorType::kDataSize; }
+
+    testing::AssertionResult Check(const void* data, size_t size) override {
+        DAWN_ASSERT(size == sizeof(ColorType) * mExpected.size());
+        const ColorType* actual = static_cast<const ColorType*>(data);
+
+        for (size_t i = 0; i < mExpected.size(); ++i) {
+            if (!mExpected[i].Equals(actual[i], mTolerance)) {
+                return testing::AssertionFailure()
+                       << "Expected data[" << i << "] to be " << mExpected[i].ToString()
+                       << ", actual " << actual[i].ToString() << "\n";
+            }
+        }
+        return testing::AssertionSuccess();
+    }
+
+  private:
+    std::vector<ColorType> mExpected;
+    ColorType mTolerance;
+};
+
 class CopyTests {
   protected:
     struct TextureSpec {
@@ -259,9 +375,9 @@
 namespace {
 using TextureFormat = wgpu::TextureFormat;
 DAWN_TEST_PARAM_STRUCT(CopyTextureFormatParams, TextureFormat);
-}  // namespace
 
-class CopyTests_T2B : public CopyTests, public DawnTestWithParams<CopyTextureFormatParams> {
+class CopyTests_WithFormatParam : public CopyTests,
+                                  public DawnTestWithParams<CopyTextureFormatParams> {
   protected:
     struct TextureSpec : CopyTests::TextureSpec {
         TextureSpec() { format = GetParam().mTextureFormat; }
@@ -269,17 +385,50 @@
 
     std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
         std::vector<wgpu::FeatureName> requiredFeatures = {};
-        if (SupportsFeatures({wgpu::FeatureName::FlexibleTextureViews})) {
-            requiredFeatures.push_back(wgpu::FeatureName::FlexibleTextureViews);
-        }
         if (SupportsFeatures({wgpu::FeatureName::DawnTexelCopyBufferRowAlignment})) {
             requiredFeatures.push_back(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment);
         }
         return requiredFeatures;
     }
 
+    uint32_t GetTextureBytesPerRowAlignment() const {
+        if (!device.HasFeature(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment)) {
+            return kTextureBytesPerRowAlignment;
+        }
+        wgpu::Limits limits{};
+        wgpu::DawnTexelCopyBufferRowAlignmentLimits alignmentLimits{};
+        limits.nextInChain = &alignmentLimits;
+        device.GetLimits(&limits);
+        return alignmentLimits.minTexelCopyBufferRowAlignment;
+    }
+    BufferSpec MinimumBufferSpec(uint32_t width, uint32_t height, uint32_t depth = 1) {
+        return CopyTests::MinimumBufferSpec(width, height, depth, GetParam().mTextureFormat,
+                                            GetTextureBytesPerRowAlignment());
+    }
+    BufferSpec MinimumBufferSpec(wgpu::Extent3D copyExtent,
+                                 uint32_t overrideBytesPerRow = kStrideComputeDefault,
+                                 uint32_t overrideRowsPerImage = kStrideComputeDefault) {
+        return CopyTests::MinimumBufferSpec(copyExtent, overrideBytesPerRow, overrideRowsPerImage,
+                                            GetParam().mTextureFormat,
+                                            GetTextureBytesPerRowAlignment());
+    }
+};
+
+}  // namespace
+
+class CopyTests_T2B : public CopyTests_WithFormatParam {
+  protected:
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        std::vector<wgpu::FeatureName> requiredFeatures =
+            CopyTests_WithFormatParam::GetRequiredFeatures();
+        if (SupportsFeatures({wgpu::FeatureName::FlexibleTextureViews})) {
+            requiredFeatures.push_back(wgpu::FeatureName::FlexibleTextureViews);
+        }
+        return requiredFeatures;
+    }
+
     void SetUp() override {
-        DawnTestWithParams<CopyTextureFormatParams>::SetUp();
+        CopyTests_WithFormatParam::SetUp();
 
         auto format = GetParam().mTextureFormat;
 
@@ -306,27 +455,6 @@
                                format == wgpu::TextureFormat::RG11B10Ufloat) &&
                               (IsD3D11() || IsOpenGLES()) && IsIntelGen12());
     }
-    uint32_t GetTextureBytesPerRowAlignment() const {
-        if (!device.HasFeature(wgpu::FeatureName::DawnTexelCopyBufferRowAlignment)) {
-            return kTextureBytesPerRowAlignment;
-        }
-        wgpu::Limits limits{};
-        wgpu::DawnTexelCopyBufferRowAlignmentLimits alignmentLimits{};
-        limits.nextInChain = &alignmentLimits;
-        device.GetLimits(&limits);
-        return alignmentLimits.minTexelCopyBufferRowAlignment;
-    }
-    BufferSpec MinimumBufferSpec(uint32_t width, uint32_t height, uint32_t depth = 1) {
-        return CopyTests::MinimumBufferSpec(width, height, depth, GetParam().mTextureFormat,
-                                            GetTextureBytesPerRowAlignment());
-    }
-    BufferSpec MinimumBufferSpec(wgpu::Extent3D copyExtent,
-                                 uint32_t overrideBytesPerRow = kStrideComputeDefault,
-                                 uint32_t overrideRowsPerImage = kStrideComputeDefault) {
-        return CopyTests::MinimumBufferSpec(copyExtent, overrideBytesPerRow, overrideRowsPerImage,
-                                            GetParam().mTextureFormat,
-                                            GetTextureBytesPerRowAlignment());
-    }
 
     void DoTest(
         const TextureSpec& textureSpec,
@@ -480,29 +608,86 @@
     }
 };
 
-class CopyTests_B2T : public CopyTests, public DawnTest {
+class CopyTests_B2T : public CopyTests_WithFormatParam {
   protected:
-    static void FillBufferData(utils::RGBA8* data, size_t count) {
-        for (size_t i = 0; i < count; ++i) {
-            data[i] =
-                utils::RGBA8(static_cast<uint8_t>(i % 256), static_cast<uint8_t>((i / 256) % 256),
-                             static_cast<uint8_t>((i / 256 / 256) % 256), 255);
-        }
-    }
-
     void DoTest(const TextureSpec& textureSpec,
                 const BufferSpec& bufferSpec,
                 const wgpu::Extent3D& copySize,
                 wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
-        // TODO(crbug.com/dawn/818): support testing arbitrary formats
-        ASSERT_EQ(kDefaultFormat, textureSpec.format);
-        // Create a buffer of size `size` and populate it with data
-        const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
-        std::vector<utils::RGBA8> bufferData(bufferSpec.size / bytesPerTexel);
-        FillBufferData(bufferData.data(), bufferData.size());
-        wgpu::Buffer buffer =
-            utils::CreateBufferFromData(device, bufferData.data(), bufferSpec.size,
-                                        wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
+        switch (textureSpec.format) {
+            case wgpu::TextureFormat::R8Unorm:
+                DoTestImpl<Color<uint8_t, 1>>(textureSpec, bufferSpec, copySize, dimension,
+                                              /*tolerance=*/Color<uint8_t, 1>(1));
+                break;
+            case wgpu::TextureFormat::RG8Unorm:
+                DoTestImpl<Color<uint8_t, 2>>(textureSpec, bufferSpec, copySize, dimension,
+                                              /*tolerance=*/Color<uint8_t, 2>(1));
+                break;
+            case wgpu::TextureFormat::RGBA8Unorm:
+            case wgpu::TextureFormat::BGRA8Unorm:
+                DoTestImpl<Color<uint8_t, 4>>(textureSpec, bufferSpec, copySize, dimension,
+                                              /*tolerance=*/Color<uint8_t, 4>(1));
+                break;
+            case wgpu::TextureFormat::R16Float:
+                DoTestImpl<ColorF16<1>>(textureSpec, bufferSpec, copySize, dimension,
+                                        /*tolerance=*/ColorF16<1>(0.001f));
+                break;
+            case wgpu::TextureFormat::RG16Float:
+                DoTestImpl<ColorF16<2>>(textureSpec, bufferSpec, copySize, dimension,
+                                        /*tolerance=*/ColorF16<2>(0.001f));
+                break;
+            case wgpu::TextureFormat::RGBA16Float:
+                DoTestImpl<ColorF16<4>>(textureSpec, bufferSpec, copySize, dimension,
+                                        /*tolerance=*/ColorF16<4>(0.001f));
+                break;
+            case wgpu::TextureFormat::R32Float:
+                DoTestImpl<Color<float, 1>>(textureSpec, bufferSpec, copySize, dimension,
+                                            /*tolerance=*/Color<float, 1>(0.0001f));
+                break;
+            case wgpu::TextureFormat::RG32Float:
+                DoTestImpl<Color<float, 2>>(textureSpec, bufferSpec, copySize, dimension,
+                                            /*tolerance=*/Color<float, 2>(0.0001f));
+                break;
+            case wgpu::TextureFormat::RGBA32Float:
+                DoTestImpl<Color<float, 4>>(textureSpec, bufferSpec, copySize, dimension,
+                                            /*tolerance=*/Color<float, 4>(0.0001f));
+                break;
+            default:
+                DAWN_UNREACHABLE();
+        }
+    }
+
+    template <class PixelType>
+    void DoTestImpl(const TextureSpec& textureSpec,
+                    const BufferSpec& bufferSpec,
+                    const wgpu::Extent3D& copySize,
+                    wgpu::TextureDimension dimension,
+                    PixelType tolerance = {}) {
+        const uint32_t bytesPerTexel = PixelType::kDataSize;
+        DAWN_ASSERT(bytesPerTexel == utils::GetTexelBlockSizeInBytes(textureSpec.format));
+        const utils::TextureDataCopyLayout copyLayout =
+            utils::GetTextureDataCopyLayoutForTextureAtLevel(
+                textureSpec.format, textureSpec.textureSize, textureSpec.copyLevel, dimension,
+                bufferSpec.rowsPerImage, GetTextureBytesPerRowAlignment());
+
+        // Create a buffer and populate it with data
+        wgpu::Buffer buffer;
+        std::vector<uint8_t> bufferData(bufferSpec.offset, 0xff);
+        {
+            const std::vector<uint8_t> copyData =
+                GetExpectedTextureData(textureSpec.format, copyLayout);
+
+            bufferData.insert(bufferData.end(), copyData.begin(), copyData.end());
+            bufferData.resize(Align(bufferSpec.size, 4));
+
+            wgpu::BufferDescriptor descriptor;
+            descriptor.size = bufferData.size();
+            descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
+            descriptor.mappedAtCreation = true;
+            buffer = device.CreateBuffer(&descriptor);
+            memcpy(buffer.GetMappedRange(), bufferData.data(), bufferData.size());
+            buffer.Unmap();
+        }
 
         // Create a texture that is `width` x `height` with (`level` + 1) mip levels.
         wgpu::TextureDescriptor descriptor;
@@ -525,11 +710,6 @@
         wgpu::CommandBuffer commands = encoder.Finish();
         queue.Submit(1, &commands);
 
-        const utils::TextureDataCopyLayout copyLayout =
-            utils::GetTextureDataCopyLayoutForTextureAtLevel(
-                textureSpec.format, textureSpec.textureSize, textureSpec.copyLevel, dimension,
-                bufferSpec.rowsPerImage);
-
         uint32_t copyLayer = copySize.depthOrArrayLayers;
         uint32_t copyDepth = 1;
         if (dimension == wgpu::TextureDimension::e3D) {
@@ -546,16 +726,19 @@
         for (uint32_t layer = 0; layer < copyLayer; ++layer) {
             // Copy and pack the data used to create the buffer in the specified copy region to have
             // the same format as the expected texture data.
-            std::vector<utils::RGBA8> expected(texelCountPerLayer);
-            CopyTextureData(bytesPerTexel, bufferData.data() + bufferOffset / bytesPerTexel,
-                            copySize.width, copySize.height, copyDepth, bufferSpec.bytesPerRow,
+            std::vector<PixelType> expected(texelCountPerLayer);
+            CopyTextureData(bytesPerTexel, bufferData.data() + bufferOffset, copySize.width,
+                            copySize.height, copyDepth, bufferSpec.bytesPerRow,
                             bufferSpec.rowsPerImage, expected.data(),
                             copySize.width * bytesPerTexel, copySize.height);
 
-            EXPECT_TEXTURE_EQ(expected.data(), texture,
-                              {textureSpec.copyOrigin.x, textureSpec.copyOrigin.y,
-                               textureSpec.copyOrigin.z + layer},
-                              {copySize.width, copySize.height, copyDepth}, textureSpec.copyLevel)
+            EXPECT_TEXTURE_EQ(
+                new ColorExpectation<PixelType>(
+                    expected.data(), copySize.width * copySize.height * copyDepth, tolerance),
+                texture,
+                {textureSpec.copyOrigin.x, textureSpec.copyOrigin.y,
+                 textureSpec.copyOrigin.z + layer},
+                {copySize.width, copySize.height, copyDepth}, textureSpec.copyLevel)
                 << "Buffer to Texture copy failed copying " << bufferSpec.size
                 << "-byte buffer with offset " << bufferSpec.offset << " and bytes per row "
                 << bufferSpec.bytesPerRow << " to [(" << textureSpec.copyOrigin.x << ", "
@@ -2183,6 +2366,8 @@
         BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
         bufferSpec.size += i;
         bufferSpec.offset += i;
+        bufferSpec.size = Align(bufferSpec.size, bytesPerTexel);
+        bufferSpec.offset = Align(bufferSpec.offset, bytesPerTexel);
         DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
     }
 }
@@ -2414,16 +2599,17 @@
 
     TextureSpec textureSpec;
     textureSpec.textureSize = {kWidth, kHeight, kDepth};
+    const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
     BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
 
     // The tests below are designed to test TextureCopySplitter for 3D textures on D3D12.
     // Base: no split for a row + no empty first row
-    bufferSpec.offset = 60;
+    bufferSpec.offset = Align(60u, bytesPerTexel);
     bufferSpec.size += bufferSpec.offset;
     DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
 
     // This test will cover: no split for a row + empty first row
-    bufferSpec.offset = 260;
+    bufferSpec.offset = Align(260u, bytesPerTexel);
     bufferSpec.size += bufferSpec.offset;
     DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
 }
@@ -2435,11 +2621,12 @@
 
     TextureSpec textureSpec;
     textureSpec.textureSize = {kWidth, kHeight, kDepth};
+    const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
     BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
 
     // The test below is designed to test TextureCopySplitter for 3D textures on D3D12.
     // This test will cover: split for a row + no empty first row for both split regions
-    bufferSpec.offset = 260;
+    bufferSpec.offset = Align(260u, bytesPerTexel);
     bufferSpec.size += bufferSpec.offset;
     DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
 }
@@ -2475,16 +2662,17 @@
 
     TextureSpec textureSpec;
     textureSpec.textureSize = {kWidth, kHeight, kDepth};
+    const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
     BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
 
     // The tests below are designed to test TextureCopySplitter for 3D textures on D3D12.
     // Base: no split for a row, no empty row, and copy height is 1
-    bufferSpec.offset = 60;
+    bufferSpec.offset = Align(60u, bytesPerTexel);
     bufferSpec.size += bufferSpec.offset;
     DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
 
     // This test will cover: no split for a row + empty first row, and copy height is 1
-    bufferSpec.offset = 260;
+    bufferSpec.offset = Align(260u, bytesPerTexel);
     bufferSpec.size += bufferSpec.offset;
     DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
 }
@@ -2550,13 +2738,42 @@
     }
 }
 
-DAWN_INSTANTIATE_TEST(CopyTests_B2T,
-                      D3D11Backend(),
-                      D3D12Backend(),
-                      MetalBackend(),
-                      OpenGLBackend(),
-                      OpenGLESBackend(),
-                      VulkanBackend());
+// Test that copying a texture 1D works.
+TEST_P(CopyTests_B2T, Texture1DFull) {
+    constexpr uint32_t kWidth = 256;
+    constexpr uint32_t kHeight = 1;
+    constexpr uint32_t kDepth = 1;
+
+    TextureSpec textureSpec;
+    textureSpec.textureSize = {kWidth, kHeight, kDepth};
+
+    DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kDepth), {kWidth, kHeight, kDepth},
+           wgpu::TextureDimension::e1D);
+}
+
+DAWN_INSTANTIATE_TEST_P(CopyTests_B2T,
+                        {D3D11Backend(), D3D11Backend({"d3d11_disable_cpu_buffers"}),
+                         D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
+                         VulkanBackend()},
+                        {
+                            wgpu::TextureFormat::R8Unorm,
+                            wgpu::TextureFormat::RG8Unorm,
+                            wgpu::TextureFormat::RGBA8Unorm,
+
+                            wgpu::TextureFormat::R16Float,
+
+                            wgpu::TextureFormat::RG16Float,
+
+                            wgpu::TextureFormat::R32Float,
+
+                            wgpu::TextureFormat::RG32Float,
+
+                            wgpu::TextureFormat::RGBA16Float,
+
+                            wgpu::TextureFormat::RGBA32Float,
+
+                            wgpu::TextureFormat::BGRA8Unorm,
+                        });
 
 TEST_P(CopyTests_T2T, Texture) {
     constexpr uint32_t kWidth = 256;
diff --git a/src/dawn/tests/end2end/TextureZeroInitTests.cpp b/src/dawn/tests/end2end/TextureZeroInitTests.cpp
index 015016a..f865bf4 100644
--- a/src/dawn/tests/end2end/TextureZeroInitTests.cpp
+++ b/src/dawn/tests/end2end/TextureZeroInitTests.cpp
@@ -351,6 +351,11 @@
 // Test for a copy only to a subset of the subresource, lazy init is necessary to clear the other
 // half.
 TEST_P(TextureZeroInitTest, CopyBufferToTextureHalf) {
+    // TODO(348653642): D3D11 emulates B2T with a render pass, and render pass' lazy clear
+    // is not currently counted properly. So GetLazyClearCountForTesting() would not return the
+    // expected value.
+    DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("use_blit_for_b2t"));
+
     wgpu::TextureDescriptor descriptor =
         CreateTextureDescriptor(4, 1,
                                 wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding |
diff --git a/src/dawn/utils/TestUtils.cpp b/src/dawn/utils/TestUtils.cpp
index 62c59c4..b0cb4bb 100644
--- a/src/dawn/utils/TestUtils.cpp
+++ b/src/dawn/utils/TestUtils.cpp
@@ -61,11 +61,13 @@
     return Align(bytesPerBlock * (width / blockWidth), textureBytesPerRowAlignment);
 }
 
-TextureDataCopyLayout GetTextureDataCopyLayoutForTextureAtLevel(wgpu::TextureFormat format,
-                                                                wgpu::Extent3D textureSizeAtLevel0,
-                                                                uint32_t mipmapLevel,
-                                                                wgpu::TextureDimension dimension,
-                                                                uint32_t rowsPerImage) {
+TextureDataCopyLayout GetTextureDataCopyLayoutForTextureAtLevel(
+    wgpu::TextureFormat format,
+    wgpu::Extent3D textureSizeAtLevel0,
+    uint32_t mipmapLevel,
+    wgpu::TextureDimension dimension,
+    uint32_t rowsPerImage,
+    uint32_t textureBytesPerRowAlignment) {
     // Compressed texture formats not supported in this function yet.
     DAWN_ASSERT(dawn::utils::GetTextureFormatBlockWidth(format) == 1);
 
@@ -80,7 +82,8 @@
             std::max(textureSizeAtLevel0.depthOrArrayLayers >> mipmapLevel, 1u);
     }
 
-    layout.bytesPerRow = GetMinimumBytesPerRow(format, layout.mipSize.width);
+    layout.bytesPerRow =
+        GetMinimumBytesPerRow(format, layout.mipSize.width, textureBytesPerRowAlignment);
 
     if (rowsPerImage == wgpu::kCopyStrideUndefined) {
         rowsPerImage = layout.mipSize.height;
diff --git a/src/dawn/utils/TestUtils.h b/src/dawn/utils/TestUtils.h
index 0e4e8e9..c943cda 100644
--- a/src/dawn/utils/TestUtils.h
+++ b/src/dawn/utils/TestUtils.h
@@ -76,7 +76,8 @@
     wgpu::Extent3D textureSizeAtLevel0,
     uint32_t mipmapLevel,
     wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D,
-    uint32_t rowsPerImage = wgpu::kCopyStrideUndefined);
+    uint32_t rowsPerImage = wgpu::kCopyStrideUndefined,
+    uint32_t textureBytesPerRowAlignment = kTextureBytesPerRowAlignment);
 
 uint64_t RequiredBytesInCopy(uint64_t bytesPerRow,
                              uint64_t rowsPerImage,