Skip to main content

embree3/
bvh.rs

1//! Safe wrapper over embree's standalone BVH builder (`rtcBuildBVH` +
2//! `rtcThreadLocalAlloc`).
3//!
4//! Unlike the scene BVH that `rtcCommitScene` builds implicitly, this is a
5//! *bring-your-own-node-layout* builder: embree decides the tree topology and
6//! the build heuristic, and for every node it produces it calls back to
7//! allocate and fill the caller's own node type in embree's arena. The caller
8//! therefore owns the in-memory layout and its own traversal; embree owns only
9//! the structure.
10//!
11//! # Entry point
12//!
13//! [`Device::create_bvh`](crate::Device::create_bvh) returns a [`Bvh`]; a build
14//! runs through [`Bvh::build_scoped`], driven by a caller-supplied
15//! [`BvhBuilder`]. The resulting tree is navigated inside the build scope
16//! through [`BvhResult`].
17//!
18//! # Design notes
19//!
20//! - **Generative scope.** `build_scoped` takes a `for<'id>
21//!   FnOnce(BvhResult<'id, B>)` closure, so the branded lifetime `'id` is
22//!   unique per call: a [`NodePtr`] handle cannot escape the closure or be
23//!   resolved against a different build, and a handle becomes a `&Node` only
24//!   through [`BvhResult::resolve`]. A plain invariant lifetime would not be
25//!   enough: two independent builds could be inferred to share one lifetime,
26//!   letting a handle from one resolve against the other's arena; the
27//!   `for<'id>` bound makes `'id` fresh per call so the compiler cannot unify
28//!   two builds.
29//!
30//! - **Arena memory.** Nodes are bump-allocated through [`Allocator`]
31//!   (`rtcThreadLocalAlloc`) and freed wholesale when the [`Bvh`] drops;
32//!   **`Drop` never runs on a node**. The [`BvhNode`] bound (`Copy + Send +
33//!   Sync`, no owned resources) is what makes that sound. Child links are
34//!   opaque [`NodePtr`] handles, never live references held during the build.
35//!
36//! - **Threaded callbacks.** Embree invokes the [`BvhBuilder`] methods from
37//!   many worker threads (hence the `Send + Sync` bound). The `extern "C"`
38//!   trampolines recover the builder from a raw `userPtr` (`BuildState`) and
39//!   rebrand the callback-local pointers to the result's `'id`
40//!   (`build_with_brand`); the soundness invariant is that every pointer in one
41//!   callback belongs to the build identified by that callback's `userPtr`.
42//!
43//! - **Version-gated.** The safe path relies on a per-node setter-serialization
44//!   property audited for embree 3.13.5, checked at runtime against the linked
45//!   library's version.
46//!
47//! See [`BvhBuilder`] for the callback contract and a worked example.
48use std::{
49    marker::PhantomData,
50    os::raw::{c_uint, c_void},
51    panic::{catch_unwind, AssertUnwindSafe},
52    ptr::NonNull,
53};
54
55use crate::{sys::*, Bounds, BuildPrimitive, BuildQuality, Device, DeviceProperty, Error};
56
57/// A reference-counted standalone BVH (`RTCBVH`). Build target for
58/// [`Bvh::build_scoped`].
59///
60/// Not `Clone`: the result of a build borrows the `Bvh` exclusively, and a
61/// second handle could rebuild and free the arena out from under a live result.
62pub struct Bvh {
63    device: Device,
64    handle: RTCBVH,
65}
66
67impl Drop for Bvh {
68    fn drop(&mut self) { unsafe { rtcReleaseBVH(self.handle) } }
69}
70
71impl Bvh {
72    pub(crate) fn new(device: &Device) -> Result<Self, Error> {
73        let handle = unsafe { rtcNewBVH(device.handle) };
74        if handle.is_null() {
75            Err(device.get_error())
76        } else {
77            Ok(Self {
78                device: device.clone(),
79                handle,
80            })
81        }
82    }
83}
84
85/// Marker for a type that may live in the BVH arena.
86///
87/// # Safety
88///
89/// The arena frees node memory in bulk without running `Drop`, and embree
90/// creates/mutates nodes across worker threads. Implementors must own no
91/// external resources (a raw pointer to an owned heap allocation would leak; an
92/// embedded foreign reference could dangle) and must be sound to construct and
93/// mutate across threads. A node should contain only plain `Copy` data,
94/// [`Bounds`], and [`NodePtr`] handles from the same build.
95///
96/// **Family-level rebrand requirement.** This trait is implemented across all
97/// lifetimes (`unsafe impl<'id> BvhNode for MyNode<'id>`), and that blanket
98/// impl is what makes the builder's *rebrand* sound: nodes are allocated at a
99/// callback-local lifetime and the wrapper later reinterprets the root as
100/// [`BvhBuilder::Node`]`<'id>` for the result's brand. For any lifetimes `'a`
101/// and `'b`, `MyNode<'a>` and `MyNode<'b>` must therefore be **layout-identical
102/// and safely rebrandable**. That holds automatically for any type meeting the
103/// rule above: Rust layout never depends on a lifetime, and the only permitted
104/// lifetime-carrying members are `NodePtr` phantom brands (which rebrand for
105/// free). Do not embed any other lifetime-dependent state.
106pub unsafe trait BvhNode: Copy + Send + Sync {}
107
108/// Opaque, generatively branded handle to an arena node. `Copy`, pointer-sized,
109/// and not dereferenceable except through [`BvhResult::resolve`] inside the
110/// build scope.
111#[repr(transparent)]
112pub struct NodePtr<'id, N> {
113    ptr: NonNull<N>,
114    _brand: PhantomData<fn(&'id ()) -> &'id ()>,
115}
116
117impl<'id, N> Clone for NodePtr<'id, N> {
118    fn clone(&self) -> Self { *self }
119}
120impl<'id, N> Copy for NodePtr<'id, N> {}
121
122// Deliberate: an opaque non-dereferenceable handle is safe to move/share (the
123// brand still confines it to the scope).
124unsafe impl<'id, N> Send for NodePtr<'id, N> {}
125unsafe impl<'id, N> Sync for NodePtr<'id, N> {}
126
127/// Per-callback-thread bump allocator over the BVH arena
128/// (`rtcThreadLocalAlloc`). `!Send + !Sync`: valid only on its current callback
129/// thread.
130pub struct Allocator<'id> {
131    raw: RTCThreadLocalAllocator,
132    _brand: PhantomData<&'id ()>,
133    _not_send_sync: PhantomData<*mut ()>,
134}
135
136impl<'id> Allocator<'id> {
137    unsafe fn from_raw(raw: RTCThreadLocalAllocator) -> Self {
138        Self {
139            raw,
140            _brand: PhantomData,
141            _not_send_sync: PhantomData,
142        }
143    }
144
145    /// Bump-allocate one `T` in the arena and move `value` into it.
146    ///
147    /// ZSTs are rejected **unconditionally** (also in release): embree relies
148    /// on node-pointer identity, and a zero-size allocation would alias
149    /// every node to one address. This runs behind FFI, so it aborts rather
150    /// than unwinds. (`build_with_brand` also rejects a ZST `Node` up front
151    /// with a clean `Err`; this is the defensive backstop for any other
152    /// `alloc::<T>` call.)
153    pub fn alloc<T: Copy>(&self, value: T) -> &'id mut T {
154        if std::mem::size_of::<T>() == 0 {
155            std::process::abort();
156        }
157        let layout = std::alloc::Layout::new::<T>();
158        let p = unsafe { rtcThreadLocalAlloc(self.raw, layout.size(), layout.align()) } as *mut T;
159        if p.is_null() {
160            // No unwinding across FFI: OOM aborts.
161            std::process::abort();
162        }
163        unsafe {
164            p.write(value);
165            &mut *p
166        }
167    }
168}
169
170/// Checked, view-scoped accessor over embree's `void**` child array.
171pub struct Children<'id, N> {
172    ptr: *const *mut c_void,
173    len: usize,
174    _m: PhantomData<NodePtr<'id, N>>,
175}
176
177impl<'id, N> Children<'id, N> {
178    unsafe fn from_raw(ptr: *mut *mut c_void, len: usize) -> Self {
179        Self {
180            ptr: ptr as *const *mut c_void,
181            len,
182            _m: PhantomData,
183        }
184    }
185    /// The number of children stored in the node.
186    pub fn len(&self) -> usize { self.len }
187    /// Whether the node has no children.
188    pub fn is_empty(&self) -> bool { self.len == 0 }
189    /// The `i`th child handle, or `None` if out of range.
190    pub fn get(&self, i: usize) -> Option<NodePtr<'id, N>> {
191        if i >= self.len {
192            return None;
193        }
194        let raw = unsafe { *self.ptr.add(i) };
195        NonNull::new(raw as *mut N).map(|ptr| NodePtr {
196            ptr,
197            _brand: PhantomData,
198        })
199    }
200}
201
202/// Checked accessor over embree's `RTCBounds*` array. References borrow the
203/// view (the bounds may live in callback-local storage).
204pub struct ChildBounds<'a> {
205    ptr: *const *const RTCBounds,
206    len: usize,
207    _m: PhantomData<&'a Bounds>,
208}
209
210impl<'a> ChildBounds<'a> {
211    unsafe fn from_raw(ptr: *mut *const RTCBounds, len: usize) -> Self {
212        Self {
213            ptr: ptr as *const *const RTCBounds,
214            len,
215            _m: PhantomData,
216        }
217    }
218    /// The number of child bounding boxes in the view.
219    #[inline]
220    pub fn len(&self) -> usize { self.len }
221    /// Whether the view has no child bounds.
222    #[inline]
223    pub fn is_empty(&self) -> bool { self.len == 0 }
224    /// The `i`th child's bounds, borrowed from this view, or `None` if out of
225    /// range.
226    #[inline]
227    pub fn get(&self, i: usize) -> Option<&Bounds> {
228        if i >= self.len {
229            return None;
230        }
231        unsafe { (*self.ptr.add(i)).as_ref() }
232    }
233}
234
235/// Materializes embree's BVH topology into a caller-defined node layout.
236///
237/// [`Bvh::build_scoped`] runs embree's builder, which decides the tree's shape
238/// and, for every node it produces, calls back into this trait to allocate and
239/// fill *your* node type in embree's arena. You own the in-memory layout (and
240/// therefore your own traversal); embree owns only the topology and the build
241/// heuristic. This is the safe wrapper over embree's `rtcBuildBVH` callback
242/// API.
243///
244/// # Callback lifecycle
245///
246/// For each interior node, embree calls (in this order, serialized per node):
247/// 1. [`create_node`](Self::create_node) - allocate the node; *its children do
248///    not exist yet*.
249/// 2. (embree recurses and builds the children.)
250/// 3. [`set_bounds`](Self::set_bounds) - store each child's bounding box into
251///    the node.
252/// 4. [`set_children`](Self::set_children) - store each child's handle into the
253///    node.
254///
255/// For each leaf, embree calls [`create_leaf`](Self::create_leaf) with the
256/// primitives that fall into it. When [`SPATIAL_SPLITS`](Self::SPATIAL_SPLITS)
257/// is enabled (and quality is `HIGH`), [`split`](Self::split) may be called to
258/// divide a primitive across a plane; when [`PROGRESS`](Self::PROGRESS) is
259/// enabled, [`progress`](Self::progress) is called to report.
260///
261/// # Threading
262///
263/// Embree invokes these callbacks from **many worker threads**, so `Self` is
264/// `Send + Sync` and every method takes a shared `&self`. Any per-build mutable
265/// state must therefore live behind atomics or locks, not `&mut self`. (Embree
266/// does not invoke two callbacks for the *same* node concurrently; this is the
267/// one audited assumption noted in the crate docs.)
268///
269/// # Memory
270///
271/// Nodes are bump-allocated in embree's arena via the [`Allocator`] handed to
272/// `create_node`/`create_leaf`, and the whole arena is freed at once when the
273/// [`Bvh`] is dropped - **`Drop` never runs on a node**. The [`BvhNode`] bound
274/// enforces what that requires (`Copy`, `Send + Sync`, no owned resources).
275/// Child links are opaque, scope-branded [`NodePtr`] handles; you can only turn
276/// one back into a `&Node` after the build, through [`BvhResult::resolve`].
277///
278/// # Example
279///
280/// ```no_run
281/// use embree3::{
282///     Allocator, Bounds, BuildConfig, BuildPrimitive, BvhBuilder, BvhNode, ChildBounds, Children,
283///     Device, NodePtr,
284/// };
285///
286/// const EMPTY: Bounds = Bounds {
287///     lower_x: 0.0,
288///     lower_y: 0.0,
289///     lower_z: 0.0,
290///     align0: 0.0,
291///     upper_x: 0.0,
292///     upper_y: 0.0,
293///     upper_z: 0.0,
294///     align1: 0.0,
295/// };
296///
297/// // A single, generatively branded node type (one constant `N` sizes both the child
298/// // array and `MAX_CHILDREN`). Inner children are `Option` because they are filled in
299/// // `set_children`, after `create_node` has already returned the node.
300/// const N: usize = 2;
301/// #[derive(Clone, Copy)]
302/// enum Node<'id> {
303///     Inner {
304///         bounds: [Bounds; N],
305///         kids: [Option<NodePtr<'id, Node<'id>>>; N],
306///     },
307///     Leaf {
308///         prim_id: u32,
309///     },
310/// }
311/// unsafe impl<'id> BvhNode for Node<'id> {}
312///
313/// struct Builder;
314/// impl BvhBuilder for Builder {
315///     type Node<'id> = Node<'id>;
316///     const MAX_CHILDREN: usize = N;
317///
318///     fn create_node<'id>(&self, a: &Allocator<'id>, _n: usize) -> &'id mut Node<'id> {
319///         a.alloc(Node::Inner {
320///             bounds: [EMPTY; N],
321///             kids: [None; N],
322///         })
323///     }
324///     fn set_children<'id>(&self, node: &mut Node<'id>, c: Children<'id, Node<'id>>) {
325///         if let Node::Inner { kids, .. } = node {
326///             for i in 0..c.len().min(N) {
327///                 kids[i] = c.get(i);
328///             }
329///         }
330///     }
331///     fn set_bounds<'id>(&self, node: &mut Node<'id>, b: ChildBounds<'_>) {
332///         if let Node::Inner { bounds, .. } = node {
333///             for i in 0..b.len().min(N) {
334///                 if let Some(cb) = b.get(i) {
335///                     bounds[i] = *cb;
336///                 }
337///             }
338///         }
339///     }
340///     fn create_leaf<'id>(
341///         &self,
342///         a: &Allocator<'id>,
343///         prims: &[BuildPrimitive],
344///     ) -> &'id mut Node<'id> {
345///         // With `max_leaf_size = 1` each leaf holds one primitive.
346///         a.alloc(Node::Leaf {
347///             prim_id: prims[0].primID,
348///         })
349///     }
350/// }
351///
352/// let device = Device::new().unwrap();
353/// let mut bvh = device.create_bvh().unwrap();
354/// let mut prims: Vec<BuildPrimitive> = Vec::new(); // fill with your primitive AABBs
355/// let cfg = BuildConfig {
356///     max_leaf_size: 1,
357///     ..Default::default()
358/// };
359///
360/// // Navigate the tree inside the scope; handles cannot escape `f`.
361/// let has_root = bvh
362///     .build_scoped(&cfg, &mut prims, &Builder, |r| r.root().is_some())
363///     .unwrap();
364/// ```
365pub trait BvhBuilder: Send + Sync {
366    /// The arena node type, generatively branded by the build scope `'id`.
367    ///
368    /// Almost always an `enum { Inner { .. }, Leaf { .. } }`. Because
369    /// [`create_node`](Self::create_node) runs before a node's children exist,
370    /// the inner variant stores child handles as `[Option<NodePtr<'id,
371    /// Self::Node<'id>>>; N]` (filled in
372    /// [`set_children`](Self::set_children)); `Option<NodePtr>` is
373    /// niche-optimized to a single pointer, so the `Option` costs nothing.
374    /// `N` is a concrete constant that must equal
375    /// [`MAX_CHILDREN`](Self::MAX_CHILDREN).
376    ///
377    /// The `+ 'id` bound makes `&'id Node<'id>` well-formed; see [`BvhNode`]
378    /// for the family-level rebrand requirement the builder relies on.
379    type Node<'id>: BvhNode + 'id;
380
381    /// Maximum number of children an inner node can hold, i.e. the length of
382    /// the node's child array.
383    ///
384    /// [`Bvh::build_scoped`] rejects a `BuildConfig::max_branching_factor`
385    /// greater than this, so [`set_children`](Self::set_children) can never
386    /// receive more children than the array holds.
387    const MAX_CHILDREN: usize;
388
389    /// Enable spatial splits. When `true`, the [`split`](Self::split) callback
390    /// is registered and embree may split primitives across node boundaries
391    /// (only at `BuildQuality::HIGH`); [`Bvh::build_scoped`] reserves the
392    /// extra primitive-array capacity this needs. Leaving it `false` (the
393    /// default) registers no split callback, so build behavior is unchanged.
394    const SPATIAL_SPLITS: bool = false;
395
396    /// Enable progress reporting through [`progress`](Self::progress). Default
397    /// `false` (no progress callback is registered).
398    const PROGRESS: bool = false;
399
400    /// Allocate an interior node that will have `child_count` children
401    /// (`child_count <= `[`MAX_CHILDREN`](Self::MAX_CHILDREN)) and return it.
402    ///
403    /// Allocate in the arena with [`Allocator::alloc`] and return the
404    /// reference. The children do **not** exist yet, so initialize the
405    /// child slots to a placeholder (e.g. `[None; N]`);
406    /// [`set_bounds`](Self::set_bounds) and
407    /// [`set_children`](Self::set_children) fill them once the children
408    /// have been built.
409    fn create_node<'id>(
410        &self,
411        alloc: &Allocator<'id>,
412        child_count: usize,
413    ) -> &'id mut Self::Node<'id>;
414
415    /// Store the handles of `node`'s children into `node`.
416    ///
417    /// The children were produced earlier by [`create_node`](Self::create_node)
418    /// / [`create_leaf`](Self::create_leaf). `children` is a checked,
419    /// scope-branded view: `children.get(i)` yields the `i`th child's
420    /// [`NodePtr`] (or `None` past the end). Copy the handles into the
421    /// node's child array; a handle can only be dereferenced after the
422    /// build, through [`BvhResult::resolve`].
423    fn set_children<'id>(
424        &self,
425        node: &mut Self::Node<'id>,
426        children: Children<'id, Self::Node<'id>>,
427    );
428
429    /// Store the bounding boxes of `node`'s children into `node`.
430    ///
431    /// `bounds.get(i)` borrows the `i`th child's [`Bounds`] **from the view** -
432    /// embree may keep it in callback-local storage, so copy the value out
433    /// rather than retaining the reference.
434    fn set_bounds<'id>(&self, node: &mut Self::Node<'id>, bounds: ChildBounds<'_>);
435
436    /// Allocate a leaf node over `prims` and return it.
437    ///
438    /// `prims` is the complete set of primitives in this leaf
439    /// (`1 ..= BuildConfig::max_leaf_size`). The leaf must represent **all** of
440    /// them (store their `primID`s, an index range, etc.) - keeping only
441    /// `prims[0]` silently drops the rest unless you build with
442    /// `max_leaf_size == 1`. Allocate via [`Allocator::alloc`]; a
443    /// leaf cannot own heap memory (see [`BvhNode`]).
444    fn create_leaf<'id>(
445        &self,
446        alloc: &Allocator<'id>,
447        prims: &[BuildPrimitive],
448    ) -> &'id mut Self::Node<'id>;
449
450    /// Split a primitive's bounding box by the plane `dim = pos`, returning its
451    /// `(left, right)` sub-boxes.
452    ///
453    /// Only called when [`SPATIAL_SPLITS`](Self::SPATIAL_SPLITS) is `true`.
454    /// `dim` is the axis (`0 = x`, `1 = y`, `2 = z`). The default clips the
455    /// primitive's box at `pos` along `dim` (a valid, conservative
456    /// geometric split); override it for tighter, geometry-aware splits.
457    fn split(&self, prim: &BuildPrimitive, dim: u32, pos: f32) -> (Bounds, Bounds) {
458        let lo_full = bounds_of(prim);
459        let mut lo = lo_full;
460        let mut hi = lo_full;
461        set_axis_upper(&mut lo, dim, pos);
462        set_axis_lower(&mut hi, dim, pos);
463        (lo, hi)
464    }
465
466    /// Report build progress, with `fraction` advancing toward `1.0`.
467    ///
468    /// Only called when [`PROGRESS`](Self::PROGRESS) is `true`. **Report
469    /// only:** embree 3.13.5's standalone builder discards the callback's
470    /// result, so this cannot cancel the build (hence the `()` return
471    /// rather than a `bool`).
472    fn progress(&self, _fraction: f64) {}
473}
474
475/// Build settings. `Default` reproduces `rtcDefaultBuildArguments`.
476#[derive(Clone, Debug)]
477pub struct BuildConfig {
478    /// Build quality / speed-vs-quality trade-off.
479    pub quality: BuildQuality,
480    /// Optimize the build for fast *rebuilds* of dynamic scenes, at the cost of
481    /// higher memory use (embree's `RTC_BUILD_FLAG_DYNAMIC`). `false`
482    /// builds for best query performance.
483    pub dynamic: bool,
484    /// Maximum number of children per interior node. Must not exceed the
485    /// builder's [`MAX_CHILDREN`](BvhBuilder::MAX_CHILDREN).
486    pub max_branching_factor: u32,
487    /// Maximum depth of the tree.
488    pub max_depth: u32,
489    /// Number of primitives per SAH evaluation block.
490    pub sah_block_size: u32,
491    /// Minimum number of primitives in a leaf.
492    pub min_leaf_size: u32,
493    /// Maximum number of primitives in a leaf.
494    pub max_leaf_size: u32,
495    /// Estimated cost of traversing one node, relative to `intersection_cost`
496    /// (tunes the SAH).
497    pub traversal_cost: f32,
498    /// Estimated cost of one primitive intersection, relative to
499    /// `traversal_cost` (tunes the SAH).
500    pub intersection_cost: f32,
501}
502
503impl Default for BuildConfig {
504    fn default() -> Self {
505        Self {
506            quality: BuildQuality::MEDIUM,
507            dynamic: false,
508            max_branching_factor: 2,
509            max_depth: 32,
510            sah_block_size: 1,
511            min_leaf_size: 1,
512            max_leaf_size: 32,
513            traversal_cost: 1.0,
514            intersection_cost: 1.0,
515        }
516    }
517}
518
519impl BuildConfig {
520    fn validate(&self, prim_count: usize, max_children: usize) -> Result<(), Error> {
521        let bad = Err(Error::INVALID_ARGUMENT);
522        match self.quality {
523            BuildQuality::LOW | BuildQuality::MEDIUM | BuildQuality::HIGH => {}
524            _ => return bad,
525        }
526        let branch_cap = if self.quality == BuildQuality::LOW {
527            8
528        } else {
529            16
530        };
531        if self.max_branching_factor < 2
532            || self.max_branching_factor > branch_cap
533            || self.max_branching_factor as usize > max_children
534        {
535            return bad;
536        }
537        if self.max_leaf_size < 1 || self.max_leaf_size > 32 {
538            return bad;
539        }
540        if self.min_leaf_size < 1 || self.min_leaf_size > self.max_leaf_size {
541            return bad;
542        }
543        if self.sah_block_size < 1 || self.max_depth < 1 {
544            return bad;
545        }
546        if !self.traversal_cost.is_finite()
547            || self.traversal_cost <= 0.0
548            || !self.intersection_cost.is_finite()
549            || self.intersection_cost <= 0.0
550        {
551            return bad;
552        }
553        if self.quality == BuildQuality::LOW && prim_count > u32::MAX as usize {
554            return bad;
555        }
556        Ok(())
557    }
558}
559
560/// Result of a build, valid only inside the generative scope. Branded by `'id`.
561pub struct BvhResult<'id, B: BvhBuilder> {
562    root: Option<NodePtr<'id, B::Node<'id>>>,
563    _brand: PhantomData<fn(&'id ()) -> &'id ()>,
564}
565
566impl<'id, B: BvhBuilder> BvhResult<'id, B> {
567    /// The root node, or `None` for an empty tree (zero primitives).
568    pub fn root(&self) -> Option<&B::Node<'id>> { self.root.map(|p| unsafe { p.ptr.as_ref() }) }
569    /// The root handle.
570    pub fn root_ptr(&self) -> Option<NodePtr<'id, B::Node<'id>>> { self.root }
571    /// Resolve a child handle to a node reference. Safe with no runtime check:
572    /// the `'id` brand proves the handle came from this same build, and the
573    /// arena cannot have been freed or rebuilt -- the generative `build_scoped`
574    /// closure that produced this [`BvhResult`] holds the `&mut Bvh`
575    /// exclusively for its whole duration, so no rebuild or drop can run
576    /// while `'id` is live. (The returned reference borrows `self`, tying
577    /// it to the result handle.)
578    pub fn resolve(&self, p: NodePtr<'id, B::Node<'id>>) -> &B::Node<'id> {
579        unsafe { p.ptr.as_ref() }
580    }
581}
582
583/// Internal state passed through `userPtr`. The builder is held as a raw
584/// pointer (valid for the synchronous `rtcBuildBVH` call by the rebrand
585/// invariant), which avoids a `B: 'x` outlives requirement when a trampoline
586/// recovers it at a fresh lifetime. The generative brand lives only on
587/// [`BvhResult`], not here.
588struct BuildState<B> {
589    builder: *const B,
590}
591
592fn catch_abort<R>(f: impl FnOnce() -> R) -> R {
593    match catch_unwind(AssertUnwindSafe(f)) {
594        Ok(r) => r,
595        Err(_) => std::process::abort(),
596    }
597}
598
599// Trampolines.
600// Each recovers `&BuildState` then rebrands all pointers at one inferred
601// lifetime 'x (the unsafe invariant: every pointer in one synchronous callback
602// belongs to the in-flight build whose BuildState is `userPtr`).
603
604// Recover the builder reference from `userPtr`. The returned `&B` lives for an
605// inferred lifetime; soundness is the rebrand invariant (the pointer belongs to
606// the in-flight build).
607#[inline]
608unsafe fn builder_of<'x, B: BvhBuilder>(user: *mut c_void) -> &'x B {
609    &*((*(user as *const BuildState<B>)).builder)
610}
611
612unsafe extern "C" fn create_node_tramp<B: BvhBuilder>(
613    alloc: RTCThreadLocalAllocator,
614    child_count: c_uint,
615    user: *mut c_void,
616) -> *mut c_void {
617    catch_abort(|| unsafe {
618        let builder = builder_of::<B>(user);
619        let allocator = Allocator::from_raw(alloc);
620        let node = builder.create_node(&allocator, child_count as usize);
621        node as *mut _ as *mut c_void
622    })
623}
624
625unsafe extern "C" fn create_leaf_tramp<B: BvhBuilder>(
626    alloc: RTCThreadLocalAllocator,
627    prims: *const RTCBuildPrimitive,
628    prim_count: usize,
629    user: *mut c_void,
630) -> *mut c_void {
631    catch_abort(|| unsafe {
632        let builder = builder_of::<B>(user);
633        let allocator = Allocator::from_raw(alloc);
634        let slice = std::slice::from_raw_parts(prims, prim_count);
635        let node = builder.create_leaf(&allocator, slice);
636        node as *mut _ as *mut c_void
637    })
638}
639
640unsafe extern "C" fn set_children_tramp<B: BvhBuilder>(
641    node: *mut c_void,
642    children: *mut *mut c_void,
643    child_count: c_uint,
644    user: *mut c_void,
645) {
646    catch_abort(|| unsafe {
647        let builder = builder_of::<B>(user);
648        let node = &mut *(node as *mut B::Node<'_>);
649        let kids = Children::from_raw(children, child_count as usize);
650        builder.set_children(node, kids);
651    })
652}
653
654unsafe extern "C" fn set_bounds_tramp<B: BvhBuilder>(
655    node: *mut c_void,
656    bounds: *mut *const RTCBounds,
657    child_count: c_uint,
658    user: *mut c_void,
659) {
660    catch_abort(|| unsafe {
661        let builder = builder_of::<B>(user);
662        let node = &mut *(node as *mut B::Node<'_>);
663        let view = ChildBounds::from_raw(bounds, child_count as usize);
664        builder.set_bounds(node, view);
665    })
666}
667
668unsafe extern "C" fn split_tramp<B: BvhBuilder>(
669    prim: *const RTCBuildPrimitive,
670    dim: c_uint,
671    pos: f32,
672    lbounds: *mut RTCBounds,
673    rbounds: *mut RTCBounds,
674    user: *mut c_void,
675) {
676    catch_abort(|| unsafe {
677        let builder = builder_of::<B>(user);
678        let (lo, hi) = builder.split(&*prim, dim, pos);
679        *lbounds = lo;
680        *rbounds = hi;
681    });
682}
683
684unsafe extern "C" fn progress_tramp<B: BvhBuilder>(user: *mut c_void, n: f64) -> bool {
685    catch_abort(|| unsafe {
686        let builder = builder_of::<B>(user);
687        builder.progress(n);
688    });
689    true
690}
691
692impl Bvh {
693    /// Build a BVH over `primitives` inside a generative scope. The result and
694    /// any [`NodePtr`] handles cannot escape `f`; return owned data
695    /// (counts, a `Vec`, a copied-out tree) instead.
696    ///
697    /// The generative brand makes escaping a handle a compile error, which is
698    /// what prevents resolving a handle against a different build:
699    ///
700    /// ```compile_fail
701    /// use embree3::{
702    ///     Allocator, Bvh, BvhBuilder, BvhNode, BuildConfig, BuildPrimitive, ChildBounds,
703    ///     Children, Device,
704    /// };
705    /// #[derive(Clone, Copy)]
706    /// struct Node {
707    ///     n: u32,
708    /// }
709    /// unsafe impl BvhNode for Node {}
710    /// struct B;
711    /// impl BvhBuilder for B {
712    ///     type Node<'id> = Node;
713    ///     const MAX_CHILDREN: usize = 2;
714    ///     fn create_node<'id>(&self, a: &Allocator<'id>, _n: usize) -> &'id mut Node {
715    ///         a.alloc(Node { n: 0 })
716    ///     }
717    ///     fn set_children<'id>(&self, _n: &mut Node, _c: Children<'id, Node>) {}
718    ///     fn set_bounds<'id>(&self, _n: &mut Node, _b: ChildBounds<'_>) {}
719    ///     fn create_leaf<'id>(&self, a: &Allocator<'id>, _p: &[BuildPrimitive]) -> &'id mut Node {
720    ///         a.alloc(Node { n: 0 })
721    ///     }
722    /// }
723    /// let device = Device::new().unwrap();
724    /// let mut bvh = device.create_bvh().unwrap();
725    /// let mut prims: Vec<BuildPrimitive> = Vec::new();
726    /// // ERROR: a branded handle cannot escape the generative scope.
727    /// let _escaped = bvh.build_scoped(&BuildConfig::default(), &mut prims, &B, |r| r.root_ptr());
728    /// ```
729    ///
730    /// The cross-build case directly: a handle from one build cannot be
731    /// resolved by a nested, independent build (their generative brands
732    /// differ):
733    ///
734    /// ```compile_fail
735    /// use embree3::{
736    ///     Allocator, Bvh, BvhBuilder, BvhNode, BuildConfig, BuildPrimitive, ChildBounds,
737    ///     Children, Device,
738    /// };
739    /// #[derive(Clone, Copy)]
740    /// struct Node {
741    ///     n: u32,
742    /// }
743    /// unsafe impl BvhNode for Node {}
744    /// struct B;
745    /// impl BvhBuilder for B {
746    ///     type Node<'id> = Node;
747    ///     const MAX_CHILDREN: usize = 2;
748    ///     fn create_node<'id>(&self, a: &Allocator<'id>, _n: usize) -> &'id mut Node {
749    ///         a.alloc(Node { n: 0 })
750    ///     }
751    ///     fn set_children<'id>(&self, _n: &mut Node, _c: Children<'id, Node>) {}
752    ///     fn set_bounds<'id>(&self, _n: &mut Node, _b: ChildBounds<'_>) {}
753    ///     fn create_leaf<'id>(&self, a: &Allocator<'id>, _p: &[BuildPrimitive]) -> &'id mut Node {
754    ///         a.alloc(Node { n: 0 })
755    ///     }
756    /// }
757    /// let device = Device::new().unwrap();
758    /// let mut outer = device.create_bvh().unwrap();
759    /// let mut inner = device.create_bvh().unwrap();
760    /// let mut po: Vec<BuildPrimitive> = Vec::new();
761    /// let mut pi: Vec<BuildPrimitive> = Vec::new();
762    /// let cfg = BuildConfig::default();
763    /// outer.build_scoped(&cfg, &mut po, &B, |ra| {
764    ///     // ERROR: `ra`'s handle has a different brand than `rb`'s scope, so it cannot be
765    ///     // passed to `rb.resolve`. (Returns a `Copy` field to isolate that one error.)
766    ///     inner.build_scoped(&cfg, &mut pi, &B, |rb| {
767    ///         rb.resolve(ra.root_ptr().unwrap()).n
768    ///     })
769    /// }).unwrap().unwrap();
770    /// ```
771    pub fn build_scoped<B, F, R>(
772        &mut self,
773        config: &BuildConfig,
774        primitives: &mut Vec<BuildPrimitive>,
775        builder: &B,
776        f: F,
777    ) -> Result<R, Error>
778    where
779        B: BvhBuilder,
780        F: for<'id> FnOnce(BvhResult<'id, B>) -> R,
781    {
782        self.build_with_brand(config, primitives, builder, f)
783    }
784
785    fn build_with_brand<'id, B, F, R>(
786        &mut self,
787        config: &BuildConfig,
788        primitives: &mut Vec<BuildPrimitive>,
789        builder: &B,
790        f: F,
791    ) -> Result<R, Error>
792    where
793        B: BvhBuilder,
794        F: FnOnce(BvhResult<'id, B>) -> R,
795    {
796        config.validate(primitives.len(), B::MAX_CHILDREN)?;
797        if std::mem::size_of::<B::Node<'id>>() == 0 {
798            return Err(Error::INVALID_ARGUMENT);
799        }
800        // Version check (NOT hard enforcement): the safe build path relies on the
801        // per-node setter serialization audited for embree 3.13.5. This rejects any
802        // other *reported* version, but is a version check plus a
803        // trusted-native-library assumption -- a patched libembree3 reporting
804        // 31305 with different callback ordering is not detected; hard
805        // enforcement would need a vendored/audited binary.
806        if self.device.get_property(DeviceProperty::VERSION)? != RTC_VERSION as isize {
807            return Err(Error::INVALID_OPERATION);
808        }
809
810        if primitives.is_empty() {
811            return Ok(f(BvhResult {
812                root: None,
813                _brand: PhantomData,
814            }));
815        }
816
817        // Capacity for spatial splits.
818        if B::SPATIAL_SPLITS && config.quality == BuildQuality::HIGH {
819            let need = primitives
820                .len()
821                .checked_mul(2)
822                .ok_or(Error::INVALID_ARGUMENT)?;
823            primitives.reserve(need - primitives.len());
824        }
825        let capacity = primitives.capacity();
826        let count = primitives.len();
827
828        let state = BuildState::<B> {
829            builder: builder as *const B,
830        };
831
832        let mut args: RTCBuildArguments = unsafe { std::mem::zeroed() };
833        args.byteSize = std::mem::size_of::<RTCBuildArguments>();
834        args.buildQuality = config.quality;
835        args.buildFlags = if config.dynamic {
836            RTCBuildFlags::DYNAMIC
837        } else {
838            RTCBuildFlags::NONE
839        };
840        args.maxBranchingFactor = config.max_branching_factor;
841        args.maxDepth = config.max_depth;
842        args.sahBlockSize = config.sah_block_size;
843        args.minLeafSize = config.min_leaf_size;
844        args.maxLeafSize = config.max_leaf_size;
845        args.traversalCost = config.traversal_cost;
846        args.intersectionCost = config.intersection_cost;
847        args.bvh = self.handle;
848        args.primitives = primitives.as_mut_ptr();
849        args.primitiveCount = count;
850        args.primitiveArrayCapacity = capacity;
851        args.createNode = Some(create_node_tramp::<B>);
852        args.setNodeChildren = Some(set_children_tramp::<B>);
853        args.setNodeBounds = Some(set_bounds_tramp::<B>);
854        args.createLeaf = Some(create_leaf_tramp::<B>);
855        args.splitPrimitive = if B::SPATIAL_SPLITS {
856            Some(split_tramp::<B>)
857        } else {
858            None
859        };
860        args.buildProgress = if B::PROGRESS {
861            Some(progress_tramp::<B>)
862        } else {
863            None
864        };
865        args.userPtr = &state as *const BuildState<B> as *mut c_void;
866
867        let _ = self.device.get_error();
868        let root = unsafe { rtcBuildBVH(&args) };
869        if root.is_null() {
870            return Err(match self.device.get_error() {
871                Error::NONE => Error::UNKNOWN,
872                e => e,
873            });
874        }
875        // SAFETY (rebrand 2): the callbacks allocated nodes at a callback-local
876        // lifetime; here the root is reinterpreted as `B::Node<'id>` for the
877        // result's brand. Sound by the `BvhNode` family-level rebrand
878        // requirement (all `Node<'_>` are layout-identical and differ only by
879        // phantom `NodePtr` brands), and the arena outlives `'id`.
880        let root = NonNull::new(root as *mut B::Node<'id>).map(|ptr| NodePtr {
881            ptr,
882            _brand: PhantomData,
883        });
884        Ok(f(BvhResult {
885            root,
886            _brand: PhantomData,
887        }))
888    }
889}
890
891fn bounds_of(p: &BuildPrimitive) -> Bounds {
892    Bounds {
893        lower_x: p.lower_x,
894        lower_y: p.lower_y,
895        lower_z: p.lower_z,
896        align0: 0.0,
897        upper_x: p.upper_x,
898        upper_y: p.upper_y,
899        upper_z: p.upper_z,
900        align1: 0.0,
901    }
902}
903fn set_axis_upper(b: &mut Bounds, dim: u32, pos: f32) {
904    match dim {
905        0 => b.upper_x = pos,
906        1 => b.upper_y = pos,
907        _ => b.upper_z = pos,
908    }
909}
910fn set_axis_lower(b: &mut Bounds, dim: u32, pos: f32) {
911    match dim {
912        0 => b.lower_x = pos,
913        1 => b.lower_y = pos,
914        _ => b.lower_z = pos,
915    }
916}