wgpu/api/
blas.rs

1#[cfg(wgpu_core)]
2use core::ops::Deref;
3
4use alloc::{boxed::Box, vec::Vec};
5
6use wgt::{WasmNotSend, WasmNotSendSync};
7
8use crate::dispatch;
9use crate::{Buffer, Label};
10
11/// Descriptor for the size defining attributes of a triangle geometry, for a bottom level acceleration structure.
12pub type BlasTriangleGeometrySizeDescriptor = wgt::BlasTriangleGeometrySizeDescriptor;
13static_assertions::assert_impl_all!(BlasTriangleGeometrySizeDescriptor: Send, Sync);
14
15/// Descriptor for the size defining attributes of an AABB geometry group in a bottom level acceleration structure.
16pub type BlasAABBGeometrySizeDescriptor = wgt::BlasAABBGeometrySizeDescriptor;
17static_assertions::assert_impl_all!(BlasAABBGeometrySizeDescriptor: Send, Sync);
18
19/// Minimum stride for AABB geometry (24 bytes: two `vec3<f32>`).
20pub use wgt::AABB_GEOMETRY_MIN_STRIDE;
21
22/// Descriptor for the size defining attributes, for a bottom level acceleration structure.
23pub type BlasGeometrySizeDescriptors = wgt::BlasGeometrySizeDescriptors;
24static_assertions::assert_impl_all!(BlasGeometrySizeDescriptors: Send, Sync);
25
26/// Flags for an acceleration structure.
27pub type AccelerationStructureFlags = wgt::AccelerationStructureFlags;
28static_assertions::assert_impl_all!(AccelerationStructureFlags: Send, Sync);
29
30/// Flags for a geometry inside a bottom level acceleration structure.
31pub type AccelerationStructureGeometryFlags = wgt::AccelerationStructureGeometryFlags;
32static_assertions::assert_impl_all!(AccelerationStructureGeometryFlags: Send, Sync);
33
34/// Update mode for acceleration structure builds.
35pub type AccelerationStructureUpdateMode = wgt::AccelerationStructureUpdateMode;
36static_assertions::assert_impl_all!(AccelerationStructureUpdateMode: Send, Sync);
37
38/// Descriptor to create bottom level acceleration structures.
39pub type CreateBlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;
40static_assertions::assert_impl_all!(CreateBlasDescriptor<'_>: Send, Sync);
41
42/// Safe instance for a [Tlas].
43///
44/// A TlasInstance may be made invalid, if a TlasInstance is invalid, any attempt to build a [Tlas] containing an
45/// invalid TlasInstance will generate a validation error
46///
47/// Each one contains:
48/// - A reference to a BLAS, this ***must*** be interacted with using [TlasInstance::new] or [TlasInstance::set_blas], a
49///   TlasInstance that references a BLAS keeps that BLAS from being dropped
50/// - A user accessible transformation matrix
51/// - A user accessible mask
52/// - A user accessible custom index
53///
54/// [Tlas]: crate::Tlas
55#[derive(Debug, Clone)]
56pub struct TlasInstance {
57    pub(crate) blas: dispatch::DispatchBlas,
58    /// Affine transform matrix 3x4 (rows x columns, row major order).
59    pub transform: [f32; 12],
60    /// Custom index for the instance used inside the shader.
61    ///
62    /// This must only use the lower 24 bits, if any bits are outside that range (byte 4 does not equal 0) the TlasInstance becomes
63    /// invalid and generates a validation error when built
64    pub custom_data: u32,
65    /// Mask for the instance used inside the shader to filter instances.
66    /// Reports hit only if `(shader_cull_mask & tlas_instance.mask) != 0u`.
67    pub mask: u8,
68}
69
70impl TlasInstance {
71    /// Construct TlasInstance.
72    /// - blas: Reference to the bottom level acceleration structure
73    /// - transform: Transform buffer offset in bytes (optional, required if transform buffer is present)
74    /// - custom_data: Custom index for the instance used inside the shader (max 24 bits)
75    /// - mask: Mask for the instance used inside the shader to filter instances
76    ///
77    /// Note: while one of these contains a reference to a BLAS that BLAS will not be dropped,
78    /// but it can still be destroyed. Destroying a BLAS that is referenced by one or more
79    /// TlasInstance(s) will immediately make them invalid. If one or more of those invalid
80    /// TlasInstances is inside a TlasPackage that is attempted to be built, the build will
81    /// generate a validation error.
82    pub fn new(blas: &Blas, transform: [f32; 12], custom_data: u32, mask: u8) -> Self {
83        Self {
84            blas: blas.inner.clone(),
85            transform,
86            custom_data,
87            mask,
88        }
89    }
90
91    /// Set the bottom level acceleration structure.
92    ///
93    /// See the note on [TlasInstance] about the
94    /// guarantees of keeping a BLAS alive.
95    pub fn set_blas(&mut self, blas: &Blas) {
96        self.blas = blas.inner.clone();
97    }
98
99    /// Returns custom implementation of the BLAS referenced by this instance
100    /// (if the custom backend is active and the BLAS is internally of type T).
101    #[cfg(custom)]
102    pub fn blas_as_custom<T: crate::custom::BlasInterface>(&self) -> Option<&T> {
103        self.blas.as_custom()
104    }
105}
106
107#[derive(Debug)]
108/// Definition for a triangle geometry for a Bottom Level Acceleration Structure (BLAS).
109///
110/// The size must match the rest of the structures fields, otherwise the build will fail.
111/// (e.g. if index count is present in the size, the index buffer must be present as well.)
112pub struct BlasTriangleGeometry<'a> {
113    /// Sub descriptor for the size defining attributes of a triangle geometry.
114    pub size: &'a BlasTriangleGeometrySizeDescriptor,
115    /// Vertex buffer.
116    pub vertex_buffer: &'a Buffer,
117    /// Offset into the vertex buffer as a factor of the vertex stride.
118    pub first_vertex: u32,
119    /// Vertex stride, must be greater than [`wgpu_types::VertexFormat::min_acceleration_structure_vertex_stride`]
120    /// of the format and must be a multiple of [`wgpu_types::VertexFormat::acceleration_structure_stride_alignment`].
121    pub vertex_stride: wgt::BufferAddress,
122    /// Index buffer (optional).
123    pub index_buffer: Option<&'a Buffer>,
124    /// Number of indexes to skip in the index buffer (optional, required if index buffer is present).
125    pub first_index: Option<u32>,
126    /// Transform buffer containing 3x4 (rows x columns, row major) affine transform matrices `[f32; 12]` (optional).
127    pub transform_buffer: Option<&'a Buffer>,
128    /// Transform buffer offset in bytes (optional, required if transform buffer is present).
129    pub transform_buffer_offset: Option<wgt::BufferAddress>,
130}
131static_assertions::assert_impl_all!(BlasTriangleGeometry<'_>: WasmNotSendSync);
132
133/// Definition for an axis-aligned bounding box geometry group for a bottom level acceleration structure.
134///
135/// Buffer data must contain `size.primitive_count` primitives at `primitive_offset`, each `size.stride` bytes,
136/// with `stride` at least [`AABB_GEOMETRY_MIN_STRIDE`] and a multiple of 8.
137#[derive(Debug)]
138pub struct BlasAabbGeometry<'a> {
139    /// Sub descriptor for the size defining attributes of this geometry.
140    pub size: &'a BlasAABBGeometrySizeDescriptor,
141    /// Stride in bytes between consecutive AABB primitives in the buffer (at least
142    /// [`AABB_GEOMETRY_MIN_STRIDE`], and must be a multiple of 8).
143    pub stride: wgt::BufferAddress,
144    /// Buffer containing packed AABB primitives (layout determined by `size.stride`).
145    pub aabb_buffer: &'a Buffer,
146    /// Byte offset to the first AABB primitive (must be a multiple of 8).
147    pub primitive_offset: u32,
148}
149static_assertions::assert_impl_all!(BlasAabbGeometry<'_>: WasmNotSendSync);
150
151/// Contains the sets of geometry that go into a [Blas].
152#[derive(Debug)]
153pub enum BlasGeometries<'a> {
154    /// Triangle geometry variant.
155    TriangleGeometries(Vec<BlasTriangleGeometry<'a>>),
156    /// Procedural AABB geometry variant.
157    AabbGeometries(Vec<BlasAabbGeometry<'a>>),
158}
159static_assertions::assert_impl_all!(BlasGeometries<'_>: WasmNotSendSync);
160
161/// Builds the given sets of geometry into the given [Blas].
162#[derive(Debug)]
163pub struct BlasBuildEntry<'a> {
164    /// Reference to the acceleration structure.
165    pub blas: &'a Blas,
166    /// Geometries.
167    pub geometry: BlasGeometries<'a>,
168}
169static_assertions::assert_impl_all!(BlasBuildEntry<'_>: WasmNotSendSync);
170
171/// Bottom Level Acceleration Structure (BLAS).
172///
173/// A BLAS is a device-specific raytracing acceleration structure that contains geometry data.
174///
175/// These BLASes are combined with transform in a [TlasInstance] to create a [Tlas].
176///
177/// [Tlas]: crate::Tlas
178#[derive(Debug, Clone)]
179pub struct Blas {
180    pub(crate) handle: Option<u64>,
181    pub(crate) inner: dispatch::DispatchBlas,
182}
183static_assertions::assert_impl_all!(Blas: WasmNotSendSync);
184
185crate::cmp::impl_eq_ord_hash_proxy!(Blas => .inner);
186
187impl Blas {
188    /// Raw handle to the acceleration structure, used inside raw instance buffers.
189    pub fn handle(&self) -> Option<u64> {
190        self.handle
191    }
192
193    /// Get the [`wgpu_hal`] acceleration structure from this `Blas`.
194    ///
195    /// Find the Api struct corresponding to the active backend in [`wgpu_hal::api`],
196    /// and pass that struct to the to the `A` type parameter.
197    ///
198    /// Returns a guard that dereferences to the type of the hal backend
199    /// which implements [`A::AccelerationStructure`].
200    ///
201    /// # Types
202    ///
203    /// The returned type depends on the backend:
204    ///
205    #[doc = crate::macros::hal_type_vulkan!("AccelerationStructure")]
206    #[doc = crate::macros::hal_type_metal!("AccelerationStructure")]
207    #[doc = crate::macros::hal_type_dx12!("AccelerationStructure")]
208    #[doc = crate::macros::hal_type_gles!("AccelerationStructure")]
209    ///
210    /// # Deadlocks
211    ///
212    /// - The returned guard holds a read-lock on a device-local "destruction"
213    ///   lock, which will cause all calls to `destroy` to block until the
214    ///   guard is released.
215    ///
216    /// # Errors
217    ///
218    /// This method will return None if:
219    /// - The acceleration structure is not from the backend specified by `A`.
220    /// - The acceleration structure is from the `webgpu` or `custom` backend.
221    ///
222    /// # Safety
223    ///
224    /// - The returned resource must not be destroyed unless the guard
225    ///   is the last reference to it and it is not in use by the GPU.
226    ///   The guard and handle may be dropped at any time however.
227    /// - All the safety requirements of wgpu-hal must be upheld.
228    ///
229    /// [`A::AccelerationStructure`]: hal::Api::AccelerationStructure
230    #[cfg(wgpu_core)]
231    pub unsafe fn as_hal<A: hal::Api>(
232        &mut self,
233    ) -> Option<impl Deref<Target = A::AccelerationStructure> + WasmNotSendSync> {
234        let blas = self.inner.as_core_opt()?;
235        unsafe { blas.context.blas_as_hal::<A>(blas) }
236    }
237
238    #[cfg(custom)]
239    /// Returns custom implementation of Blas (if custom backend and is internally T)
240    pub fn as_custom<T: crate::custom::BlasInterface>(&self) -> Option<&T> {
241        self.inner.as_custom()
242    }
243}
244
245/// Context version of [BlasTriangleGeometry].
246#[expect(missing_debug_implementations)]
247pub struct ContextBlasTriangleGeometry<'a> {
248    #[expect(dead_code)]
249    pub(crate) size: &'a BlasTriangleGeometrySizeDescriptor,
250    #[expect(dead_code)]
251    pub(crate) vertex_buffer: &'a dispatch::DispatchBuffer,
252    #[expect(dead_code)]
253    pub(crate) index_buffer: Option<&'a dispatch::DispatchBuffer>,
254    #[expect(dead_code)]
255    pub(crate) transform_buffer: Option<&'a dispatch::DispatchBuffer>,
256    #[expect(dead_code)]
257    pub(crate) first_vertex: u32,
258    #[expect(dead_code)]
259    pub(crate) vertex_stride: wgt::BufferAddress,
260    #[expect(dead_code)]
261    pub(crate) index_buffer_offset: Option<wgt::BufferAddress>,
262    #[expect(dead_code)]
263    pub(crate) transform_buffer_offset: Option<wgt::BufferAddress>,
264}
265
266/// Context version of [BlasGeometries].
267#[expect(missing_debug_implementations)]
268pub enum ContextBlasGeometries<'a> {
269    /// Triangle geometries.
270    TriangleGeometries(Box<dyn Iterator<Item = ContextBlasTriangleGeometry<'a>> + 'a>),
271    /// AABB geometries.
272    AabbGeometries(Box<dyn Iterator<Item = ContextBlasAabbGeometry<'a>> + 'a>),
273}
274
275/// Context version of [BlasAabbGeometry].
276#[expect(missing_debug_implementations)]
277pub struct ContextBlasAabbGeometry<'a> {
278    #[expect(dead_code)]
279    pub(crate) size: &'a BlasAABBGeometrySizeDescriptor,
280    #[expect(dead_code)]
281    pub(crate) stride: wgt::BufferAddress,
282    #[expect(dead_code)]
283    pub(crate) aabb_buffer: &'a dispatch::DispatchBuffer,
284    #[expect(dead_code)]
285    pub(crate) primitive_offset: u32,
286}
287
288/// Context version see [BlasBuildEntry].
289#[expect(missing_debug_implementations)]
290pub struct ContextBlasBuildEntry<'a> {
291    #[expect(dead_code)]
292    pub(crate) blas: &'a dispatch::DispatchBlas,
293    #[expect(dead_code)]
294    pub(crate) geometries: ContextBlasGeometries<'a>,
295}
296
297/// Error occurred when trying to asynchronously prepare a blas for compaction.
298#[derive(Clone, PartialEq, Eq, Debug)]
299pub struct BlasAsyncError;
300static_assertions::assert_impl_all!(BlasAsyncError: Send, Sync);
301
302impl core::fmt::Display for BlasAsyncError {
303    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
304        write!(
305            f,
306            "Error occurred when trying to asynchronously prepare a blas for compaction"
307        )
308    }
309}
310
311impl core::error::Error for BlasAsyncError {}
312
313impl Blas {
314    /// Asynchronously prepares this BLAS for compaction. The callback is called once all builds
315    /// using this BLAS are finished and the BLAS is compactable. This can be checked using
316    /// [`Blas::ready_for_compaction`]. Rebuilding this BLAS will reset its compacted state, and it
317    /// will need to be prepared again.
318    ///
319    /// ### Interaction with other functions
320    /// On native, `queue.submit(..)` and polling devices (that is calling `instance.poll_all` or
321    /// `device.poll`) with [`PollType::Poll`] may call the callback. On native, polling devices with
322    /// [`PollType::Wait`] (optionally with a submission index greater
323    /// than the last submit the BLAS was used in) will guarantee callback is called.
324    ///
325    /// [`PollType::Poll`]: wgpu_types::PollType::Poll
326    /// [`PollType::Wait`]: wgpu_types::PollType::Wait
327    pub fn prepare_compaction_async(
328        &self,
329        callback: impl FnOnce(Result<(), BlasAsyncError>) + WasmNotSend + 'static,
330    ) {
331        self.inner.prepare_compact_async(Box::new(callback));
332    }
333
334    /// Checks whether this BLAS is ready for compaction. The returned value is `true` if
335    /// [`Blas::prepare_compaction_async`]'s callback was called with a non-error value, otherwise
336    /// this is `false`.
337    pub fn ready_for_compaction(&self) -> bool {
338        self.inner.ready_for_compaction()
339    }
340}