Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
sweep_sah_builder.h
Go to the documentation of this file.
1#ifndef BVH_V2_SWEEP_SAH_BUILDER_H
2#define BVH_V2_SWEEP_SAH_BUILDER_H
3
5
6#include <stack>
7#include <tuple>
8#include <algorithm>
9#include <optional>
10#include <numeric>
11#include <cassert>
12
13namespace bvh::v2 {
14
15/// Single-threaded top-down builder that partitions primitives based on the Surface
16/// Area Heuristic (SAH). Primitives are only sorted once along each axis.
17template <typename Node>
18class SweepSahBuilder : public TopDownSahBuilder<Node> {
20 using typename TopDownSahBuilder<Node>::Vec;
21 using typename TopDownSahBuilder<Node>::BBox;
22
26
27public:
29
31 std::span<const BBox> bboxes,
32 std::span<const Vec> centers,
33 const Config& config = {})
34 {
35 return SweepSahBuilder(bboxes, centers, config).build();
36 }
37
38protected:
39 struct Split {
40 size_t pos;
42 size_t axis;
43 };
44
45 std::vector<bool> marks_;
46 std::vector<Scalar> accum_;
47 std::vector<size_t> prim_ids_[Node::dimension];
48
50 std::span<const BBox> bboxes,
51 std::span<const Vec> centers,
52 const Config& config)
53 : TopDownSahBuilder<Node>(bboxes, centers, config)
54 {
55 marks_.resize(bboxes.size());
56 accum_.resize(bboxes.size());
57 for (size_t axis = 0; axis < Node::dimension; ++axis) {
58 prim_ids_[axis].resize(bboxes.size());
59 std::iota(prim_ids_[axis].begin(), prim_ids_[axis].end(), 0);
60 std::sort(prim_ids_[axis].begin(), prim_ids_[axis].end(), [&] (size_t i, size_t j) {
61 return centers[i][axis] < centers[j][axis];
62 });
63 }
64 }
65
66 std::vector<size_t>& get_prim_ids() override { return prim_ids_[0]; }
67
68 void find_best_split(size_t axis, size_t begin, size_t end, Split& best_split) {
69 size_t first_right = begin;
70
71 // Sweep from the right to the left, computing the partial SAH cost
72 auto right_bbox = BBox::make_empty();
73 for (size_t i = end - 1; i > begin;) {
74 static constexpr size_t chunk_size = 32;
75 size_t next = i - std::min(i - begin, chunk_size);
76 auto right_cost = static_cast<Scalar>(0.);
77 for (; i > next; --i) {
78 right_bbox.extend(bboxes_[prim_ids_[axis][i]]);
79 accum_[i] = right_cost = config_.sah.get_leaf_cost(i, end, right_bbox);
80 }
81 // Every `chunk_size` elements, check that we are not above the maximum cost
82 if (right_cost > best_split.cost) {
83 first_right = i;
84 break;
85 }
86 }
87
88 // Sweep from the left to the right, computing the full cost
89 auto left_bbox = BBox::make_empty();
90 for (size_t i = begin; i < first_right; ++i)
91 left_bbox.extend(bboxes_[prim_ids_[axis][i]]);
92 for (size_t i = first_right; i < end - 1; ++i) {
93 left_bbox.extend(bboxes_[prim_ids_[axis][i]]);
94 auto left_cost = config_.sah.get_leaf_cost(begin, i + 1, left_bbox);
95 auto cost = left_cost + accum_[i + 1];
96 if (cost < best_split.cost)
97 best_split = Split { i + 1, cost, axis };
98 else if (left_cost > best_split.cost)
99 break;
100 }
101 }
102
103 BVH_ALWAYS_INLINE void mark_primitives(size_t axis, size_t begin, size_t split_pos, size_t end) {
104 for (size_t i = begin; i < split_pos; ++i) marks_[prim_ids_[axis][i]] = true;
105 for (size_t i = split_pos; i < end; ++i) marks_[prim_ids_[axis][i]] = false;
106 }
107
108 std::optional<size_t> try_split(const BBox& bbox, size_t begin, size_t end) override {
109 // Find the best split over all axes
110 auto leaf_cost = config_.sah.get_non_split_cost(begin, end, bbox);
111 auto best_split = Split { (begin + end + 1) / 2, leaf_cost, 0 };
112 for (size_t axis = 0; axis < Node::dimension; ++axis)
113 find_best_split(axis, begin, end, best_split);
114
115 // Make sure that the split is good before proceeding with it
116 if (best_split.cost >= leaf_cost) {
117 if (end - begin <= config_.max_leaf_size)
118 return std::nullopt;
119
120 // If the number of primitives is too high, fallback on a split at the
121 // median on the largest axis.
122 best_split.pos = (begin + end + 1) / 2;
123 best_split.axis = bbox.get_diagonal().get_largest_axis();
124 }
125
126 // Partition primitives (keeping the order intact so that the next recursive calls do not
127 // need to sort primitives again).
128 mark_primitives(best_split.axis, begin, best_split.pos, end);
129 for (size_t axis = 0; axis < Node::dimension; ++axis) {
130 if (axis == best_split.axis)
131 continue;
132 std::stable_partition(
133 prim_ids_[axis].begin() + begin,
134 prim_ids_[axis].begin() + end,
135 [&] (size_t i) { return marks_[i]; });
136 }
137
138 return std::make_optional(best_split.pos);
139 }
140};
141
142} // namespace bvh::v2
143
144#endif
BVH_ALWAYS_INLINE T get_leaf_cost(size_t begin, size_t end, const BBox< T, N > &bbox) const
BVH_ALWAYS_INLINE T get_non_split_cost(size_t begin, size_t end, const BBox< T, N > &bbox) const
Single-threaded top-down builder that partitions primitives based on the Surface Area Heuristic (SAH)...
BVH_ALWAYS_INLINE SweepSahBuilder(std::span< const BBox > bboxes, std::span< const Vec > centers, const Config &config)
std::vector< size_t > & get_prim_ids() override
std::vector< size_t > prim_ids_[Node::dimension]
void find_best_split(size_t axis, size_t begin, size_t end, Split &best_split)
static BVH_ALWAYS_INLINE Bvh< Node > build(std::span< const BBox > bboxes, std::span< const Vec > centers, const Config &config={})
BVH_ALWAYS_INLINE void mark_primitives(size_t axis, size_t begin, size_t split_pos, size_t end)
std::vector< Scalar > accum_
std::optional< size_t > try_split(const BBox &bbox, size_t begin, size_t end) override
std::vector< bool > marks_
Base class for all SAH-based, top-down builders.
std::span< const BBox > bboxes_
Definition bbox.h:9
#define BVH_ALWAYS_INLINE
Definition platform.h:19
BVH_ALWAYS_INLINE Vec< T, N > get_diagonal() const
Definition bbox.h:29
static BVH_ALWAYS_INLINE constexpr BBox make_empty()
Definition bbox.h:40
Binary BVH node, containing its bounds and an index into its children or the primitives it contains.
Definition node.h:23
static constexpr size_t dimension
Definition node.h:27
SplitHeuristic< Scalar > sah
SAH heuristic parameters that control how primitives are partitioned.
size_t max_leaf_size
Nodes that cannot be split based on the SAH and have a number of primitives larger than this will be ...