D3D11: split Buffer class into GPUOnlyBuffer and StagingBuffer

Bug: 345471009
Change-Id: I3a3d6754f2284782e1d9ef9c9b67558c407ac845
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/192900
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Quyen Le <lehoangquyen@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/native/ObjectBase.h b/src/dawn/native/ObjectBase.h
index 62edb9f..67c9465 100644
--- a/src/dawn/native/ObjectBase.h
+++ b/src/dawn/native/ObjectBase.h
@@ -166,11 +166,11 @@
     // called once through the exposed Destroy function.
     virtual void DestroyImpl() = 0;
 
+    virtual void SetLabelImpl();
+
   private:
     friend class ApiObjectList;
 
-    virtual void SetLabelImpl();
-
     std::string mLabel;
 };
 
diff --git a/src/dawn/native/d3d11/BindGroupTrackerD3D11.cpp b/src/dawn/native/d3d11/BindGroupTrackerD3D11.cpp
index 62bfa00..8629f34 100644
--- a/src/dawn/native/d3d11/BindGroupTrackerD3D11.cpp
+++ b/src/dawn/native/d3d11/BindGroupTrackerD3D11.cpp
@@ -190,10 +190,10 @@
                                     bindingInfo.visibility,
                                     wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute));
                                 ComPtr<ID3D11UnorderedAccessView> d3d11UAV;
-                                DAWN_TRY_ASSIGN(d3d11UAV, ToBackend(binding.buffer)
+                                DAWN_TRY_ASSIGN(d3d11UAV, ToGPUOnlyBuffer(binding.buffer)
                                                               ->CreateD3D11UnorderedAccessView1(
                                                                   offset, binding.size));
-                                ToBackend(binding.buffer)->MarkMutated();
+                                ToGPUOnlyBuffer(binding.buffer)->MarkMutated();
                                 uavsInBindGroup.insert(uavsInBindGroup.begin(),
                                                        std::move(d3d11UAV));
                                 break;
@@ -309,9 +309,10 @@
 
                 switch (layout.type) {
                     case wgpu::BufferBindingType::Uniform: {
-                        ToBackend(binding.buffer)->EnsureConstantBufferIsUpdated(mCommandContext);
+                        ToGPUOnlyBuffer(binding.buffer)
+                            ->EnsureConstantBufferIsUpdated(mCommandContext);
                         ID3D11Buffer* d3d11Buffer =
-                            ToBackend(binding.buffer)->GetD3D11ConstantBuffer();
+                            ToGPUOnlyBuffer(binding.buffer)->GetD3D11ConstantBuffer();
                         // https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1
                         // Offset and size are measured in shader constants, which are 16 bytes
                         // (4*32-bit components). And the offsets and counts must be multiples
@@ -345,10 +346,10 @@
                                      wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute));
                         if (bindingVisibility & wgpu::ShaderStage::Compute) {
                             ComPtr<ID3D11UnorderedAccessView> d3d11UAV;
-                            DAWN_TRY_ASSIGN(d3d11UAV, ToBackend(binding.buffer)
+                            DAWN_TRY_ASSIGN(d3d11UAV, ToGPUOnlyBuffer(binding.buffer)
                                                           ->CreateD3D11UnorderedAccessView1(
                                                               offset, binding.size));
-                            ToBackend(binding.buffer)->MarkMutated();
+                            ToGPUOnlyBuffer(binding.buffer)->MarkMutated();
                             deviceContext->CSSetUnorderedAccessViews(
                                 bindingSlot, 1, d3d11UAV.GetAddressOf(), nullptr);
                         }
@@ -357,7 +358,7 @@
                     case wgpu::BufferBindingType::ReadOnlyStorage: {
                         ComPtr<ID3D11ShaderResourceView> d3d11SRV;
                         DAWN_TRY_ASSIGN(d3d11SRV,
-                                        ToBackend(binding.buffer)
+                                        ToGPUOnlyBuffer(binding.buffer)
                                             ->CreateD3D11ShaderResourceView(offset, binding.size));
                         if (bindingVisibility & wgpu::ShaderStage::Vertex) {
                             deviceContext->VSSetShaderResources(bindingSlot, 1,
diff --git a/src/dawn/native/d3d11/BufferD3D11.cpp b/src/dawn/native/d3d11/BufferD3D11.cpp
index 2bc1a86..70812df 100644
--- a/src/dawn/native/d3d11/BufferD3D11.cpp
+++ b/src/dawn/native/d3d11/BufferD3D11.cpp
@@ -55,6 +55,8 @@
 constexpr wgpu::BufferUsage kD3D11AllowedUniformBufferUsages =
     wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
 
+constexpr wgpu::BufferUsage kCopyUsages = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
+
 // Resource usage    Default    Dynamic   Immutable   Staging
 // ------------------------------------------------------------
 //  GPU-read         Yes        Yes       Yes         Yes[1]
@@ -74,12 +76,9 @@
     return usage == (wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite);
 }
 
-D3D11_USAGE D3D11BufferUsage(wgpu::BufferUsage usage) {
-    if (IsMappable(usage)) {
-        return D3D11_USAGE_STAGING;
-    } else {
-        return D3D11_USAGE_DEFAULT;
-    }
+bool IsStaging(wgpu::BufferUsage usage) {
+    // Must have at least MapWrite or MapRead bit
+    return IsMappable(usage) && IsSubset(usage, kMappableBufferUsages | kCopyUsages);
 }
 
 UINT D3D11BufferBindFlags(wgpu::BufferUsage usage) {
@@ -101,8 +100,6 @@
         bindFlags |= D3D11_BIND_SHADER_RESOURCE;
     }
 
-    constexpr wgpu::BufferUsage kCopyUsages =
-        wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
     // If the buffer only has CopySrc and CopyDst usages are used as staging buffers for copy.
     // Because D3D11 doesn't allow copying between buffer and texture, we will use compute shader
     // to copy data between buffer and texture. So the buffer needs to be bound as unordered access
@@ -114,21 +111,6 @@
     return bindFlags;
 }
 
-UINT D3D11CpuAccessFlags(wgpu::BufferUsage usage) {
-    UINT cpuAccessFlags = 0;
-    if (IsMappable(usage)) {
-        // D3D11 doesn't allow copying between buffer and texture.
-        //  - For buffer to texture copy, we need to use a staging(mappable) texture, and memcpy the
-        //    data from the staging buffer to the staging texture first. So D3D11_CPU_ACCESS_READ is
-        //    needed for MapWrite usage.
-        //  - For texture to buffer copy, we may need copy texture to a staging (mappable)
-        //    texture, and then memcpy the data from the staging texture to the staging buffer. So
-        //    D3D11_CPU_ACCESS_WRITE is needed to MapRead usage.
-        cpuAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
-    }
-    return cpuAccessFlags;
-}
-
 UINT D3D11BufferMiscFlags(wgpu::BufferUsage usage) {
     UINT miscFlags = 0;
     if (usage & (wgpu::BufferUsage::Storage | kInternalStorageBuffer)) {
@@ -161,22 +143,189 @@
 // then written into the dest GPU buffer via ID3D11DeviceContext::UpdateSubresource.
 class UploadBuffer final : public Buffer {
     using Buffer::Buffer;
-    ~UploadBuffer() override;
+    ~UploadBuffer() override = default;
 
-    MaybeError InitializeInternal() override;
-    MaybeError MapInternal(const ScopedCommandRecordingContext* commandContext) override;
-    void UnmapInternal(const ScopedCommandRecordingContext* commandContext) override;
+    MaybeError InitializeInternal() override {
+        mUploadData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(GetAllocatedSize()));
+        if (mUploadData == nullptr) {
+            return DAWN_OUT_OF_MEMORY_ERROR("Failed to allocate memory for buffer uploading.");
+        }
+        return {};
+    }
+
+    MaybeError MapInternal(const ScopedCommandRecordingContext* commandContext) override {
+        mMappedData = mUploadData.get();
+        return {};
+    }
+
+    void UnmapInternal(const ScopedCommandRecordingContext* commandContext) override {
+        mMappedData = nullptr;
+    }
 
     MaybeError ClearInternal(const ScopedCommandRecordingContext* commandContext,
                              uint8_t clearValue,
-                             uint64_t offset = 0,
-                             uint64_t size = 0) override;
+                             uint64_t offset,
+                             uint64_t size) override {
+        memset(mUploadData.get() + offset, clearValue, size);
+        return {};
+    }
 
-    uint8_t* GetUploadData() override;
+    MaybeError CopyToInternal(const ScopedCommandRecordingContext* commandContext,
+                              uint64_t sourceOffset,
+                              size_t size,
+                              Buffer* destination,
+                              uint64_t destinationOffset) override {
+        return destination->WriteInternal(commandContext, destinationOffset,
+                                          mUploadData.get() + sourceOffset, size);
+    }
+
+    MaybeError CopyFromD3DInternal(const ScopedCommandRecordingContext* commandContext,
+                                   ID3D11Buffer* srcD3D11Buffer,
+                                   uint64_t sourceOffset,
+                                   size_t size,
+                                   uint64_t destinationOffset) override {
+        // Upload buffers shouldn't be copied to.
+        DAWN_UNREACHABLE();
+        return {};
+    }
+
+    MaybeError WriteInternal(const ScopedCommandRecordingContext* commandContext,
+                             uint64_t offset,
+                             const void* data,
+                             size_t size) override {
+        const auto* src = static_cast<const uint8_t*>(data);
+        std::copy(src, src + size, mUploadData.get() + offset);
+        return {};
+    }
 
     std::unique_ptr<uint8_t[]> mUploadData;
 };
 
+// Buffer that supports mapping and copying.
+class StagingBuffer final : public Buffer {
+    using Buffer::Buffer;
+
+    void DestroyImpl() override {
+        // TODO(crbug.com/dawn/831): DestroyImpl is called from two places.
+        // - It may be called if the buffer is explicitly destroyed with APIDestroy.
+        //   This case is NOT thread-safe and needs proper synchronization with other
+        //   simultaneous uses of the buffer.
+        // - It may be called when the last ref to the buffer is dropped and the buffer
+        //   is implicitly destroyed. This case is thread-safe because there are no
+        //   other threads using the buffer since there are no other live refs.
+        Buffer::DestroyImpl();
+
+        mD3d11Buffer = nullptr;
+    }
+
+    void SetLabelImpl() override {
+        SetDebugName(ToBackend(GetDevice()), mD3d11Buffer.Get(), "Dawn_StagingBuffer", GetLabel());
+    }
+
+    MaybeError InitializeInternal() override {
+        DAWN_ASSERT(IsStaging(GetUsage()));
+
+        D3D11_BUFFER_DESC bufferDescriptor;
+        bufferDescriptor.ByteWidth = mAllocatedSize;
+        bufferDescriptor.Usage = D3D11_USAGE_STAGING;
+        bufferDescriptor.BindFlags = 0;
+        // D3D11 doesn't allow copying between buffer and texture.
+        //  - For buffer to texture copy, we need to use a staging(mappable) texture, and memcpy the
+        //    data from the staging buffer to the staging texture first. So D3D11_CPU_ACCESS_READ is
+        //    needed for MapWrite usage.
+        //  - For texture to buffer copy, we may need copy texture to a staging (mappable)
+        //    texture, and then memcpy the data from the staging texture to the staging buffer. So
+        //    D3D11_CPU_ACCESS_WRITE is needed to MapRead usage.
+        bufferDescriptor.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
+        bufferDescriptor.MiscFlags = 0;
+        bufferDescriptor.StructureByteStride = 0;
+
+        DAWN_TRY(
+            CheckOutOfMemoryHRESULT(ToBackend(GetDevice())
+                                        ->GetD3D11Device()
+                                        ->CreateBuffer(&bufferDescriptor, nullptr, &mD3d11Buffer),
+                                    "ID3D11Device::CreateBuffer"));
+
+        return {};
+    }
+
+    MaybeError MapInternal(const ScopedCommandRecordingContext* commandContext) override {
+        DAWN_ASSERT(IsMappable(GetUsage()));
+        DAWN_ASSERT(!mMappedData);
+
+        // Always map buffer with D3D11_MAP_READ_WRITE even for mapping wgpu::MapMode:Read, because
+        // we need write permission to initialize the buffer.
+        // TODO(dawn:1705): investigate the performance impact of mapping with D3D11_MAP_READ_WRITE.
+        D3D11_MAPPED_SUBRESOURCE mappedResource;
+        DAWN_TRY(CheckHRESULT(commandContext->Map(mD3d11Buffer.Get(),
+                                                  /*Subresource=*/0, D3D11_MAP_READ_WRITE,
+                                                  /*MapFlags=*/0, &mappedResource),
+                              "ID3D11DeviceContext::Map"));
+        mMappedData = static_cast<uint8_t*>(mappedResource.pData);
+
+        return {};
+    }
+
+    void UnmapInternal(const ScopedCommandRecordingContext* commandContext) override {
+        DAWN_ASSERT(mMappedData);
+        commandContext->Unmap(mD3d11Buffer.Get(),
+                              /*Subresource=*/0);
+        mMappedData = nullptr;
+    }
+
+    MaybeError CopyToInternal(const ScopedCommandRecordingContext* commandContext,
+                              uint64_t sourceOffset,
+                              size_t size,
+                              Buffer* destination,
+                              uint64_t destinationOffset) override {
+        return destination->CopyFromD3DInternal(commandContext, mD3d11Buffer.Get(), sourceOffset,
+                                                size, destinationOffset);
+    }
+
+    MaybeError CopyFromD3DInternal(const ScopedCommandRecordingContext* commandContext,
+                                   ID3D11Buffer* d3d11SourceBuffer,
+                                   uint64_t sourceOffset,
+                                   size_t size,
+                                   uint64_t destinationOffset) override {
+        D3D11_BOX srcBox;
+        srcBox.left = static_cast<UINT>(sourceOffset);
+        srcBox.top = 0;
+        srcBox.front = 0;
+        srcBox.right = static_cast<UINT>(sourceOffset + size);
+        srcBox.bottom = 1;
+        srcBox.back = 1;
+
+        DAWN_ASSERT(d3d11SourceBuffer);
+
+        commandContext->CopySubresourceRegion(mD3d11Buffer.Get(), /*DstSubresource=*/0,
+                                              /*DstX=*/destinationOffset,
+                                              /*DstY=*/0,
+                                              /*DstZ=*/0, d3d11SourceBuffer, /*SrcSubresource=*/0,
+                                              &srcBox);
+
+        return {};
+    }
+
+    MaybeError WriteInternal(const ScopedCommandRecordingContext* commandContext,
+                             uint64_t offset,
+                             const void* data,
+                             size_t size) override {
+        if (size == 0) {
+            return {};
+        }
+
+        ScopedMap scopedMap;
+        DAWN_TRY_ASSIGN(scopedMap, ScopedMap::Create(commandContext, this));
+
+        DAWN_ASSERT(scopedMap.GetMappedData());
+        memcpy(scopedMap.GetMappedData() + offset, data, size);
+
+        return {};
+    }
+
+    ComPtr<ID3D11Buffer> mD3d11Buffer;
+};
+
 // static
 ResultOrError<Ref<Buffer>> Buffer::Create(Device* device,
                                           const UnpackedPtr<BufferDescriptor>& descriptor,
@@ -189,8 +338,10 @@
     Ref<Buffer> buffer;
     if (useUploadBuffer) {
         buffer = AcquireRef(new UploadBuffer(device, descriptor));
+    } else if (IsStaging(descriptor->usage)) {
+        buffer = AcquireRef(new StagingBuffer(device, descriptor));
     } else {
-        buffer = AcquireRef(new Buffer(device, descriptor));
+        buffer = AcquireRef(new GPUOnlyBuffer(device, descriptor));
     }
     DAWN_TRY(buffer->Initialize(descriptor->mappedAtCreation, commandContext));
     return buffer;
@@ -222,12 +373,12 @@
     if (!mappedAtCreation) {
         if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
             if (commandContext) {
-                DAWN_TRY(ClearInternal(commandContext, 1u));
+                DAWN_TRY(ClearWholeBuffer(commandContext, 1u));
             } else {
                 auto tmpCommandContext =
                     ToBackend(GetDevice()->GetQueue())
                         ->GetScopedPendingCommandContext(QueueBase::SubmitMode::Normal);
-                DAWN_TRY(ClearInternal(&tmpCommandContext, 1u));
+                DAWN_TRY(ClearWholeBuffer(&tmpCommandContext, 1u));
             }
         }
 
@@ -253,81 +404,12 @@
     return {};
 }
 
-MaybeError Buffer::InitializeInternal() {
-    bool needsConstantBuffer = GetUsage() & wgpu::BufferUsage::Uniform;
-    bool onlyNeedsConstantBuffer =
-        needsConstantBuffer && IsSubset(GetUsage(), kD3D11AllowedUniformBufferUsages);
-
-    if (!onlyNeedsConstantBuffer) {
-        // Create mD3d11NonConstantBuffer
-        wgpu::BufferUsage nonUniformUsage = GetUsage() & ~wgpu::BufferUsage::Uniform;
-        D3D11_BUFFER_DESC bufferDescriptor;
-        bufferDescriptor.ByteWidth = mAllocatedSize;
-        bufferDescriptor.Usage = D3D11BufferUsage(nonUniformUsage);
-        bufferDescriptor.BindFlags = D3D11BufferBindFlags(nonUniformUsage);
-        bufferDescriptor.CPUAccessFlags = D3D11CpuAccessFlags(nonUniformUsage);
-        bufferDescriptor.MiscFlags = D3D11BufferMiscFlags(nonUniformUsage);
-        bufferDescriptor.StructureByteStride = 0;
-
-        DAWN_TRY(CheckOutOfMemoryHRESULT(
-            ToBackend(GetDevice())
-                ->GetD3D11Device()
-                ->CreateBuffer(&bufferDescriptor, nullptr, &mD3d11NonConstantBuffer),
-            "ID3D11Device::CreateBuffer"));
-    }
-
-    if (needsConstantBuffer) {
-        // Create mD3d11ConstantBuffer
-        D3D11_BUFFER_DESC bufferDescriptor;
-        bufferDescriptor.ByteWidth = mAllocatedSize;
-        bufferDescriptor.Usage = D3D11_USAGE_DEFAULT;
-        bufferDescriptor.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
-        bufferDescriptor.CPUAccessFlags = 0;
-        bufferDescriptor.MiscFlags = 0;
-        bufferDescriptor.StructureByteStride = 0;
-
-        DAWN_TRY(CheckOutOfMemoryHRESULT(
-            ToBackend(GetDevice())
-                ->GetD3D11Device()
-                ->CreateBuffer(&bufferDescriptor, nullptr, &mD3d11ConstantBuffer),
-            "ID3D11Device::CreateBuffer"));
-    }
-
-    DAWN_ASSERT(mD3d11NonConstantBuffer || mD3d11ConstantBuffer);
-
-    return {};
-}
-
 Buffer::~Buffer() = default;
 
 bool Buffer::IsCPUWritableAtCreation() const {
     return IsMappable(GetUsage());
 }
 
-MaybeError Buffer::MapInternal(const ScopedCommandRecordingContext* commandContext) {
-    DAWN_ASSERT(IsMappable(GetUsage()));
-    DAWN_ASSERT(!mMappedData);
-
-    // Always map buffer with D3D11_MAP_READ_WRITE even for mapping wgpu::MapMode:Read, because we
-    // need write permission to initialize the buffer.
-    // TODO(dawn:1705): investigate the performance impact of mapping with D3D11_MAP_READ_WRITE.
-    D3D11_MAPPED_SUBRESOURCE mappedResource;
-    DAWN_TRY(CheckHRESULT(commandContext->Map(mD3d11NonConstantBuffer.Get(),
-                                              /*Subresource=*/0, D3D11_MAP_READ_WRITE,
-                                              /*MapFlags=*/0, &mappedResource),
-                          "ID3D11DeviceContext::Map"));
-    mMappedData = reinterpret_cast<uint8_t*>(mappedResource.pData);
-
-    return {};
-}
-
-void Buffer::UnmapInternal(const ScopedCommandRecordingContext* commandContext) {
-    DAWN_ASSERT(mMappedData);
-    commandContext->Unmap(mD3d11NonConstantBuffer.Get(),
-                          /*Subresource=*/0);
-    mMappedData = nullptr;
-}
-
 MaybeError Buffer::MapAtCreationImpl() {
     DAWN_ASSERT(IsMappable(GetUsage()));
     auto commandContext = ToBackend(GetDevice()->GetQueue())
@@ -336,7 +418,7 @@
 }
 
 MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) {
-    DAWN_ASSERT(mD3d11NonConstantBuffer || GetUploadData());
+    DAWN_ASSERT(IsMappable(GetUsage()));
 
     mMapReadySerial = mLastUsageSerial;
     const ExecutionSerial completedSerial = GetDevice()->GetQueue()->GetCompletedCommandSerial();
@@ -368,7 +450,7 @@
 }
 
 void Buffer::UnmapImpl() {
-    DAWN_ASSERT(mD3d11NonConstantBuffer || GetUploadData());
+    DAWN_ASSERT(IsMappable(GetUsage()));
     mMapReadySerial = kMaxExecutionSerial;
     if (mMappedData) {
         auto commandContext = ToBackend(GetDevice()->GetQueue())
@@ -395,14 +477,6 @@
     if (mMappedData) {
         UnmapImpl();
     }
-    mD3d11ConstantBuffer = nullptr;
-    mD3d11NonConstantBuffer = nullptr;
-}
-
-void Buffer::SetLabelImpl() {
-    SetDebugName(ToBackend(GetDevice()), mD3d11NonConstantBuffer.Get(), "Dawn_Buffer", GetLabel());
-    SetDebugName(ToBackend(GetDevice()), mD3d11ConstantBuffer.Get(), "Dawn_ConstantBuffer",
-                 GetLabel());
 }
 
 MaybeError Buffer::EnsureDataInitialized(const ScopedCommandRecordingContext* commandContext) {
@@ -450,77 +524,13 @@
 MaybeError Buffer::InitializeToZero(const ScopedCommandRecordingContext* commandContext) {
     DAWN_ASSERT(NeedsInitialization());
 
-    DAWN_TRY(ClearInternal(commandContext, uint8_t(0u)));
+    DAWN_TRY(ClearWholeBuffer(commandContext, uint8_t(0u)));
     SetInitialized(true);
     GetDevice()->IncrementLazyClearCountForTesting();
 
     return {};
 }
 
-void Buffer::MarkMutated() {
-    mConstantBufferIsUpdated = false;
-}
-
-void Buffer::EnsureConstantBufferIsUpdated(const ScopedCommandRecordingContext* commandContext) {
-    if (mConstantBufferIsUpdated) {
-        return;
-    }
-
-    DAWN_ASSERT(mD3d11NonConstantBuffer);
-    DAWN_ASSERT(mD3d11ConstantBuffer);
-    commandContext->CopyResource(mD3d11ConstantBuffer.Get(), mD3d11NonConstantBuffer.Get());
-    mConstantBufferIsUpdated = true;
-}
-
-ResultOrError<ComPtr<ID3D11ShaderResourceView>> Buffer::CreateD3D11ShaderResourceView(
-    uint64_t offset,
-    uint64_t size) const {
-    DAWN_ASSERT(IsAligned(offset, 4u));
-    DAWN_ASSERT(IsAligned(size, 4u));
-    UINT firstElement = static_cast<UINT>(offset / 4);
-    UINT numElements = static_cast<UINT>(size / 4);
-
-    D3D11_SHADER_RESOURCE_VIEW_DESC desc;
-    desc.Format = DXGI_FORMAT_R32_TYPELESS;
-    desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
-    desc.BufferEx.FirstElement = firstElement;
-    desc.BufferEx.NumElements = numElements;
-    desc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW;
-    ComPtr<ID3D11ShaderResourceView> srv;
-    DAWN_TRY(
-        CheckHRESULT(ToBackend(GetDevice())
-                         ->GetD3D11Device()
-                         ->CreateShaderResourceView(mD3d11NonConstantBuffer.Get(), &desc, &srv),
-                     "ShaderResourceView creation"));
-
-    return srv;
-}
-
-ResultOrError<ComPtr<ID3D11UnorderedAccessView1>> Buffer::CreateD3D11UnorderedAccessView1(
-    uint64_t offset,
-    uint64_t size) const {
-    DAWN_ASSERT(IsAligned(offset, 4u));
-    DAWN_ASSERT(IsAligned(size, 4u));
-
-    UINT firstElement = static_cast<UINT>(offset / 4);
-    UINT numElements = static_cast<UINT>(size / 4);
-
-    D3D11_UNORDERED_ACCESS_VIEW_DESC1 desc;
-    desc.Format = DXGI_FORMAT_R32_TYPELESS;
-    desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
-    desc.Buffer.FirstElement = firstElement;
-    desc.Buffer.NumElements = numElements;
-    desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
-
-    ComPtr<ID3D11UnorderedAccessView1> uav;
-    DAWN_TRY(
-        CheckHRESULT(ToBackend(GetDevice())
-                         ->GetD3D11Device5()
-                         ->CreateUnorderedAccessView1(mD3d11NonConstantBuffer.Get(), &desc, &uav),
-                     "UnorderedAccessView creation"));
-    return uav;
-}
-
 MaybeError Buffer::Clear(const ScopedCommandRecordingContext* commandContext,
                          uint8_t clearValue,
                          uint64_t offset,
@@ -541,21 +551,16 @@
     return ClearInternal(commandContext, clearValue, offset, size);
 }
 
+MaybeError Buffer::ClearWholeBuffer(const ScopedCommandRecordingContext* commandContext,
+                                    uint8_t clearValue) {
+    return ClearInternal(commandContext, clearValue, 0, GetAllocatedSize());
+}
+
 MaybeError Buffer::ClearInternal(const ScopedCommandRecordingContext* commandContext,
                                  uint8_t clearValue,
                                  uint64_t offset,
                                  uint64_t size) {
-    if (size <= 0) {
-        DAWN_ASSERT(offset == 0);
-        size = GetAllocatedSize();
-    }
-
-    if (mMappedData) {
-        memset(mMappedData.get() + offset, clearValue, size);
-        // The WebGPU uniform buffer is not mappable.
-        DAWN_ASSERT(!mD3d11ConstantBuffer);
-        return {};
-    }
+    DAWN_ASSERT(size != 0);
 
     // TODO(dawn:1705): use a reusable zero staging buffer to clear the buffer to avoid this CPU to
     // GPU copy.
@@ -580,28 +585,227 @@
     return WriteInternal(commandContext, offset, data, size);
 }
 
-MaybeError Buffer::WriteInternal(const ScopedCommandRecordingContext* commandContext,
-                                 uint64_t offset,
-                                 const void* data,
-                                 size_t size) {
+// static
+MaybeError Buffer::Copy(const ScopedCommandRecordingContext* commandContext,
+                        Buffer* source,
+                        uint64_t sourceOffset,
+                        size_t size,
+                        Buffer* destination,
+                        uint64_t destinationOffset) {
+    DAWN_ASSERT(size != 0);
+
+    DAWN_TRY(source->EnsureDataInitialized(commandContext));
+    DAWN_TRY(
+        destination->EnsureDataInitializedAsDestination(commandContext, destinationOffset, size));
+    return source->CopyToInternal(commandContext, sourceOffset, size, destination,
+                                  destinationOffset);
+}
+
+ResultOrError<Buffer::ScopedMap> Buffer::ScopedMap::Create(
+    const ScopedCommandRecordingContext* commandContext,
+    Buffer* buffer) {
+    if (!IsMappable(buffer->GetUsage())) {
+        return ScopedMap();
+    }
+
+    if (buffer->mMappedData) {
+        return ScopedMap(commandContext, buffer, /*needsUnmap=*/false);
+    }
+
+    DAWN_TRY(buffer->MapInternal(commandContext));
+    return ScopedMap(commandContext, buffer, /*needsUnmap=*/true);
+}
+
+// ScopedMap
+Buffer::ScopedMap::ScopedMap() = default;
+
+Buffer::ScopedMap::ScopedMap(const ScopedCommandRecordingContext* commandContext,
+                             Buffer* buffer,
+                             bool needsUnmap)
+    : mCommandContext(commandContext), mBuffer(buffer), mNeedsUnmap(needsUnmap) {}
+
+Buffer::ScopedMap::~ScopedMap() {
+    Reset();
+}
+
+Buffer::ScopedMap::ScopedMap(Buffer::ScopedMap&& other) {
+    this->operator=(std::move(other));
+}
+
+Buffer::ScopedMap& Buffer::ScopedMap::operator=(Buffer::ScopedMap&& other) {
+    Reset();
+    mCommandContext = other.mCommandContext;
+    mBuffer = other.mBuffer;
+    mNeedsUnmap = other.mNeedsUnmap;
+    other.mBuffer = nullptr;
+    other.mNeedsUnmap = false;
+    return *this;
+}
+
+void Buffer::ScopedMap::Reset() {
+    if (mNeedsUnmap) {
+        mBuffer->UnmapInternal(mCommandContext);
+    }
+    mCommandContext = nullptr;
+    mBuffer = nullptr;
+    mNeedsUnmap = false;
+}
+
+uint8_t* Buffer::ScopedMap::GetMappedData() const {
+    return mBuffer ? mBuffer->mMappedData.get() : nullptr;
+}
+
+// GPUOnlyBuffer
+void GPUOnlyBuffer::DestroyImpl() {
+    // TODO(crbug.com/dawn/831): DestroyImpl is called from two places.
+    // - It may be called if the buffer is explicitly destroyed with APIDestroy.
+    //   This case is NOT thread-safe and needs proper synchronization with other
+    //   simultaneous uses of the buffer.
+    // - It may be called when the last ref to the buffer is dropped and the buffer
+    //   is implicitly destroyed. This case is thread-safe because there are no
+    //   other threads using the buffer since there are no other live refs.
+    Buffer::DestroyImpl();
+
+    mD3d11ConstantBuffer = nullptr;
+    mD3d11NonConstantBuffer = nullptr;
+}
+
+void GPUOnlyBuffer::SetLabelImpl() {
+    SetDebugName(ToBackend(GetDevice()), mD3d11NonConstantBuffer.Get(), "Dawn_Buffer", GetLabel());
+    SetDebugName(ToBackend(GetDevice()), mD3d11ConstantBuffer.Get(), "Dawn_ConstantBuffer",
+                 GetLabel());
+}
+
+MaybeError GPUOnlyBuffer::InitializeInternal() {
+    DAWN_ASSERT(!IsMappable(GetUsage()));
+
+    bool needsConstantBuffer = GetUsage() & wgpu::BufferUsage::Uniform;
+    bool onlyNeedsConstantBuffer =
+        needsConstantBuffer && IsSubset(GetUsage(), kD3D11AllowedUniformBufferUsages);
+
+    if (!onlyNeedsConstantBuffer) {
+        // Create mD3d11NonConstantBuffer
+        wgpu::BufferUsage nonUniformUsage = GetUsage() & ~wgpu::BufferUsage::Uniform;
+        D3D11_BUFFER_DESC bufferDescriptor;
+        bufferDescriptor.ByteWidth = mAllocatedSize;
+        bufferDescriptor.Usage = D3D11_USAGE_DEFAULT;
+        bufferDescriptor.BindFlags = D3D11BufferBindFlags(nonUniformUsage);
+        bufferDescriptor.CPUAccessFlags = 0;
+        bufferDescriptor.MiscFlags = D3D11BufferMiscFlags(nonUniformUsage);
+        bufferDescriptor.StructureByteStride = 0;
+
+        DAWN_TRY(CheckOutOfMemoryHRESULT(
+            ToBackend(GetDevice())
+                ->GetD3D11Device()
+                ->CreateBuffer(&bufferDescriptor, nullptr, &mD3d11NonConstantBuffer),
+            "ID3D11Device::CreateBuffer"));
+    }
+
+    if (needsConstantBuffer) {
+        // Create mD3d11ConstantBuffer
+        D3D11_BUFFER_DESC bufferDescriptor;
+        bufferDescriptor.ByteWidth = mAllocatedSize;
+        bufferDescriptor.Usage = D3D11_USAGE_DEFAULT;
+        bufferDescriptor.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+        bufferDescriptor.CPUAccessFlags = 0;
+        bufferDescriptor.MiscFlags = 0;
+        bufferDescriptor.StructureByteStride = 0;
+
+        DAWN_TRY(CheckOutOfMemoryHRESULT(
+            ToBackend(GetDevice())
+                ->GetD3D11Device()
+                ->CreateBuffer(&bufferDescriptor, nullptr, &mD3d11ConstantBuffer),
+            "ID3D11Device::CreateBuffer"));
+    }
+
+    DAWN_ASSERT(mD3d11NonConstantBuffer || mD3d11ConstantBuffer);
+
+    return {};
+}
+
+MaybeError GPUOnlyBuffer::MapInternal(const ScopedCommandRecordingContext* commandContext) {
+    DAWN_UNREACHABLE();
+
+    return {};
+}
+
+void GPUOnlyBuffer::UnmapInternal(const ScopedCommandRecordingContext* commandContext) {
+    DAWN_UNREACHABLE();
+}
+
+void GPUOnlyBuffer::MarkMutated() {
+    mConstantBufferIsUpdated = false;
+}
+
+void GPUOnlyBuffer::EnsureConstantBufferIsUpdated(
+    const ScopedCommandRecordingContext* commandContext) {
+    if (mConstantBufferIsUpdated) {
+        return;
+    }
+
+    DAWN_ASSERT(mD3d11NonConstantBuffer);
+    DAWN_ASSERT(mD3d11ConstantBuffer);
+    commandContext->CopyResource(mD3d11ConstantBuffer.Get(), mD3d11NonConstantBuffer.Get());
+    mConstantBufferIsUpdated = true;
+}
+
+ResultOrError<ComPtr<ID3D11ShaderResourceView>> GPUOnlyBuffer::CreateD3D11ShaderResourceView(
+    uint64_t offset,
+    uint64_t size) const {
+    DAWN_ASSERT(IsAligned(offset, 4u));
+    DAWN_ASSERT(IsAligned(size, 4u));
+    UINT firstElement = static_cast<UINT>(offset / 4);
+    UINT numElements = static_cast<UINT>(size / 4);
+
+    D3D11_SHADER_RESOURCE_VIEW_DESC desc;
+    desc.Format = DXGI_FORMAT_R32_TYPELESS;
+    desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
+    desc.BufferEx.FirstElement = firstElement;
+    desc.BufferEx.NumElements = numElements;
+    desc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW;
+    ComPtr<ID3D11ShaderResourceView> srv;
+    DAWN_TRY(
+        CheckHRESULT(ToBackend(GetDevice())
+                         ->GetD3D11Device()
+                         ->CreateShaderResourceView(mD3d11NonConstantBuffer.Get(), &desc, &srv),
+                     "ShaderResourceView creation"));
+
+    return srv;
+}
+
+ResultOrError<ComPtr<ID3D11UnorderedAccessView1>> GPUOnlyBuffer::CreateD3D11UnorderedAccessView1(
+    uint64_t offset,
+    uint64_t size) const {
+    DAWN_ASSERT(IsAligned(offset, 4u));
+    DAWN_ASSERT(IsAligned(size, 4u));
+
+    UINT firstElement = static_cast<UINT>(offset / 4);
+    UINT numElements = static_cast<UINT>(size / 4);
+
+    D3D11_UNORDERED_ACCESS_VIEW_DESC1 desc;
+    desc.Format = DXGI_FORMAT_R32_TYPELESS;
+    desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
+    desc.Buffer.FirstElement = firstElement;
+    desc.Buffer.NumElements = numElements;
+    desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
+
+    ComPtr<ID3D11UnorderedAccessView1> uav;
+    DAWN_TRY(
+        CheckHRESULT(ToBackend(GetDevice())
+                         ->GetD3D11Device5()
+                         ->CreateUnorderedAccessView1(mD3d11NonConstantBuffer.Get(), &desc, &uav),
+                     "UnorderedAccessView creation"));
+    return uav;
+}
+
+MaybeError GPUOnlyBuffer::WriteInternal(const ScopedCommandRecordingContext* commandContext,
+                                        uint64_t offset,
+                                        const void* data,
+                                        size_t size) {
     if (size == 0) {
         return {};
     }
 
-    // Map the buffer if it is possible, so WriteInternal() can write the mapped memory directly.
-    ScopedMap scopedMap;
-    DAWN_TRY_ASSIGN(scopedMap, ScopedMap::Create(commandContext, this));
-
-    if (scopedMap.GetMappedData()) {
-        memcpy(scopedMap.GetMappedData() + offset, data, size);
-        // The WebGPU uniform buffer is not mappable.
-        DAWN_ASSERT(!mD3d11ConstantBuffer);
-        return {};
-    }
-
-    // UpdateSubresource can only be used to update non-mappable buffers.
-    DAWN_ASSERT(!IsMappable(GetUsage()));
-
     if (mD3d11NonConstantBuffer) {
         D3D11_BOX box;
         box.left = static_cast<UINT>(offset);
@@ -676,7 +880,8 @@
     DAWN_TRY_ASSIGN(stagingBuffer, ToBackend(GetDevice())->GetStagingBuffer(commandContext, size));
     stagingBuffer->MarkUsedInPendingCommands();
     DAWN_TRY(ToBackend(stagingBuffer)->WriteInternal(commandContext, 0, data, size));
-    DAWN_TRY(Buffer::CopyInternal(commandContext, ToBackend(stagingBuffer.Get()),
+    DAWN_TRY(ToBackend(stagingBuffer.Get())
+                 ->CopyToInternal(commandContext,
                                   /*sourceOffset=*/0,
                                   /*size=*/size, this, offset));
     ToBackend(GetDevice())->ReturnStagingBuffer(std::move(stagingBuffer));
@@ -684,37 +889,24 @@
     return {};
 }
 
-// static
-MaybeError Buffer::Copy(const ScopedCommandRecordingContext* commandContext,
-                        Buffer* source,
-                        uint64_t sourceOffset,
-                        size_t size,
-                        Buffer* destination,
-                        uint64_t destinationOffset) {
-    DAWN_ASSERT(size != 0);
+MaybeError GPUOnlyBuffer::CopyToInternal(const ScopedCommandRecordingContext* commandContext,
+                                         uint64_t sourceOffset,
+                                         size_t size,
+                                         Buffer* destination,
+                                         uint64_t destinationOffset) {
+    ID3D11Buffer* d3d11SourceBuffer =
+        mD3d11NonConstantBuffer ? mD3d11NonConstantBuffer.Get() : mD3d11ConstantBuffer.Get();
+    DAWN_ASSERT(d3d11SourceBuffer);
 
-    DAWN_TRY(source->EnsureDataInitialized(commandContext));
-    DAWN_TRY(
-        destination->EnsureDataInitializedAsDestination(commandContext, destinationOffset, size));
-    return CopyInternal(commandContext, source, sourceOffset, size, destination, destinationOffset);
+    return destination->CopyFromD3DInternal(commandContext, d3d11SourceBuffer, sourceOffset, size,
+                                            destinationOffset);
 }
 
-// static
-MaybeError Buffer::CopyInternal(const ScopedCommandRecordingContext* commandContext,
-                                Buffer* source,
-                                uint64_t sourceOffset,
-                                size_t size,
-                                Buffer* destination,
-                                uint64_t destinationOffset) {
-    // Upload buffers shouldn't be copied to.
-    DAWN_ASSERT(!destination->GetUploadData());
-    // Use UpdateSubresource1() if the source is an upload buffer.
-    if (source->GetUploadData()) {
-        DAWN_TRY(destination->WriteInternal(commandContext, destinationOffset,
-                                            source->GetUploadData() + sourceOffset, size));
-        return {};
-    }
-
+MaybeError GPUOnlyBuffer::CopyFromD3DInternal(const ScopedCommandRecordingContext* commandContext,
+                                              ID3D11Buffer* d3d11SourceBuffer,
+                                              uint64_t sourceOffset,
+                                              size_t size,
+                                              uint64_t destinationOffset) {
     D3D11_BOX srcBox;
     srcBox.left = static_cast<UINT>(sourceOffset);
     srcBox.top = 0;
@@ -722,127 +914,30 @@
     srcBox.right = static_cast<UINT>(sourceOffset + size);
     srcBox.bottom = 1;
     srcBox.back = 1;
-    ID3D11Buffer* d3d11SourceBuffer = source->mD3d11NonConstantBuffer
-                                          ? source->mD3d11NonConstantBuffer.Get()
-                                          : source->mD3d11ConstantBuffer.Get();
-    DAWN_ASSERT(d3d11SourceBuffer);
 
-    if (destination->mD3d11NonConstantBuffer) {
-        commandContext->CopySubresourceRegion(
-            destination->mD3d11NonConstantBuffer.Get(), /*DstSubresource=*/0,
-            /*DstX=*/destinationOffset,
-            /*DstY=*/0,
-            /*DstZ=*/0, d3d11SourceBuffer, /*SrcSubresource=*/0, &srcBox);
+    if (mD3d11NonConstantBuffer) {
+        commandContext->CopySubresourceRegion(mD3d11NonConstantBuffer.Get(), /*DstSubresource=*/0,
+                                              /*DstX=*/destinationOffset,
+                                              /*DstY=*/0,
+                                              /*DstZ=*/0, d3d11SourceBuffer, /*SrcSubresource=*/0,
+                                              &srcBox);
     }
 
     // if mConstantBufferIsUpdated is false, the content of mD3d11ConstantBuffer  will be
     // updated by EnsureConstantBufferIsUpdated() when the constant buffer is about to be used.
-    if (!destination->mConstantBufferIsUpdated) {
+    if (!mConstantBufferIsUpdated) {
         return {};
     }
 
-    if (destination->mD3d11ConstantBuffer) {
-        commandContext->CopySubresourceRegion(
-            destination->mD3d11ConstantBuffer.Get(), /*DstSubresource=*/0,
-            /*DstX=*/destinationOffset,
-            /*DstY=*/0,
-            /*DstZ=*/0, d3d11SourceBuffer, /*SrcSubresource=*/0, &srcBox);
+    if (mD3d11ConstantBuffer) {
+        commandContext->CopySubresourceRegion(mD3d11ConstantBuffer.Get(), /*DstSubresource=*/0,
+                                              /*DstX=*/destinationOffset,
+                                              /*DstY=*/0,
+                                              /*DstZ=*/0, d3d11SourceBuffer, /*SrcSubresource=*/0,
+                                              &srcBox);
     }
 
     return {};
 }
 
-uint8_t* Buffer::GetUploadData() {
-    return nullptr;
-}
-
-ResultOrError<Buffer::ScopedMap> Buffer::ScopedMap::Create(
-    const ScopedCommandRecordingContext* commandContext,
-    Buffer* buffer) {
-    if (!IsMappable(buffer->GetUsage())) {
-        return ScopedMap();
-    }
-
-    if (buffer->mMappedData) {
-        return ScopedMap(commandContext, buffer, /*needsUnmap=*/false);
-    }
-
-    DAWN_TRY(buffer->MapInternal(commandContext));
-    return ScopedMap(commandContext, buffer, /*needsUnmap=*/true);
-}
-
-Buffer::ScopedMap::ScopedMap() = default;
-
-Buffer::ScopedMap::ScopedMap(const ScopedCommandRecordingContext* commandContext,
-                             Buffer* buffer,
-                             bool needsUnmap)
-    : mCommandContext(commandContext), mBuffer(buffer), mNeedsUnmap(needsUnmap) {}
-
-Buffer::ScopedMap::~ScopedMap() {
-    Reset();
-}
-
-Buffer::ScopedMap::ScopedMap(Buffer::ScopedMap&& other) {
-    this->operator=(std::move(other));
-}
-
-Buffer::ScopedMap& Buffer::ScopedMap::operator=(Buffer::ScopedMap&& other) {
-    Reset();
-    mCommandContext = other.mCommandContext;
-    mBuffer = other.mBuffer;
-    mNeedsUnmap = other.mNeedsUnmap;
-    other.mBuffer = nullptr;
-    other.mNeedsUnmap = false;
-    return *this;
-}
-
-void Buffer::ScopedMap::Reset() {
-    if (mNeedsUnmap) {
-        mBuffer->UnmapInternal(mCommandContext);
-    }
-    mCommandContext = nullptr;
-    mBuffer = nullptr;
-    mNeedsUnmap = false;
-}
-
-uint8_t* Buffer::ScopedMap::GetMappedData() const {
-    return mBuffer ? mBuffer->mMappedData.get() : nullptr;
-}
-
-UploadBuffer::~UploadBuffer() = default;
-
-MaybeError UploadBuffer::InitializeInternal() {
-    mUploadData = std::unique_ptr<uint8_t[]>(AllocNoThrow<uint8_t>(GetAllocatedSize()));
-    if (mUploadData == nullptr) {
-        return DAWN_OUT_OF_MEMORY_ERROR("Failed to allocate memory for buffer uploading.");
-    }
-    return {};
-}
-
-uint8_t* UploadBuffer::GetUploadData() {
-    return mUploadData.get();
-}
-
-MaybeError UploadBuffer::MapInternal(const ScopedCommandRecordingContext* commandContext) {
-    mMappedData = mUploadData.get();
-    return {};
-}
-
-void UploadBuffer::UnmapInternal(const ScopedCommandRecordingContext* commandContext) {
-    mMappedData = nullptr;
-}
-
-MaybeError UploadBuffer::ClearInternal(const ScopedCommandRecordingContext* commandContext,
-                                       uint8_t clearValue,
-                                       uint64_t offset,
-                                       uint64_t size) {
-    if (size == 0) {
-        DAWN_ASSERT(offset == 0);
-        size = GetAllocatedSize();
-    }
-
-    memset(mUploadData.get() + offset, clearValue, size);
-    return {};
-}
-
 }  // namespace dawn::native::d3d11
diff --git a/src/dawn/native/d3d11/BufferD3D11.h b/src/dawn/native/d3d11/BufferD3D11.h
index 288de0a..02791ff 100644
--- a/src/dawn/native/d3d11/BufferD3D11.h
+++ b/src/dawn/native/d3d11/BufferD3D11.h
@@ -30,9 +30,11 @@
 
 #include <limits>
 #include <memory>
+#include <utility>
 
 #include "dawn/native/Buffer.h"
 #include "dawn/native/d3d/d3d_platform.h"
+#include "dawn/native/d3d11/Forward.h"
 #include "partition_alloc/pointers/raw_ptr.h"
 
 namespace dawn::native::d3d11 {
@@ -56,22 +58,6 @@
         const ScopedCommandRecordingContext* commandContext,
         const CopyTextureToBufferCmd* copy);
 
-    // Dawn API
-    void SetLabelImpl() override;
-
-    ID3D11Buffer* GetD3D11ConstantBuffer() const { return mD3d11ConstantBuffer.Get(); }
-    ID3D11Buffer* GetD3D11NonConstantBuffer() const { return mD3d11NonConstantBuffer.Get(); }
-    // Mark the mD3d11NonConstantBuffer is mutated by shaders, if mD3d11ConstantBuffer exists,
-    // it will be synced with mD3d11NonConstantBuffer before binding it to the constant buffer slot.
-    void MarkMutated();
-    // Update content of the mD3d11ConstantBuffer from mD3d11NonConstantBuffer if needed.
-    void EnsureConstantBufferIsUpdated(const ScopedCommandRecordingContext* commandContext);
-    ResultOrError<ComPtr<ID3D11ShaderResourceView>> CreateD3D11ShaderResourceView(
-        uint64_t offset,
-        uint64_t size) const;
-    ResultOrError<ComPtr<ID3D11UnorderedAccessView1>> CreateD3D11UnorderedAccessView1(
-        uint64_t offset,
-        uint64_t size) const;
     MaybeError Clear(const ScopedCommandRecordingContext* commandContext,
                      uint8_t clearValue,
                      uint64_t offset,
@@ -92,6 +78,24 @@
     MaybeError FinalizeMap(ScopedCommandRecordingContext* commandContext,
                            ExecutionSerial completedSerial);
 
+    // Write the buffer without checking if the buffer is initialized.
+    virtual MaybeError WriteInternal(const ScopedCommandRecordingContext* commandContext,
+                                     uint64_t bufferOffset,
+                                     const void* data,
+                                     size_t size) = 0;
+    // Copy this buffer to the destination without checking if the buffer is initialized.
+    virtual MaybeError CopyToInternal(const ScopedCommandRecordingContext* commandContext,
+                                      uint64_t sourceOffset,
+                                      size_t size,
+                                      Buffer* destination,
+                                      uint64_t destinationOffset) = 0;
+    // Copy from a D3D buffer to this buffer without checking if the buffer is initialized.
+    virtual MaybeError CopyFromD3DInternal(const ScopedCommandRecordingContext* commandContext,
+                                           ID3D11Buffer* srcD3D11Buffer,
+                                           uint64_t sourceOffset,
+                                           size_t size,
+                                           uint64_t destinationOffset) = 0;
+
     class ScopedMap : public NonCopyable {
       public:
         // Map buffer and return a ScopedMap object. If the buffer is not mappable,
@@ -125,18 +129,20 @@
 
     ~Buffer() override;
 
-    virtual MaybeError InitializeInternal();
+    void DestroyImpl() override;
 
-    virtual MaybeError MapInternal(const ScopedCommandRecordingContext* commandContext);
-    virtual void UnmapInternal(const ScopedCommandRecordingContext* commandContext);
+    virtual MaybeError InitializeInternal() = 0;
+
+    virtual MaybeError MapInternal(const ScopedCommandRecordingContext* commandContext) = 0;
+    virtual void UnmapInternal(const ScopedCommandRecordingContext* commandContext) = 0;
 
     // Clear the buffer without checking if the buffer is initialized.
+    MaybeError ClearWholeBuffer(const ScopedCommandRecordingContext* commandContext,
+                                uint8_t clearValue);
     virtual MaybeError ClearInternal(const ScopedCommandRecordingContext* commandContext,
                                      uint8_t clearValue,
-                                     uint64_t offset = 0,
-                                     uint64_t size = 0);
-
-    virtual uint8_t* GetUploadData();
+                                     uint64_t offset,
+                                     uint64_t size);
 
     raw_ptr<uint8_t, AllowPtrArithmetic> mMappedData = nullptr;
 
@@ -145,32 +151,75 @@
                           const ScopedCommandRecordingContext* commandContext);
     MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) override;
     void UnmapImpl() override;
-    void DestroyImpl() override;
     bool IsCPUWritableAtCreation() const override;
     MaybeError MapAtCreationImpl() override;
     void* GetMappedPointer() override;
 
     MaybeError InitializeToZero(const ScopedCommandRecordingContext* commandContext);
-    // Write the buffer without checking if the buffer is initialized.
-    MaybeError WriteInternal(const ScopedCommandRecordingContext* commandContext,
-                             uint64_t bufferOffset,
-                             const void* data,
-                             size_t size);
-    // Copy the buffer without checking if the buffer is initialized.
-    static MaybeError CopyInternal(const ScopedCommandRecordingContext* commandContext,
-                                   Buffer* source,
-                                   uint64_t sourceOffset,
-                                   size_t size,
-                                   Buffer* destination,
-                                   uint64_t destinationOffset);
+
+    ExecutionSerial mMapReadySerial = kMaxExecutionSerial;
+};
+
+// Buffer that doesn't support mapping.
+class GPUOnlyBuffer final : public Buffer {
+  public:
+    ID3D11Buffer* GetD3D11ConstantBuffer() const { return mD3d11ConstantBuffer.Get(); }
+    ID3D11Buffer* GetD3D11NonConstantBuffer() const { return mD3d11NonConstantBuffer.Get(); }
+
+    // Mark the mD3d11NonConstantBuffer is mutated by shaders, if mD3d11ConstantBuffer exists,
+    // it will be synced with mD3d11NonConstantBuffer before binding it to the constant buffer slot.
+    void MarkMutated();
+    // Update content of the mD3d11ConstantBuffer from mD3d11NonConstantBuffer if needed.
+    void EnsureConstantBufferIsUpdated(const ScopedCommandRecordingContext* commandContext);
+    ResultOrError<ComPtr<ID3D11ShaderResourceView>> CreateD3D11ShaderResourceView(
+        uint64_t offset,
+        uint64_t size) const;
+    ResultOrError<ComPtr<ID3D11UnorderedAccessView1>> CreateD3D11UnorderedAccessView1(
+        uint64_t offset,
+        uint64_t size) const;
+
+  private:
+    using Buffer::Buffer;
+
+    // Dawn API
+    void DestroyImpl() override;
+    void SetLabelImpl() override;
+
     // The buffer object for constant buffer usage.
     ComPtr<ID3D11Buffer> mD3d11ConstantBuffer;
     // The buffer object for non-constant buffer usages(e.g. storage buffer, vertex buffer, etc.)
     ComPtr<ID3D11Buffer> mD3d11NonConstantBuffer;
+
+    MaybeError InitializeInternal() override;
+    MaybeError MapInternal(const ScopedCommandRecordingContext* commandContext) override;
+    void UnmapInternal(const ScopedCommandRecordingContext* commandContext) override;
+    MaybeError CopyToInternal(const ScopedCommandRecordingContext* commandContext,
+                              uint64_t sourceOffset,
+                              size_t size,
+                              Buffer* destination,
+                              uint64_t destinationOffset) override;
+    MaybeError CopyFromD3DInternal(const ScopedCommandRecordingContext* commandContext,
+                                   ID3D11Buffer* srcD3D11Buffer,
+                                   uint64_t sourceOffset,
+                                   size_t size,
+                                   uint64_t destinationOffset) override;
+
+    MaybeError WriteInternal(const ScopedCommandRecordingContext* commandContext,
+                             uint64_t bufferOffset,
+                             const void* data,
+                             size_t size) override;
+
     bool mConstantBufferIsUpdated = true;
-    ExecutionSerial mMapReadySerial = kMaxExecutionSerial;
 };
 
+static inline GPUOnlyBuffer* ToGPUOnlyBuffer(BufferBase* buffer) {
+    return static_cast<GPUOnlyBuffer*>(ToBackend(buffer));
+}
+
+static inline Ref<GPUOnlyBuffer> ToGPUOnlyBuffer(Ref<BufferBase>&& buffer) {
+    return std::move(buffer).Cast<Ref<GPUOnlyBuffer>>();
+}
+
 }  // namespace dawn::native::d3d11
 
 #endif  // SRC_DAWN_NATIVE_D3D11_BUFFERD3D11_H_
diff --git a/src/dawn/native/d3d11/CommandBufferD3D11.cpp b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
index f5710d0..4cee230 100644
--- a/src/dawn/native/d3d11/CommandBufferD3D11.cpp
+++ b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
@@ -495,7 +495,7 @@
 
                 DAWN_TRY(bindGroupTracker.Apply());
 
-                Buffer* indirectBuffer = ToBackend(dispatch->indirectBuffer.Get());
+                auto* indirectBuffer = ToGPUOnlyBuffer(dispatch->indirectBuffer.Get());
 
                 if (lastPipeline->UsesNumWorkgroups()) {
                     // Copy indirect args into the uniform buffer for built-in workgroup variables.
@@ -667,7 +667,7 @@
             case Command::DrawIndirect: {
                 DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>();
 
-                Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get());
+                auto* indirectBuffer = ToGPUOnlyBuffer(draw->indirectBuffer.Get());
                 DAWN_ASSERT(indirectBuffer != nullptr);
 
                 DAWN_TRY(bindGroupTracker.Apply());
@@ -693,7 +693,7 @@
             case Command::DrawIndexedIndirect: {
                 DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>();
 
-                Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get());
+                auto* indirectBuffer = ToGPUOnlyBuffer(draw->indirectBuffer.Get());
                 DAWN_ASSERT(indirectBuffer != nullptr);
 
                 DAWN_TRY(bindGroupTracker.Apply());
@@ -746,15 +746,16 @@
                 DXGI_FORMAT indexBufferFormat = DXGIIndexFormat(cmd->format);
 
                 commandContext->GetD3D11DeviceContext4()->IASetIndexBuffer(
-                    ToBackend(cmd->buffer)->GetD3D11NonConstantBuffer(), indexBufferFormat,
-                    indexBufferBaseOffset);
+                    ToGPUOnlyBuffer(cmd->buffer.Get())->GetD3D11NonConstantBuffer(),
+                    indexBufferFormat, indexBufferBaseOffset);
 
                 break;
             }
 
             case Command::SetVertexBuffer: {
                 SetVertexBufferCmd* cmd = iter->NextCommand<SetVertexBufferCmd>();
-                ID3D11Buffer* buffer = ToBackend(cmd->buffer)->GetD3D11NonConstantBuffer();
+                ID3D11Buffer* buffer =
+                    ToGPUOnlyBuffer(cmd->buffer.Get())->GetD3D11NonConstantBuffer();
                 vertexBufferTracker.OnSetVertexBuffer(cmd->slot, buffer, cmd->offset);
                 break;
             }
diff --git a/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp b/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp
index ecdf290..2c7e400 100644
--- a/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp
+++ b/src/dawn/native/d3d11/CommandRecordingContextD3D11.cpp
@@ -261,7 +261,7 @@
 }
 
 void CommandRecordingContext::SetInternalUniformBuffer(Ref<BufferBase> uniformBuffer) {
-    mUniformBuffer = ToBackend(std::move(uniformBuffer));
+    mUniformBuffer = ToGPUOnlyBuffer(std::move(uniformBuffer));
 
     // Always bind the uniform buffer to the reserved slot for all pipelines.
     // This buffer will be updated with the correct values before each draw or dispatch call.
diff --git a/src/dawn/native/d3d11/CommandRecordingContextD3D11.h b/src/dawn/native/d3d11/CommandRecordingContextD3D11.h
index 5a932a8..40370a2 100644
--- a/src/dawn/native/d3d11/CommandRecordingContextD3D11.h
+++ b/src/dawn/native/d3d11/CommandRecordingContextD3D11.h
@@ -40,6 +40,7 @@
 
 class CommandAllocatorManager;
 class Buffer;
+class GPUOnlyBuffer;
 class Device;
 
 class CommandRecordingContext;
@@ -106,7 +107,7 @@
     // The maximum number of builtin elements is 4 (vec4). It must be multiple of 4.
     static constexpr size_t kMaxNumBuiltinElements = 4;
     // The uniform buffer for built-in variables.
-    Ref<Buffer> mUniformBuffer;
+    Ref<GPUOnlyBuffer> mUniformBuffer;
     std::array<uint32_t, kMaxNumBuiltinElements> mUniformBufferData;
     bool mUniformBufferDirty = true;
 
diff --git a/src/dawn/tests/white_box/D3D11BufferTests.cpp b/src/dawn/tests/white_box/D3D11BufferTests.cpp
index 1ac1458..eb21c0a 100644
--- a/src/dawn/tests/white_box/D3D11BufferTests.cpp
+++ b/src/dawn/tests/white_box/D3D11BufferTests.cpp
@@ -109,8 +109,8 @@
     {
         wgpu::BufferUsage usage = wgpu::BufferUsage::Uniform;
         wgpu::Buffer buffer = CreateBuffer(4, usage);
-        native::d3d11::Buffer* d3d11Buffer =
-            native::d3d11::ToBackend(native::FromAPI(buffer.Get()));
+        native::d3d11::GPUOnlyBuffer* d3d11Buffer =
+            native::d3d11::ToGPUOnlyBuffer(native::FromAPI(buffer.Get()));
 
         EXPECT_EQ(d3d11Buffer->GetD3D11NonConstantBuffer(), nullptr);
         EXPECT_NE(d3d11Buffer->GetD3D11ConstantBuffer(), nullptr);
@@ -119,8 +119,8 @@
         wgpu::BufferUsage usage =
             wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
         wgpu::Buffer buffer = CreateBuffer(4, usage);
-        native::d3d11::Buffer* d3d11Buffer =
-            native::d3d11::ToBackend(native::FromAPI(buffer.Get()));
+        native::d3d11::GPUOnlyBuffer* d3d11Buffer =
+            native::d3d11::ToGPUOnlyBuffer(native::FromAPI(buffer.Get()));
 
         EXPECT_EQ(d3d11Buffer->GetD3D11NonConstantBuffer(), nullptr);
         EXPECT_NE(d3d11Buffer->GetD3D11ConstantBuffer(), nullptr);
@@ -128,8 +128,8 @@
     {
         wgpu::BufferUsage usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Vertex;
         wgpu::Buffer buffer = CreateBuffer(4, usage);
-        native::d3d11::Buffer* d3d11Buffer =
-            native::d3d11::ToBackend(native::FromAPI(buffer.Get()));
+        native::d3d11::GPUOnlyBuffer* d3d11Buffer =
+            native::d3d11::ToGPUOnlyBuffer(native::FromAPI(buffer.Get()));
 
         EXPECT_NE(d3d11Buffer->GetD3D11NonConstantBuffer(), nullptr);
         EXPECT_NE(d3d11Buffer->GetD3D11ConstantBuffer(), nullptr);
@@ -137,8 +137,8 @@
     {
         wgpu::BufferUsage usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Index;
         wgpu::Buffer buffer = CreateBuffer(4, usage);
-        native::d3d11::Buffer* d3d11Buffer =
-            native::d3d11::ToBackend(native::FromAPI(buffer.Get()));
+        native::d3d11::GPUOnlyBuffer* d3d11Buffer =
+            native::d3d11::ToGPUOnlyBuffer(native::FromAPI(buffer.Get()));
 
         EXPECT_NE(d3d11Buffer->GetD3D11NonConstantBuffer(), nullptr);
         EXPECT_NE(d3d11Buffer->GetD3D11ConstantBuffer(), nullptr);
@@ -146,8 +146,8 @@
     {
         wgpu::BufferUsage usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Indirect;
         wgpu::Buffer buffer = CreateBuffer(4, usage);
-        native::d3d11::Buffer* d3d11Buffer =
-            native::d3d11::ToBackend(native::FromAPI(buffer.Get()));
+        native::d3d11::GPUOnlyBuffer* d3d11Buffer =
+            native::d3d11::ToGPUOnlyBuffer(native::FromAPI(buffer.Get()));
 
         EXPECT_NE(d3d11Buffer->GetD3D11NonConstantBuffer(), nullptr);
         EXPECT_NE(d3d11Buffer->GetD3D11ConstantBuffer(), nullptr);
@@ -155,8 +155,8 @@
     {
         wgpu::BufferUsage usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage;
         wgpu::Buffer buffer = CreateBuffer(4, usage);
-        native::d3d11::Buffer* d3d11Buffer =
-            native::d3d11::ToBackend(native::FromAPI(buffer.Get()));
+        native::d3d11::GPUOnlyBuffer* d3d11Buffer =
+            native::d3d11::ToGPUOnlyBuffer(native::FromAPI(buffer.Get()));
 
         EXPECT_NE(d3d11Buffer->GetD3D11NonConstantBuffer(), nullptr);
         EXPECT_NE(d3d11Buffer->GetD3D11ConstantBuffer(), nullptr);
@@ -164,8 +164,8 @@
     {
         wgpu::BufferUsage usage = wgpu::BufferUsage::Storage;
         wgpu::Buffer buffer = CreateBuffer(4, usage);
-        native::d3d11::Buffer* d3d11Buffer =
-            native::d3d11::ToBackend(native::FromAPI(buffer.Get()));
+        native::d3d11::GPUOnlyBuffer* d3d11Buffer =
+            native::d3d11::ToGPUOnlyBuffer(native::FromAPI(buffer.Get()));
 
         EXPECT_NE(d3d11Buffer->GetD3D11NonConstantBuffer(), nullptr);
         EXPECT_EQ(d3d11Buffer->GetD3D11ConstantBuffer(), nullptr);
@@ -179,8 +179,8 @@
         wgpu::BufferUsage usage =
             wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
         wgpu::Buffer buffer = CreateBuffer(data.size(), usage);
-        native::d3d11::Buffer* d3d11Buffer =
-            native::d3d11::ToBackend(native::FromAPI(buffer.Get()));
+        native::d3d11::GPUOnlyBuffer* d3d11Buffer =
+            native::d3d11::ToGPUOnlyBuffer(native::FromAPI(buffer.Get()));
 
         EXPECT_EQ(d3d11Buffer->GetD3D11NonConstantBuffer(), nullptr);
         EXPECT_NE(d3d11Buffer->GetD3D11ConstantBuffer(), nullptr);
@@ -195,8 +195,8 @@
         wgpu::BufferUsage usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Vertex |
                                   wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
         wgpu::Buffer buffer = CreateBuffer(data.size(), usage);
-        native::d3d11::Buffer* d3d11Buffer =
-            native::d3d11::ToBackend(native::FromAPI(buffer.Get()));
+        native::d3d11::GPUOnlyBuffer* d3d11Buffer =
+            native::d3d11::ToGPUOnlyBuffer(native::FromAPI(buffer.Get()));
 
         EXPECT_NE(d3d11Buffer->GetD3D11NonConstantBuffer(), nullptr);
         EXPECT_NE(d3d11Buffer->GetD3D11ConstantBuffer(), nullptr);
@@ -218,7 +218,8 @@
     wgpu::BufferUsage usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage |
                               wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
     wgpu::Buffer buffer = CreateBuffer(bufferSize, usage);
-    native::d3d11::Buffer* d3d11Buffer = native::d3d11::ToBackend(native::FromAPI(buffer.Get()));
+    native::d3d11::GPUOnlyBuffer* d3d11Buffer =
+        native::d3d11::ToGPUOnlyBuffer(native::FromAPI(buffer.Get()));
 
     EXPECT_NE(d3d11Buffer->GetD3D11NonConstantBuffer(), nullptr);
     EXPECT_NE(d3d11Buffer->GetD3D11ConstantBuffer(), nullptr);