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, 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#[derive(Debug, Clone)]
308#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
309pub struct TraceBlasTriangleGeometry {
310    pub size: wgt::BlasTriangleGeometrySizeDescriptor,
311    pub vertex_buffer: BufferId,
312    pub index_buffer: Option<BufferId>,
313    pub transform_buffer: Option<BufferId>,
314    pub first_vertex: u32,
315    pub vertex_stride: BufferAddress,
316    pub first_index: Option<u32>,
317    pub transform_buffer_offset: Option<BufferAddress>,
318}
319
320#[derive(Debug, Clone)]
321#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
322pub enum TraceBlasGeometries {
323    TriangleGeometries(Vec<TraceBlasTriangleGeometry>),
324}
325
326#[derive(Debug, Clone)]
327#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
328pub struct TraceBlasBuildEntry {
329    pub blas_id: BlasId,
330    pub geometries: TraceBlasGeometries,
331}
332
333#[derive(Debug, Clone)]
334#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
335pub struct TraceTlasInstance {
336    pub blas_id: BlasId,
337    pub transform: [f32; 12],
338    pub custom_data: u32,
339    pub mask: u8,
340}
341
342#[derive(Debug, Clone)]
343#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
344pub struct TraceTlasPackage {
345    pub tlas_id: TlasId,
346    pub instances: Vec<Option<TraceTlasInstance>>,
347    pub lowest_unmodified: u32,
348}
349
350#[derive(Clone, Debug, Error)]
351pub enum BlasPrepareCompactError {
352    #[error(transparent)]
353    Device(#[from] DeviceError),
354    #[error(transparent)]
355    InvalidResource(#[from] InvalidResourceError),
356    #[error("Compaction is already being prepared")]
357    CompactionPreparingAlready,
358    #[error("Cannot compact an already compacted BLAS")]
359    DoubleCompaction,
360    #[error("BLAS is not yet built")]
361    NotBuilt,
362    #[error("BLAS does not support compaction (is AccelerationStructureFlags::ALLOW_COMPACTION missing?)")]
363    CompactionUnsupported,
364}
365
366impl WebGpuError for BlasPrepareCompactError {
367    fn webgpu_error_type(&self) -> ErrorType {
368        let e: &dyn WebGpuError = match self {
369            Self::Device(e) => e,
370            Self::InvalidResource(e) => e,
371            Self::CompactionPreparingAlready
372            | Self::DoubleCompaction
373            | Self::NotBuilt
374            | Self::CompactionUnsupported => return ErrorType::Validation,
375        };
376        e.webgpu_error_type()
377    }
378}
379
380#[derive(Clone, Debug, Error)]
381pub enum CompactBlasError {
382    #[error(transparent)]
383    Encoder(#[from] EncoderStateError),
384
385    #[error(transparent)]
386    Device(#[from] DeviceError),
387
388    #[error(transparent)]
389    InvalidResource(#[from] InvalidResourceError),
390
391    #[error(transparent)]
392    DestroyedResource(#[from] DestroyedResourceError),
393
394    #[error(transparent)]
395    MissingFeatures(#[from] MissingFeatures),
396
397    #[error("BLAS was not prepared for compaction")]
398    BlasNotReady,
399}
400
401impl WebGpuError for CompactBlasError {
402    fn webgpu_error_type(&self) -> ErrorType {
403        let e: &dyn WebGpuError = match self {
404            Self::Encoder(e) => e,
405            Self::Device(e) => e,
406            Self::InvalidResource(e) => e,
407            Self::DestroyedResource(e) => e,
408            Self::MissingFeatures(e) => e,
409            Self::BlasNotReady => return ErrorType::Validation,
410        };
411        e.webgpu_error_type()
412    }
413}
414
415pub type BlasCompactReadyPendingClosure = (Option<BlasCompactCallback>, BlasPrepareCompactResult);