wgpu_core/
ray_tracing.rs

1// Ray tracing
2// Major missing optimizations (no api surface changes needed):
3// - use custom tracker to track build state
4// - no forced rebuilt (build mode deduction)
5// - lazy instance buffer allocation
6// - maybe share scratch and instance staging buffer allocation
7// - partial instance buffer uploads (api surface already designed with this in mind)
8// - Batch BLAS read-backs (if it shows up in performance).
9// - ([non performance] extract function in build (rust function extraction with guards is a pain))
10
11use alloc::{boxed::Box, sync::Arc, vec::Vec};
12
13use thiserror::Error;
14use wgt::{
15    error::{ErrorType, WebGpuError},
16    AccelerationStructureGeometryFlags, BufferAddress, IndexFormat, VertexFormat,
17};
18
19use crate::{
20    command::EncoderStateError,
21    device::{DeviceError, MissingFeatures},
22    id::{BlasId, BufferId, TlasId},
23    resource::{
24        Blas, BlasCompactCallback, BlasPrepareCompactResult, Buffer, DestroyedResourceError,
25        InvalidResourceError, MissingBufferUsageError, ResourceErrorIdent, Tlas,
26    },
27};
28
29#[derive(Clone, Debug, Error)]
30pub enum CreateBlasError {
31    #[error(transparent)]
32    Device(#[from] DeviceError),
33    #[error(transparent)]
34    MissingFeatures(#[from] MissingFeatures),
35    #[error(
36        "Only one of 'index_count' and 'index_format' was provided (either provide both or none)"
37    )]
38    MissingIndexData,
39    #[error("Provided format was not within allowed formats. Provided format: {0:?}. Allowed formats: {1:?}")]
40    InvalidVertexFormat(VertexFormat, Vec<VertexFormat>),
41    #[error("Limit `max_blas_geometry_count` is {0}, but the BLAS had {1} geometries")]
42    TooManyGeometries(u32, u32),
43    #[error(
44        "Limit `max_blas_primitive_count` is {0}, but the BLAS had a maximum of {1} primitives"
45    )]
46    TooManyPrimitives(u32, u32),
47}
48
49impl WebGpuError for CreateBlasError {
50    fn webgpu_error_type(&self) -> ErrorType {
51        let e: &dyn WebGpuError = match self {
52            Self::Device(e) => e,
53            Self::MissingFeatures(e) => e,
54            Self::MissingIndexData
55            | Self::InvalidVertexFormat(..)
56            | Self::TooManyGeometries(..)
57            | Self::TooManyPrimitives(..) => return ErrorType::Validation,
58        };
59        e.webgpu_error_type()
60    }
61}
62
63#[derive(Clone, Debug, Error)]
64pub enum CreateTlasError {
65    #[error(transparent)]
66    Device(#[from] DeviceError),
67    #[error(transparent)]
68    MissingFeatures(#[from] MissingFeatures),
69    #[error("Flag {0:?} is not allowed on a TLAS")]
70    DisallowedFlag(wgt::AccelerationStructureFlags),
71    #[error("Limit `max_tlas_instance_count` is {0}, but the TLAS had a maximum of {1} instances")]
72    TooManyInstances(u32, u32),
73}
74
75impl WebGpuError for CreateTlasError {
76    fn webgpu_error_type(&self) -> ErrorType {
77        let e: &dyn WebGpuError = match self {
78            Self::Device(e) => e,
79            Self::MissingFeatures(e) => e,
80            Self::DisallowedFlag(..) | Self::TooManyInstances(..) => return ErrorType::Validation,
81        };
82        e.webgpu_error_type()
83    }
84}
85
86/// Error encountered while attempting to do a copy on a command encoder.
87#[derive(Clone, Debug, Error)]
88pub enum BuildAccelerationStructureError {
89    #[error(transparent)]
90    EncoderState(#[from] EncoderStateError),
91
92    #[error(transparent)]
93    Device(#[from] DeviceError),
94
95    #[error(transparent)]
96    InvalidResource(#[from] InvalidResourceError),
97
98    #[error(transparent)]
99    DestroyedResource(#[from] DestroyedResourceError),
100
101    #[error(transparent)]
102    MissingBufferUsage(#[from] MissingBufferUsageError),
103
104    #[error(transparent)]
105    MissingFeatures(#[from] MissingFeatures),
106
107    #[error(
108        "Buffer {0:?} size is insufficient for provided size information (size: {1}, required: {2}"
109    )]
110    InsufficientBufferSize(ResourceErrorIdent, u64, u64),
111
112    #[error("Buffer {0:?} associated offset doesn't align with the index type")]
113    UnalignedIndexBufferOffset(ResourceErrorIdent),
114
115    #[error("Buffer {0:?} associated offset is unaligned")]
116    UnalignedTransformBufferOffset(ResourceErrorIdent),
117
118    #[error("Buffer {0:?} associated index count not divisible by 3 (count: {1}")]
119    InvalidIndexCount(ResourceErrorIdent, u32),
120
121    #[error("Buffer {0:?} associated data contains None")]
122    MissingAssociatedData(ResourceErrorIdent),
123
124    #[error(
125        "Blas {0:?} build sizes to may be greater than the descriptor at build time specified"
126    )]
127    IncompatibleBlasBuildSizes(ResourceErrorIdent),
128
129    #[error("Blas {0:?} flags are different, creation flags: {1:?}, provided: {2:?}")]
130    IncompatibleBlasFlags(
131        ResourceErrorIdent,
132        AccelerationStructureGeometryFlags,
133        AccelerationStructureGeometryFlags,
134    ),
135
136    #[error("Blas {0:?} build vertex count is greater than creation count (needs to be less than or equal to), creation: {1:?}, build: {2:?}")]
137    IncompatibleBlasVertexCount(ResourceErrorIdent, u32, u32),
138
139    #[error("Blas {0:?} vertex formats are different, creation format: {1:?}, provided: {2:?}")]
140    DifferentBlasVertexFormats(ResourceErrorIdent, VertexFormat, VertexFormat),
141
142    #[error("Blas {0:?} stride was required to be at least {1} but stride given was {2}")]
143    VertexStrideTooSmall(ResourceErrorIdent, u64, u64),
144
145    #[error("Blas {0:?} stride was required to be a multiple of {1} but stride given was {2}")]
146    VertexStrideUnaligned(ResourceErrorIdent, u64, u64),
147
148    #[error("Blas {0:?} index count was provided at creation or building, but not the other")]
149    BlasIndexCountProvidedMismatch(ResourceErrorIdent),
150
151    #[error("Blas {0:?} build index count is greater than creation count (needs to be less than or equal to), creation: {1:?}, build: {2:?}")]
152    IncompatibleBlasIndexCount(ResourceErrorIdent, u32, u32),
153
154    #[error("Blas {0:?} index formats are different, creation format: {1:?}, provided: {2:?}")]
155    DifferentBlasIndexFormats(ResourceErrorIdent, Option<IndexFormat>, Option<IndexFormat>),
156
157    #[error("Blas {0:?} is compacted and so cannot be built")]
158    CompactedBlas(ResourceErrorIdent),
159
160    #[error("Blas {0:?} build sizes require index buffer but none was provided")]
161    MissingIndexBuffer(ResourceErrorIdent),
162
163    #[error(
164        "Tlas {0:?} an associated instances contains an invalid custom index (more than 24bits)"
165    )]
166    TlasInvalidCustomIndex(ResourceErrorIdent),
167
168    #[error(
169        "Tlas {0:?} has {1} active instances but only {2} are allowed as specified by the descriptor at creation"
170    )]
171    TlasInstanceCountExceeded(ResourceErrorIdent, u32, u32),
172
173    #[error("Blas {0:?} has flag USE_TRANSFORM but the transform buffer is missing")]
174    TransformMissing(ResourceErrorIdent),
175
176    #[error("Blas {0:?} is missing the flag USE_TRANSFORM but the transform buffer is set")]
177    UseTransformMissing(ResourceErrorIdent),
178    #[error(
179        "Tlas {0:?} dependent {1:?} is missing AccelerationStructureFlags::ALLOW_RAY_HIT_VERTEX_RETURN"
180    )]
181    TlasDependentMissingVertexReturn(ResourceErrorIdent, ResourceErrorIdent),
182}
183
184impl WebGpuError for BuildAccelerationStructureError {
185    fn webgpu_error_type(&self) -> ErrorType {
186        let e: &dyn WebGpuError = match self {
187            Self::EncoderState(e) => e,
188            Self::Device(e) => e,
189            Self::InvalidResource(e) => e,
190            Self::DestroyedResource(e) => e,
191            Self::MissingBufferUsage(e) => e,
192            Self::MissingFeatures(e) => e,
193            Self::InsufficientBufferSize(..)
194            | Self::UnalignedIndexBufferOffset(..)
195            | Self::UnalignedTransformBufferOffset(..)
196            | Self::InvalidIndexCount(..)
197            | Self::MissingAssociatedData(..)
198            | Self::IncompatibleBlasBuildSizes(..)
199            | Self::IncompatibleBlasFlags(..)
200            | Self::IncompatibleBlasVertexCount(..)
201            | Self::DifferentBlasVertexFormats(..)
202            | Self::VertexStrideTooSmall(..)
203            | Self::VertexStrideUnaligned(..)
204            | Self::BlasIndexCountProvidedMismatch(..)
205            | Self::IncompatibleBlasIndexCount(..)
206            | Self::DifferentBlasIndexFormats(..)
207            | Self::CompactedBlas(..)
208            | Self::MissingIndexBuffer(..)
209            | Self::TlasInvalidCustomIndex(..)
210            | Self::TlasInstanceCountExceeded(..)
211            | Self::TransformMissing(..)
212            | Self::UseTransformMissing(..)
213            | Self::TlasDependentMissingVertexReturn(..) => return ErrorType::Validation,
214        };
215        e.webgpu_error_type()
216    }
217}
218
219#[derive(Clone, Debug, Error)]
220pub enum ValidateAsActionsError {
221    #[error(transparent)]
222    DestroyedResource(#[from] DestroyedResourceError),
223
224    #[error("Tlas {0:?} is used before it is built")]
225    UsedUnbuiltTlas(ResourceErrorIdent),
226
227    #[error("Blas {0:?} is used before it is built (in Tlas {1:?})")]
228    UsedUnbuiltBlas(ResourceErrorIdent, ResourceErrorIdent),
229
230    #[error("Blas {0:?} is newer than the containing Tlas {1:?}")]
231    BlasNewerThenTlas(ResourceErrorIdent, ResourceErrorIdent),
232}
233
234impl WebGpuError for ValidateAsActionsError {
235    fn webgpu_error_type(&self) -> ErrorType {
236        let e: &dyn WebGpuError = match self {
237            Self::DestroyedResource(e) => e,
238            Self::UsedUnbuiltTlas(..) | Self::UsedUnbuiltBlas(..) | Self::BlasNewerThenTlas(..) => {
239                return ErrorType::Validation
240            }
241        };
242        e.webgpu_error_type()
243    }
244}
245
246#[derive(Debug)]
247pub struct BlasTriangleGeometry<'a> {
248    pub size: &'a wgt::BlasTriangleGeometrySizeDescriptor,
249    pub vertex_buffer: BufferId,
250    pub index_buffer: Option<BufferId>,
251    pub transform_buffer: Option<BufferId>,
252    pub first_vertex: u32,
253    pub vertex_stride: BufferAddress,
254    pub first_index: Option<u32>,
255    pub transform_buffer_offset: Option<BufferAddress>,
256}
257
258pub enum BlasGeometries<'a> {
259    TriangleGeometries(Box<dyn Iterator<Item = BlasTriangleGeometry<'a>> + 'a>),
260}
261
262pub struct BlasBuildEntry<'a> {
263    pub blas_id: BlasId,
264    pub geometries: BlasGeometries<'a>,
265}
266
267#[derive(Debug, Clone)]
268#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
269pub struct TlasBuildEntry {
270    pub tlas_id: TlasId,
271    pub instance_buffer_id: BufferId,
272    pub instance_count: u32,
273}
274
275#[derive(Debug)]
276pub struct TlasInstance<'a> {
277    pub blas_id: BlasId,
278    pub transform: &'a [f32; 12],
279    pub custom_data: u32,
280    pub mask: u8,
281}
282
283pub struct TlasPackage<'a> {
284    pub tlas_id: TlasId,
285    pub instances: Box<dyn Iterator<Item = Option<TlasInstance<'a>>> + 'a>,
286    pub lowest_unmodified: u32,
287}
288
289#[derive(Debug, Clone)]
290pub(crate) struct TlasBuild {
291    pub tlas: Arc<Tlas>,
292    pub dependencies: Vec<Arc<Blas>>,
293}
294
295#[derive(Debug, Clone, Default)]
296pub(crate) struct AsBuild {
297    pub blas_s_built: Vec<Arc<Blas>>,
298    pub tlas_s_built: Vec<TlasBuild>,
299}
300
301#[derive(Debug, Clone)]
302pub(crate) enum AsAction {
303    Build(AsBuild),
304    UseTlas(Arc<Tlas>),
305}
306
307/// Like [`BlasTriangleGeometry`], but with owned data.
308#[derive(Debug, Clone)]
309#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
310pub struct TraceBlasTriangleGeometry {
311    pub size: wgt::BlasTriangleGeometrySizeDescriptor,
312    pub vertex_buffer: BufferId,
313    pub index_buffer: Option<BufferId>,
314    pub transform_buffer: Option<BufferId>,
315    pub first_vertex: u32,
316    pub vertex_stride: BufferAddress,
317    pub first_index: Option<u32>,
318    pub transform_buffer_offset: Option<BufferAddress>,
319}
320
321#[derive(Debug, Clone)]
322#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
323pub enum TraceBlasGeometries {
324    TriangleGeometries(Vec<TraceBlasTriangleGeometry>),
325}
326
327#[derive(Debug, Clone)]
328#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
329pub struct TraceBlasBuildEntry {
330    pub blas_id: BlasId,
331    pub geometries: TraceBlasGeometries,
332}
333
334#[derive(Debug, Clone)]
335#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
336pub struct TraceTlasInstance {
337    pub blas_id: BlasId,
338    pub transform: [f32; 12],
339    pub custom_data: u32,
340    pub mask: u8,
341}
342
343#[derive(Debug, Clone)]
344#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
345pub struct TraceTlasPackage {
346    pub tlas_id: TlasId,
347    pub instances: Vec<Option<TraceTlasInstance>>,
348    pub lowest_unmodified: u32,
349}
350
351/// Like [`BlasTriangleGeometry`], but with `Arc`s.
352#[derive(Debug, Clone)]
353pub struct ArcBlasTriangleGeometry {
354    pub size: wgt::BlasTriangleGeometrySizeDescriptor,
355    pub vertex_buffer: Arc<Buffer>,
356    pub index_buffer: Option<Arc<Buffer>>,
357    pub transform_buffer: Option<Arc<Buffer>>,
358    pub first_vertex: u32,
359    pub vertex_stride: BufferAddress,
360    pub first_index: Option<u32>,
361    pub transform_buffer_offset: Option<BufferAddress>,
362}
363
364/// [`BlasTriangleGeometry`], without the resources.
365#[derive(Debug, Clone)]
366pub struct BlasTriangleGeometryInfo {
367    pub size: wgt::BlasTriangleGeometrySizeDescriptor,
368    pub first_vertex: u32,
369    pub vertex_stride: BufferAddress,
370    pub first_index: Option<u32>,
371    pub transform_buffer_offset: Option<BufferAddress>,
372}
373
374#[derive(Debug, Clone)]
375pub enum ArcBlasGeometries {
376    TriangleGeometries(Vec<ArcBlasTriangleGeometry>),
377}
378
379#[derive(Debug, Clone)]
380pub struct ArcBlasBuildEntry {
381    pub blas: Arc<Blas>,
382    pub geometries: ArcBlasGeometries,
383}
384
385#[derive(Debug, Clone)]
386pub struct ArcTlasInstance {
387    pub blas: Arc<Blas>,
388    pub transform: [f32; 12],
389    pub custom_data: u32,
390    pub mask: u8,
391}
392
393#[derive(Debug, Clone)]
394pub struct ArcTlasPackage {
395    pub tlas: Arc<Tlas>,
396    pub instances: Vec<Option<ArcTlasInstance>>,
397    pub lowest_unmodified: u32,
398}
399
400#[derive(Clone, Debug, Error)]
401pub enum BlasPrepareCompactError {
402    #[error(transparent)]
403    Device(#[from] DeviceError),
404    #[error(transparent)]
405    InvalidResource(#[from] InvalidResourceError),
406    #[error("Compaction is already being prepared")]
407    CompactionPreparingAlready,
408    #[error("Cannot compact an already compacted BLAS")]
409    DoubleCompaction,
410    #[error("BLAS is not yet built")]
411    NotBuilt,
412    #[error("BLAS does not support compaction (is AccelerationStructureFlags::ALLOW_COMPACTION missing?)")]
413    CompactionUnsupported,
414}
415
416impl WebGpuError for BlasPrepareCompactError {
417    fn webgpu_error_type(&self) -> ErrorType {
418        let e: &dyn WebGpuError = match self {
419            Self::Device(e) => e,
420            Self::InvalidResource(e) => e,
421            Self::CompactionPreparingAlready
422            | Self::DoubleCompaction
423            | Self::NotBuilt
424            | Self::CompactionUnsupported => return ErrorType::Validation,
425        };
426        e.webgpu_error_type()
427    }
428}
429
430#[derive(Clone, Debug, Error)]
431pub enum CompactBlasError {
432    #[error(transparent)]
433    Encoder(#[from] EncoderStateError),
434
435    #[error(transparent)]
436    Device(#[from] DeviceError),
437
438    #[error(transparent)]
439    InvalidResource(#[from] InvalidResourceError),
440
441    #[error(transparent)]
442    DestroyedResource(#[from] DestroyedResourceError),
443
444    #[error(transparent)]
445    MissingFeatures(#[from] MissingFeatures),
446
447    #[error("BLAS was not prepared for compaction")]
448    BlasNotReady,
449}
450
451impl WebGpuError for CompactBlasError {
452    fn webgpu_error_type(&self) -> ErrorType {
453        let e: &dyn WebGpuError = match self {
454            Self::Encoder(e) => e,
455            Self::Device(e) => e,
456            Self::InvalidResource(e) => e,
457            Self::DestroyedResource(e) => e,
458            Self::MissingFeatures(e) => e,
459            Self::BlasNotReady => return ErrorType::Validation,
460        };
461        e.webgpu_error_type()
462    }
463}
464
465pub type BlasCompactReadyPendingClosure = (Option<BlasCompactCallback>, BlasPrepareCompactResult);