1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
|
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CC_BASE_RTREE_H_
#define CC_BASE_RTREE_H_
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <cmath>
#include <map>
#include <optional>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/numerics/clamped_math.h"
#include "ui/gfx/geometry/rect.h"
namespace cc {
// The following description and most of the implementation is borrowed from
// Skia's SkRTree implementation.
//
// An R-Tree implementation. In short, it is a balanced n-ary tree containing a
// hierarchy of bounding rectangles.
//
// It only supports bulk-loading, i.e. creation from a batch of bounding
// rectangles. This performs a bottom-up bulk load using the STR
// (sort-tile-recursive) algorithm.
//
// Things to do: Experiment with other bulk-load algorithms (in particular the
// Hilbert pack variant, which groups rects by position on the Hilbert curve, is
// probably worth a look). There also exist top-down bulk load variants
// (VAMSplit, TopDownGreedy, etc).
//
// For more details see:
//
// Beckmann, N.; Kriegel, H. P.; Schneider, R.; Seeger, B. (1990).
// "The R*-tree: an efficient and robust access method for points and
// rectangles"
template <typename T>
class RTree {
public:
RTree();
RTree(const RTree&) = delete;
~RTree();
RTree& operator=(const RTree&) = delete;
// Constructs the rtree from a given container of gfx::Rects. Queries using
// Search will then return indices into this container.
template <typename Container>
void Build(const Container& items);
// Build helper that takes a functions to provide rects and payloads.
// `bounds_getter(i)` should return the gfx::Rect representing the bounds of
// the ith item, and `payload_getter(i)` should return the payload (aka T) of
// the ith item.
template <typename BoundsFunctor, typename PayloadFunctor>
void Build(size_t item_count,
const BoundsFunctor& bounds_getter,
const PayloadFunctor& payload_getter);
// If false, this rtree does not have valid bounds and:
// - Search* will have degraded performance.
bool has_valid_bounds() const { return has_valid_bounds_; }
// Given a query rect, for each element that intersects the rect,
// result_handler is called with the payload and the rect of the element,
// in the order they appeared in the initial container.
template <typename ResultFunctor>
void Search(const gfx::Rect& query,
const ResultFunctor& result_handler) const;
// Given a query rect, returns elements that intersect the rect. Elements are
// returned in the order they appeared in the initial container.
void Search(const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects = nullptr) const;
// Given a query rect, returns non-owning pointers to elements that intersect
// the rect. Elements are returned in the order they appeared in the initial
// container.
void SearchRefs(const gfx::Rect& query, std::vector<const T*>* results) const;
// Returns the total bounds of all items in this rtree.
std::optional<gfx::Rect> bounds() const;
// Returns respective bounds of all items in this rtree in the order of items.
// Production code except tracing should not use this method.
std::map<T, gfx::Rect> GetAllBoundsForTracing() const;
private:
// These values were empirically determined to produce reasonable performance
// in most cases.
static constexpr size_t kMinChildren = 6;
static constexpr size_t kMaxChildren = 11;
template <typename U>
struct Node;
template <typename U>
struct Branch {
// When the node level is 0, then the node is a leaf and the branch has a
// valid index pointing to an element in the vector that was used to build
// this rtree. When the level is not 0, it's an internal node and it has a
// valid subtree pointer.
// RAW_PTR_EXCLUSION: Performance reasons (based on analysis of
// speedometer3).
RAW_PTR_EXCLUSION Node<U>* subtree = nullptr;
U payload;
gfx::Rect bounds;
Branch() = default;
Branch(U payload, const gfx::Rect& bounds)
: payload(std::move(payload)), bounds(bounds) {}
};
template <typename U>
struct Node {
uint16_t num_children = 0;
uint16_t level = 0;
Branch<U> children[kMaxChildren];
explicit Node(uint16_t level) : level(level) {}
};
template <typename ResultFunctor>
void SearchRecursive(const Node<T>& node,
const gfx::Rect& query,
const ResultFunctor& result_handler) const;
// The following two functions are slow fallback versions of SearchRecursive
// and SearchRefsRecursive for when !has_valid_bounds().
template <typename ResultFunctor>
void SearchRecursiveFallback(const Node<T>& node,
const gfx::Rect& query,
const ResultFunctor& result_handler) const;
// Consumes the input array.
Branch<T> BuildRecursive(std::vector<Branch<T>>* branches, uint16_t level);
Node<T>* AllocateNodeAtLevel(uint16_t level);
void GetAllBoundsRecursive(const Node<T>& node,
std::map<T, gfx::Rect>* results) const;
// This is the count of data elements (rather than total nodes in the tree)
size_t num_data_elements_ = 0u;
std::vector<Node<T>> nodes_;
Branch<T> root_;
// If false, the rtree encountered overflow does not have reliable bounds.
bool has_valid_bounds_ = true;
};
template <typename T>
RTree<T>::RTree() = default;
template <typename T>
RTree<T>::~RTree() = default;
template <typename T>
template <typename Container>
void RTree<T>::Build(const Container& items) {
Build(
items.size(), [&items](size_t index) { return items[index]; },
[](size_t index) { return index; });
}
template <typename T>
template <typename BoundsFunctor, typename PayloadFunctor>
void RTree<T>::Build(size_t item_count,
const BoundsFunctor& bounds_getter,
const PayloadFunctor& payload_getter) {
DCHECK_EQ(0u, num_data_elements_);
std::vector<Branch<T>> branches;
branches.reserve(item_count);
for (size_t i = 0; i < item_count; i++) {
const gfx::Rect& bounds = bounds_getter(i);
if (bounds.IsEmpty()) {
continue;
}
branches.emplace_back(payload_getter(i), bounds);
}
num_data_elements_ = branches.size();
if (num_data_elements_ == 1u) {
nodes_.reserve(1);
Node<T>* node = AllocateNodeAtLevel(0);
root_.subtree = node;
root_.bounds = branches[0].bounds;
node->num_children = 1;
node->children[0] = std::move(branches[0]);
} else if (num_data_elements_ > 1u) {
// Determine a reasonable upper bound on the number of nodes to prevent
// reallocations. This is basically (n**d - 1) / (n - 1), which is the
// number of nodes in a complete tree with n branches at each node. In the
// code n = |branch_count|, d = |depth|. However, we normally would have
// kMaxChildren branch factor, but that can be broken if some children
// don't have enough nodes. That can happen for at most kMinChildren nodes
// (since otherwise, we'd create a new node).
size_t branch_count = kMaxChildren;
double depth = log(branches.size()) / log(branch_count);
size_t node_count =
static_cast<size_t>((std::pow(branch_count, depth) - 1) /
(branch_count - 1)) +
kMinChildren;
nodes_.reserve(node_count);
root_ = BuildRecursive(&branches, 0);
}
// We should've wasted at most kMinChildren nodes.
DCHECK_LE(nodes_.capacity() - nodes_.size(), kMinChildren);
}
template <typename T>
auto RTree<T>::AllocateNodeAtLevel(uint16_t level) -> Node<T>* {
// We don't allow reallocations, since that would invalidate references to
// existing nodes, so verify that capacity > size.
DCHECK_GT(nodes_.capacity(), nodes_.size());
nodes_.emplace_back(level);
return &nodes_.back();
}
template <typename T>
auto RTree<T>::BuildRecursive(std::vector<Branch<T>>* branches, uint16_t level)
-> Branch<T> {
// Only one branch. It will be the root.
if (branches->size() == 1) {
return std::move((*branches)[0]);
}
// TODO(vmpstr): Investigate if branches should be sorted in y.
// The comment from Skia reads:
// We might sort our branches here, but we expect Blink gives us a reasonable
// x,y order. Skipping a call to sort (in Y) here resulted in a 17% win for
// recording with negligible difference in playback speed.
size_t remainder = branches->size() % kMaxChildren;
if (remainder > 0) {
// If the remainder isn't enough to fill a node, we'll add fewer nodes to
// other branches.
if (remainder >= kMinChildren) {
remainder = 0;
} else {
remainder = kMinChildren - remainder;
}
}
size_t current_branch = 0;
size_t new_branch_index = 0;
while (current_branch < branches->size()) {
size_t increment_by = kMaxChildren;
if (remainder != 0) {
// if need be, omit some nodes to make up for remainder
if (remainder <= kMaxChildren - kMinChildren) {
increment_by -= remainder;
remainder = 0;
} else {
increment_by = kMinChildren;
remainder -= kMaxChildren - kMinChildren;
}
}
Node<T>* node = AllocateNodeAtLevel(level);
node->num_children = 1;
node->children[0] = (*branches)[current_branch];
Branch<T> branch;
branch.bounds = (*branches)[current_branch].bounds;
branch.subtree = node;
++current_branch;
int x = branch.bounds.x();
int y = branch.bounds.y();
int right = branch.bounds.right();
int bottom = branch.bounds.bottom();
for (size_t k = 1; k < increment_by && current_branch < branches->size();
++k) {
// We use a custom union instead of gfx::Rect::Union here, since this
// bypasses some empty checks and extra setters, which improves
// performance.
auto& bounds = (*branches)[current_branch].bounds;
x = std::min(x, bounds.x());
y = std::min(y, bounds.y());
right = std::max(right, bounds.right());
bottom = std::max(bottom, bounds.bottom());
UNSAFE_TODO(node->children[k]) = (*branches)[current_branch];
++node->num_children;
++current_branch;
}
branch.bounds.SetRect(x, y, base::ClampSub(right, x),
base::ClampSub(bottom, y));
// If we had to clamp right/bottom values, we've overflowed.
bool overflow =
branch.bounds.right() != right || branch.bounds.bottom() != bottom;
has_valid_bounds_ &= !overflow;
DCHECK_LT(new_branch_index, current_branch);
(*branches)[new_branch_index] = std::move(branch);
++new_branch_index;
}
branches->resize(new_branch_index);
return BuildRecursive(branches, level + 1);
}
template <typename T>
template <typename ResultFunctor>
void RTree<T>::Search(const gfx::Rect& query,
const ResultFunctor& result_handler) const {
if (num_data_elements_ == 0) {
return;
}
CHECK(root_.subtree);
if (!has_valid_bounds_) {
SearchRecursiveFallback(*root_.subtree, query, result_handler);
} else if (query.Intersects(root_.bounds)) {
SearchRecursive(*root_.subtree, query, result_handler);
}
}
template <typename T>
void RTree<T>::Search(const gfx::Rect& query,
std::vector<T>* results,
std::vector<gfx::Rect>* rects) const {
results->clear();
if (rects) {
rects->clear();
}
Search(query, [results, rects](const T& payload, const gfx::Rect& rect) {
results->push_back(payload);
if (rects) {
rects->push_back(rect);
}
});
}
template <typename T>
void RTree<T>::SearchRefs(const gfx::Rect& query,
std::vector<const T*>* results) const {
results->clear();
Search(query, [results](const T& payload, const gfx::Rect&) {
results->push_back(&payload);
});
}
template <typename T>
template <typename ResultFunctor>
void RTree<T>::SearchRecursive(const Node<T>& node,
const gfx::Rect& query,
const ResultFunctor& result_handler) const {
for (uint16_t i = 0; i < node.num_children; ++i) {
const auto& child = UNSAFE_TODO(node.children[i]);
if (query.Intersects(child.bounds)) {
if (node.level == 0) {
result_handler(child.payload, child.bounds);
} else {
CHECK(child.subtree);
SearchRecursive(*child.subtree, query, result_handler);
}
}
}
}
// When !has_valid_bounds(), any non-leaf bounds may have overflowed and be
// invalid. Iterate over the entire tree, checking bounds at each leaf.
template <typename T>
template <typename ResultFunctor>
void RTree<T>::SearchRecursiveFallback(
const Node<T>& node,
const gfx::Rect& query,
const ResultFunctor& result_handler) const {
for (uint16_t i = 0; i < node.num_children; ++i) {
const auto& child = UNSAFE_TODO(node.children[i]);
if (node.level == 0) {
if (query.Intersects(child.bounds)) {
result_handler(child.payload, child.bounds);
}
} else {
CHECK(child.subtree);
SearchRecursive(*child.subtree, query, result_handler);
}
}
}
template <typename T>
std::optional<gfx::Rect> RTree<T>::bounds() const {
if (has_valid_bounds_) {
return root_.bounds;
}
return std::nullopt;
}
template <typename T>
std::map<T, gfx::Rect> RTree<T>::GetAllBoundsForTracing() const {
std::map<T, gfx::Rect> results;
if (num_data_elements_ > 0) {
CHECK(root_.subtree);
GetAllBoundsRecursive(*root_.subtree, &results);
}
return results;
}
template <typename T>
void RTree<T>::GetAllBoundsRecursive(const Node<T>& node,
std::map<T, gfx::Rect>* results) const {
for (uint16_t i = 0; i < node.num_children; ++i) {
const auto& child = UNSAFE_TODO(node.children[i]);
if (node.level == 0) {
(*results)[child.payload] = child.bounds;
} else {
CHECK(child.subtree);
GetAllBoundsRecursive(*child.subtree, results);
}
}
}
} // namespace cc
#endif // CC_BASE_RTREE_H_
|