• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "dawn_native/d3d12/BufferD3D12.h"
16 
17 #include "common/Assert.h"
18 #include "common/Constants.h"
19 #include "common/Math.h"
20 #include "dawn_native/CommandBuffer.h"
21 #include "dawn_native/DynamicUploader.h"
22 #include "dawn_native/d3d12/CommandRecordingContext.h"
23 #include "dawn_native/d3d12/D3D12Error.h"
24 #include "dawn_native/d3d12/DeviceD3D12.h"
25 #include "dawn_native/d3d12/HeapD3D12.h"
26 #include "dawn_native/d3d12/ResidencyManagerD3D12.h"
27 #include "dawn_native/d3d12/UtilsD3D12.h"
28 
29 namespace dawn_native { namespace d3d12 {
30 
31     namespace {
D3D12ResourceFlags(wgpu::BufferUsage usage)32         D3D12_RESOURCE_FLAGS D3D12ResourceFlags(wgpu::BufferUsage usage) {
33             D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE;
34 
35             if (usage & (wgpu::BufferUsage::Storage | kInternalStorageBuffer)) {
36                 flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
37             }
38 
39             return flags;
40         }
41 
D3D12BufferUsage(wgpu::BufferUsage usage)42         D3D12_RESOURCE_STATES D3D12BufferUsage(wgpu::BufferUsage usage) {
43             D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON;
44 
45             if (usage & wgpu::BufferUsage::CopySrc) {
46                 resourceState |= D3D12_RESOURCE_STATE_COPY_SOURCE;
47             }
48             if (usage & wgpu::BufferUsage::CopyDst) {
49                 resourceState |= D3D12_RESOURCE_STATE_COPY_DEST;
50             }
51             if (usage & (wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Uniform)) {
52                 resourceState |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
53             }
54             if (usage & wgpu::BufferUsage::Index) {
55                 resourceState |= D3D12_RESOURCE_STATE_INDEX_BUFFER;
56             }
57             if (usage & (wgpu::BufferUsage::Storage | kInternalStorageBuffer)) {
58                 resourceState |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
59             }
60             if (usage & kReadOnlyStorageBuffer) {
61                 resourceState |= (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE |
62                                   D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
63             }
64             if (usage & wgpu::BufferUsage::Indirect) {
65                 resourceState |= D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
66             }
67             if (usage & wgpu::BufferUsage::QueryResolve) {
68                 resourceState |= D3D12_RESOURCE_STATE_COPY_DEST;
69             }
70 
71             return resourceState;
72         }
73 
D3D12HeapType(wgpu::BufferUsage allowedUsage)74         D3D12_HEAP_TYPE D3D12HeapType(wgpu::BufferUsage allowedUsage) {
75             if (allowedUsage & wgpu::BufferUsage::MapRead) {
76                 return D3D12_HEAP_TYPE_READBACK;
77             } else if (allowedUsage & wgpu::BufferUsage::MapWrite) {
78                 return D3D12_HEAP_TYPE_UPLOAD;
79             } else {
80                 return D3D12_HEAP_TYPE_DEFAULT;
81             }
82         }
83 
D3D12BufferSizeAlignment(wgpu::BufferUsage usage)84         size_t D3D12BufferSizeAlignment(wgpu::BufferUsage usage) {
85             if ((usage & wgpu::BufferUsage::Uniform) != 0) {
86                 // D3D buffers are always resource size aligned to 64KB. However, D3D12's validation
87                 // forbids binding a CBV to an unaligned size. To prevent, one can always safely
88                 // align the buffer size to the CBV data alignment as other buffer usages
89                 // ignore it (no size check). The validation will still enforce bound checks with
90                 // the unaligned size returned by GetSize().
91                 // https://docs.microsoft.com/en-us/windows/win32/direct3d12/uploading-resources#buffer-alignment
92                 return D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT;
93             }
94             return 1;
95         }
96     }  // namespace
97 
98     // static
Create(Device * device,const BufferDescriptor * descriptor)99     ResultOrError<Ref<Buffer>> Buffer::Create(Device* device, const BufferDescriptor* descriptor) {
100         Ref<Buffer> buffer = AcquireRef(new Buffer(device, descriptor));
101         DAWN_TRY(buffer->Initialize(descriptor->mappedAtCreation));
102         return buffer;
103     }
104 
Buffer(Device * device,const BufferDescriptor * descriptor)105     Buffer::Buffer(Device* device, const BufferDescriptor* descriptor)
106         : BufferBase(device, descriptor) {
107     }
108 
Initialize(bool mappedAtCreation)109     MaybeError Buffer::Initialize(bool mappedAtCreation) {
110         // Allocate at least 4 bytes so clamped accesses are always in bounds.
111         uint64_t size = std::max(GetSize(), uint64_t(4u));
112         size_t alignment = D3D12BufferSizeAlignment(GetUsage());
113         if (size > std::numeric_limits<uint64_t>::max() - alignment) {
114             // Alignment would overlow.
115             return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large");
116         }
117         mAllocatedSize = Align(size, alignment);
118 
119         D3D12_RESOURCE_DESC resourceDescriptor;
120         resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
121         resourceDescriptor.Alignment = 0;
122         resourceDescriptor.Width = mAllocatedSize;
123         resourceDescriptor.Height = 1;
124         resourceDescriptor.DepthOrArraySize = 1;
125         resourceDescriptor.MipLevels = 1;
126         resourceDescriptor.Format = DXGI_FORMAT_UNKNOWN;
127         resourceDescriptor.SampleDesc.Count = 1;
128         resourceDescriptor.SampleDesc.Quality = 0;
129         resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
130         // Add CopyDst for non-mappable buffer initialization with mappedAtCreation
131         // and robust resource initialization.
132         resourceDescriptor.Flags = D3D12ResourceFlags(GetUsage() | wgpu::BufferUsage::CopyDst);
133 
134         auto heapType = D3D12HeapType(GetUsage());
135         auto bufferUsage = D3D12_RESOURCE_STATE_COMMON;
136 
137         // D3D12 requires buffers on the READBACK heap to have the D3D12_RESOURCE_STATE_COPY_DEST
138         // state
139         if (heapType == D3D12_HEAP_TYPE_READBACK) {
140             bufferUsage |= D3D12_RESOURCE_STATE_COPY_DEST;
141             mFixedResourceState = true;
142             mLastUsage = wgpu::BufferUsage::CopyDst;
143         }
144 
145         // D3D12 requires buffers on the UPLOAD heap to have the D3D12_RESOURCE_STATE_GENERIC_READ
146         // state
147         if (heapType == D3D12_HEAP_TYPE_UPLOAD) {
148             bufferUsage |= D3D12_RESOURCE_STATE_GENERIC_READ;
149             mFixedResourceState = true;
150             mLastUsage = wgpu::BufferUsage::CopySrc;
151         }
152 
153         DAWN_TRY_ASSIGN(
154             mResourceAllocation,
155             ToBackend(GetDevice())->AllocateMemory(heapType, resourceDescriptor, bufferUsage));
156 
157         SetLabelImpl();
158 
159         // The buffers with mappedAtCreation == true will be initialized in
160         // BufferBase::MapAtCreation().
161         if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting) &&
162             !mappedAtCreation) {
163             CommandRecordingContext* commandRecordingContext;
164             DAWN_TRY_ASSIGN(commandRecordingContext,
165                             ToBackend(GetDevice())->GetPendingCommandContext());
166 
167             DAWN_TRY(ClearBuffer(commandRecordingContext, uint8_t(1u)));
168         }
169 
170         // Initialize the padding bytes to zero.
171         if (GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse) &&
172             !mappedAtCreation) {
173             uint32_t paddingBytes = GetAllocatedSize() - GetSize();
174             if (paddingBytes > 0) {
175                 CommandRecordingContext* commandRecordingContext;
176                 DAWN_TRY_ASSIGN(commandRecordingContext,
177                                 ToBackend(GetDevice())->GetPendingCommandContext());
178 
179                 uint32_t clearSize = paddingBytes;
180                 uint64_t clearOffset = GetSize();
181                 DAWN_TRY(ClearBuffer(commandRecordingContext, 0, clearOffset, clearSize));
182             }
183         }
184 
185         return {};
186     }
187 
188     Buffer::~Buffer() = default;
189 
GetD3D12Resource() const190     ID3D12Resource* Buffer::GetD3D12Resource() const {
191         return mResourceAllocation.GetD3D12Resource();
192     }
193 
194     // When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a
195     // ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can
196     // cause subsequent errors.
TrackUsageAndGetResourceBarrier(CommandRecordingContext * commandContext,D3D12_RESOURCE_BARRIER * barrier,wgpu::BufferUsage newUsage)197     bool Buffer::TrackUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
198                                                  D3D12_RESOURCE_BARRIER* barrier,
199                                                  wgpu::BufferUsage newUsage) {
200         // Track the underlying heap to ensure residency.
201         Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
202         commandContext->TrackHeapUsage(heap, GetDevice()->GetPendingCommandSerial());
203 
204         // Return the resource barrier.
205         return TransitionUsageAndGetResourceBarrier(commandContext, barrier, newUsage);
206     }
207 
TrackUsageAndTransitionNow(CommandRecordingContext * commandContext,wgpu::BufferUsage newUsage)208     void Buffer::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext,
209                                             wgpu::BufferUsage newUsage) {
210         D3D12_RESOURCE_BARRIER barrier;
211 
212         if (TrackUsageAndGetResourceBarrier(commandContext, &barrier, newUsage)) {
213             commandContext->GetCommandList()->ResourceBarrier(1, &barrier);
214         }
215     }
216 
217     // When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a
218     // ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can
219     // cause subsequent errors.
TransitionUsageAndGetResourceBarrier(CommandRecordingContext * commandContext,D3D12_RESOURCE_BARRIER * barrier,wgpu::BufferUsage newUsage)220     bool Buffer::TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
221                                                       D3D12_RESOURCE_BARRIER* barrier,
222                                                       wgpu::BufferUsage newUsage) {
223         // Resources in upload and readback heaps must be kept in the COPY_SOURCE/DEST state
224         if (mFixedResourceState) {
225             ASSERT(mLastUsage == newUsage);
226             return false;
227         }
228 
229         D3D12_RESOURCE_STATES lastState = D3D12BufferUsage(mLastUsage);
230         D3D12_RESOURCE_STATES newState = D3D12BufferUsage(newUsage);
231 
232         // If the transition is from-UAV-to-UAV, then a UAV barrier is needed.
233         // If one of the usages isn't UAV, then other barriers are used.
234         bool needsUAVBarrier = lastState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS &&
235                                newState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
236 
237         if (needsUAVBarrier) {
238             barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
239             barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
240             barrier->UAV.pResource = GetD3D12Resource();
241 
242             mLastUsage = newUsage;
243             return true;
244         }
245 
246         // We can skip transitions to already current usages.
247         if (IsSubset(newUsage, mLastUsage)) {
248             return false;
249         }
250 
251         mLastUsage = newUsage;
252 
253         // The COMMON state represents a state where no write operations can be pending, which makes
254         // it possible to transition to and from some states without synchronizaton (i.e. without an
255         // explicit ResourceBarrier call). A buffer can be implicitly promoted to 1) a single write
256         // state, or 2) multiple read states. A buffer that is accessed within a command list will
257         // always implicitly decay to the COMMON state after the call to ExecuteCommandLists
258         // completes - this is because all buffer writes are guaranteed to be completed before the
259         // next ExecuteCommandLists call executes.
260         // https://docs.microsoft.com/en-us/windows/desktop/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12#implicit-state-transitions
261 
262         // To track implicit decays, we must record the pending serial on which a transition will
263         // occur. When that buffer is used again, the previously recorded serial must be compared to
264         // the last completed serial to determine if the buffer has implicity decayed to the common
265         // state.
266         const ExecutionSerial pendingCommandSerial =
267             ToBackend(GetDevice())->GetPendingCommandSerial();
268         if (pendingCommandSerial > mLastUsedSerial) {
269             lastState = D3D12_RESOURCE_STATE_COMMON;
270             mLastUsedSerial = pendingCommandSerial;
271         }
272 
273         // All possible buffer states used by Dawn are eligible for implicit promotion from COMMON.
274         // These are: COPY_SOURCE, VERTEX_AND_COPY_BUFFER, INDEX_BUFFER, COPY_DEST,
275         // UNORDERED_ACCESS, and INDIRECT_ARGUMENT. Note that for implicit promotion, the
276         // destination state cannot be 1) more than one write state, or 2) both a read and write
277         // state. This goes unchecked here because it should not be allowed through render/compute
278         // pass validation.
279         if (lastState == D3D12_RESOURCE_STATE_COMMON) {
280             return false;
281         }
282 
283         // TODO(crbug.com/dawn/1024): The before and after states must be different. Remove this
284         // workaround and use D3D12 states instead of WebGPU usages to manage the tracking of
285         // barrier state.
286         if (lastState == newState) {
287             return false;
288         }
289 
290         barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
291         barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
292         barrier->Transition.pResource = GetD3D12Resource();
293         barrier->Transition.StateBefore = lastState;
294         barrier->Transition.StateAfter = newState;
295         barrier->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
296 
297         return true;
298     }
299 
GetVA() const300     D3D12_GPU_VIRTUAL_ADDRESS Buffer::GetVA() const {
301         return mResourceAllocation.GetGPUPointer();
302     }
303 
IsCPUWritableAtCreation() const304     bool Buffer::IsCPUWritableAtCreation() const {
305         // We use a staging buffer for the buffers with mappedAtCreation == true and created on the
306         // READBACK heap because for the buffers on the READBACK heap, the data written on the CPU
307         // side won't be uploaded to GPU. When we enable zero-initialization, the CPU side memory
308         // of the buffer is all written to 0 but not the GPU side memory, so on the next mapping
309         // operation the zeroes get overwritten by whatever was in the GPU memory when the buffer
310         // was created. With a staging buffer, the data on the CPU side will first upload to the
311         // staging buffer, and copied from the staging buffer to the GPU memory of the current
312         // buffer in the unmap() call.
313         // TODO(enga): Handle CPU-visible memory on UMA
314         return (GetUsage() & wgpu::BufferUsage::MapWrite) != 0;
315     }
316 
MapInternal(bool isWrite,size_t offset,size_t size,const char * contextInfo)317     MaybeError Buffer::MapInternal(bool isWrite,
318                                    size_t offset,
319                                    size_t size,
320                                    const char* contextInfo) {
321         // The mapped buffer can be accessed at any time, so it must be locked to ensure it is never
322         // evicted. This buffer should already have been made resident when it was created.
323         Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
324         DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap));
325 
326         D3D12_RANGE range = {offset, offset + size};
327         // mMappedData is the pointer to the start of the resource, irrespective of offset.
328         // MSDN says (note the weird use of "never"):
329         //
330         //   When ppData is not NULL, the pointer returned is never offset by any values in
331         //   pReadRange.
332         //
333         // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12resource-map
334         DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &range, &mMappedData), contextInfo));
335 
336         if (isWrite) {
337             mWrittenMappedRange = range;
338         }
339 
340         return {};
341     }
342 
MapAtCreationImpl()343     MaybeError Buffer::MapAtCreationImpl() {
344         // We will use a staging buffer for MapRead buffers instead so we just clear the staging
345         // buffer and initialize the original buffer by copying the staging buffer to the original
346         // buffer one the first time Unmap() is called.
347         ASSERT((GetUsage() & wgpu::BufferUsage::MapWrite) != 0);
348 
349         // The buffers with mappedAtCreation == true will be initialized in
350         // BufferBase::MapAtCreation().
351         DAWN_TRY(MapInternal(true, 0, size_t(GetAllocatedSize()), "D3D12 map at creation"));
352 
353         return {};
354     }
355 
MapAsyncImpl(wgpu::MapMode mode,size_t offset,size_t size)356     MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) {
357         CommandRecordingContext* commandContext;
358         DAWN_TRY_ASSIGN(commandContext, ToBackend(GetDevice())->GetPendingCommandContext());
359         DAWN_TRY(EnsureDataInitialized(commandContext));
360 
361         return MapInternal(mode & wgpu::MapMode::Write, offset, size, "D3D12 map async");
362     }
363 
UnmapImpl()364     void Buffer::UnmapImpl() {
365         GetD3D12Resource()->Unmap(0, &mWrittenMappedRange);
366         mMappedData = nullptr;
367         mWrittenMappedRange = {0, 0};
368 
369         // When buffers are mapped, they are locked to keep them in resident memory. We must unlock
370         // them when they are unmapped.
371         Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
372         ToBackend(GetDevice())->GetResidencyManager()->UnlockAllocation(heap);
373     }
374 
GetMappedPointerImpl()375     void* Buffer::GetMappedPointerImpl() {
376         // The frontend asks that the pointer returned is from the start of the resource
377         // irrespective of the offset passed in MapAsyncImpl, which is what mMappedData is.
378         return mMappedData;
379     }
380 
DestroyImpl()381     void Buffer::DestroyImpl() {
382         if (mMappedData != nullptr) {
383             // If the buffer is currently mapped, unmap without flushing the writes to the GPU
384             // since the buffer cannot be used anymore. UnmapImpl checks mWrittenRange to know
385             // which parts to flush, so we set it to an empty range to prevent flushes.
386             mWrittenMappedRange = {0, 0};
387         }
388         BufferBase::DestroyImpl();
389 
390         ToBackend(GetDevice())->DeallocateMemory(mResourceAllocation);
391     }
392 
CheckIsResidentForTesting() const393     bool Buffer::CheckIsResidentForTesting() const {
394         Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
395         return heap->IsInList() || heap->IsResidencyLocked();
396     }
397 
CheckAllocationMethodForTesting(AllocationMethod allocationMethod) const398     bool Buffer::CheckAllocationMethodForTesting(AllocationMethod allocationMethod) const {
399         return mResourceAllocation.GetInfo().mMethod == allocationMethod;
400     }
401 
EnsureDataInitialized(CommandRecordingContext * commandContext)402     MaybeError Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
403         if (!NeedsInitialization()) {
404             return {};
405         }
406 
407         DAWN_TRY(InitializeToZero(commandContext));
408         return {};
409     }
410 
EnsureDataInitializedAsDestination(CommandRecordingContext * commandContext,uint64_t offset,uint64_t size)411     ResultOrError<bool> Buffer::EnsureDataInitializedAsDestination(
412         CommandRecordingContext* commandContext,
413         uint64_t offset,
414         uint64_t size) {
415         if (!NeedsInitialization()) {
416             return {false};
417         }
418 
419         if (IsFullBufferRange(offset, size)) {
420             SetIsDataInitialized();
421             return {false};
422         }
423 
424         DAWN_TRY(InitializeToZero(commandContext));
425         return {true};
426     }
427 
EnsureDataInitializedAsDestination(CommandRecordingContext * commandContext,const CopyTextureToBufferCmd * copy)428     MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
429                                                           const CopyTextureToBufferCmd* copy) {
430         if (!NeedsInitialization()) {
431             return {};
432         }
433 
434         if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) {
435             SetIsDataInitialized();
436         } else {
437             DAWN_TRY(InitializeToZero(commandContext));
438         }
439 
440         return {};
441     }
442 
SetLabelImpl()443     void Buffer::SetLabelImpl() {
444         SetDebugName(ToBackend(GetDevice()), mResourceAllocation.GetD3D12Resource(), "Dawn_Buffer",
445                      GetLabel());
446     }
447 
InitializeToZero(CommandRecordingContext * commandContext)448     MaybeError Buffer::InitializeToZero(CommandRecordingContext* commandContext) {
449         ASSERT(NeedsInitialization());
450 
451         // TODO(crbug.com/dawn/484): skip initializing the buffer when it is created on a heap
452         // that has already been zero initialized.
453         DAWN_TRY(ClearBuffer(commandContext, uint8_t(0u)));
454         SetIsDataInitialized();
455         GetDevice()->IncrementLazyClearCountForTesting();
456 
457         return {};
458     }
459 
ClearBuffer(CommandRecordingContext * commandContext,uint8_t clearValue,uint64_t offset,uint64_t size)460     MaybeError Buffer::ClearBuffer(CommandRecordingContext* commandContext,
461                                    uint8_t clearValue,
462                                    uint64_t offset,
463                                    uint64_t size) {
464         Device* device = ToBackend(GetDevice());
465         size = size > 0 ? size : GetAllocatedSize();
466 
467         // The state of the buffers on UPLOAD heap must always be GENERIC_READ and cannot be
468         // changed away, so we can only clear such buffer with buffer mapping.
469         if (D3D12HeapType(GetUsage()) == D3D12_HEAP_TYPE_UPLOAD) {
470             DAWN_TRY(MapInternal(true, static_cast<size_t>(offset), static_cast<size_t>(size),
471                                  "D3D12 map at clear buffer"));
472             memset(mMappedData, clearValue, size);
473             UnmapImpl();
474         } else if (clearValue == 0u) {
475             DAWN_TRY(device->ClearBufferToZero(commandContext, this, offset, size));
476         } else {
477             // TODO(crbug.com/dawn/852): use ClearUnorderedAccessView*() when the buffer usage
478             // includes STORAGE.
479             DynamicUploader* uploader = device->GetDynamicUploader();
480             UploadHandle uploadHandle;
481             DAWN_TRY_ASSIGN(uploadHandle,
482                             uploader->Allocate(size, device->GetPendingCommandSerial(),
483                                                kCopyBufferToBufferOffsetAlignment));
484 
485             memset(uploadHandle.mappedBuffer, clearValue, size);
486 
487             device->CopyFromStagingToBufferImpl(commandContext, uploadHandle.stagingBuffer,
488                                                 uploadHandle.startOffset, this, offset, size);
489         }
490 
491         return {};
492     }
493 }}  // namespace dawn_native::d3d12
494