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