Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(84)

Side by Side Diff: gpu/command_buffer/common/id_allocator.cc

Issue 684093008: command_buffer: Implement IdAllocator by recording ranges in an AVL tree Base URL: https://chromium.googlesource.com/chromium/src.git@new-05-path-fragment-input-gen
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // This file contains the implementation of IdAllocator. 5 // This file contains the implementation of IdAllocator.
6 6
7 #include "gpu/command_buffer/common/id_allocator.h" 7 #include "gpu/command_buffer/common/id_allocator.h"
8 8
9 #include <limits>
10
9 #include "base/logging.h" 11 #include "base/logging.h"
10 12
11 namespace gpu { 13 namespace gpu {
12 14
13 IdAllocator::IdAllocator() {} 15 // IdAllocator stores the list of allocated ids in an AVL tree. The
14 16 // tree node |IdAllocator::Node| represents maximal closed interval
15 IdAllocator::~IdAllocator() {} 17 // (min, max) of allocated ids. The tree is ordered by the range
18 // minimum value.
19
20 struct IdAllocator::Node {
21 ResourceId min;
22 ResourceId max;
23 Node* left;
24 Node* right;
25 int height;
26
27 Node(ResourceId min, ResourceId max)
28 : min(min), max(max), left(NULL), right(NULL), height(0) {}
29 };
30
31 IdAllocator::IdAllocator() : root_(NULL) {
32 }
33
34 IdAllocator::~IdAllocator() {
35 if (root_ != NULL) {
36 Destroy(root_);
37 }
38 }
16 39
17 ResourceId IdAllocator::AllocateID() { 40 ResourceId IdAllocator::AllocateID() {
18 ResourceId id; 41 return AllocateIDRange(1);
19 ResourceIdSet::iterator iter = free_ids_.begin(); 42 }
20 if (iter != free_ids_.end()) { 43
21 id = *iter; 44 ResourceId IdAllocator::AllocateIDAtOrAbove(ResourceId desired_id) {
45 if (desired_id == 0) {
46 desired_id = 1;
47 }
48
49 if (root_ == NULL) {
50 root_ = Insert(desired_id, desired_id, root_);
51 return desired_id;
52 }
53
54 ResourceId result = 0;
55 root_ = AddRange(1,
56 &result,
57 desired_id,
58 std::numeric_limits<ResourceId>::max(),
59 root_,
60 NULL,
61 NULL);
62
63 if (result == 0) {
64 root_ = AddRange(1,
65 &result,
66 1,
67 std::numeric_limits<ResourceId>::max(),
68 root_,
69 NULL,
70 NULL);
71 }
72
73 return result;
74 }
75
76 ResourceId IdAllocator::AllocateIDRange(uint32_t range) {
77 if (root_ == NULL) {
78 root_ = Insert(1, range, NULL);
79 return 1;
80 }
81
82 ResourceId result = 0;
83 root_ = AddRange(range,
84 &result,
85 1,
86 std::numeric_limits<ResourceId>::max(),
87 root_,
88 NULL,
89 NULL);
90 return result;
91 }
92
93 bool IdAllocator::MarkAsUsed(ResourceId id) {
94 if (root_ == NULL) {
95 root_ = Insert(id, id, NULL);
96 return true;
97 }
98
99 ResourceId result = 0;
100 root_ = AddRange(1, &result, id, id, root_, NULL, NULL);
101 return result != 0;
102 }
103
104 void IdAllocator::FreeID(ResourceId id) {
105 FreeIDRange(id, 1);
106 }
107
108 void IdAllocator::FreeIDRange(ResourceId first_id, uint32_t range) {
109 if (root_ == NULL) {
110 return;
111 }
112
113 root_ = RemoveRange(first_id, first_id + range - 1u, root_);
114 DCHECK(root_ == NULL || root_->min > 0);
115 }
116
117 bool IdAllocator::InUse(ResourceId id) const {
118 if (id == 0) {
119 return false;
120 }
121
122 Node* pos = root_;
123 while (pos != NULL) {
124 if (id >= pos->min && id <= pos->max) {
125 return true;
126 }
127
128 if (id < pos->min) {
129 pos = pos->left;
130 } else {
131 pos = pos->right;
132 }
133 }
134
135 return false;
136 }
137
138 inline int IdAllocator::GetHeight(Node* node) {
139 return node ? node->height : 0;
140 }
141
142 inline void IdAllocator::UpdateHeight(Node* node) {
143 node->height = std::max(GetHeight(node->left), GetHeight(node->right)) + 1;
144 }
145
146 inline int IdAllocator::GetBalance(Node* node) {
147 return node ? GetHeight(node->left) - GetHeight(node->right) : 0;
148 }
149
150 inline IdAllocator::Node* IdAllocator::RotateLeft(Node* node) {
151 Node* new_root = node->right;
152 node->right = new_root->left;
153 new_root->left = node;
154 UpdateHeight(node);
155 UpdateHeight(new_root);
156 return new_root;
157 }
158
159 inline IdAllocator::Node* IdAllocator::RotateRight(Node* node) {
160 Node* new_root = node->left;
161 node->left = new_root->right;
162 new_root->right = node;
163 UpdateHeight(node);
164 UpdateHeight(new_root);
165 return new_root;
166 }
167
168 IdAllocator::Node* IdAllocator::Balance(Node* node) {
169 UpdateHeight(node);
170 int balance = GetBalance(node);
171 if (balance > 1) {
172 if (GetBalance(node->left) >= 0) {
173 return RotateRight(node);
174 }
175 node->left = RotateLeft(node->left);
176 return RotateRight(node);
177 }
178
179 if (balance < -1) {
180 if (GetBalance(node->right) <= 0) {
181 return RotateLeft(node);
182 }
183 node->right = RotateRight(node->right);
184 return RotateLeft(node);
185 }
186
187 return node;
188 }
189
190 // Finds the largest node of |max_subtree| and stores its
191 // contents to |to_replace|. Deletes the found node.
192 IdAllocator::Node* IdAllocator::ReplaceMax(Node* to_replace,
193 Node* max_subtree) {
194 if (max_subtree->right != NULL) {
195 max_subtree->right = ReplaceMax(to_replace, max_subtree->right);
196 return Balance(max_subtree);
197 }
198
199 to_replace->min = max_subtree->min;
200 to_replace->max = max_subtree->max;
201 Node* result = max_subtree->left;
202 delete max_subtree;
203 return result;
204 }
205
206 IdAllocator::Node* IdAllocator::Delete(Node* node) {
207 if (node->left != NULL && node->right != NULL) {
208 node->left = ReplaceMax(node, node->left);
209 return Balance(node);
210 }
211
212 Node* result = node->left != NULL ? node->left : node->right;
213 delete node;
214 return result;
215 }
216
217 IdAllocator::Node* IdAllocator::Insert(ResourceId min,
218 ResourceId max,
219 Node* pos) {
220 if (pos == NULL) {
221 return new Node(min, max);
222 }
223
224 if (min < pos->min) {
225 pos->left = Insert(min, max, pos->left);
22 } else { 226 } else {
23 id = LastUsedId() + 1; 227 pos->right = Insert(min, max, pos->right);
24 if (!id) { 228 }
25 // We wrapped around to 0. 229
26 id = FindFirstUnusedId(); 230 return Balance(pos);
27 } 231 }
28 } 232
29 MarkAsUsed(id); 233 // Allocates |range| amount of ids and stores the first id allocated
30 return id; 234 // in |first_id_out|. In case of failed allocation, |first_id_out|
31 } 235 // remains 0. The allocated id range will start at the smallest possible
32 236 // value between closed range [|first_id_min|, |first_id_max|].
33 ResourceId IdAllocator::AllocateIDAtOrAbove(ResourceId desired_id) { 237
34 ResourceId id; 238 // Works by recursing through the tree. When taking the left branch
35 ResourceIdSet::iterator iter = free_ids_.lower_bound(desired_id); 239 // (searching the smaller id space), |right_limit| is updated. When taking
36 if (iter != free_ids_.end()) { 240 // the right branch (searching the larger id space), |left_limit| is updated.
37 id = *iter; 241 // When the id space is found, uses |left_limit| or |right_limit| to identify
38 } else if (LastUsedId() < desired_id) { 242 // whether the new id range needs to be merged at the end of the limiter or the
39 id = desired_id; 243 // current position in the tree. Additional option the special case where passed
244 // in
245 // |first_id_min| causes a new range to be created.
246 IdAllocator::Node* IdAllocator::AddRange(uint32_t range,
247 ResourceId* first_id_out,
248 ResourceId first_id_min,
249 ResourceId first_id_max,
250 Node* node,
251 Node* left_limit,
252 Node* right_limit) {
253 DCHECK(*first_id_out == 0);
254
255 ResourceId new_range_max = first_id_min + range - 1u;
256 if (new_range_max < first_id_min) {
257 return node;
258 }
259
260 if (node->min > new_range_max) {
261 if (node->left != NULL) {
262 node->left = AddRange(range,
263 first_id_out,
264 first_id_min,
265 first_id_max,
266 node->left,
267 left_limit,
268 node);
269 if (*first_id_out != 0) {
270 return Balance(node);
271 }
272 } else {
273 *first_id_out = first_id_min;
274 bool merges_node = node->min - new_range_max == 1u;
275 bool merges_left_limit =
276 left_limit != NULL && (first_id_min - left_limit->max == 1u);
277 if (merges_node && merges_left_limit) {
278 left_limit->max = node->max;
279 return Delete(node);
280 } else if (merges_node) {
281 node->min = first_id_min;
282 return node;
283 } else if (merges_left_limit) {
284 left_limit->max = new_range_max;
285 return node;
286 }
287 return Insert(first_id_min, new_range_max, node);
288 }
289 }
290
291 if (first_id_min <= node->max) {
292 first_id_min = node->max + 1u;
293 if (first_id_min < node->max) {
294 return node;
295 }
296 }
297
298 if (first_id_min > first_id_max) {
299 return node;
300 }
301
302 if (node->right != NULL) {
303 node->right = AddRange(range,
304 first_id_out,
305 first_id_min,
306 first_id_max,
307 node->right,
308 node,
309 right_limit);
310 if (*first_id_out != 0) {
311 return Balance(node);
312 }
313 return node;
314 }
315
316 new_range_max = first_id_min + range - 1u;
317 if (new_range_max < first_id_min) {
318 return node;
319 }
320
321 if (right_limit != NULL) {
322 if (new_range_max < right_limit->min) {
323 *first_id_out = first_id_min;
324 if (right_limit->min - new_range_max == 1u) {
325 right_limit->min = node->min;
326 return Delete(node);
327 }
328 node->max = new_range_max;
329 return node;
330 }
40 } else { 331 } else {
41 id = LastUsedId() + 1; 332 *first_id_out = first_id_min;
42 if (!id) { 333 if (first_id_min - node->max == 1u) {
43 // We wrapped around to 0. 334 node->max = new_range_max;
44 id = FindFirstUnusedId(); 335 return node;
45 } 336 }
46 } 337 return Insert(first_id_min, new_range_max, node);
47 MarkAsUsed(id); 338 }
48 return id; 339
49 } 340 return node;
50 341 }
51 ResourceId IdAllocator::AllocateIDRange(uint32_t range) { 342
52 DCHECK(range > 0); 343 IdAllocator::Node* IdAllocator::RemoveRange(ResourceId first_id,
53 if (range == 1) { 344 ResourceId last_id,
54 return AllocateID(); 345 Node* pos) {
55 } 346 if (pos == NULL) {
56 347 return NULL;
57 ResourceId first_id = kInvalidResource; 348 }
58 349
59 { 350 if (first_id < pos->min) {
60 // Try to re-use ids from free_ids. 351 pos->left = RemoveRange(first_id, std::min(pos->min, last_id), pos->left);
61 ResourceIdSet::const_iterator iter = free_ids_.begin(); 352 }
62 ResourceIdSet::const_iterator end = free_ids_.end(); 353
63 const ResourceIdSet::size_type size = free_ids_.size(); 354 if (last_id > pos->max) {
64 if (iter != end && size >= range) { 355 pos->right = RemoveRange(std::max(pos->max, first_id), last_id, pos->right);
65 ResourceId first = *iter; 356 }
66 ResourceIdSet::size_type needed_size = range; 357
67 ResourceId prev = first; 358 if (first_id <= pos->min) {
68 ++iter; 359 if (last_id >= pos->min) {
69 for (; iter != end && size >= needed_size; ++iter) { 360 ResourceId old_max = pos->max;
70 if (prev + 1 == *iter) { 361 if (last_id < old_max) {
71 if (first + range - 1 == *iter) { 362 pos->min = last_id + 1u;
72 first_id = first; 363 pos->max = old_max;
73 break; 364 } else {
74 } 365 pos = Delete(pos);
75 } else {
76 needed_size += prev - first + 1;
77 first = *iter;
78 }
79 prev = *iter;
80 } 366 }
81 } 367 }
82 } 368 } else if (first_id <= pos->max) {
83 369 ResourceId old_max = pos->max;
84 if (first_id == kInvalidResource) { 370 pos->max = first_id - 1u;
85 // Allocate after the last id. 371 if (last_id < old_max) {
86 ResourceId first = LastUsedId() + 1; 372 pos = Insert(last_id + 1u, old_max, pos);
87 if (first != 0) { 373 }
88 ResourceId last = first + range - 1; 374 }
89 if (first < last) { 375
90 first_id = first; 376 if (pos == NULL) {
91 } 377 return NULL;
92 } 378 }
93 } 379
94 380 // The nodes |pos->left| and |pos->right| are both
95 if (first_id == kInvalidResource) { 381 // balanced. However, |pos| is potentially unbalanced. Since
96 // Scan the gaps between used ids. 382 // multiple operations might be done during update of |pos->left| and
97 ResourceId prev = 0; 383 // |pos->right|, we must balance |pos| multiple times.
98 for (auto id : used_ids_) { 384 while (true) {
99 if (id - prev > range) { 385 int balance = GetBalance(pos);
100 first_id = prev + 1; 386 if (balance >= -1 && balance <= 1) {
101 break; 387 break;
102 } 388 }
103 prev = id; 389 pos = Balance(pos);
104 } 390 }
105 } 391
106 392 return pos;
107 if (first_id != kInvalidResource) { 393 }
108 for (uint32 ii = 0; ii < range; ++ii) { 394
109 MarkAsUsed(first_id + ii); 395 void IdAllocator::Destroy(Node* pos) {
110 } 396 do {
111 } 397 if (pos->left != NULL) {
112 398 Destroy(pos->left);
113 return first_id; 399 }
114 } 400 Node* right = pos->right;
115 401 delete pos;
116 bool IdAllocator::MarkAsUsed(ResourceId id) { 402 pos = right;
117 DCHECK(id); 403 } while (pos != NULL);
118 free_ids_.erase(id);
119 std::pair<ResourceIdSet::iterator, bool> result = used_ids_.insert(id);
120 return result.second;
121 }
122
123 void IdAllocator::FreeID(ResourceId id) {
124 if (id) {
125 used_ids_.erase(id);
126 free_ids_.insert(id);
127 }
128 }
129
130 void IdAllocator::FreeIDRange(ResourceId first_id, uint32 range) {
131 for (uint32 ii = 0; ii < range; ++ii) {
132 FreeID(first_id + ii);
133 }
134 }
135
136 bool IdAllocator::InUse(ResourceId id) const {
137 return id == kInvalidResource || used_ids_.find(id) != used_ids_.end();
138 }
139
140 ResourceId IdAllocator::LastUsedId() const {
141 if (used_ids_.empty()) {
142 return 0u;
143 } else {
144 return *used_ids_.rbegin();
145 }
146 }
147
148 ResourceId IdAllocator::FindFirstUnusedId() const {
149 ResourceId id = 1;
150 for (ResourceIdSet::const_iterator it = used_ids_.begin();
151 it != used_ids_.end(); ++it) {
152 if ((*it) != id) {
153 return id;
154 }
155 ++id;
156 }
157 return id;
158 } 404 }
159 405
160 } // namespace gpu 406 } // namespace gpu
OLDNEW
« no previous file with comments | « gpu/command_buffer/common/id_allocator.h ('k') | gpu/command_buffer/common/id_allocator_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698