1 // Copyright 2018 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/DynamicUploader.h" 16 #include "common/Math.h" 17 #include "dawn_native/Device.h" 18 19 namespace dawn_native { 20 DynamicUploader(DeviceBase * device)21 DynamicUploader::DynamicUploader(DeviceBase* device) : mDevice(device) { 22 } 23 CreateStagingBuffer(size_t size)24 ResultOrError<std::unique_ptr<StagingBufferBase>> DynamicUploader::CreateStagingBuffer( 25 size_t size) { 26 std::unique_ptr<StagingBufferBase> stagingBuffer; 27 DAWN_TRY_ASSIGN(stagingBuffer, mDevice->CreateStagingBuffer(size)); 28 DAWN_TRY(stagingBuffer->Initialize()); 29 return stagingBuffer; 30 } 31 ReleaseStagingBuffer(std::unique_ptr<StagingBufferBase> stagingBuffer)32 void DynamicUploader::ReleaseStagingBuffer(std::unique_ptr<StagingBufferBase> stagingBuffer) { 33 mReleasedStagingBuffers.Enqueue(std::move(stagingBuffer), 34 mDevice->GetPendingCommandSerial()); 35 } 36 CreateAndAppendBuffer(size_t size)37 MaybeError DynamicUploader::CreateAndAppendBuffer(size_t size) { 38 std::unique_ptr<RingBuffer> ringBuffer = std::make_unique<RingBuffer>(mDevice, size); 39 DAWN_TRY(ringBuffer->Initialize()); 40 mRingBuffers.emplace_back(std::move(ringBuffer)); 41 return {}; 42 } 43 Allocate(uint32_t size,uint32_t alignment)44 ResultOrError<UploadHandle> DynamicUploader::Allocate(uint32_t size, uint32_t alignment) { 45 ASSERT(IsPowerOfTwo(alignment)); 46 47 // Align the requested allocation size 48 const size_t alignedSize = Align(size, alignment); 49 50 RingBuffer* largestRingBuffer = GetLargestBuffer(); 51 UploadHandle uploadHandle = largestRingBuffer->SubAllocate(alignedSize); 52 53 // Upon failure, append a newly created (and much larger) ring buffer to fulfill the 54 // request. 55 if (uploadHandle.mappedBuffer == nullptr) { 56 // Compute the new max size (in powers of two to preserve alignment). 57 size_t newMaxSize = largestRingBuffer->GetSize(); 58 while (newMaxSize < size) { 59 newMaxSize *= 2; 60 } 61 62 // TODO(bryan.bernhart@intel.com): Fall-back to no sub-allocations should this fail. 63 DAWN_TRY(CreateAndAppendBuffer(newMaxSize)); 64 largestRingBuffer = GetLargestBuffer(); 65 uploadHandle = largestRingBuffer->SubAllocate(alignedSize); 66 } 67 68 uploadHandle.stagingBuffer = largestRingBuffer->GetStagingBuffer(); 69 70 return uploadHandle; 71 } 72 Tick(Serial lastCompletedSerial)73 void DynamicUploader::Tick(Serial lastCompletedSerial) { 74 // Reclaim memory within the ring buffers by ticking (or removing requests no longer 75 // in-flight). 76 for (size_t i = 0; i < mRingBuffers.size(); ++i) { 77 mRingBuffers[i]->Tick(lastCompletedSerial); 78 79 // Never erase the last buffer as to prevent re-creating smaller buffers 80 // again. The last buffer is the largest. 81 if (mRingBuffers[i]->Empty() && i < mRingBuffers.size() - 1) { 82 mRingBuffers.erase(mRingBuffers.begin() + i); 83 } 84 } 85 mReleasedStagingBuffers.ClearUpTo(lastCompletedSerial); 86 } 87 GetLargestBuffer()88 RingBuffer* DynamicUploader::GetLargestBuffer() { 89 ASSERT(!mRingBuffers.empty()); 90 return mRingBuffers.back().get(); 91 } 92 IsEmpty() const93 bool DynamicUploader::IsEmpty() const { 94 return mRingBuffers.empty(); 95 } 96 } // namespace dawn_native