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

Unified 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, 2 months 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gpu/command_buffer/common/id_allocator.cc
diff --git a/gpu/command_buffer/common/id_allocator.cc b/gpu/command_buffer/common/id_allocator.cc
index 61d2c4a91135381921171a4d1fd0f418fbf77d06..29ec66ae2d92443a8832efd17d794283a1825b23 100644
--- a/gpu/command_buffer/common/id_allocator.cc
+++ b/gpu/command_buffer/common/id_allocator.cc
@@ -6,155 +6,401 @@
#include "gpu/command_buffer/common/id_allocator.h"
+#include <limits>
+
#include "base/logging.h"
namespace gpu {
-IdAllocator::IdAllocator() {}
+// IdAllocator stores the list of allocated ids in an AVL tree. The
+// tree node |IdAllocator::Node| represents maximal closed interval
+// (min, max) of allocated ids. The tree is ordered by the range
+// minimum value.
-IdAllocator::~IdAllocator() {}
+struct IdAllocator::Node {
+ ResourceId min;
+ ResourceId max;
+ Node* left;
+ Node* right;
+ int height;
-ResourceId IdAllocator::AllocateID() {
- ResourceId id;
- ResourceIdSet::iterator iter = free_ids_.begin();
- if (iter != free_ids_.end()) {
- id = *iter;
- } else {
- id = LastUsedId() + 1;
- if (!id) {
- // We wrapped around to 0.
- id = FindFirstUnusedId();
- }
+ Node(ResourceId min, ResourceId max)
+ : min(min), max(max), left(NULL), right(NULL), height(0) {}
+};
+
+IdAllocator::IdAllocator() : root_(NULL) {
+}
+
+IdAllocator::~IdAllocator() {
+ if (root_ != NULL) {
+ Destroy(root_);
}
- MarkAsUsed(id);
- return id;
+}
+
+ResourceId IdAllocator::AllocateID() {
+ return AllocateIDRange(1);
}
ResourceId IdAllocator::AllocateIDAtOrAbove(ResourceId desired_id) {
- ResourceId id;
- ResourceIdSet::iterator iter = free_ids_.lower_bound(desired_id);
- if (iter != free_ids_.end()) {
- id = *iter;
- } else if (LastUsedId() < desired_id) {
- id = desired_id;
- } else {
- id = LastUsedId() + 1;
- if (!id) {
- // We wrapped around to 0.
- id = FindFirstUnusedId();
- }
+ if (desired_id == 0) {
+ desired_id = 1;
+ }
+
+ if (root_ == NULL) {
+ root_ = Insert(desired_id, desired_id, root_);
+ return desired_id;
+ }
+
+ ResourceId result = 0;
+ root_ = AddRange(1,
+ &result,
+ desired_id,
+ std::numeric_limits<ResourceId>::max(),
+ root_,
+ NULL,
+ NULL);
+
+ if (result == 0) {
+ root_ = AddRange(1,
+ &result,
+ 1,
+ std::numeric_limits<ResourceId>::max(),
+ root_,
+ NULL,
+ NULL);
}
- MarkAsUsed(id);
- return id;
+
+ return result;
}
ResourceId IdAllocator::AllocateIDRange(uint32_t range) {
- DCHECK(range > 0);
- if (range == 1) {
- return AllocateID();
- }
-
- ResourceId first_id = kInvalidResource;
-
- {
- // Try to re-use ids from free_ids.
- ResourceIdSet::const_iterator iter = free_ids_.begin();
- ResourceIdSet::const_iterator end = free_ids_.end();
- const ResourceIdSet::size_type size = free_ids_.size();
- if (iter != end && size >= range) {
- ResourceId first = *iter;
- ResourceIdSet::size_type needed_size = range;
- ResourceId prev = first;
- ++iter;
- for (; iter != end && size >= needed_size; ++iter) {
- if (prev + 1 == *iter) {
- if (first + range - 1 == *iter) {
- first_id = first;
- break;
- }
- } else {
- needed_size += prev - first + 1;
- first = *iter;
- }
- prev = *iter;
- }
- }
+ if (root_ == NULL) {
+ root_ = Insert(1, range, NULL);
+ return 1;
}
- if (first_id == kInvalidResource) {
- // Allocate after the last id.
- ResourceId first = LastUsedId() + 1;
- if (first != 0) {
- ResourceId last = first + range - 1;
- if (first < last) {
- first_id = first;
- }
+ ResourceId result = 0;
+ root_ = AddRange(range,
+ &result,
+ 1,
+ std::numeric_limits<ResourceId>::max(),
+ root_,
+ NULL,
+ NULL);
+ return result;
+}
+
+bool IdAllocator::MarkAsUsed(ResourceId id) {
+ if (root_ == NULL) {
+ root_ = Insert(id, id, NULL);
+ return true;
+ }
+
+ ResourceId result = 0;
+ root_ = AddRange(1, &result, id, id, root_, NULL, NULL);
+ return result != 0;
+}
+
+void IdAllocator::FreeID(ResourceId id) {
+ FreeIDRange(id, 1);
+}
+
+void IdAllocator::FreeIDRange(ResourceId first_id, uint32_t range) {
+ if (root_ == NULL) {
+ return;
+ }
+
+ root_ = RemoveRange(first_id, first_id + range - 1u, root_);
+ DCHECK(root_ == NULL || root_->min > 0);
+}
+
+bool IdAllocator::InUse(ResourceId id) const {
+ if (id == 0) {
+ return false;
+ }
+
+ Node* pos = root_;
+ while (pos != NULL) {
+ if (id >= pos->min && id <= pos->max) {
+ return true;
+ }
+
+ if (id < pos->min) {
+ pos = pos->left;
+ } else {
+ pos = pos->right;
}
}
- if (first_id == kInvalidResource) {
- // Scan the gaps between used ids.
- ResourceId prev = 0;
- for (auto id : used_ids_) {
- if (id - prev > range) {
- first_id = prev + 1;
- break;
- }
- prev = id;
+ return false;
+}
+
+inline int IdAllocator::GetHeight(Node* node) {
+ return node ? node->height : 0;
+}
+
+inline void IdAllocator::UpdateHeight(Node* node) {
+ node->height = std::max(GetHeight(node->left), GetHeight(node->right)) + 1;
+}
+
+inline int IdAllocator::GetBalance(Node* node) {
+ return node ? GetHeight(node->left) - GetHeight(node->right) : 0;
+}
+
+inline IdAllocator::Node* IdAllocator::RotateLeft(Node* node) {
+ Node* new_root = node->right;
+ node->right = new_root->left;
+ new_root->left = node;
+ UpdateHeight(node);
+ UpdateHeight(new_root);
+ return new_root;
+}
+
+inline IdAllocator::Node* IdAllocator::RotateRight(Node* node) {
+ Node* new_root = node->left;
+ node->left = new_root->right;
+ new_root->right = node;
+ UpdateHeight(node);
+ UpdateHeight(new_root);
+ return new_root;
+}
+
+IdAllocator::Node* IdAllocator::Balance(Node* node) {
+ UpdateHeight(node);
+ int balance = GetBalance(node);
+ if (balance > 1) {
+ if (GetBalance(node->left) >= 0) {
+ return RotateRight(node);
}
+ node->left = RotateLeft(node->left);
+ return RotateRight(node);
}
- if (first_id != kInvalidResource) {
- for (uint32 ii = 0; ii < range; ++ii) {
- MarkAsUsed(first_id + ii);
+ if (balance < -1) {
+ if (GetBalance(node->right) <= 0) {
+ return RotateLeft(node);
}
+ node->right = RotateRight(node->right);
+ return RotateLeft(node);
}
- return first_id;
+ return node;
}
-bool IdAllocator::MarkAsUsed(ResourceId id) {
- DCHECK(id);
- free_ids_.erase(id);
- std::pair<ResourceIdSet::iterator, bool> result = used_ids_.insert(id);
- return result.second;
+// Finds the largest node of |max_subtree| and stores its
+// contents to |to_replace|. Deletes the found node.
+IdAllocator::Node* IdAllocator::ReplaceMax(Node* to_replace,
+ Node* max_subtree) {
+ if (max_subtree->right != NULL) {
+ max_subtree->right = ReplaceMax(to_replace, max_subtree->right);
+ return Balance(max_subtree);
+ }
+
+ to_replace->min = max_subtree->min;
+ to_replace->max = max_subtree->max;
+ Node* result = max_subtree->left;
+ delete max_subtree;
+ return result;
}
-void IdAllocator::FreeID(ResourceId id) {
- if (id) {
- used_ids_.erase(id);
- free_ids_.insert(id);
+IdAllocator::Node* IdAllocator::Delete(Node* node) {
+ if (node->left != NULL && node->right != NULL) {
+ node->left = ReplaceMax(node, node->left);
+ return Balance(node);
}
+
+ Node* result = node->left != NULL ? node->left : node->right;
+ delete node;
+ return result;
}
-void IdAllocator::FreeIDRange(ResourceId first_id, uint32 range) {
- for (uint32 ii = 0; ii < range; ++ii) {
- FreeID(first_id + ii);
+IdAllocator::Node* IdAllocator::Insert(ResourceId min,
+ ResourceId max,
+ Node* pos) {
+ if (pos == NULL) {
+ return new Node(min, max);
}
-}
-bool IdAllocator::InUse(ResourceId id) const {
- return id == kInvalidResource || used_ids_.find(id) != used_ids_.end();
+ if (min < pos->min) {
+ pos->left = Insert(min, max, pos->left);
+ } else {
+ pos->right = Insert(min, max, pos->right);
+ }
+
+ return Balance(pos);
}
-ResourceId IdAllocator::LastUsedId() const {
- if (used_ids_.empty()) {
- return 0u;
+// Allocates |range| amount of ids and stores the first id allocated
+// in |first_id_out|. In case of failed allocation, |first_id_out|
+// remains 0. The allocated id range will start at the smallest possible
+// value between closed range [|first_id_min|, |first_id_max|].
+
+// Works by recursing through the tree. When taking the left branch
+// (searching the smaller id space), |right_limit| is updated. When taking
+// the right branch (searching the larger id space), |left_limit| is updated.
+// When the id space is found, uses |left_limit| or |right_limit| to identify
+// whether the new id range needs to be merged at the end of the limiter or the
+// current position in the tree. Additional option the special case where passed
+// in
+// |first_id_min| causes a new range to be created.
+IdAllocator::Node* IdAllocator::AddRange(uint32_t range,
+ ResourceId* first_id_out,
+ ResourceId first_id_min,
+ ResourceId first_id_max,
+ Node* node,
+ Node* left_limit,
+ Node* right_limit) {
+ DCHECK(*first_id_out == 0);
+
+ ResourceId new_range_max = first_id_min + range - 1u;
+ if (new_range_max < first_id_min) {
+ return node;
+ }
+
+ if (node->min > new_range_max) {
+ if (node->left != NULL) {
+ node->left = AddRange(range,
+ first_id_out,
+ first_id_min,
+ first_id_max,
+ node->left,
+ left_limit,
+ node);
+ if (*first_id_out != 0) {
+ return Balance(node);
+ }
+ } else {
+ *first_id_out = first_id_min;
+ bool merges_node = node->min - new_range_max == 1u;
+ bool merges_left_limit =
+ left_limit != NULL && (first_id_min - left_limit->max == 1u);
+ if (merges_node && merges_left_limit) {
+ left_limit->max = node->max;
+ return Delete(node);
+ } else if (merges_node) {
+ node->min = first_id_min;
+ return node;
+ } else if (merges_left_limit) {
+ left_limit->max = new_range_max;
+ return node;
+ }
+ return Insert(first_id_min, new_range_max, node);
+ }
+ }
+
+ if (first_id_min <= node->max) {
+ first_id_min = node->max + 1u;
+ if (first_id_min < node->max) {
+ return node;
+ }
+ }
+
+ if (first_id_min > first_id_max) {
+ return node;
+ }
+
+ if (node->right != NULL) {
+ node->right = AddRange(range,
+ first_id_out,
+ first_id_min,
+ first_id_max,
+ node->right,
+ node,
+ right_limit);
+ if (*first_id_out != 0) {
+ return Balance(node);
+ }
+ return node;
+ }
+
+ new_range_max = first_id_min + range - 1u;
+ if (new_range_max < first_id_min) {
+ return node;
+ }
+
+ if (right_limit != NULL) {
+ if (new_range_max < right_limit->min) {
+ *first_id_out = first_id_min;
+ if (right_limit->min - new_range_max == 1u) {
+ right_limit->min = node->min;
+ return Delete(node);
+ }
+ node->max = new_range_max;
+ return node;
+ }
} else {
- return *used_ids_.rbegin();
+ *first_id_out = first_id_min;
+ if (first_id_min - node->max == 1u) {
+ node->max = new_range_max;
+ return node;
+ }
+ return Insert(first_id_min, new_range_max, node);
}
+
+ return node;
}
-ResourceId IdAllocator::FindFirstUnusedId() const {
- ResourceId id = 1;
- for (ResourceIdSet::const_iterator it = used_ids_.begin();
- it != used_ids_.end(); ++it) {
- if ((*it) != id) {
- return id;
+IdAllocator::Node* IdAllocator::RemoveRange(ResourceId first_id,
+ ResourceId last_id,
+ Node* pos) {
+ if (pos == NULL) {
+ return NULL;
+ }
+
+ if (first_id < pos->min) {
+ pos->left = RemoveRange(first_id, std::min(pos->min, last_id), pos->left);
+ }
+
+ if (last_id > pos->max) {
+ pos->right = RemoveRange(std::max(pos->max, first_id), last_id, pos->right);
+ }
+
+ if (first_id <= pos->min) {
+ if (last_id >= pos->min) {
+ ResourceId old_max = pos->max;
+ if (last_id < old_max) {
+ pos->min = last_id + 1u;
+ pos->max = old_max;
+ } else {
+ pos = Delete(pos);
+ }
+ }
+ } else if (first_id <= pos->max) {
+ ResourceId old_max = pos->max;
+ pos->max = first_id - 1u;
+ if (last_id < old_max) {
+ pos = Insert(last_id + 1u, old_max, pos);
+ }
+ }
+
+ if (pos == NULL) {
+ return NULL;
+ }
+
+ // The nodes |pos->left| and |pos->right| are both
+ // balanced. However, |pos| is potentially unbalanced. Since
+ // multiple operations might be done during update of |pos->left| and
+ // |pos->right|, we must balance |pos| multiple times.
+ while (true) {
+ int balance = GetBalance(pos);
+ if (balance >= -1 && balance <= 1) {
+ break;
}
- ++id;
+ pos = Balance(pos);
}
- return id;
+
+ return pos;
+}
+
+void IdAllocator::Destroy(Node* pos) {
+ do {
+ if (pos->left != NULL) {
+ Destroy(pos->left);
+ }
+ Node* right = pos->right;
+ delete pos;
+ pos = right;
+ } while (pos != NULL);
}
} // namespace gpu
« 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