|
|
| PackedBVH ()=delete |
| | Deleted default constructor. Use build() (or another static build), TreeBVH::pack() or TreeBVH::packWith() to construct.
|
| |
| | PackedBVH (const Layout &a_layout, const PoolLocation &a_location) noexcept |
| | Rebuild a handle from a stored layout and the location of the pool that holds it.
|
| |
| Layout | layout () const noexcept |
| | The BVH's arrays without its location, for storing it inside another pool-resident object.
|
| |
| | PackedBVH (Pool &a_pool, const TreeBVH< T, P, BV, K > &a_tree) |
| | Construct by packing a TreeBVH (identity primitive type).
|
| |
| template<class Q , class Converter > |
| | PackedBVH (Pool &a_pool, const TreeBVH< T, Q, BV, K > &a_tree, Converter &&a_converter) |
| | Construct by packing a TreeBVH with primitive-type conversion.
|
| |
| | PackedBVH (Pool &a_pool, const std::vector< Node > &a_linearNodes, const std::vector< BV > &a_nodeBoxes, const std::vector< P > &a_primitives) |
| | Adopt a node array, its boxes and a primitive array built elsewhere.
|
| |
| | ~PackedBVH ()=default |
| | Destructor.
|
| |
| | PackedBVH (const PackedBVH &a_other)=default |
| | Copy constructor.
|
| |
| PackedBVH & | operator= (const PackedBVH &a_other)=default |
| | Copy assignment operator.
|
| |
| | PackedBVH (PackedBVH &&a_other) noexcept=default |
| | Move constructor.
|
| |
| PackedBVH & | operator= (PackedBVH &&a_other) noexcept=default |
| | Move assignment operator.
|
| |
| PODSpan< const P > | getPrimitives () const noexcept |
| | Get the global primitive list (in leaf-traversal order).
|
| |
| PODSpan< P > | getPrimitives () noexcept |
| | Get the global primitive list for modification (in leaf-traversal order).
|
| |
| PODSpan< const Node > | getNodes () const noexcept |
| | Get the flat node array (depth-first pre-order, root at index 0).
|
| |
| PackedBVH | rebasedView (const Pool &a_pool) const noexcept |
| | Produce a copy of this BVH that resolves against a_pool.
|
| |
| PackedBVH | deepCopy (Pool &a_dstPool) const |
| | Duplicate this BVH's storage into a_dstPool.
|
| |
| const BV & | getBoundingVolume () const noexcept |
| | Get the bounding volume of the root node.
|
| |
| BV | getBoundingVolume (const Node &a_node) const noexcept |
| | Get the bounding volume of one of this BVH's nodes.
|
| |
| T | getDistanceToBoundingVolume2 (const Node &a_node, const Vec3T< T > &a_point) const noexcept |
| | Get the squared distance from a_point to one of this BVH's nodes' bounding volume.
|
| |
| T | getDistanceToChildBoundingVolume2 (const Node &a_parent, size_t a_k, const Vec3T< T > &a_point) const noexcept |
| | Get the squared distance from a_point to the bounding volume of child a_k of an interior node.
|
| |
| BV | computeBoundingVolume () const noexcept |
| | Compute and return the bounding volume of this BVH.
|
| |
| template<class Query , class State , class LeafEvaluator , class BoundFn , class VisitFn = DescendAll, class Order = NearestFirst> |
| void | traverse (const Query &a_query, State &a_state, LeafEvaluator &&a_leaf, BoundFn &&a_bound, VisitFn &&a_visit=DescendAll(), Order a_order=Order()) const noexcept |
| | Walk the tree with a query, depth first: the general traversal.
|
| |
| template<class BVConstructor > |
| void | refit (const BVConstructor &a_bvConstructor) |
| | Refit every node's bounding volume in place after the primitives have moved.
|
| |
| void * | base () noexcept |
| | The base address this object's arrays are offsets from; see PoolHandle::base().
|
| |
| const void * | base () const noexcept |
| | The base address this object's arrays are offsets from; see PoolHandle::base().
|
| |
Public Member Functions inherited from EBGeometry::PoolHandle |
| PoolLocation | location () const noexcept |
| | Where this handle's arrays live.
|
| |
| bool | isAttachedTo (const Pool &a_pool) const noexcept |
| | Check whether this handle's arrays were reserved from a_pool.
|
| |
|
| static BVH::ConstructionOptions | defaultConstructionOptions () noexcept |
| | The leaf-size settings build() uses when it is given none.
|
| |
| static PackedBVH | build (Pool &a_pool, std::vector< std::pair< P, BV > > a_primsAndBVs, BVH::Construction a_construction, const BVH::ConstructionOptions &a_options=defaultConstructionOptions()) |
| | Build directly from a flat primitive list with one of the preset methods.
|
| |
| template<class S = SFC::Morton> |
| static PackedBVH | buildBottomUp (Pool &a_pool, std::vector< std::pair< P, BV > > a_primsAndBVs, size_t a_targetLeafSize) |
| | Build directly from a flat primitive list along a space-filling curve, without ever building a TreeBVH.
|
| |
| static PackedBVH | buildTopDown (Pool &a_pool, std::vector< std::pair< P, BV > > a_primsAndBVs, const BVH::Partitioner< P, BV, K > &a_partitioner, const BVH::LeafPredicate< T, P, BV, K > &a_stopCrit) |
| | Build directly from a flat primitive list via top-down (optionally SAH) recursive partitioning, without ever building a TreeBVH.
|
| |
| static PackedBVH | buildTopDown (Pool &a_pool, std::vector< std::pair< P, BV > > a_primsAndBVs, const BVH::Partitioner< P, BV, K > &a_partitioner) |
| | Build directly from a primitive list, top-down, with DefaultLeafPredicate.
|
| |
| static PackedBVH | buildClusterSAH (Pool &a_pool, std::vector< std::pair< P, BV > > a_primsAndBVs, BVH::ClusterSpec a_spec) |
| | Build directly via ClusterSAH: cluster primitives, then SAH over the clusters.
|
| |
| static constexpr size_t | traversalStackDepth () noexcept |
| | Traversal stack size, in entries, for the current compilation pass.
|
| |
|
Protected Member Functions inherited from EBGeometry::PoolHandle |
|
| PoolHandle () noexcept=default |
| | An unset location; the first reservation attaches it.
|
| |
| | PoolHandle (const PoolLocation &a_location) noexcept |
| | Adopt a location, as a handle rebuilt from a stored layout does.
|
| |
| | PoolHandle (const PoolHandle &) noexcept=default |
| | Copy a handle, as a derived handle's copy does.
|
| |
|
| PoolHandle (PoolHandle &&) noexcept=default |
| | Move a handle; the same as a copy.
|
| |
| PoolHandle & | operator= (const PoolHandle &) noexcept=default |
| | Copy-assign a handle, as a derived handle's assignment does.
|
| |
| PoolHandle & | operator= (PoolHandle &&) noexcept=default |
| | Move-assign a handle; the same as a copy.
|
| |
|
| ~PoolHandle () noexcept=default |
| | Destructor; protected, since a PoolHandle exists only as the base of a handle.
|
| |
| void * | base () noexcept |
| | The base address this handle's offsets resolve against; see PoolLocation::base().
|
| |
| const void * | base () const noexcept |
| | The base address this handle's offsets resolve against, for read-only access.
|
| |
| void | attach (const Pool &a_pool, const char *a_who) noexcept |
| | Follow a_pool, as the first reservation does; see PoolLocation::attach().
|
| |
| PoolLocation | rebasedLocation (const Pool &a_pool, uint64_t a_endByte, const char *a_who) const noexcept |
| | The location of this handle's arrays in another pool of its mirror chain; see PoolLocation::rebasedOnto().
|
| |
| void | setLocation (const PoolLocation &a_location) noexcept |
| | Replace the location, for a view or a copy made by a derived handle.
|
| |
template<class T, class P, size_t K>
class EBGeometry::BVH::PackedBVH< T, P, K >
Forward declaration of the linearised BVH, defined below.
Linearised, AABB-backed BVH with SIMD-accelerated traversal.
PackedBVH is the runtime query class. It stores a flat depth-first array of Node structs, a contiguous primitive list, one SoA row of child boxes per interior node (what the SIMD child tests read), and the root's box. Every box is stored once, in its parent's row; a node holds no box of its own.
Instances are built directly from a list of (primitive, bounding volume) pairs with build(), buildTopDown(), buildBottomUp() or buildClusterSAH(), by packing a TreeBVH with TreeBVH::pack() (same primitive type) or TreeBVH::packWith() (type-converting pack), or by adopting a node array built elsewhere.
PackedBVH imposes no interface requirement on P by itself – it holds primitives opaquely and only ever hands their range back to a caller-supplied leaf callback of traverse(). Nearest-surface (signed-distance-style) queries are not a PackedBVH member; callers build their own thin wrapper around traverse() with a query type (PointQuery for a point, RayQuery for a ray, or their own), supplying whatever primitive interface their own query needs.
The query type tests a node's K child boxes, read from the parent's SoA row in one pass. For PointQuery the SIMD paths are selected at compile time (BVH::QueryDetail::pointDistances2()):
- K==4, T==float → SSE4.1 (__m128)
- K==4, T==double → AVX (__m256d)
- K==8, T==float → AVX (__m256)
- K==8, T==double → AVX-512F (__m512d), or AVX (two __m256d passes) without AVX-512F
- K==16, T==float → AVX-512F (__m512) All other combinations, and device compilation, use a scalar loop. RayQuery's slab test runs on SIMD::Pack lanes where a register's width divides K.
Primitives are stored by value, inline in the flat array: no per-primitive heap allocation and no pointer chase on a leaf visit. P must therefore be a self-contained, trivially copyable value type – a prerequisite for mirroring the whole BVH into a device address space with a byte copy. That rules out a polymorphic primitive: a BVH over primitives of different types (a CSG union of a mesh and a sphere, say) needs the runtime dispatch of the tape. A primitive that itself lives in a Pool, such as a TriMeshSDF inside a BVHUnion, is stored as its layout and paired with this BVH's location() when it is evaluated; storing the handle itself is a compile error (see PoolHandle). TreeBVH is unaffected by any of this – it always stores primitives as shared_ptr.
- Template Parameters
-
| T | Floating-point precision. |
| P | Primitive type. |
| K | BVH branching factor. |
template<class T , class P , size_t K>
template<class Q , class Converter >
Construct by packing a TreeBVH with primitive-type conversion.
The source tree holds primitives of type Q; the packed BVH holds primitives of type P. The most common reason for this mismatch is an SoA packing step: a tree built over individual triangles (Q = Triangle<T>) can be repacked into a BVH whose leaves hold SIMD-width groups (P = TriangleAoSoA<T, W>, as TriMeshSDF does), enabling vectorized distance evaluation at every leaf visit.
The converter is called once per leaf of the source tree and must return a std::vector<P> containing all target primitives for that leaf:
a_converter(leafPrims, offset, count) → std::vector<P>
where leafPrims is the leaf's PrimitiveList<Q>, offset is the index of the leaf's first primitive within leafPrims (always 0U, since the whole leaf list is passed), and count is the number of primitives in the leaf. All returned vectors are appended to one contiguous buffer, which is then copied into this PackedBVH's pool-backed primitive array.
The source tree must have been built with BV == AABBT<T>; bounding volumes are reused without conversion.
- Template Parameters
-
| Q | Source primitive type stored in the source tree. |
| Converter | Callable: (PrimitiveList<Q>, uint32_t offset, uint32_t count) → std::vector<P>. |
- Parameters
-
| [in,out] | a_pool | Pool the packed arrays are reserved from; must outlive this object. |
| [in] | a_tree | Source tree. |
| [in] | a_converter | Leaf-conversion function. |
template<class T , class P , size_t K>
| EBGeometry::BVH::PackedBVH< T, P, K >::PackedBVH |
( |
Pool & |
a_pool, |
|
|
const std::vector< Node > & |
a_linearNodes, |
|
|
const std::vector< BV > & |
a_nodeBoxes, |
|
|
const std::vector< P > & |
a_primitives |
|
) |
| |
|
inline |
Adopt a node array, its boxes and a primitive array built elsewhere.
For a specialized builder that produces the flat representation itself – e.g. PointCloudBVH, which runs its own index-based point-cloud build – rather than going through a TreeBVH or a list of (primitive, bounding volume) pairs. The arrays are copied into a_pool and the SIMD box rows are built from the boxes, exactly as for every other constructor.
The node array must be a depth-first pre-order flattening with the root at index 0: every interior node's K children lie strictly after it and inside the array, and every leaf's primitive range lies inside a_primitives. refit() and the traversal-depth bound both rely on that shape. It is checked by requireWellFormed(), always on rather than as an EBGEOMETRY_EXPECT, because a malformed array would otherwise surface as an out-of-bounds read in Release; an empty node array (an empty BVH) is accepted only with an empty primitive array.
- Parameters
-
| [in,out] | a_pool | Pool the BVH's arrays are reserved from; must outlive this object. |
| [in] | a_linearNodes | Flattened node array (copied into the pool). A node's box row and box slot are assigned here, whatever the array held. |
| [in] | a_nodeBoxes | The box of every node, indexed like a_linearNodes: a node does not store its own box, so the boxes are passed alongside. Copied into the SIMD rows and the root box. |
| [in] | a_primitives | Global primitive list in leaf-traversal order (copied into the pool). |
template<class T , class P , size_t K>
template<class S = SFC::Morton>
Build directly from a flat primitive list along a space-filling curve, without ever building a TreeBVH.
Bypasses TreeBVH entirely: no per-node shared_ptr<TreeBVH> allocation, and no per-primitive shared_ptr allocation either. Primitives are sorted along the space-filling curve S (same normalization as TreeBVH::bottomUpSortAndPartition(), via SFC::computeBins()), then split into consecutive leaves and merged bottom-up into a K-ary tree.
Every interior node has exactly K children, and such a tree has a leaf count L with L = 1 (mod K - 1). The build takes the smallest such L that keeps every leaf at or below a_targetLeafSize primitives, and splits the primitives as evenly as possible, so leaf sizes differ by at most one. When that L would exceed the number of primitives, it takes the largest such L below it instead, and leaves may then hold slightly more than the target (for example, five primitives with K = 4 and a target of one give four leaves). When a level of the merge has a node count that is not a multiple of K, the remainder is carried up to the next level. Every node has exactly one parent, so a traversal reaches each primitive exactly once.
- Template Parameters
-
- Parameters
-
| [in,out] | a_pool | Pool the packed arrays are reserved from; must outlive the result. |
| [in] | a_primsAndBVs | Primitives and their bounding volumes, taken by value (a sink parameter the caller can std::move in) – never requires shared_ptr-wrapping. |
| [in] | a_targetLeafSize | Target (maximum, where the leaf-count rule above allows it) number of primitives per leaf. Must be > 0. |
- Returns
- The packed BVH.
template<class T , class P , size_t K>
| static PackedBVH EBGeometry::BVH::PackedBVH< T, P, K >::buildTopDown |
( |
Pool & |
a_pool, |
|
|
std::vector< std::pair< P, BV > > |
a_primsAndBVs, |
|
|
const BVH::Partitioner< P, BV, K > & |
a_partitioner, |
|
|
const BVH::LeafPredicate< T, P, BV, K > & |
a_stopCrit |
|
) |
| |
|
inlinestatic |
Build directly from a flat primitive list via top-down (optionally SAH) recursive partitioning, without ever building a TreeBVH.
Reuses the existing (shared_ptr-based) Partitioner/LeafPredicate machinery – BVCentroidPartitioner, BinnedSAHPartitioner, PrimitiveCentroidPartitioner, or any caller-supplied one – exactly as TreeBVH::topDownSortAndPartition() does, but writes nodes directly into the flat node array in depth-first pre-order as the recursion unwinds, instead of building a persistent, shared_ptr<TreeBVH>-linked tree first. Unlike buildBottomUp(), no relayout pass is needed here: top-down recursion visits the root before its children, matching PackedBVH's "root at index 0" invariant for free.
This still shared_ptr-wraps each primitive once up front (needed to reuse the existing Partitioner/LeafPredicate signatures, which operate on PrimAndBVList), and a lightweight, stack-local TreeBVH is constructed (and immediately discarded) at every split purely to evaluate the LeafPredicate and read off its primitive list – exactly the same primitive-handle copying TreeBVH::topDownSortAndPartition() already does at every node. What this build avoids is the persistent, heap-allocated shared_ptr<TreeBVH> node kept alive for the tree's lifetime at every level – measured as the dominant cost of the traditional build-then-pack() path (see the "Direct construction" section of the Sphinx docs).
- Parameters
-
| [in,out] | a_pool | Pool the packed arrays are reserved from; must outlive the result. |
| [in] | a_primsAndBVs | Primitives and their bounding volumes, taken by value (a sink parameter the caller can std::move in) – never requires shared_ptr-wrapping by the caller. |
| [in] | a_partitioner | Partitioning function. Divides a (primitive, BV) list into K non-empty sub-lists (an empty one aborts; see TreeBVH::topDownSortAndPartition()), e.g. BinnedSAHPartitioner for an SAH build. |
| [in] | a_stopCrit | Stop function. Returns true when a node should become a leaf. The overload without it uses DefaultLeafPredicate. |
- Returns
- The packed BVH.
template<class T , class P , size_t K>
template<class BVConstructor >
Refit every node's bounding volume in place after the primitives have moved.
The flat-array counterpart of TreeBVH::refit(): keeps the node array, the primitive array, and every leaf's primitive range exactly as they are, recomputing only the bounding volumes. Because m_linearNodes is a depth-first pre-order flattening (root at index 0, every child at a higher index than its parent), a single reverse sweep over the array refits children before parents with no recursion or explicit stack: each leaf's box becomes the union of its primitives' boxes (recomputed via a_bvConstructor), and each interior node's box the union of its K children's freshly-refitted boxes. Each box is written where it is stored, in its parent's SIMD row (the root's in the BVH), so every traversal sees the new boxes, and nothing is allocated.
Same use and caveats as TreeBVH::refit(): cheap per-frame maintenance for a geometry whose primitives shift without migrating between leaves, and not a substitute for a rebuild once a deformation is large enough to degrade the tree. a_bvConstructor receives each primitive (drawn from the primitive array) and must return its current bounding volume.
- Template Parameters
-
| BVConstructor | Callable: (const P&) -> BV, returning one primitive's current bounding volume. |
- Parameters
-
| [in] | a_bvConstructor | Bounding-volume constructor for a single primitive. |
template<class T , class P , size_t K>
template<class Query , class State , class
LeafEvaluator , class BoundFn , class VisitFn = DescendAll, class Order = NearestFirst>
Walk the tree with a query, depth first: the general traversal.
The caller chooses what is tracked through the tree (a_query: a PointQuery, a RayQuery, or a query type of their own; see EBGeometry_BVHQuery.hpp), what is remembered between leaves (a_state), what happens at a leaf (a_leaf), how near a node must be to be entered (a_bound), what to do with a node before entering it (a_visit), and the order a node's children are visited in (a_order).
At each interior node the query gives the K children their keys in one pass (Query::keys(), SIMD on the host). A child is entered if its key is <= the bound and, for a floating-point key, not +infinity (which means the query cannot reach it, e.g. a ray that misses its box; so a bound that starts at +infinity does not let the walk into every box a ray misses); those are visited in the order a_order gives, the first at once and the rest from a stack. The bound is read again at every node, so a leaf visited anywhere earlier tightens the pruning of every node after it. When the walk returns to a level it recomputes the next child's key (Query::key(row, k), bit for bit the same) and compares it with the current bound; under NearestFirst the first child out of bound drops the rest of the level.
Every node about to be entered (the root, an interior node, a leaf), once it has passed the bound, is handed to a_visit with its index, the node and its key. Returning false skips it and its subtree; the callback may update the state first, for instance to account for the whole subtree at once from the caller's own per-node data. The default, DescendAll, always descends and compiles away.
The stack keeps one entry per level (the node and the order of the children still to visit), sized from traversalStackDepth(), so it is a fixed array on a device. An empty BVH visits nothing.
One query per walk: QueryLanes<Query>::value must be 1.
- Template Parameters
-
| Query | Query type: keys(row), key(row, k), key(box); see BVH::PointQuery. |
| State | The caller's search state. |
| LeafEvaluator | Callable as (State&, size_t offset, size_t count) -> void: scans primitives [offset, offset + count) of getPrimitives() and updates the state. |
| BoundFn | Callable as (const State&) -> Key: the current bound. A child whose key is above it is not entered; a bound below every key (e.g. -infinity) ends the walk. |
| VisitFn | Callable as (State&, uint32_t nodeIndex, const Node&, const Key&) -> bool, or DescendAll. |
| Order | NearestFirst (default), FarthestFirst, AsStored (first slot first), or a comparator (keyA, keyB) -> bool or (keyA, nodeIndexA, keyB, nodeIndexB) -> bool, true when the first child is to be visited before the second. |
- Parameters
-
| [in] | a_query | The query. |
| [in,out] | a_state | The search state. |
| [in] | a_leaf | Leaf callback. |
| [in] | a_bound | Bound callback. |
| [in] | a_visit | Visit callback. |
| [in] | a_order | Visit order. |