Skip to main content

tinybvh_rs/layouts/
cwbvh.rs

1//! CWBVH GPU-friendly layout
2//!
3//! Based on "Efficient Incoherent Ray Traversal on GPUs Through Compressed Wide BVHs".
4
5use crate::{ffi, mbvh, Error};
6use std::fmt::Debug;
7
8pub struct PrimitiveIter {
9    primitive_base_index: u32,
10    child_meta: [u8; 8],
11
12    curr_meta_idx: u8,
13    curr_tri_count: u8,
14}
15
16impl PrimitiveIter {
17    fn new(base_index: u32, meta: [u8; 8]) -> Self {
18        Self {
19            primitive_base_index: base_index,
20            child_meta: meta,
21
22            curr_meta_idx: 0,
23            curr_tri_count: 0,
24        }
25    }
26}
27
28impl Iterator for PrimitiveIter {
29    type Item = u32;
30
31    fn next(&mut self) -> Option<Self::Item> {
32        if self.curr_meta_idx as usize >= self.child_meta.len() {
33            return None;
34        }
35        while self.curr_meta_idx < self.child_meta.len() as u8 {
36            let meta = self.child_meta[self.curr_meta_idx as usize];
37            let triangles_count = (meta & 0b11100000).count_ones() as u8;
38            let current_tri_count = self.curr_tri_count;
39            self.curr_tri_count += 1;
40            if current_tri_count < triangles_count {
41                let start = meta & 0b00011111;
42                return Some(self.primitive_base_index + start as u32 + current_tri_count as u32);
43            }
44            self.curr_meta_idx += 1;
45            self.curr_tri_count = 0;
46        }
47        None
48    }
49}
50
51/// Format specified in:
52/// "Efficient Incoherent Ray Traversal on GPUs Through Compressed Wide BVHs", Ylitie et al. 2017.
53///
54/// Node layout used by [`BVH`].
55#[repr(C)]
56#[derive(Clone, Copy, Debug, Default, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
57pub struct Node {
58    /// AABB min.
59    pub min: [f32; 3],
60    /// Exponent used for child AABB decompression.
61    pub exyz: [u8; 3],
62    /// `1` for node, `0` for leaf.
63    pub imask: u8,
64    /// First child index.
65    pub child_base_idx: u32,
66    // First primitive index.
67    pub primitive_base_idx: u32,
68    /// Child [0..7] metadata.
69    pub child_meta: [u8; 8],
70    // AABB minimum x-axis compressed bound, one entry per child.
71    pub qlo_x: [u8; 8],
72    // AABB minimum y-axis compressed bound, one entry per child.
73    pub qlo_y: [u8; 8],
74    // AABB minimum z-axis compressed bound, one entry per child.
75    pub qlo_z: [u8; 8],
76    // AABB maximum x-axis compressed bound, one entry per child.
77    pub qhi_x: [u8; 8],
78    // AABB maximum y-axis compressed bound, one entry per child.
79    pub qhi_y: [u8; 8],
80    // AABB maximum z-axis compressed bound, one entry per child.
81    pub qhi_z: [u8; 8],
82}
83
84impl Node {
85    /// Returns `true` if the node is a leaf.
86    pub fn is_leaf(&self) -> bool {
87        self.imask == 0
88    }
89
90    pub fn primitives(&self) -> PrimitiveIter {
91        if !self.is_leaf() {
92            return PrimitiveIter::new(0, [0, 0, 0, 0, 0, 0, 0, 0]);
93        }
94        PrimitiveIter::new(self.primitive_base_idx, self.child_meta)
95    }
96}
97
98/// Custom primitive used by [`BVH`].
99#[repr(C)]
100#[derive(Clone, Copy, Default, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
101pub struct Primitive {
102    pub edge_1: [f32; 3],
103    pub padding_0: u32,
104    pub edge_2: [f32; 3],
105    pub padding_1: u32,
106    pub vertex_0: [f32; 3],
107    pub original_primitive: u32,
108}
109
110impl Debug for Primitive {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        f.debug_struct("cwbvh::Primitive")
113            .field("vertex_0", &self.vertex_0)
114            .field("edge_1", &self.edge_1)
115            .field("edge_2", &self.edge_2)
116            .field("original_primitive", &self.original_primitive)
117            .finish()
118    }
119}
120
121/// CWBVH with node layout [`Node`].
122pub struct BVH {
123    inner: cxx::UniquePtr<ffi::BVH8_CWBVH>,
124}
125
126impl BVH {
127    pub fn new(original: &mbvh::BVH) -> Result<Self, Error> {
128        let bvh = BVH {
129            inner: ffi::CWBVH_new(),
130        };
131        bvh.convert(original)
132    }
133
134    pub fn convert(mut self, original: &mbvh::BVH) -> Result<Self, Error> {
135        Error::validate_leaf_count(3, original.max_primitives_per_leaf)?;
136        self.inner
137            .pin_mut()
138            .ConvertFrom(original.inner.as_ref().unwrap(), true);
139        Ok(self)
140    }
141
142    pub fn nodes(&self) -> &[Node] {
143        // TODO: Create CWBVH node in tinybvh to avoid that.
144        let ptr = ffi::CWBVH_nodes(&self.inner) as *const Node;
145        let count = ffi::CWBVH_nodes_count(&self.inner);
146        unsafe { std::slice::from_raw_parts(ptr, count as usize) }
147    }
148
149    /// Encoded primitive data.
150    ///
151    /// This layout is intersected using a custom primitive array
152    /// instead of the original list used during building.
153    pub fn primitives(&self) -> &[Primitive] {
154        // TODO: Create struct in tinybvh to avoid that.
155        let ptr = ffi::CWBVH_primitives(&self.inner) as *const Primitive;
156        let count = ffi::CWBVH_primitives_count(&self.inner);
157        unsafe { std::slice::from_raw_parts(ptr, count as usize) }
158    }
159}