Handle account removal correctly on all platforms.
[chromium-blink-merge.git] / gpu / command_buffer / client / fenced_allocator.cc
blob80038571be9fccf4e5274101e0959c33aa2586c9
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // This file contains the implementation of the FencedAllocator class.
7 #include "gpu/command_buffer/client/fenced_allocator.h"
9 #include <algorithm>
11 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
13 namespace gpu {
15 namespace {
17 // Allocation alignment, must be a power of two.
18 const unsigned int kAllocAlignment = 16;
20 // Round down to the largest multiple of kAllocAlignment no greater than |size|.
21 unsigned int RoundDown(unsigned int size) {
22 return size & ~(kAllocAlignment - 1);
25 // Round up to the smallest multiple of kAllocAlignment no smaller than |size|.
26 unsigned int RoundUp(unsigned int size) {
27 return (size + (kAllocAlignment - 1)) & ~(kAllocAlignment - 1);
30 } // namespace
32 #ifndef _MSC_VER
33 const FencedAllocator::Offset FencedAllocator::kInvalidOffset;
34 #endif
36 FencedAllocator::FencedAllocator(unsigned int size,
37 CommandBufferHelper* helper,
38 const base::Closure& poll_callback)
39 : helper_(helper),
40 poll_callback_(poll_callback),
41 bytes_in_use_(0) {
42 Block block = { FREE, 0, RoundDown(size), kUnusedToken };
43 blocks_.push_back(block);
46 FencedAllocator::~FencedAllocator() {
47 // Free blocks pending tokens.
48 for (unsigned int i = 0; i < blocks_.size(); ++i) {
49 if (blocks_[i].state == FREE_PENDING_TOKEN) {
50 i = WaitForTokenAndFreeBlock(i);
54 DCHECK_EQ(blocks_.size(), 1u);
55 DCHECK_EQ(blocks_[0].state, FREE);
58 // Looks for a non-allocated block that is big enough. Search in the FREE
59 // blocks first (for direct usage), first-fit, then in the FREE_PENDING_TOKEN
60 // blocks, waiting for them. The current implementation isn't smart about
61 // optimizing what to wait for, just looks inside the block in order (first-fit
62 // as well).
63 FencedAllocator::Offset FencedAllocator::Alloc(unsigned int size) {
64 // size of 0 is not allowed because it would be inconsistent to only sometimes
65 // have it succeed. Example: Alloc(SizeOfBuffer), Alloc(0).
66 if (size == 0) {
67 return kInvalidOffset;
70 // Round up the allocation size to ensure alignment.
71 size = RoundUp(size);
73 // Try first to allocate in a free block.
74 for (unsigned int i = 0; i < blocks_.size(); ++i) {
75 Block &block = blocks_[i];
76 if (block.state == FREE && block.size >= size) {
77 return AllocInBlock(i, size);
81 // No free block is available. Look for blocks pending tokens, and wait for
82 // them to be re-usable.
83 for (unsigned int i = 0; i < blocks_.size(); ++i) {
84 if (blocks_[i].state != FREE_PENDING_TOKEN)
85 continue;
86 i = WaitForTokenAndFreeBlock(i);
87 if (blocks_[i].size >= size)
88 return AllocInBlock(i, size);
90 return kInvalidOffset;
93 // Looks for the corresponding block, mark it FREE, and collapse it if
94 // necessary.
95 void FencedAllocator::Free(FencedAllocator::Offset offset) {
96 BlockIndex index = GetBlockByOffset(offset);
97 DCHECK_NE(blocks_[index].state, FREE);
98 Block &block = blocks_[index];
100 if (block.state == IN_USE)
101 bytes_in_use_ -= block.size;
103 block.state = FREE;
104 CollapseFreeBlock(index);
107 // Looks for the corresponding block, mark it FREE_PENDING_TOKEN.
108 void FencedAllocator::FreePendingToken(
109 FencedAllocator::Offset offset, int32 token) {
110 BlockIndex index = GetBlockByOffset(offset);
111 Block &block = blocks_[index];
112 if (block.state == IN_USE)
113 bytes_in_use_ -= block.size;
114 block.state = FREE_PENDING_TOKEN;
115 block.token = token;
118 // Gets the max of the size of the blocks marked as free.
119 unsigned int FencedAllocator::GetLargestFreeSize() {
120 FreeUnused();
121 unsigned int max_size = 0;
122 for (unsigned int i = 0; i < blocks_.size(); ++i) {
123 Block &block = blocks_[i];
124 if (block.state == FREE)
125 max_size = std::max(max_size, block.size);
127 return max_size;
130 // Gets the size of the largest segment of blocks that are either FREE or
131 // FREE_PENDING_TOKEN.
132 unsigned int FencedAllocator::GetLargestFreeOrPendingSize() {
133 unsigned int max_size = 0;
134 unsigned int current_size = 0;
135 for (unsigned int i = 0; i < blocks_.size(); ++i) {
136 Block &block = blocks_[i];
137 if (block.state == IN_USE) {
138 max_size = std::max(max_size, current_size);
139 current_size = 0;
140 } else {
141 DCHECK(block.state == FREE || block.state == FREE_PENDING_TOKEN);
142 current_size += block.size;
145 return std::max(max_size, current_size);
148 // Makes sure that:
149 // - there is at least one block.
150 // - there are no contiguous FREE blocks (they should have been collapsed).
151 // - the successive offsets match the block sizes, and they are in order.
152 bool FencedAllocator::CheckConsistency() {
153 if (blocks_.size() < 1) return false;
154 for (unsigned int i = 0; i < blocks_.size() - 1; ++i) {
155 Block &current = blocks_[i];
156 Block &next = blocks_[i + 1];
157 // This test is NOT included in the next one, because offset is unsigned.
158 if (next.offset <= current.offset)
159 return false;
160 if (next.offset != current.offset + current.size)
161 return false;
162 if (current.state == FREE && next.state == FREE)
163 return false;
165 return true;
168 // Returns false if all blocks are actually FREE, in which
169 // case they would be coalesced into one block, true otherwise.
170 bool FencedAllocator::InUse() {
171 return blocks_.size() != 1 || blocks_[0].state != FREE;
174 // Collapse the block to the next one, then to the previous one. Provided the
175 // structure is consistent, those are the only blocks eligible for collapse.
176 FencedAllocator::BlockIndex FencedAllocator::CollapseFreeBlock(
177 BlockIndex index) {
178 if (index + 1 < blocks_.size()) {
179 Block &next = blocks_[index + 1];
180 if (next.state == FREE) {
181 blocks_[index].size += next.size;
182 blocks_.erase(blocks_.begin() + index + 1);
185 if (index > 0) {
186 Block &prev = blocks_[index - 1];
187 if (prev.state == FREE) {
188 prev.size += blocks_[index].size;
189 blocks_.erase(blocks_.begin() + index);
190 --index;
193 return index;
196 // Waits for the block's token, then mark the block as free, then collapse it.
197 FencedAllocator::BlockIndex FencedAllocator::WaitForTokenAndFreeBlock(
198 BlockIndex index) {
199 Block &block = blocks_[index];
200 DCHECK_EQ(block.state, FREE_PENDING_TOKEN);
201 helper_->WaitForToken(block.token);
202 block.state = FREE;
203 return CollapseFreeBlock(index);
206 // Frees any blocks pending a token for which the token has been read.
207 void FencedAllocator::FreeUnused() {
208 // Free any potential blocks that has its lifetime handled outside.
209 poll_callback_.Run();
211 for (unsigned int i = 0; i < blocks_.size();) {
212 Block& block = blocks_[i];
213 if (block.state == FREE_PENDING_TOKEN &&
214 helper_->HasTokenPassed(block.token)) {
215 block.state = FREE;
216 i = CollapseFreeBlock(i);
217 } else {
218 ++i;
223 // If the block is exactly the requested size, simply mark it IN_USE, otherwise
224 // split it and mark the first one (of the requested size) IN_USE.
225 FencedAllocator::Offset FencedAllocator::AllocInBlock(BlockIndex index,
226 unsigned int size) {
227 Block &block = blocks_[index];
228 DCHECK_GE(block.size, size);
229 DCHECK_EQ(block.state, FREE);
230 Offset offset = block.offset;
231 bytes_in_use_ += size;
232 if (block.size == size) {
233 block.state = IN_USE;
234 return offset;
236 Block newblock = { FREE, offset + size, block.size - size, kUnusedToken};
237 block.state = IN_USE;
238 block.size = size;
239 // this is the last thing being done because it may invalidate block;
240 blocks_.insert(blocks_.begin() + index + 1, newblock);
241 return offset;
244 // The blocks are in offset order, so we can do a binary search.
245 FencedAllocator::BlockIndex FencedAllocator::GetBlockByOffset(Offset offset) {
246 Block templ = { IN_USE, offset, 0, kUnusedToken };
247 Container::iterator it = std::lower_bound(blocks_.begin(), blocks_.end(),
248 templ, OffsetCmp());
249 DCHECK(it != blocks_.end() && it->offset == offset);
250 return it-blocks_.begin();
253 } // namespace gpu