// Copyright 2018 The Dawn Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dawn_native/DynamicUploader.h" #include "common/Math.h" #include "dawn_native/Device.h" namespace dawn_native { DynamicUploader::DynamicUploader(DeviceBase* device) : mDevice(device) { } ResultOrError> DynamicUploader::CreateStagingBuffer( size_t size) { std::unique_ptr stagingBuffer; DAWN_TRY_ASSIGN(stagingBuffer, mDevice->CreateStagingBuffer(size)); DAWN_TRY(stagingBuffer->Initialize()); return stagingBuffer; } void DynamicUploader::ReleaseStagingBuffer(std::unique_ptr stagingBuffer) { mReleasedStagingBuffers.Enqueue(std::move(stagingBuffer), mDevice->GetPendingCommandSerial()); } MaybeError DynamicUploader::CreateAndAppendBuffer(size_t size) { std::unique_ptr ringBuffer = std::make_unique(mDevice, size); DAWN_TRY(ringBuffer->Initialize()); mRingBuffers.emplace_back(std::move(ringBuffer)); return {}; } ResultOrError DynamicUploader::Allocate(uint32_t size, uint32_t alignment) { ASSERT(IsPowerOfTwo(alignment)); // Align the requested allocation size const size_t alignedSize = Align(size, alignment); RingBuffer* largestRingBuffer = GetLargestBuffer(); UploadHandle uploadHandle = largestRingBuffer->SubAllocate(alignedSize); // Upon failure, append a newly created (and much larger) ring buffer to fulfill the // request. if (uploadHandle.mappedBuffer == nullptr) { // Compute the new max size (in powers of two to preserve alignment). size_t newMaxSize = largestRingBuffer->GetSize(); while (newMaxSize < size) { newMaxSize *= 2; } // TODO(bryan.bernhart@intel.com): Fall-back to no sub-allocations should this fail. DAWN_TRY(CreateAndAppendBuffer(newMaxSize)); largestRingBuffer = GetLargestBuffer(); uploadHandle = largestRingBuffer->SubAllocate(alignedSize); } uploadHandle.stagingBuffer = largestRingBuffer->GetStagingBuffer(); return uploadHandle; } void DynamicUploader::Tick(Serial lastCompletedSerial) { // Reclaim memory within the ring buffers by ticking (or removing requests no longer // in-flight). for (size_t i = 0; i < mRingBuffers.size(); ++i) { mRingBuffers[i]->Tick(lastCompletedSerial); // Never erase the last buffer as to prevent re-creating smaller buffers // again. The last buffer is the largest. if (mRingBuffers[i]->Empty() && i < mRingBuffers.size() - 1) { mRingBuffers.erase(mRingBuffers.begin() + i); } } mReleasedStagingBuffers.ClearUpTo(lastCompletedSerial); } RingBuffer* DynamicUploader::GetLargestBuffer() { ASSERT(!mRingBuffers.empty()); return mRingBuffers.back().get(); } bool DynamicUploader::IsEmpty() const { return mRingBuffers.empty(); } } // namespace dawn_native