Corentin Wallez | 4a9ef4e | 2018-07-18 11:40:26 +0200 | [diff] [blame] | 1 | // Copyright 2017 The Dawn Authors |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 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 | |
Corentin Wallez | d37523f | 2018-07-24 13:53:51 +0200 | [diff] [blame] | 15 | #include "dawn_native/CommandAllocator.h" |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 16 | |
Corentin Wallez | fd589f3 | 2017-07-10 13:46:05 -0400 | [diff] [blame] | 17 | #include "common/Assert.h" |
Corentin Wallez | fffe6df | 2017-07-06 14:41:13 -0400 | [diff] [blame] | 18 | #include "common/Math.h" |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 19 | |
Corentin Wallez | 944b60f | 2017-05-29 11:33:33 -0700 | [diff] [blame] | 20 | #include <algorithm> |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 21 | #include <climits> |
| 22 | #include <cstdlib> |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 23 | |
Corentin Wallez | 49a65d0 | 2018-07-24 16:45:45 +0200 | [diff] [blame] | 24 | namespace dawn_native { |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 25 | |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 26 | // TODO(cwallez@chromium.org): figure out a way to have more type safety for the iterator |
| 27 | |
Corentin Wallez | c1400f0 | 2017-11-24 13:59:42 -0500 | [diff] [blame] | 28 | CommandIterator::CommandIterator() : mEndOfBlock(EndOfBlock) { |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 29 | Reset(); |
| 30 | } |
| 31 | |
| 32 | CommandIterator::~CommandIterator() { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 33 | ASSERT(mDataWasDestroyed); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 34 | |
| 35 | if (!IsEmpty()) { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 36 | for (auto& block : mBlocks) { |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 37 | free(block.block); |
| 38 | } |
| 39 | } |
| 40 | } |
| 41 | |
Corentin Wallez | c1400f0 | 2017-11-24 13:59:42 -0500 | [diff] [blame] | 42 | CommandIterator::CommandIterator(CommandIterator&& other) : mEndOfBlock(EndOfBlock) { |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 43 | if (!other.IsEmpty()) { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 44 | mBlocks = std::move(other.mBlocks); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 45 | other.Reset(); |
| 46 | } |
| 47 | other.DataWasDestroyed(); |
| 48 | Reset(); |
| 49 | } |
| 50 | |
| 51 | CommandIterator& CommandIterator::operator=(CommandIterator&& other) { |
| 52 | if (!other.IsEmpty()) { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 53 | mBlocks = std::move(other.mBlocks); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 54 | other.Reset(); |
| 55 | } else { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 56 | mBlocks.clear(); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 57 | } |
| 58 | other.DataWasDestroyed(); |
| 59 | Reset(); |
| 60 | return *this; |
| 61 | } |
| 62 | |
| 63 | CommandIterator::CommandIterator(CommandAllocator&& allocator) |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 64 | : mBlocks(allocator.AcquireBlocks()), mEndOfBlock(EndOfBlock) { |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 65 | Reset(); |
| 66 | } |
| 67 | |
| 68 | CommandIterator& CommandIterator::operator=(CommandAllocator&& allocator) { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 69 | mBlocks = allocator.AcquireBlocks(); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 70 | Reset(); |
| 71 | return *this; |
| 72 | } |
| 73 | |
| 74 | void CommandIterator::Reset() { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 75 | mCurrentBlock = 0; |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 76 | |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 77 | if (mBlocks.empty()) { |
Corentin Wallez | c1400f0 | 2017-11-24 13:59:42 -0500 | [diff] [blame] | 78 | // This will case the first NextCommandId call to try to move to the next block and stop |
| 79 | // the iteration immediately, without special casing the initialization. |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 80 | mCurrentPtr = reinterpret_cast<uint8_t*>(&mEndOfBlock); |
| 81 | mBlocks.emplace_back(); |
| 82 | mBlocks[0].size = sizeof(mEndOfBlock); |
| 83 | mBlocks[0].block = mCurrentPtr; |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 84 | } else { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 85 | mCurrentPtr = AlignPtr(mBlocks[0].block, alignof(uint32_t)); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 86 | } |
| 87 | } |
| 88 | |
| 89 | void CommandIterator::DataWasDestroyed() { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 90 | mDataWasDestroyed = true; |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 91 | } |
| 92 | |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 93 | |
| 94 | // Potential TODO(cwallez@chromium.org): |
Corentin Wallez | c1400f0 | 2017-11-24 13:59:42 -0500 | [diff] [blame] | 95 | // - Host the size and pointer to next block in the block itself to avoid having an allocation |
| 96 | // in the vector |
| 97 | // - Assume T's alignof is, say 64bits, static assert it, and make commandAlignment a constant |
| 98 | // in Allocate |
| 99 | // - Be able to optimize allocation to one block, for command buffers expected to live long to |
| 100 | // avoid cache misses |
Corentin Wallez | 9fc6534 | 2018-07-18 15:28:38 +0200 | [diff] [blame] | 101 | // - Better block allocation, maybe have Dawn API to say command buffer is going to have size |
Corentin Wallez | c1400f0 | 2017-11-24 13:59:42 -0500 | [diff] [blame] | 102 | // close to another |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 103 | |
| 104 | CommandAllocator::CommandAllocator() |
Corentin Wallez | c1400f0 | 2017-11-24 13:59:42 -0500 | [diff] [blame] | 105 | : mCurrentPtr(reinterpret_cast<uint8_t*>(&mDummyEnum[0])), |
| 106 | mEndPtr(reinterpret_cast<uint8_t*>(&mDummyEnum[1])) { |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | CommandAllocator::~CommandAllocator() { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 110 | ASSERT(mBlocks.empty()); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | CommandBlocks&& CommandAllocator::AcquireBlocks() { |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 114 | ASSERT(mCurrentPtr != nullptr && mEndPtr != nullptr); |
| 115 | ASSERT(IsPtrAligned(mCurrentPtr, alignof(uint32_t))); |
| 116 | ASSERT(mCurrentPtr + sizeof(uint32_t) <= mEndPtr); |
| 117 | *reinterpret_cast<uint32_t*>(mCurrentPtr) = EndOfBlock; |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 118 | |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 119 | mCurrentPtr = nullptr; |
| 120 | mEndPtr = nullptr; |
| 121 | return std::move(mBlocks); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 122 | } |
| 123 | |
Zhenyao Mo | df9d1b5 | 2019-11-13 19:33:59 +0000 | [diff] [blame] | 124 | uint8_t* CommandAllocator::AllocateAtEnd(uint32_t commandId, |
| 125 | size_t commandSize, |
| 126 | size_t commandAlignment) { |
Corentin Wallez | 1c92c15 | 2019-03-01 12:04:58 +0000 | [diff] [blame] | 127 | static constexpr size_t kWorstCaseAdditionalSize = |
| 128 | sizeof(uint32_t) + kMaxSupportedAlignment + alignof(uint32_t) + sizeof(uint32_t); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 129 | |
Corentin Wallez | 1c92c15 | 2019-03-01 12:04:58 +0000 | [diff] [blame] | 130 | // When there is not enough space, we signal the EndOfBlock, so that the iterator knows to |
| 131 | // move to the next one. EndOfBlock on the last block means the end of the commands. |
| 132 | uint32_t* idAlloc = reinterpret_cast<uint32_t*>(mCurrentPtr); |
| 133 | *idAlloc = EndOfBlock; |
| 134 | |
| 135 | // We'll request a block that can contain at least the command ID, the command and an |
| 136 | // additional ID to contain the EndOfBlock tag. |
| 137 | size_t requestedBlockSize = commandSize + kWorstCaseAdditionalSize; |
| 138 | |
| 139 | // The computation of the request could overflow. |
| 140 | if (DAWN_UNLIKELY(requestedBlockSize <= commandSize)) { |
| 141 | return nullptr; |
| 142 | } |
| 143 | |
| 144 | if (DAWN_UNLIKELY(!GetNewBlock(requestedBlockSize))) { |
| 145 | return nullptr; |
| 146 | } |
| 147 | return Allocate(commandId, commandSize, commandAlignment); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 148 | } |
| 149 | |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 150 | bool CommandAllocator::GetNewBlock(size_t minimumSize) { |
| 151 | // Allocate blocks doubling sizes each time, to a maximum of 16k (or at least minimumSize). |
Corentin Wallez | c1400f0 | 2017-11-24 13:59:42 -0500 | [diff] [blame] | 152 | mLastAllocationSize = |
| 153 | std::max(minimumSize, std::min(mLastAllocationSize * 2, size_t(16384))); |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 154 | |
Rafael Cintron | f54bb68 | 2019-05-03 00:58:27 +0000 | [diff] [blame] | 155 | uint8_t* block = static_cast<uint8_t*>(malloc(mLastAllocationSize)); |
Corentin Wallez | 1c92c15 | 2019-03-01 12:04:58 +0000 | [diff] [blame] | 156 | if (DAWN_UNLIKELY(block == nullptr)) { |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 157 | return false; |
| 158 | } |
| 159 | |
Corentin Wallez | fbecc28 | 2017-11-23 10:32:51 -0800 | [diff] [blame] | 160 | mBlocks.push_back({mLastAllocationSize, block}); |
| 161 | mCurrentPtr = AlignPtr(block, alignof(uint32_t)); |
| 162 | mEndPtr = block + mLastAllocationSize; |
Corentin Wallez | f07e3bd | 2017-04-20 14:38:20 -0400 | [diff] [blame] | 163 | return true; |
| 164 | } |
| 165 | |
Corentin Wallez | 49a65d0 | 2018-07-24 16:45:45 +0200 | [diff] [blame] | 166 | } // namespace dawn_native |