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