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"
11 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
17 // Round down to the largest multiple of kAllocAlignment no greater than |size|.
18 unsigned int RoundDown(unsigned int size
) {
19 return size
& ~(FencedAllocator::kAllocAlignment
- 1);
22 // Round up to the smallest multiple of kAllocAlignment no smaller than |size|.
23 unsigned int RoundUp(unsigned int size
) {
24 return (size
+ (FencedAllocator::kAllocAlignment
- 1)) &
25 ~(FencedAllocator::kAllocAlignment
- 1);
31 const FencedAllocator::Offset
FencedAllocator::kInvalidOffset
;
32 const unsigned int FencedAllocator::kAllocAlignment
;
35 FencedAllocator::FencedAllocator(unsigned int size
,
36 CommandBufferHelper
* helper
,
37 const base::Closure
& poll_callback
)
39 poll_callback_(poll_callback
),
41 Block block
= { FREE
, 0, RoundDown(size
), kUnusedToken
};
42 blocks_
.push_back(block
);
45 FencedAllocator::~FencedAllocator() {
46 // Free blocks pending tokens.
47 for (unsigned int i
= 0; i
< blocks_
.size(); ++i
) {
48 if (blocks_
[i
].state
== FREE_PENDING_TOKEN
) {
49 i
= WaitForTokenAndFreeBlock(i
);
53 DCHECK_EQ(blocks_
.size(), 1u);
54 DCHECK_EQ(blocks_
[0].state
, FREE
);
57 // Looks for a non-allocated block that is big enough. Search in the FREE
58 // blocks first (for direct usage), first-fit, then in the FREE_PENDING_TOKEN
59 // blocks, waiting for them. The current implementation isn't smart about
60 // optimizing what to wait for, just looks inside the block in order (first-fit
62 FencedAllocator::Offset
FencedAllocator::Alloc(unsigned int size
) {
63 // size of 0 is not allowed because it would be inconsistent to only sometimes
64 // have it succeed. Example: Alloc(SizeOfBuffer), Alloc(0).
66 return kInvalidOffset
;
69 // Round up the allocation size to ensure alignment.
72 // Try first to allocate in a free block.
73 for (unsigned int i
= 0; i
< blocks_
.size(); ++i
) {
74 Block
&block
= blocks_
[i
];
75 if (block
.state
== FREE
&& block
.size
>= size
) {
76 return AllocInBlock(i
, size
);
80 // No free block is available. Look for blocks pending tokens, and wait for
81 // them to be re-usable.
82 for (unsigned int i
= 0; i
< blocks_
.size(); ++i
) {
83 if (blocks_
[i
].state
!= FREE_PENDING_TOKEN
)
85 i
= WaitForTokenAndFreeBlock(i
);
86 if (blocks_
[i
].size
>= size
)
87 return AllocInBlock(i
, size
);
89 return kInvalidOffset
;
92 // Looks for the corresponding block, mark it FREE, and collapse it if
94 void FencedAllocator::Free(FencedAllocator::Offset offset
) {
95 BlockIndex index
= GetBlockByOffset(offset
);
96 DCHECK_NE(blocks_
[index
].state
, FREE
);
97 Block
&block
= blocks_
[index
];
99 if (block
.state
== IN_USE
)
100 bytes_in_use_
-= block
.size
;
103 CollapseFreeBlock(index
);
106 // Looks for the corresponding block, mark it FREE_PENDING_TOKEN.
107 void FencedAllocator::FreePendingToken(
108 FencedAllocator::Offset offset
, int32 token
) {
109 BlockIndex index
= GetBlockByOffset(offset
);
110 Block
&block
= blocks_
[index
];
111 if (block
.state
== IN_USE
)
112 bytes_in_use_
-= block
.size
;
113 block
.state
= FREE_PENDING_TOKEN
;
117 // Gets the max of the size of the blocks marked as free.
118 unsigned int FencedAllocator::GetLargestFreeSize() {
120 unsigned int max_size
= 0;
121 for (unsigned int i
= 0; i
< blocks_
.size(); ++i
) {
122 Block
&block
= blocks_
[i
];
123 if (block
.state
== FREE
)
124 max_size
= std::max(max_size
, block
.size
);
129 // Gets the size of the largest segment of blocks that are either FREE or
130 // FREE_PENDING_TOKEN.
131 unsigned int FencedAllocator::GetLargestFreeOrPendingSize() {
132 unsigned int max_size
= 0;
133 unsigned int current_size
= 0;
134 for (unsigned int i
= 0; i
< blocks_
.size(); ++i
) {
135 Block
&block
= blocks_
[i
];
136 if (block
.state
== IN_USE
) {
137 max_size
= std::max(max_size
, current_size
);
140 DCHECK(block
.state
== FREE
|| block
.state
== FREE_PENDING_TOKEN
);
141 current_size
+= block
.size
;
144 return std::max(max_size
, current_size
);
148 // - there is at least one block.
149 // - there are no contiguous FREE blocks (they should have been collapsed).
150 // - the successive offsets match the block sizes, and they are in order.
151 bool FencedAllocator::CheckConsistency() {
152 if (blocks_
.size() < 1) return false;
153 for (unsigned int i
= 0; i
< blocks_
.size() - 1; ++i
) {
154 Block
¤t
= blocks_
[i
];
155 Block
&next
= blocks_
[i
+ 1];
156 // This test is NOT included in the next one, because offset is unsigned.
157 if (next
.offset
<= current
.offset
)
159 if (next
.offset
!= current
.offset
+ current
.size
)
161 if (current
.state
== FREE
&& next
.state
== FREE
)
167 // Returns false if all blocks are actually FREE, in which
168 // case they would be coalesced into one block, true otherwise.
169 bool FencedAllocator::InUse() {
170 return blocks_
.size() != 1 || blocks_
[0].state
!= FREE
;
173 // Collapse the block to the next one, then to the previous one. Provided the
174 // structure is consistent, those are the only blocks eligible for collapse.
175 FencedAllocator::BlockIndex
FencedAllocator::CollapseFreeBlock(
177 if (index
+ 1 < blocks_
.size()) {
178 Block
&next
= blocks_
[index
+ 1];
179 if (next
.state
== FREE
) {
180 blocks_
[index
].size
+= next
.size
;
181 blocks_
.erase(blocks_
.begin() + index
+ 1);
185 Block
&prev
= blocks_
[index
- 1];
186 if (prev
.state
== FREE
) {
187 prev
.size
+= blocks_
[index
].size
;
188 blocks_
.erase(blocks_
.begin() + index
);
195 // Waits for the block's token, then mark the block as free, then collapse it.
196 FencedAllocator::BlockIndex
FencedAllocator::WaitForTokenAndFreeBlock(
198 Block
&block
= blocks_
[index
];
199 DCHECK_EQ(block
.state
, FREE_PENDING_TOKEN
);
200 helper_
->WaitForToken(block
.token
);
202 return CollapseFreeBlock(index
);
205 // Frees any blocks pending a token for which the token has been read.
206 void FencedAllocator::FreeUnused() {
207 // Free any potential blocks that has its lifetime handled outside.
208 poll_callback_
.Run();
210 for (unsigned int i
= 0; i
< blocks_
.size();) {
211 Block
& block
= blocks_
[i
];
212 if (block
.state
== FREE_PENDING_TOKEN
&&
213 helper_
->HasTokenPassed(block
.token
)) {
215 i
= CollapseFreeBlock(i
);
222 // If the block is exactly the requested size, simply mark it IN_USE, otherwise
223 // split it and mark the first one (of the requested size) IN_USE.
224 FencedAllocator::Offset
FencedAllocator::AllocInBlock(BlockIndex index
,
226 Block
&block
= blocks_
[index
];
227 DCHECK_GE(block
.size
, size
);
228 DCHECK_EQ(block
.state
, FREE
);
229 Offset offset
= block
.offset
;
230 bytes_in_use_
+= size
;
231 if (block
.size
== size
) {
232 block
.state
= IN_USE
;
235 Block newblock
= { FREE
, offset
+ size
, block
.size
- size
, kUnusedToken
};
236 block
.state
= IN_USE
;
238 // this is the last thing being done because it may invalidate block;
239 blocks_
.insert(blocks_
.begin() + index
+ 1, newblock
);
243 // The blocks are in offset order, so we can do a binary search.
244 FencedAllocator::BlockIndex
FencedAllocator::GetBlockByOffset(Offset offset
) {
245 Block templ
= { IN_USE
, offset
, 0, kUnusedToken
};
246 Container::iterator it
= std::lower_bound(blocks_
.begin(), blocks_
.end(),
248 DCHECK(it
!= blocks_
.end() && it
->offset
== offset
);
249 return it
-blocks_
.begin();