blob: c1829acab9a13936717a8cfddd46fcd1d5d6d892 [file] [log] [blame]
Corentin Wallez4a9ef4e2018-07-18 11:40:26 +02001// Copyright 2017 The Dawn Authors
Corentin Wallezf07e3bd2017-04-20 14:38:20 -04002//
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
Corentin Wallezfffe6df2017-07-06 14:41:13 -040015#include "backend/CommandAllocator.h"
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040016
Corentin Wallezfd589f32017-07-10 13:46:05 -040017#include "common/Assert.h"
Corentin Wallezfffe6df2017-07-06 14:41:13 -040018#include "common/Math.h"
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040019
Corentin Wallez944b60f2017-05-29 11:33:33 -070020#include <algorithm>
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040021#include <climits>
22#include <cstdlib>
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040023
24namespace backend {
25
Corentin Wallezc1400f02017-11-24 13:59:42 -050026 constexpr uint32_t EndOfBlock = UINT_MAX; // std::numeric_limits<uint32_t>::max();
27 constexpr uint32_t AdditionalData = UINT_MAX - 1; // std::numeric_limits<uint32_t>::max() - 1;
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040028
29 // TODO(cwallez@chromium.org): figure out a way to have more type safety for the iterator
30
Corentin Wallezc1400f02017-11-24 13:59:42 -050031 CommandIterator::CommandIterator() : mEndOfBlock(EndOfBlock) {
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040032 Reset();
33 }
34
35 CommandIterator::~CommandIterator() {
Corentin Wallezfbecc282017-11-23 10:32:51 -080036 ASSERT(mDataWasDestroyed);
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040037
38 if (!IsEmpty()) {
Corentin Wallezfbecc282017-11-23 10:32:51 -080039 for (auto& block : mBlocks) {
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040040 free(block.block);
41 }
42 }
43 }
44
Corentin Wallezc1400f02017-11-24 13:59:42 -050045 CommandIterator::CommandIterator(CommandIterator&& other) : mEndOfBlock(EndOfBlock) {
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040046 if (!other.IsEmpty()) {
Corentin Wallezfbecc282017-11-23 10:32:51 -080047 mBlocks = std::move(other.mBlocks);
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040048 other.Reset();
49 }
50 other.DataWasDestroyed();
51 Reset();
52 }
53
54 CommandIterator& CommandIterator::operator=(CommandIterator&& other) {
55 if (!other.IsEmpty()) {
Corentin Wallezfbecc282017-11-23 10:32:51 -080056 mBlocks = std::move(other.mBlocks);
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040057 other.Reset();
58 } else {
Corentin Wallezfbecc282017-11-23 10:32:51 -080059 mBlocks.clear();
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040060 }
61 other.DataWasDestroyed();
62 Reset();
63 return *this;
64 }
65
66 CommandIterator::CommandIterator(CommandAllocator&& allocator)
Corentin Wallezfbecc282017-11-23 10:32:51 -080067 : mBlocks(allocator.AcquireBlocks()), mEndOfBlock(EndOfBlock) {
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040068 Reset();
69 }
70
71 CommandIterator& CommandIterator::operator=(CommandAllocator&& allocator) {
Corentin Wallezfbecc282017-11-23 10:32:51 -080072 mBlocks = allocator.AcquireBlocks();
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040073 Reset();
74 return *this;
75 }
76
77 void CommandIterator::Reset() {
Corentin Wallezfbecc282017-11-23 10:32:51 -080078 mCurrentBlock = 0;
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040079
Corentin Wallezfbecc282017-11-23 10:32:51 -080080 if (mBlocks.empty()) {
Corentin Wallezc1400f02017-11-24 13:59:42 -050081 // This will case the first NextCommandId call to try to move to the next block and stop
82 // the iteration immediately, without special casing the initialization.
Corentin Wallezfbecc282017-11-23 10:32:51 -080083 mCurrentPtr = reinterpret_cast<uint8_t*>(&mEndOfBlock);
84 mBlocks.emplace_back();
85 mBlocks[0].size = sizeof(mEndOfBlock);
86 mBlocks[0].block = mCurrentPtr;
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040087 } else {
Corentin Wallezfbecc282017-11-23 10:32:51 -080088 mCurrentPtr = AlignPtr(mBlocks[0].block, alignof(uint32_t));
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040089 }
90 }
91
92 void CommandIterator::DataWasDestroyed() {
Corentin Wallezfbecc282017-11-23 10:32:51 -080093 mDataWasDestroyed = true;
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040094 }
95
96 bool CommandIterator::IsEmpty() const {
Corentin Wallezfbecc282017-11-23 10:32:51 -080097 return mBlocks[0].block == reinterpret_cast<const uint8_t*>(&mEndOfBlock);
Corentin Wallezf07e3bd2017-04-20 14:38:20 -040098 }
99
100 bool CommandIterator::NextCommandId(uint32_t* commandId) {
Corentin Wallezfbecc282017-11-23 10:32:51 -0800101 uint8_t* idPtr = AlignPtr(mCurrentPtr, alignof(uint32_t));
Corentin Wallezc1400f02017-11-24 13:59:42 -0500102 ASSERT(idPtr + sizeof(uint32_t) <=
103 mBlocks[mCurrentBlock].block + mBlocks[mCurrentBlock].size);
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400104
105 uint32_t id = *reinterpret_cast<uint32_t*>(idPtr);
106
107 if (id == EndOfBlock) {
Corentin Wallezfbecc282017-11-23 10:32:51 -0800108 mCurrentBlock++;
109 if (mCurrentBlock >= mBlocks.size()) {
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400110 Reset();
Corentin Wallez770f25f2017-07-20 14:30:04 -0400111 *commandId = EndOfBlock;
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400112 return false;
113 }
Corentin Wallezfbecc282017-11-23 10:32:51 -0800114 mCurrentPtr = AlignPtr(mBlocks[mCurrentBlock].block, alignof(uint32_t));
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400115 return NextCommandId(commandId);
116 }
117
Corentin Wallezfbecc282017-11-23 10:32:51 -0800118 mCurrentPtr = idPtr + sizeof(uint32_t);
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400119 *commandId = id;
120 return true;
121 }
122
123 void* CommandIterator::NextCommand(size_t commandSize, size_t commandAlignment) {
Corentin Wallezfbecc282017-11-23 10:32:51 -0800124 uint8_t* commandPtr = AlignPtr(mCurrentPtr, commandAlignment);
Corentin Wallezc1400f02017-11-24 13:59:42 -0500125 ASSERT(commandPtr + sizeof(commandSize) <=
126 mBlocks[mCurrentBlock].block + mBlocks[mCurrentBlock].size);
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400127
Corentin Wallezfbecc282017-11-23 10:32:51 -0800128 mCurrentPtr = commandPtr + commandSize;
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400129 return commandPtr;
130 }
131
132 void* CommandIterator::NextData(size_t dataSize, size_t dataAlignment) {
133 uint32_t id;
134 bool hasId = NextCommandId(&id);
135 ASSERT(hasId);
136 ASSERT(id == AdditionalData);
137
138 return NextCommand(dataSize, dataAlignment);
139 }
140
141 // Potential TODO(cwallez@chromium.org):
Corentin Wallezc1400f02017-11-24 13:59:42 -0500142 // - Host the size and pointer to next block in the block itself to avoid having an allocation
143 // in the vector
144 // - Assume T's alignof is, say 64bits, static assert it, and make commandAlignment a constant
145 // in Allocate
146 // - Be able to optimize allocation to one block, for command buffers expected to live long to
147 // avoid cache misses
Corentin Wallez9fc65342018-07-18 15:28:38 +0200148 // - Better block allocation, maybe have Dawn API to say command buffer is going to have size
Corentin Wallezc1400f02017-11-24 13:59:42 -0500149 // close to another
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400150
151 CommandAllocator::CommandAllocator()
Corentin Wallezc1400f02017-11-24 13:59:42 -0500152 : mCurrentPtr(reinterpret_cast<uint8_t*>(&mDummyEnum[0])),
153 mEndPtr(reinterpret_cast<uint8_t*>(&mDummyEnum[1])) {
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400154 }
155
156 CommandAllocator::~CommandAllocator() {
Corentin Wallezfbecc282017-11-23 10:32:51 -0800157 ASSERT(mBlocks.empty());
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400158 }
159
160 CommandBlocks&& CommandAllocator::AcquireBlocks() {
Corentin Wallezfbecc282017-11-23 10:32:51 -0800161 ASSERT(mCurrentPtr != nullptr && mEndPtr != nullptr);
162 ASSERT(IsPtrAligned(mCurrentPtr, alignof(uint32_t)));
163 ASSERT(mCurrentPtr + sizeof(uint32_t) <= mEndPtr);
164 *reinterpret_cast<uint32_t*>(mCurrentPtr) = EndOfBlock;
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400165
Corentin Wallezfbecc282017-11-23 10:32:51 -0800166 mCurrentPtr = nullptr;
167 mEndPtr = nullptr;
168 return std::move(mBlocks);
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400169 }
170
Corentin Wallezc1400f02017-11-24 13:59:42 -0500171 uint8_t* CommandAllocator::Allocate(uint32_t commandId,
172 size_t commandSize,
173 size_t commandAlignment) {
Corentin Wallezfbecc282017-11-23 10:32:51 -0800174 ASSERT(mCurrentPtr != nullptr);
175 ASSERT(mEndPtr != nullptr);
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400176 ASSERT(commandId != EndOfBlock);
177
178 // It should always be possible to allocate one id, for EndOfBlock tagging,
Corentin Wallezfbecc282017-11-23 10:32:51 -0800179 ASSERT(IsPtrAligned(mCurrentPtr, alignof(uint32_t)));
180 ASSERT(mCurrentPtr + sizeof(uint32_t) <= mEndPtr);
181 uint32_t* idAlloc = reinterpret_cast<uint32_t*>(mCurrentPtr);
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400182
Corentin Wallezfbecc282017-11-23 10:32:51 -0800183 uint8_t* commandAlloc = AlignPtr(mCurrentPtr + sizeof(uint32_t), commandAlignment);
Austin Eng8867e5d2017-07-14 18:53:07 -0400184 uint8_t* nextPtr = AlignPtr(commandAlloc + commandSize, alignof(uint32_t));
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400185
186 // When there is not enough space, we signal the EndOfBlock, so that the iterator nows to
187 // move to the next one. EndOfBlock on the last block means the end of the commands.
Corentin Wallezfbecc282017-11-23 10:32:51 -0800188 if (nextPtr + sizeof(uint32_t) > mEndPtr) {
Corentin Wallezc1400f02017-11-24 13:59:42 -0500189 // Even if we are not able to get another block, the list of commands will be
190 // well-formed and iterable as this block will be that last one.
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400191 *idAlloc = EndOfBlock;
192
Corentin Wallezc1400f02017-11-24 13:59:42 -0500193 // Make sure we have space for current allocation, plus end of block and alignment
194 // padding for the first id.
Corentin Wallezfbecc282017-11-23 10:32:51 -0800195 ASSERT(nextPtr > mCurrentPtr);
Corentin Wallezc1400f02017-11-24 13:59:42 -0500196 if (!GetNewBlock(static_cast<size_t>(nextPtr - mCurrentPtr) + sizeof(uint32_t) +
197 alignof(uint32_t))) {
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400198 return nullptr;
199 }
200 return Allocate(commandId, commandSize, commandAlignment);
201 }
202
203 *idAlloc = commandId;
Corentin Wallezfbecc282017-11-23 10:32:51 -0800204 mCurrentPtr = nextPtr;
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400205 return commandAlloc;
206 }
207
208 uint8_t* CommandAllocator::AllocateData(size_t commandSize, size_t commandAlignment) {
209 return Allocate(AdditionalData, commandSize, commandAlignment);
210 }
211
212 bool CommandAllocator::GetNewBlock(size_t minimumSize) {
213 // Allocate blocks doubling sizes each time, to a maximum of 16k (or at least minimumSize).
Corentin Wallezc1400f02017-11-24 13:59:42 -0500214 mLastAllocationSize =
215 std::max(minimumSize, std::min(mLastAllocationSize * 2, size_t(16384)));
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400216
Corentin Wallezfbecc282017-11-23 10:32:51 -0800217 uint8_t* block = reinterpret_cast<uint8_t*>(malloc(mLastAllocationSize));
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400218 if (block == nullptr) {
219 return false;
220 }
221
Corentin Wallezfbecc282017-11-23 10:32:51 -0800222 mBlocks.push_back({mLastAllocationSize, block});
223 mCurrentPtr = AlignPtr(block, alignof(uint32_t));
224 mEndPtr = block + mLastAllocationSize;
Corentin Wallezf07e3bd2017-04-20 14:38:20 -0400225 return true;
226 }
227
Corentin Wallezc1400f02017-11-24 13:59:42 -0500228} // namespace backend