• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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