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 mRingBuffers.emplace_back( 23 std::unique_ptr<RingBuffer>(new RingBuffer{nullptr, {kRingBufferSize}})); 24 } 25 ReleaseStagingBuffer(std::unique_ptr<StagingBufferBase> stagingBuffer)26 void DynamicUploader::ReleaseStagingBuffer(std::unique_ptr<StagingBufferBase> stagingBuffer) { 27 mReleasedStagingBuffers.Enqueue(std::move(stagingBuffer), 28 mDevice->GetPendingCommandSerial()); 29 } 30 AllocateInternal(uint64_t allocationSize,ExecutionSerial serial)31 ResultOrError<UploadHandle> DynamicUploader::AllocateInternal(uint64_t allocationSize, 32 ExecutionSerial serial) { 33 // Disable further sub-allocation should the request be too large. 34 if (allocationSize > kRingBufferSize) { 35 std::unique_ptr<StagingBufferBase> stagingBuffer; 36 DAWN_TRY_ASSIGN(stagingBuffer, mDevice->CreateStagingBuffer(allocationSize)); 37 38 UploadHandle uploadHandle; 39 uploadHandle.mappedBuffer = static_cast<uint8_t*>(stagingBuffer->GetMappedPointer()); 40 uploadHandle.stagingBuffer = stagingBuffer.get(); 41 42 ReleaseStagingBuffer(std::move(stagingBuffer)); 43 return uploadHandle; 44 } 45 46 // Note: Validation ensures size is already aligned. 47 // First-fit: find next smallest buffer large enough to satisfy the allocation request. 48 RingBuffer* targetRingBuffer = mRingBuffers.back().get(); 49 for (auto& ringBuffer : mRingBuffers) { 50 const RingBufferAllocator& ringBufferAllocator = ringBuffer->mAllocator; 51 // Prevent overflow. 52 ASSERT(ringBufferAllocator.GetSize() >= ringBufferAllocator.GetUsedSize()); 53 const uint64_t remainingSize = 54 ringBufferAllocator.GetSize() - ringBufferAllocator.GetUsedSize(); 55 if (allocationSize <= remainingSize) { 56 targetRingBuffer = ringBuffer.get(); 57 break; 58 } 59 } 60 61 uint64_t startOffset = RingBufferAllocator::kInvalidOffset; 62 if (targetRingBuffer != nullptr) { 63 startOffset = targetRingBuffer->mAllocator.Allocate(allocationSize, serial); 64 } 65 66 // Upon failure, append a newly created ring buffer to fulfill the 67 // request. 68 if (startOffset == RingBufferAllocator::kInvalidOffset) { 69 mRingBuffers.emplace_back( 70 std::unique_ptr<RingBuffer>(new RingBuffer{nullptr, {kRingBufferSize}})); 71 72 targetRingBuffer = mRingBuffers.back().get(); 73 startOffset = targetRingBuffer->mAllocator.Allocate(allocationSize, serial); 74 } 75 76 ASSERT(startOffset != RingBufferAllocator::kInvalidOffset); 77 78 // Allocate the staging buffer backing the ringbuffer. 79 // Note: the first ringbuffer will be lazily created. 80 if (targetRingBuffer->mStagingBuffer == nullptr) { 81 std::unique_ptr<StagingBufferBase> stagingBuffer; 82 DAWN_TRY_ASSIGN(stagingBuffer, 83 mDevice->CreateStagingBuffer(targetRingBuffer->mAllocator.GetSize())); 84 targetRingBuffer->mStagingBuffer = std::move(stagingBuffer); 85 } 86 87 ASSERT(targetRingBuffer->mStagingBuffer != nullptr); 88 89 UploadHandle uploadHandle; 90 uploadHandle.stagingBuffer = targetRingBuffer->mStagingBuffer.get(); 91 uploadHandle.mappedBuffer = 92 static_cast<uint8_t*>(uploadHandle.stagingBuffer->GetMappedPointer()) + startOffset; 93 uploadHandle.startOffset = startOffset; 94 95 return uploadHandle; 96 } 97 Deallocate(ExecutionSerial lastCompletedSerial)98 void DynamicUploader::Deallocate(ExecutionSerial lastCompletedSerial) { 99 // Reclaim memory within the ring buffers by ticking (or removing requests no longer 100 // in-flight). 101 for (size_t i = 0; i < mRingBuffers.size(); ++i) { 102 mRingBuffers[i]->mAllocator.Deallocate(lastCompletedSerial); 103 104 // Never erase the last buffer as to prevent re-creating smaller buffers 105 // again. The last buffer is the largest. 106 if (mRingBuffers[i]->mAllocator.Empty() && i < mRingBuffers.size() - 1) { 107 mRingBuffers.erase(mRingBuffers.begin() + i); 108 } 109 } 110 mReleasedStagingBuffers.ClearUpTo(lastCompletedSerial); 111 } 112 113 // TODO(dawn:512): Optimize this function so that it doesn't allocate additional memory 114 // when it's not necessary. Allocate(uint64_t allocationSize,ExecutionSerial serial,uint64_t offsetAlignment)115 ResultOrError<UploadHandle> DynamicUploader::Allocate(uint64_t allocationSize, 116 ExecutionSerial serial, 117 uint64_t offsetAlignment) { 118 ASSERT(offsetAlignment > 0); 119 UploadHandle uploadHandle; 120 DAWN_TRY_ASSIGN(uploadHandle, 121 AllocateInternal(allocationSize + offsetAlignment - 1, serial)); 122 uint64_t additionalOffset = 123 Align(uploadHandle.startOffset, offsetAlignment) - uploadHandle.startOffset; 124 uploadHandle.mappedBuffer = 125 static_cast<uint8_t*>(uploadHandle.mappedBuffer) + additionalOffset; 126 uploadHandle.startOffset += additionalOffset; 127 return uploadHandle; 128 } 129 } // namespace dawn_native 130