1use alloc::{borrow::Cow, borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};
2use core::{
3 borrow::Borrow,
4 fmt,
5 mem::{self, size_of, ManuallyDrop},
6 num::NonZeroU64,
7 ops::Range,
8 ptr::NonNull,
9};
10use smallvec::SmallVec;
11use thiserror::Error;
12use wgt::{
13 error::{ErrorType, WebGpuError},
14 TextureSelector,
15};
16
17#[cfg(feature = "trace")]
18use crate::device::trace;
19use crate::{
20 binding_model::BindGroup,
21 device::{
22 queue, resource::DeferredDestroy, BufferMapPendingClosure, Device, DeviceError,
23 DeviceMismatch, HostMap, MissingDownlevelFlags, MissingFeatures,
24 },
25 init_tracker::{BufferInitTracker, TextureInitTracker},
26 lock::{rank, Mutex, RwLock},
27 ray_tracing::{BlasCompactReadyPendingClosure, BlasPrepareCompactError},
28 resource_log,
29 snatch::{SnatchGuard, Snatchable},
30 timestamp_normalization::TimestampNormalizationBindGroup,
31 track::{SharedTrackerIndexAllocator, TrackerIndex},
32 weak_vec::WeakVec,
33 Label, LabelHelpers, SubmissionIndex,
34};
35
36#[derive(Debug)]
56pub(crate) struct TrackingData {
57 tracker_index: TrackerIndex,
58 tracker_indices: Arc<SharedTrackerIndexAllocator>,
59}
60
61impl Drop for TrackingData {
62 fn drop(&mut self) {
63 self.tracker_indices.free(self.tracker_index);
64 }
65}
66
67impl TrackingData {
68 pub(crate) fn new(tracker_indices: Arc<SharedTrackerIndexAllocator>) -> Self {
69 Self {
70 tracker_index: tracker_indices.alloc(),
71 tracker_indices,
72 }
73 }
74
75 pub(crate) fn tracker_index(&self) -> TrackerIndex {
76 self.tracker_index
77 }
78}
79
80#[derive(Clone, Debug)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub struct ResourceErrorIdent {
83 r#type: Cow<'static, str>,
84 label: String,
85}
86
87impl fmt::Display for ResourceErrorIdent {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
89 write!(f, "{} with '{}' label", self.r#type, self.label)
90 }
91}
92
93pub trait ParentDevice: Labeled {
94 fn device(&self) -> &Arc<Device>;
95
96 fn is_equal(self: &Arc<Self>, other: &Arc<Self>) -> bool {
97 Arc::ptr_eq(self, other)
98 }
99
100 fn same_device_as<O: ParentDevice>(&self, other: &O) -> Result<(), DeviceError> {
101 if Arc::ptr_eq(self.device(), other.device()) {
102 Ok(())
103 } else {
104 Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
105 res: self.error_ident(),
106 res_device: self.device().error_ident(),
107 target: Some(other.error_ident()),
108 target_device: other.device().error_ident(),
109 })))
110 }
111 }
112
113 fn same_device(&self, device: &Device) -> Result<(), DeviceError> {
114 if core::ptr::eq(&**self.device(), device) {
115 Ok(())
116 } else {
117 Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
118 res: self.error_ident(),
119 res_device: self.device().error_ident(),
120 target: None,
121 target_device: device.error_ident(),
122 })))
123 }
124 }
125}
126
127#[macro_export]
128macro_rules! impl_parent_device {
129 ($ty:ident) => {
130 impl $crate::resource::ParentDevice for $ty {
131 fn device(&self) -> &Arc<Device> {
132 &self.device
133 }
134 }
135 };
136}
137
138pub trait RawResourceAccess: ParentDevice {
140 type DynResource: hal::DynResource + ?Sized;
141
142 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource>;
147
148 fn try_raw<'a>(
153 &'a self,
154 guard: &'a SnatchGuard,
155 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
156 self.raw(guard)
157 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
158 }
159}
160
161pub trait ResourceType {
162 const TYPE: &'static str;
163}
164
165#[macro_export]
166macro_rules! impl_resource_type {
167 ($ty:ident) => {
168 impl $crate::resource::ResourceType for $ty {
169 const TYPE: &'static str = stringify!($ty);
170 }
171 };
172}
173
174pub trait Labeled: ResourceType {
175 fn label(&self) -> &str;
181
182 fn error_ident(&self) -> ResourceErrorIdent {
183 ResourceErrorIdent {
184 r#type: Cow::Borrowed(Self::TYPE),
185 label: self.label().to_owned(),
186 }
187 }
188}
189
190#[macro_export]
191macro_rules! impl_labeled {
192 ($ty:ident) => {
193 impl $crate::resource::Labeled for $ty {
194 fn label(&self) -> &str {
195 &self.label
196 }
197 }
198 };
199}
200
201pub(crate) trait Trackable {
202 fn tracker_index(&self) -> TrackerIndex;
203}
204
205#[macro_export]
206macro_rules! impl_trackable {
207 ($ty:ident) => {
208 impl $crate::resource::Trackable for $ty {
209 fn tracker_index(&self) -> $crate::track::TrackerIndex {
210 self.tracking_data.tracker_index()
211 }
212 }
213 };
214}
215
216#[derive(Debug)]
217pub(crate) enum BufferMapState {
218 Init { staging_buffer: StagingBuffer },
220 Waiting(BufferPendingMapping),
222 Active {
224 mapping: hal::BufferMapping,
225 range: hal::MemoryRange,
226 host: HostMap,
227 },
228 Idle,
230}
231
232#[cfg(send_sync)]
233unsafe impl Send for BufferMapState {}
234#[cfg(send_sync)]
235unsafe impl Sync for BufferMapState {}
236
237#[cfg(send_sync)]
238pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + Send + 'static>;
239#[cfg(not(send_sync))]
240pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + 'static>;
241
242pub struct BufferMapOperation {
243 pub host: HostMap,
244 pub callback: Option<BufferMapCallback>,
245}
246
247impl fmt::Debug for BufferMapOperation {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 f.debug_struct("BufferMapOperation")
250 .field("host", &self.host)
251 .field("callback", &self.callback.as_ref().map(|_| "?"))
252 .finish()
253 }
254}
255
256#[derive(Clone, Debug, Error)]
257#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
258#[non_exhaustive]
259pub enum BufferAccessError {
260 #[error(transparent)]
261 Device(#[from] DeviceError),
262 #[error("Buffer map failed")]
263 Failed,
264 #[error(transparent)]
265 DestroyedResource(#[from] DestroyedResourceError),
266 #[error("Buffer is already mapped")]
267 AlreadyMapped,
268 #[error("Buffer map is pending")]
269 MapAlreadyPending,
270 #[error(transparent)]
271 MissingBufferUsage(#[from] MissingBufferUsageError),
272 #[error("Buffer is not mapped")]
273 NotMapped,
274 #[error(
275 "Buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`"
276 )]
277 UnalignedRange,
278 #[error("Buffer offset invalid: offset {offset} must be multiple of 8")]
279 UnalignedOffset { offset: wgt::BufferAddress },
280 #[error("Buffer range size invalid: range_size {range_size} must be multiple of 4")]
281 UnalignedRangeSize { range_size: wgt::BufferAddress },
282 #[error("Buffer access out of bounds: index {index} would underrun the buffer (limit: {min})")]
283 OutOfBoundsUnderrun {
284 index: wgt::BufferAddress,
285 min: wgt::BufferAddress,
286 },
287 #[error(
288 "Buffer access out of bounds: last index {index} would overrun the buffer (limit: {max})"
289 )]
290 OutOfBoundsOverrun {
291 index: wgt::BufferAddress,
292 max: wgt::BufferAddress,
293 },
294 #[error("Buffer map range start {start} is greater than end {end}")]
295 NegativeRange {
296 start: wgt::BufferAddress,
297 end: wgt::BufferAddress,
298 },
299 #[error("Buffer map aborted")]
300 MapAborted,
301 #[error(transparent)]
302 InvalidResource(#[from] InvalidResourceError),
303}
304
305impl WebGpuError for BufferAccessError {
306 fn webgpu_error_type(&self) -> ErrorType {
307 let e: &dyn WebGpuError = match self {
308 Self::Device(e) => e,
309 Self::InvalidResource(e) => e,
310 Self::DestroyedResource(e) => e,
311
312 Self::Failed
313 | Self::AlreadyMapped
314 | Self::MapAlreadyPending
315 | Self::MissingBufferUsage(_)
316 | Self::NotMapped
317 | Self::UnalignedRange
318 | Self::UnalignedOffset { .. }
319 | Self::UnalignedRangeSize { .. }
320 | Self::OutOfBoundsUnderrun { .. }
321 | Self::OutOfBoundsOverrun { .. }
322 | Self::NegativeRange { .. }
323 | Self::MapAborted => return ErrorType::Validation,
324 };
325 e.webgpu_error_type()
326 }
327}
328
329#[derive(Clone, Debug, Error)]
330#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
331#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
332pub struct MissingBufferUsageError {
333 pub(crate) res: ResourceErrorIdent,
334 pub(crate) actual: wgt::BufferUsages,
335 pub(crate) expected: wgt::BufferUsages,
336}
337
338impl WebGpuError for MissingBufferUsageError {
339 fn webgpu_error_type(&self) -> ErrorType {
340 ErrorType::Validation
341 }
342}
343
344#[derive(Clone, Debug, Error)]
345#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
346pub struct MissingTextureUsageError {
347 pub(crate) res: ResourceErrorIdent,
348 pub(crate) actual: wgt::TextureUsages,
349 pub(crate) expected: wgt::TextureUsages,
350}
351
352impl WebGpuError for MissingTextureUsageError {
353 fn webgpu_error_type(&self) -> ErrorType {
354 ErrorType::Validation
355 }
356}
357
358#[derive(Clone, Debug, Error)]
359#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
360#[error("{0} has been destroyed")]
361pub struct DestroyedResourceError(pub ResourceErrorIdent);
362
363impl WebGpuError for DestroyedResourceError {
364 fn webgpu_error_type(&self) -> ErrorType {
365 ErrorType::Validation
366 }
367}
368
369#[derive(Clone, Debug, Error)]
370#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
371#[error("{0} is invalid")]
372pub struct InvalidResourceError(pub ResourceErrorIdent);
373
374impl WebGpuError for InvalidResourceError {
375 fn webgpu_error_type(&self) -> ErrorType {
376 ErrorType::Validation
377 }
378}
379
380pub enum Fallible<T: ParentDevice> {
381 Valid(Arc<T>),
382 Invalid(Arc<String>),
383}
384
385impl<T: ParentDevice> Fallible<T> {
386 pub fn get(self) -> Result<Arc<T>, InvalidResourceError> {
387 match self {
388 Fallible::Valid(v) => Ok(v),
389 Fallible::Invalid(label) => Err(InvalidResourceError(ResourceErrorIdent {
390 r#type: Cow::Borrowed(T::TYPE),
391 label: (*label).clone(),
392 })),
393 }
394 }
395}
396
397impl<T: ParentDevice> Clone for Fallible<T> {
398 fn clone(&self) -> Self {
399 match self {
400 Self::Valid(v) => Self::Valid(v.clone()),
401 Self::Invalid(l) => Self::Invalid(l.clone()),
402 }
403 }
404}
405
406impl<T: ParentDevice> ResourceType for Fallible<T> {
407 const TYPE: &'static str = T::TYPE;
408}
409
410impl<T: ParentDevice + crate::storage::StorageItem> crate::storage::StorageItem for Fallible<T> {
411 type Marker = T::Marker;
412}
413
414pub type BufferAccessResult = Result<(), BufferAccessError>;
415
416#[derive(Debug)]
417pub(crate) struct BufferPendingMapping {
418 pub(crate) range: Range<wgt::BufferAddress>,
419 pub(crate) op: BufferMapOperation,
420 pub(crate) _parent_buffer: Arc<Buffer>,
422}
423
424pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
425
426#[derive(Debug)]
427pub struct Buffer {
428 pub(crate) raw: Snatchable<Box<dyn hal::DynBuffer>>,
429 pub(crate) device: Arc<Device>,
430 pub(crate) usage: wgt::BufferUsages,
431 pub(crate) size: wgt::BufferAddress,
432 pub(crate) initialization_status: RwLock<BufferInitTracker>,
433 pub(crate) label: String,
435 pub(crate) tracking_data: TrackingData,
436 pub(crate) map_state: Mutex<BufferMapState>,
437 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
438 pub(crate) timestamp_normalization_bind_group: Snatchable<TimestampNormalizationBindGroup>,
439 pub(crate) indirect_validation_bind_groups: Snatchable<crate::indirect_validation::BindGroups>,
440}
441
442impl Drop for Buffer {
443 fn drop(&mut self) {
444 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
445 raw.dispose(self.device.raw());
446 }
447
448 if let Some(raw) = self.indirect_validation_bind_groups.take() {
449 raw.dispose(self.device.raw());
450 }
451
452 if let Some(raw) = self.raw.take() {
453 resource_log!("Destroy raw {}", self.error_ident());
454 unsafe {
455 self.device.raw().destroy_buffer(raw);
456 }
457 }
458 }
459}
460
461impl RawResourceAccess for Buffer {
462 type DynResource = dyn hal::DynBuffer;
463
464 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
465 self.raw.get(guard).map(|b| b.as_ref())
466 }
467}
468
469impl Buffer {
470 pub(crate) fn check_destroyed(
471 &self,
472 guard: &SnatchGuard,
473 ) -> Result<(), DestroyedResourceError> {
474 self.raw
475 .get(guard)
476 .map(|_| ())
477 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
478 }
479
480 pub(crate) fn check_usage(
483 &self,
484 expected: wgt::BufferUsages,
485 ) -> Result<(), MissingBufferUsageError> {
486 if self.usage.contains(expected) {
487 Ok(())
488 } else {
489 Err(MissingBufferUsageError {
490 res: self.error_ident(),
491 actual: self.usage,
492 expected,
493 })
494 }
495 }
496
497 pub(crate) fn map_async(
500 self: &Arc<Self>,
501 offset: wgt::BufferAddress,
502 size: Option<wgt::BufferAddress>,
503 op: BufferMapOperation,
504 ) -> Result<SubmissionIndex, (BufferMapOperation, BufferAccessError)> {
505 let range_size = if let Some(size) = size {
506 size
507 } else {
508 self.size.saturating_sub(offset)
509 };
510
511 if offset % wgt::MAP_ALIGNMENT != 0 {
512 return Err((op, BufferAccessError::UnalignedOffset { offset }));
513 }
514 if range_size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
515 return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));
516 }
517
518 let range = offset..(offset + range_size);
519
520 if range.start % wgt::MAP_ALIGNMENT != 0 || range.end % wgt::COPY_BUFFER_ALIGNMENT != 0 {
521 return Err((op, BufferAccessError::UnalignedRange));
522 }
523
524 let (pub_usage, internal_use) = match op.host {
525 HostMap::Read => (wgt::BufferUsages::MAP_READ, wgt::BufferUses::MAP_READ),
526 HostMap::Write => (wgt::BufferUsages::MAP_WRITE, wgt::BufferUses::MAP_WRITE),
527 };
528
529 if let Err(e) = self.check_usage(pub_usage) {
530 return Err((op, e.into()));
531 }
532
533 if range.start > range.end {
534 return Err((
535 op,
536 BufferAccessError::NegativeRange {
537 start: range.start,
538 end: range.end,
539 },
540 ));
541 }
542 if range.end > self.size {
543 return Err((
544 op,
545 BufferAccessError::OutOfBoundsOverrun {
546 index: range.end,
547 max: self.size,
548 },
549 ));
550 }
551
552 let device = &self.device;
553 if let Err(e) = device.check_is_valid() {
554 return Err((op, e.into()));
555 }
556
557 {
558 let snatch_guard = device.snatchable_lock.read();
559 if let Err(e) = self.check_destroyed(&snatch_guard) {
560 return Err((op, e.into()));
561 }
562 }
563
564 {
565 let map_state = &mut *self.map_state.lock();
566 *map_state = match *map_state {
567 BufferMapState::Init { .. } | BufferMapState::Active { .. } => {
568 return Err((op, BufferAccessError::AlreadyMapped));
569 }
570 BufferMapState::Waiting(_) => {
571 return Err((op, BufferAccessError::MapAlreadyPending));
572 }
573 BufferMapState::Idle => BufferMapState::Waiting(BufferPendingMapping {
574 range,
575 op,
576 _parent_buffer: self.clone(),
577 }),
578 };
579 }
580
581 device
584 .trackers
585 .lock()
586 .buffers
587 .set_single(self, internal_use);
588
589 let submit_index = if let Some(queue) = device.get_queue() {
590 queue.lock_life().map(self).unwrap_or(0) } else {
592 let (mut operation, status) = self.map(&device.snatchable_lock.read()).unwrap();
594 if let Some(callback) = operation.callback.take() {
595 callback(status);
596 }
597 0
598 };
599
600 Ok(submit_index)
601 }
602
603 #[must_use]
605 pub(crate) fn map(&self, snatch_guard: &SnatchGuard) -> Option<BufferMapPendingClosure> {
606 let mapping = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
610 let pending_mapping = match mapping {
611 BufferMapState::Waiting(pending_mapping) => pending_mapping,
612 BufferMapState::Idle => return None,
614 BufferMapState::Active { .. } => {
617 *self.map_state.lock() = mapping;
618 return None;
619 }
620 _ => panic!("No pending mapping."),
621 };
622 let status = if pending_mapping.range.start != pending_mapping.range.end {
623 let host = pending_mapping.op.host;
624 let size = pending_mapping.range.end - pending_mapping.range.start;
625 match crate::device::map_buffer(
626 self,
627 pending_mapping.range.start,
628 size,
629 host,
630 snatch_guard,
631 ) {
632 Ok(mapping) => {
633 *self.map_state.lock() = BufferMapState::Active {
634 mapping,
635 range: pending_mapping.range.clone(),
636 host,
637 };
638 Ok(())
639 }
640 Err(e) => Err(e),
641 }
642 } else {
643 *self.map_state.lock() = BufferMapState::Active {
644 mapping: hal::BufferMapping {
645 ptr: NonNull::dangling(),
646 is_coherent: true,
647 },
648 range: pending_mapping.range,
649 host: pending_mapping.op.host,
650 };
651 Ok(())
652 };
653 Some((pending_mapping.op, status))
654 }
655
656 pub(crate) fn unmap(
658 self: &Arc<Self>,
659 #[cfg(feature = "trace")] buffer_id: crate::id::BufferId,
660 ) -> Result<(), BufferAccessError> {
661 if let Some((mut operation, status)) = self.unmap_inner(
662 #[cfg(feature = "trace")]
663 buffer_id,
664 )? {
665 if let Some(callback) = operation.callback.take() {
666 callback(status);
667 }
668 }
669
670 Ok(())
671 }
672
673 fn unmap_inner(
674 self: &Arc<Self>,
675 #[cfg(feature = "trace")] buffer_id: crate::id::BufferId,
676 ) -> Result<Option<BufferMapPendingClosure>, BufferAccessError> {
677 let device = &self.device;
678 let snatch_guard = device.snatchable_lock.read();
679 let raw_buf = self.try_raw(&snatch_guard)?;
680 match mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle) {
681 BufferMapState::Init { staging_buffer } => {
682 #[cfg(feature = "trace")]
683 if let Some(ref mut trace) = *device.trace.lock() {
684 let data = trace.make_binary("bin", staging_buffer.get_data());
685 trace.add(trace::Action::WriteBuffer {
686 id: buffer_id,
687 data,
688 range: 0..self.size,
689 queued: true,
690 });
691 }
692
693 let staging_buffer = staging_buffer.flush();
694
695 if let Some(queue) = device.get_queue() {
696 let region = wgt::BufferSize::new(self.size).map(|size| hal::BufferCopy {
697 src_offset: 0,
698 dst_offset: 0,
699 size,
700 });
701 let transition_src = hal::BufferBarrier {
702 buffer: staging_buffer.raw(),
703 usage: hal::StateTransition {
704 from: wgt::BufferUses::MAP_WRITE,
705 to: wgt::BufferUses::COPY_SRC,
706 },
707 };
708 let transition_dst = hal::BufferBarrier::<dyn hal::DynBuffer> {
709 buffer: raw_buf,
710 usage: hal::StateTransition {
711 from: wgt::BufferUses::empty(),
712 to: wgt::BufferUses::COPY_DST,
713 },
714 };
715 let mut pending_writes = queue.pending_writes.lock();
716 let encoder = pending_writes.activate();
717 unsafe {
718 encoder.transition_buffers(&[transition_src, transition_dst]);
719 if self.size > 0 {
720 encoder.copy_buffer_to_buffer(
721 staging_buffer.raw(),
722 raw_buf,
723 region.as_slice(),
724 );
725 }
726 }
727 pending_writes.consume(staging_buffer);
728 pending_writes.insert_buffer(self);
729 }
730 }
731 BufferMapState::Idle => {
732 return Err(BufferAccessError::NotMapped);
733 }
734 BufferMapState::Waiting(pending) => {
735 return Ok(Some((pending.op, Err(BufferAccessError::MapAborted))));
736 }
737 BufferMapState::Active {
738 mapping,
739 range,
740 host,
741 } => {
742 #[allow(clippy::collapsible_if)]
743 if host == HostMap::Write {
744 #[cfg(feature = "trace")]
745 if let Some(ref mut trace) = *device.trace.lock() {
746 let size = range.end - range.start;
747 let data = trace.make_binary("bin", unsafe {
748 core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize)
749 });
750 trace.add(trace::Action::WriteBuffer {
751 id: buffer_id,
752 data,
753 range: range.clone(),
754 queued: false,
755 });
756 }
757 if !mapping.is_coherent {
758 unsafe { device.raw().flush_mapped_ranges(raw_buf, &[range]) };
759 }
760 }
761 unsafe { device.raw().unmap_buffer(raw_buf) };
762 }
763 }
764 Ok(None)
765 }
766
767 pub(crate) fn destroy(self: &Arc<Self>) {
768 let device = &self.device;
769
770 let temp = {
771 let mut snatch_guard = device.snatchable_lock.write();
772
773 let raw = match self.raw.snatch(&mut snatch_guard) {
774 Some(raw) => raw,
775 None => {
776 return;
778 }
779 };
780
781 let timestamp_normalization_bind_group = self
782 .timestamp_normalization_bind_group
783 .snatch(&mut snatch_guard);
784
785 let indirect_validation_bind_groups = self
786 .indirect_validation_bind_groups
787 .snatch(&mut snatch_guard);
788
789 drop(snatch_guard);
790
791 let bind_groups = {
792 let mut guard = self.bind_groups.lock();
793 mem::take(&mut *guard)
794 };
795
796 queue::TempResource::DestroyedBuffer(DestroyedBuffer {
797 raw: ManuallyDrop::new(raw),
798 device: Arc::clone(&self.device),
799 label: self.label().to_owned(),
800 bind_groups,
801 timestamp_normalization_bind_group,
802 indirect_validation_bind_groups,
803 })
804 };
805
806 if let Some(queue) = device.get_queue() {
807 let mut pending_writes = queue.pending_writes.lock();
808 if pending_writes.contains_buffer(self) {
809 pending_writes.consume_temp(temp);
810 } else {
811 let mut life_lock = queue.lock_life();
812 let last_submit_index = life_lock.get_buffer_latest_submission_index(self);
813 if let Some(last_submit_index) = last_submit_index {
814 life_lock.schedule_resource_destruction(temp, last_submit_index);
815 }
816 }
817 }
818 }
819}
820
821#[derive(Clone, Debug, Error)]
822#[non_exhaustive]
823pub enum CreateBufferError {
824 #[error(transparent)]
825 Device(#[from] DeviceError),
826 #[error("Failed to map buffer while creating: {0}")]
827 AccessError(#[from] BufferAccessError),
828 #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
829 UnalignedSize,
830 #[error("Invalid usage flags {0:?}")]
831 InvalidUsage(wgt::BufferUsages),
832 #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
833 UsageMismatch(wgt::BufferUsages),
834 #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
835 MaxBufferSize { requested: u64, maximum: u64 },
836 #[error(transparent)]
837 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
838 #[error("Failed to create bind group for indirect buffer validation: {0}")]
839 IndirectValidationBindGroup(DeviceError),
840}
841
842crate::impl_resource_type!(Buffer);
843crate::impl_labeled!(Buffer);
844crate::impl_parent_device!(Buffer);
845crate::impl_storage_item!(Buffer);
846crate::impl_trackable!(Buffer);
847
848impl WebGpuError for CreateBufferError {
849 fn webgpu_error_type(&self) -> ErrorType {
850 let e: &dyn WebGpuError = match self {
851 Self::Device(e) => e,
852 Self::AccessError(e) => e,
853 Self::MissingDownlevelFlags(e) => e,
854 Self::IndirectValidationBindGroup(e) => e,
855
856 Self::UnalignedSize
857 | Self::InvalidUsage(_)
858 | Self::UsageMismatch(_)
859 | Self::MaxBufferSize { .. } => return ErrorType::Validation,
860 };
861 e.webgpu_error_type()
862 }
863}
864
865#[derive(Debug)]
867pub struct DestroyedBuffer {
868 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
869 device: Arc<Device>,
870 label: String,
871 bind_groups: WeakVec<BindGroup>,
872 timestamp_normalization_bind_group: Option<TimestampNormalizationBindGroup>,
873 indirect_validation_bind_groups: Option<crate::indirect_validation::BindGroups>,
874}
875
876impl DestroyedBuffer {
877 pub fn label(&self) -> &dyn fmt::Debug {
878 &self.label
879 }
880}
881
882impl Drop for DestroyedBuffer {
883 fn drop(&mut self) {
884 let mut deferred = self.device.deferred_destroy.lock();
885 deferred.push(DeferredDestroy::BindGroups(mem::take(
886 &mut self.bind_groups,
887 )));
888 drop(deferred);
889
890 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
891 raw.dispose(self.device.raw());
892 }
893
894 if let Some(raw) = self.indirect_validation_bind_groups.take() {
895 raw.dispose(self.device.raw());
896 }
897
898 resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
899 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
901 unsafe {
902 hal::DynDevice::destroy_buffer(self.device.raw(), raw);
903 }
904 }
905}
906
907#[cfg(send_sync)]
908unsafe impl Send for StagingBuffer {}
909#[cfg(send_sync)]
910unsafe impl Sync for StagingBuffer {}
911
912#[derive(Debug)]
932pub struct StagingBuffer {
933 raw: Box<dyn hal::DynBuffer>,
934 device: Arc<Device>,
935 pub(crate) size: wgt::BufferSize,
936 is_coherent: bool,
937 ptr: NonNull<u8>,
938}
939
940impl StagingBuffer {
941 pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {
942 profiling::scope!("StagingBuffer::new");
943 let stage_desc = hal::BufferDescriptor {
944 label: crate::hal_label(Some("(wgpu internal) Staging"), device.instance_flags),
945 size: size.get(),
946 usage: wgt::BufferUses::MAP_WRITE | wgt::BufferUses::COPY_SRC,
947 memory_flags: hal::MemoryFlags::TRANSIENT,
948 };
949
950 let raw = unsafe { device.raw().create_buffer(&stage_desc) }
951 .map_err(|e| device.handle_hal_error(e))?;
952 let mapping = unsafe { device.raw().map_buffer(raw.as_ref(), 0..size.get()) }
953 .map_err(|e| device.handle_hal_error(e))?;
954
955 let staging_buffer = StagingBuffer {
956 raw,
957 device: device.clone(),
958 size,
959 is_coherent: mapping.is_coherent,
960 ptr: mapping.ptr,
961 };
962
963 Ok(staging_buffer)
964 }
965
966 pub(crate) unsafe fn ptr(&self) -> NonNull<u8> {
969 self.ptr
970 }
971
972 #[cfg(feature = "trace")]
973 pub(crate) fn get_data(&self) -> &[u8] {
974 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.size.get() as usize) }
975 }
976
977 pub(crate) fn write_zeros(&mut self) {
978 unsafe { core::ptr::write_bytes(self.ptr.as_ptr(), 0, self.size.get() as usize) };
979 }
980
981 pub(crate) fn write(&mut self, data: &[u8]) {
982 assert!(data.len() >= self.size.get() as usize);
983 unsafe {
986 core::ptr::copy_nonoverlapping(
987 data.as_ptr(),
988 self.ptr.as_ptr(),
989 self.size.get() as usize,
990 );
991 }
992 }
993
994 pub(crate) unsafe fn write_with_offset(
996 &mut self,
997 data: &[u8],
998 src_offset: isize,
999 dst_offset: isize,
1000 size: usize,
1001 ) {
1002 unsafe {
1003 core::ptr::copy_nonoverlapping(
1004 data.as_ptr().offset(src_offset),
1005 self.ptr.as_ptr().offset(dst_offset),
1006 size,
1007 );
1008 }
1009 }
1010
1011 pub(crate) fn flush(self) -> FlushedStagingBuffer {
1012 let device = self.device.raw();
1013 if !self.is_coherent {
1014 #[allow(clippy::single_range_in_vec_init)]
1015 unsafe {
1016 device.flush_mapped_ranges(self.raw.as_ref(), &[0..self.size.get()])
1017 };
1018 }
1019 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1020
1021 let StagingBuffer {
1022 raw, device, size, ..
1023 } = self;
1024
1025 FlushedStagingBuffer {
1026 raw: ManuallyDrop::new(raw),
1027 device,
1028 size,
1029 }
1030 }
1031}
1032
1033crate::impl_resource_type!(StagingBuffer);
1034crate::impl_storage_item!(StagingBuffer);
1035
1036#[derive(Debug)]
1037pub struct FlushedStagingBuffer {
1038 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1039 device: Arc<Device>,
1040 pub(crate) size: wgt::BufferSize,
1041}
1042
1043impl FlushedStagingBuffer {
1044 pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {
1045 self.raw.as_ref()
1046 }
1047}
1048
1049impl Drop for FlushedStagingBuffer {
1050 fn drop(&mut self) {
1051 resource_log!("Destroy raw StagingBuffer");
1052 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1054 unsafe { self.device.raw().destroy_buffer(raw) };
1055 }
1056}
1057
1058pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
1059
1060#[derive(Debug)]
1061pub(crate) enum TextureInner {
1062 Native {
1063 raw: Box<dyn hal::DynTexture>,
1064 },
1065 Surface {
1066 raw: Box<dyn hal::DynSurfaceTexture>,
1067 },
1068}
1069
1070impl TextureInner {
1071 pub(crate) fn raw(&self) -> &dyn hal::DynTexture {
1072 match self {
1073 Self::Native { raw } => raw.as_ref(),
1074 Self::Surface { raw, .. } => raw.as_ref().borrow(),
1075 }
1076 }
1077}
1078
1079#[derive(Debug)]
1080pub enum TextureClearMode {
1081 BufferCopy,
1082 RenderPass {
1084 clear_views: SmallVec<[ManuallyDrop<Box<dyn hal::DynTextureView>>; 1]>,
1085 is_color: bool,
1086 },
1087 Surface {
1088 clear_view: ManuallyDrop<Box<dyn hal::DynTextureView>>,
1089 },
1090 None,
1093}
1094
1095#[derive(Debug)]
1096pub struct Texture {
1097 pub(crate) inner: Snatchable<TextureInner>,
1098 pub(crate) device: Arc<Device>,
1099 pub(crate) desc: wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
1100 pub(crate) _hal_usage: wgt::TextureUses,
1101 pub(crate) format_features: wgt::TextureFormatFeatures,
1102 pub(crate) initialization_status: RwLock<TextureInitTracker>,
1103 pub(crate) full_range: TextureSelector,
1104 pub(crate) label: String,
1106 pub(crate) tracking_data: TrackingData,
1107 pub(crate) clear_mode: RwLock<TextureClearMode>,
1108 pub(crate) views: Mutex<WeakVec<TextureView>>,
1109 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
1110}
1111
1112impl Texture {
1113 pub(crate) fn new(
1114 device: &Arc<Device>,
1115 inner: TextureInner,
1116 hal_usage: wgt::TextureUses,
1117 desc: &TextureDescriptor,
1118 format_features: wgt::TextureFormatFeatures,
1119 clear_mode: TextureClearMode,
1120 init: bool,
1121 ) -> Self {
1122 Texture {
1123 inner: Snatchable::new(inner),
1124 device: device.clone(),
1125 desc: desc.map_label(|_| ()),
1126 _hal_usage: hal_usage,
1127 format_features,
1128 initialization_status: RwLock::new(
1129 rank::TEXTURE_INITIALIZATION_STATUS,
1130 if init {
1131 TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count())
1132 } else {
1133 TextureInitTracker::new(desc.mip_level_count, 0)
1134 },
1135 ),
1136 full_range: TextureSelector {
1137 mips: 0..desc.mip_level_count,
1138 layers: 0..desc.array_layer_count(),
1139 },
1140 label: desc.label.to_string(),
1141 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1142 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
1143 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1144 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1145 }
1146 }
1147
1148 pub(crate) fn check_usage(
1151 &self,
1152 expected: wgt::TextureUsages,
1153 ) -> Result<(), MissingTextureUsageError> {
1154 if self.desc.usage.contains(expected) {
1155 Ok(())
1156 } else {
1157 Err(MissingTextureUsageError {
1158 res: self.error_ident(),
1159 actual: self.desc.usage,
1160 expected,
1161 })
1162 }
1163 }
1164}
1165
1166impl Drop for Texture {
1167 fn drop(&mut self) {
1168 match *self.clear_mode.write() {
1169 TextureClearMode::Surface {
1170 ref mut clear_view, ..
1171 } => {
1172 let raw = unsafe { ManuallyDrop::take(clear_view) };
1174 unsafe {
1175 self.device.raw().destroy_texture_view(raw);
1176 }
1177 }
1178 TextureClearMode::RenderPass {
1179 ref mut clear_views,
1180 ..
1181 } => {
1182 clear_views.iter_mut().for_each(|clear_view| {
1183 let raw = unsafe { ManuallyDrop::take(clear_view) };
1185 unsafe {
1186 self.device.raw().destroy_texture_view(raw);
1187 }
1188 });
1189 }
1190 _ => {}
1191 };
1192
1193 if let Some(TextureInner::Native { raw }) = self.inner.take() {
1194 resource_log!("Destroy raw {}", self.error_ident());
1195 unsafe {
1196 self.device.raw().destroy_texture(raw);
1197 }
1198 }
1199 }
1200}
1201
1202impl RawResourceAccess for Texture {
1203 type DynResource = dyn hal::DynTexture;
1204
1205 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1206 self.inner.get(guard).map(|t| t.raw())
1207 }
1208}
1209
1210impl Texture {
1211 pub(crate) fn try_inner<'a>(
1212 &'a self,
1213 guard: &'a SnatchGuard,
1214 ) -> Result<&'a TextureInner, DestroyedResourceError> {
1215 self.inner
1216 .get(guard)
1217 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1218 }
1219
1220 pub(crate) fn check_destroyed(
1221 &self,
1222 guard: &SnatchGuard,
1223 ) -> Result<(), DestroyedResourceError> {
1224 self.inner
1225 .get(guard)
1226 .map(|_| ())
1227 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1228 }
1229
1230 pub(crate) fn get_clear_view<'a>(
1231 clear_mode: &'a TextureClearMode,
1232 desc: &'a wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
1233 mip_level: u32,
1234 depth_or_layer: u32,
1235 ) -> &'a dyn hal::DynTextureView {
1236 match *clear_mode {
1237 TextureClearMode::BufferCopy => {
1238 panic!("Given texture is cleared with buffer copies, not render passes")
1239 }
1240 TextureClearMode::None => {
1241 panic!("Given texture can't be cleared")
1242 }
1243 TextureClearMode::Surface { ref clear_view, .. } => clear_view.as_ref(),
1244 TextureClearMode::RenderPass {
1245 ref clear_views, ..
1246 } => {
1247 let index = if desc.dimension == wgt::TextureDimension::D3 {
1248 (0..mip_level).fold(0, |acc, mip| {
1249 acc + (desc.size.depth_or_array_layers >> mip).max(1)
1250 })
1251 } else {
1252 mip_level * desc.size.depth_or_array_layers
1253 } + depth_or_layer;
1254 clear_views[index as usize].as_ref()
1255 }
1256 }
1257 }
1258
1259 pub(crate) fn destroy(self: &Arc<Self>) {
1260 let device = &self.device;
1261
1262 let temp = {
1263 let raw = match self.inner.snatch(&mut device.snatchable_lock.write()) {
1264 Some(TextureInner::Native { raw }) => raw,
1265 Some(TextureInner::Surface { .. }) => {
1266 return;
1267 }
1268 None => {
1269 return;
1271 }
1272 };
1273
1274 let views = {
1275 let mut guard = self.views.lock();
1276 mem::take(&mut *guard)
1277 };
1278
1279 let bind_groups = {
1280 let mut guard = self.bind_groups.lock();
1281 mem::take(&mut *guard)
1282 };
1283
1284 queue::TempResource::DestroyedTexture(DestroyedTexture {
1285 raw: ManuallyDrop::new(raw),
1286 views,
1287 clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None),
1288 bind_groups,
1289 device: Arc::clone(&self.device),
1290 label: self.label().to_owned(),
1291 })
1292 };
1293
1294 if let Some(queue) = device.get_queue() {
1295 let mut pending_writes = queue.pending_writes.lock();
1296 if pending_writes.contains_texture(self) {
1297 pending_writes.consume_temp(temp);
1298 } else {
1299 let mut life_lock = queue.lock_life();
1300 let last_submit_index = life_lock.get_texture_latest_submission_index(self);
1301 if let Some(last_submit_index) = last_submit_index {
1302 life_lock.schedule_resource_destruction(temp, last_submit_index);
1303 }
1304 }
1305 }
1306 }
1307}
1308
1309#[derive(Debug)]
1311pub struct DestroyedTexture {
1312 raw: ManuallyDrop<Box<dyn hal::DynTexture>>,
1313 views: WeakVec<TextureView>,
1314 clear_mode: TextureClearMode,
1315 bind_groups: WeakVec<BindGroup>,
1316 device: Arc<Device>,
1317 label: String,
1318}
1319
1320impl DestroyedTexture {
1321 pub fn label(&self) -> &dyn fmt::Debug {
1322 &self.label
1323 }
1324}
1325
1326impl Drop for DestroyedTexture {
1327 fn drop(&mut self) {
1328 let device = &self.device;
1329
1330 let mut deferred = device.deferred_destroy.lock();
1331 deferred.push(DeferredDestroy::TextureViews(mem::take(&mut self.views)));
1332 deferred.push(DeferredDestroy::BindGroups(mem::take(
1333 &mut self.bind_groups,
1334 )));
1335 drop(deferred);
1336
1337 match mem::replace(&mut self.clear_mode, TextureClearMode::None) {
1338 TextureClearMode::RenderPass { clear_views, .. } => {
1339 for clear_view in clear_views {
1340 let raw = ManuallyDrop::into_inner(clear_view);
1341 unsafe { self.device.raw().destroy_texture_view(raw) };
1342 }
1343 }
1344 TextureClearMode::Surface { clear_view } => {
1345 let raw = ManuallyDrop::into_inner(clear_view);
1346 unsafe { self.device.raw().destroy_texture_view(raw) };
1347 }
1348 _ => (),
1349 }
1350
1351 resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
1352 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1354 unsafe {
1355 self.device.raw().destroy_texture(raw);
1356 }
1357 }
1358}
1359
1360#[derive(Clone, Copy, Debug)]
1361pub enum TextureErrorDimension {
1362 X,
1363 Y,
1364 Z,
1365}
1366
1367#[derive(Clone, Debug, Error)]
1368#[non_exhaustive]
1369pub enum TextureDimensionError {
1370 #[error("Dimension {0:?} is zero")]
1371 Zero(TextureErrorDimension),
1372 #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
1373 LimitExceeded {
1374 dim: TextureErrorDimension,
1375 given: u32,
1376 limit: u32,
1377 },
1378 #[error("Sample count {0} is invalid")]
1379 InvalidSampleCount(u32),
1380 #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
1381 NotMultipleOfBlockWidth {
1382 width: u32,
1383 block_width: u32,
1384 format: wgt::TextureFormat,
1385 },
1386 #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
1387 NotMultipleOfBlockHeight {
1388 height: u32,
1389 block_height: u32,
1390 format: wgt::TextureFormat,
1391 },
1392 #[error(
1393 "Width {width} is not a multiple of {format:?}'s width multiple requirement ({multiple})"
1394 )]
1395 WidthNotMultipleOf {
1396 width: u32,
1397 multiple: u32,
1398 format: wgt::TextureFormat,
1399 },
1400 #[error("Height {height} is not a multiple of {format:?}'s height multiple requirement ({multiple})")]
1401 HeightNotMultipleOf {
1402 height: u32,
1403 multiple: u32,
1404 format: wgt::TextureFormat,
1405 },
1406 #[error("Multisampled texture depth or array layers must be 1, got {0}")]
1407 MultisampledDepthOrArrayLayer(u32),
1408}
1409
1410impl WebGpuError for TextureDimensionError {
1411 fn webgpu_error_type(&self) -> ErrorType {
1412 ErrorType::Validation
1413 }
1414}
1415
1416#[derive(Clone, Debug, Error)]
1417#[non_exhaustive]
1418pub enum CreateTextureError {
1419 #[error(transparent)]
1420 Device(#[from] DeviceError),
1421 #[error(transparent)]
1422 CreateTextureView(#[from] CreateTextureViewError),
1423 #[error("Invalid usage flags {0:?}")]
1424 InvalidUsage(wgt::TextureUsages),
1425 #[error(transparent)]
1426 InvalidDimension(#[from] TextureDimensionError),
1427 #[error("Depth texture ({1:?}) can't be created as {0:?}")]
1428 InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
1429 #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
1430 InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
1431 #[error(
1432 "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
1433 )]
1434 InvalidMipLevelCount { requested: u32, maximum: u32 },
1435 #[error(
1436 "Texture usages {0:?} are not allowed on a texture of type {1:?}{downlevel_suffix}",
1437 downlevel_suffix = if *.2 { " due to downlevel restrictions" } else { "" }
1438 )]
1439 InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
1440 #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
1441 InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
1442 #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
1443 InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
1444 #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
1445 InvalidMultisampledStorageBinding,
1446 #[error("Format {0:?} does not support multisampling")]
1447 InvalidMultisampledFormat(wgt::TextureFormat),
1448 #[error("Sample count {0} is not supported by format {1:?} on this device. The WebGPU spec guarantees {2:?} samples are supported by this format. With the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature your device supports {3:?}.")]
1449 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
1450 #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
1451 MultisampledNotRenderAttachment,
1452 #[error("Texture format {0:?} can't be used due to missing features")]
1453 MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
1454 #[error(transparent)]
1455 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1456}
1457
1458crate::impl_resource_type!(Texture);
1459crate::impl_labeled!(Texture);
1460crate::impl_parent_device!(Texture);
1461crate::impl_storage_item!(Texture);
1462crate::impl_trackable!(Texture);
1463
1464impl Borrow<TextureSelector> for Texture {
1465 fn borrow(&self) -> &TextureSelector {
1466 &self.full_range
1467 }
1468}
1469
1470impl WebGpuError for CreateTextureError {
1471 fn webgpu_error_type(&self) -> ErrorType {
1472 let e: &dyn WebGpuError = match self {
1473 Self::Device(e) => e,
1474 Self::CreateTextureView(e) => e,
1475 Self::InvalidDimension(e) => e,
1476 Self::MissingFeatures(_, e) => e,
1477 Self::MissingDownlevelFlags(e) => e,
1478
1479 Self::InvalidUsage(_)
1480 | Self::InvalidDepthDimension(_, _)
1481 | Self::InvalidCompressedDimension(_, _)
1482 | Self::InvalidMipLevelCount { .. }
1483 | Self::InvalidFormatUsages(_, _, _)
1484 | Self::InvalidViewFormat(_, _)
1485 | Self::InvalidDimensionUsages(_, _)
1486 | Self::InvalidMultisampledStorageBinding
1487 | Self::InvalidMultisampledFormat(_)
1488 | Self::InvalidSampleCount(..)
1489 | Self::MultisampledNotRenderAttachment => return ErrorType::Validation,
1490 };
1491 e.webgpu_error_type()
1492 }
1493}
1494
1495#[derive(Clone, Debug, Default, Eq, PartialEq)]
1497#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1498#[cfg_attr(feature = "serde", serde(default))]
1499pub struct TextureViewDescriptor<'a> {
1500 pub label: Label<'a>,
1504 pub format: Option<wgt::TextureFormat>,
1509 pub dimension: Option<wgt::TextureViewDimension>,
1515 pub usage: Option<wgt::TextureUsages>,
1518 pub range: wgt::ImageSubresourceRange,
1520}
1521
1522#[derive(Debug)]
1523pub(crate) struct HalTextureViewDescriptor {
1524 pub texture_format: wgt::TextureFormat,
1525 pub format: wgt::TextureFormat,
1526 pub usage: wgt::TextureUsages,
1527 pub dimension: wgt::TextureViewDimension,
1528 pub range: wgt::ImageSubresourceRange,
1529}
1530
1531impl HalTextureViewDescriptor {
1532 pub fn aspects(&self) -> hal::FormatAspects {
1533 hal::FormatAspects::new(self.texture_format, self.range.aspect)
1534 }
1535}
1536
1537#[derive(Debug, Copy, Clone, Error)]
1538pub enum TextureViewNotRenderableReason {
1539 #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
1540 Usage(wgt::TextureUsages),
1541 #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
1542 Dimension(wgt::TextureViewDimension),
1543 #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
1544 MipLevelCount(u32),
1545 #[error("This texture view has more than one array layer. View array layers: {0:?}")]
1546 ArrayLayerCount(u32),
1547 #[error(
1548 "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
1549 )]
1550 Aspects(hal::FormatAspects),
1551}
1552
1553#[derive(Debug)]
1554pub struct TextureView {
1555 pub(crate) raw: Snatchable<Box<dyn hal::DynTextureView>>,
1556 pub(crate) parent: Arc<Texture>,
1558 pub(crate) device: Arc<Device>,
1559 pub(crate) desc: HalTextureViewDescriptor,
1560 pub(crate) format_features: wgt::TextureFormatFeatures,
1561 pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
1563 pub(crate) samples: u32,
1564 pub(crate) selector: TextureSelector,
1565 pub(crate) label: String,
1567 pub(crate) tracking_data: TrackingData,
1568}
1569
1570impl Drop for TextureView {
1571 fn drop(&mut self) {
1572 if let Some(raw) = self.raw.take() {
1573 resource_log!("Destroy raw {}", self.error_ident());
1574 unsafe {
1575 self.device.raw().destroy_texture_view(raw);
1576 }
1577 }
1578 }
1579}
1580
1581impl RawResourceAccess for TextureView {
1582 type DynResource = dyn hal::DynTextureView;
1583
1584 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1585 self.raw.get(guard).map(|it| it.as_ref())
1586 }
1587
1588 fn try_raw<'a>(
1589 &'a self,
1590 guard: &'a SnatchGuard,
1591 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
1592 self.parent.check_destroyed(guard)?;
1593
1594 self.raw(guard)
1595 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1596 }
1597}
1598
1599impl TextureView {
1600 pub(crate) fn check_usage(
1603 &self,
1604 expected: wgt::TextureUsages,
1605 ) -> Result<(), MissingTextureUsageError> {
1606 if self.desc.usage.contains(expected) {
1607 Ok(())
1608 } else {
1609 Err(MissingTextureUsageError {
1610 res: self.error_ident(),
1611 actual: self.desc.usage,
1612 expected,
1613 })
1614 }
1615 }
1616}
1617
1618#[derive(Clone, Debug, Error)]
1619#[non_exhaustive]
1620pub enum CreateTextureViewError {
1621 #[error(transparent)]
1622 Device(#[from] DeviceError),
1623 #[error(transparent)]
1624 DestroyedResource(#[from] DestroyedResourceError),
1625 #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
1626 InvalidTextureViewDimension {
1627 view: wgt::TextureViewDimension,
1628 texture: wgt::TextureDimension,
1629 },
1630 #[error("Texture view format `{0:?}` is not renderable")]
1631 TextureViewFormatNotRenderable(wgt::TextureFormat),
1632 #[error("Texture view format `{0:?}` is not storage bindable")]
1633 TextureViewFormatNotStorage(wgt::TextureFormat),
1634 #[error("Invalid texture view usage `{view:?}` with texture of usage `{texture:?}`")]
1635 InvalidTextureViewUsage {
1636 view: wgt::TextureUsages,
1637 texture: wgt::TextureUsages,
1638 },
1639 #[error("Invalid texture view dimension `{0:?}` of a multisampled texture")]
1640 InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
1641 #[error("Invalid texture depth `{depth}` for texture view of dimension `Cubemap`. Cubemap views must use images of size 6.")]
1642 InvalidCubemapTextureDepth { depth: u32 },
1643 #[error("Invalid texture depth `{depth}` for texture view of dimension `CubemapArray`. Cubemap views must use images with sizes which are a multiple of 6.")]
1644 InvalidCubemapArrayTextureDepth { depth: u32 },
1645 #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
1646 InvalidCubeTextureViewSize,
1647 #[error("Mip level count is 0")]
1648 ZeroMipLevelCount,
1649 #[error("Array layer count is 0")]
1650 ZeroArrayLayerCount,
1651 #[error(
1652 "TextureView mip level count + base mip level {requested} must be <= Texture mip level count {total}"
1653 )]
1654 TooManyMipLevels { requested: u32, total: u32 },
1655 #[error("TextureView array layer count + base array layer {requested} must be <= Texture depth/array layer count {total}")]
1656 TooManyArrayLayers { requested: u32, total: u32 },
1657 #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
1658 InvalidArrayLayerCount {
1659 requested: u32,
1660 dim: wgt::TextureViewDimension,
1661 },
1662 #[error("Aspect {requested_aspect:?} is not in the source texture format {texture_format:?}")]
1663 InvalidAspect {
1664 texture_format: wgt::TextureFormat,
1665 requested_aspect: wgt::TextureAspect,
1666 },
1667 #[error("Unable to view texture {texture:?} as {view:?}")]
1668 FormatReinterpretation {
1669 texture: wgt::TextureFormat,
1670 view: wgt::TextureFormat,
1671 },
1672 #[error(transparent)]
1673 InvalidResource(#[from] InvalidResourceError),
1674 #[error(transparent)]
1675 MissingFeatures(#[from] MissingFeatures),
1676}
1677
1678impl WebGpuError for CreateTextureViewError {
1679 fn webgpu_error_type(&self) -> ErrorType {
1680 match self {
1681 Self::Device(e) => e.webgpu_error_type(),
1682
1683 Self::InvalidTextureViewDimension { .. }
1684 | Self::InvalidResource(_)
1685 | Self::InvalidMultisampledTextureViewDimension(_)
1686 | Self::InvalidCubemapTextureDepth { .. }
1687 | Self::InvalidCubemapArrayTextureDepth { .. }
1688 | Self::InvalidCubeTextureViewSize
1689 | Self::ZeroMipLevelCount
1690 | Self::ZeroArrayLayerCount
1691 | Self::TooManyMipLevels { .. }
1692 | Self::TooManyArrayLayers { .. }
1693 | Self::InvalidArrayLayerCount { .. }
1694 | Self::InvalidAspect { .. }
1695 | Self::FormatReinterpretation { .. }
1696 | Self::DestroyedResource(_)
1697 | Self::TextureViewFormatNotRenderable(_)
1698 | Self::TextureViewFormatNotStorage(_)
1699 | Self::InvalidTextureViewUsage { .. }
1700 | Self::MissingFeatures(_) => ErrorType::Validation,
1701 }
1702 }
1703}
1704
1705#[derive(Clone, Debug, Error)]
1706#[non_exhaustive]
1707pub enum TextureViewDestroyError {}
1708
1709crate::impl_resource_type!(TextureView);
1710crate::impl_labeled!(TextureView);
1711crate::impl_parent_device!(TextureView);
1712crate::impl_storage_item!(TextureView);
1713crate::impl_trackable!(TextureView);
1714
1715#[derive(Clone, Debug, PartialEq)]
1717#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1718pub struct SamplerDescriptor<'a> {
1719 pub label: Label<'a>,
1723 pub address_modes: [wgt::AddressMode; 3],
1725 pub mag_filter: wgt::FilterMode,
1727 pub min_filter: wgt::FilterMode,
1729 pub mipmap_filter: wgt::FilterMode,
1731 pub lod_min_clamp: f32,
1733 pub lod_max_clamp: f32,
1735 pub compare: Option<wgt::CompareFunction>,
1737 pub anisotropy_clamp: u16,
1739 pub border_color: Option<wgt::SamplerBorderColor>,
1742}
1743
1744#[derive(Debug)]
1745pub struct Sampler {
1746 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynSampler>>,
1747 pub(crate) device: Arc<Device>,
1748 pub(crate) label: String,
1750 pub(crate) tracking_data: TrackingData,
1751 pub(crate) comparison: bool,
1753 pub(crate) filtering: bool,
1755}
1756
1757impl Drop for Sampler {
1758 fn drop(&mut self) {
1759 resource_log!("Destroy raw {}", self.error_ident());
1760 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1762 unsafe {
1763 self.device.raw().destroy_sampler(raw);
1764 }
1765 }
1766}
1767
1768impl Sampler {
1769 pub(crate) fn raw(&self) -> &dyn hal::DynSampler {
1770 self.raw.as_ref()
1771 }
1772}
1773
1774#[derive(Copy, Clone)]
1775pub enum SamplerFilterErrorType {
1776 MagFilter,
1777 MinFilter,
1778 MipmapFilter,
1779}
1780
1781impl fmt::Debug for SamplerFilterErrorType {
1782 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1783 match *self {
1784 SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
1785 SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
1786 SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
1787 }
1788 }
1789}
1790
1791#[derive(Clone, Debug, Error)]
1792#[non_exhaustive]
1793pub enum CreateSamplerError {
1794 #[error(transparent)]
1795 Device(#[from] DeviceError),
1796 #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
1797 InvalidLodMinClamp(f32),
1798 #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
1799 InvalidLodMaxClamp {
1800 lod_min_clamp: f32,
1801 lod_max_clamp: f32,
1802 },
1803 #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
1804 InvalidAnisotropy(u16),
1805 #[error("Invalid filter mode for {filter_type:?}: {filter_mode:?}. When anistropic clamp is not 1 (it is {anisotropic_clamp}), all filter modes must be linear.")]
1806 InvalidFilterModeWithAnisotropy {
1807 filter_type: SamplerFilterErrorType,
1808 filter_mode: wgt::FilterMode,
1809 anisotropic_clamp: u16,
1810 },
1811 #[error(transparent)]
1812 MissingFeatures(#[from] MissingFeatures),
1813}
1814
1815crate::impl_resource_type!(Sampler);
1816crate::impl_labeled!(Sampler);
1817crate::impl_parent_device!(Sampler);
1818crate::impl_storage_item!(Sampler);
1819crate::impl_trackable!(Sampler);
1820
1821impl WebGpuError for CreateSamplerError {
1822 fn webgpu_error_type(&self) -> ErrorType {
1823 let e: &dyn WebGpuError = match self {
1824 Self::Device(e) => e,
1825 Self::MissingFeatures(e) => e,
1826
1827 Self::InvalidLodMinClamp(_)
1828 | Self::InvalidLodMaxClamp { .. }
1829 | Self::InvalidAnisotropy(_)
1830 | Self::InvalidFilterModeWithAnisotropy { .. } => return ErrorType::Validation,
1831 };
1832 e.webgpu_error_type()
1833 }
1834}
1835
1836#[derive(Clone, Debug, Error)]
1837#[non_exhaustive]
1838pub enum CreateQuerySetError {
1839 #[error(transparent)]
1840 Device(#[from] DeviceError),
1841 #[error("QuerySets cannot be made with zero queries")]
1842 ZeroCount,
1843 #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
1844 TooManyQueries { count: u32, maximum: u32 },
1845 #[error(transparent)]
1846 MissingFeatures(#[from] MissingFeatures),
1847}
1848
1849impl WebGpuError for CreateQuerySetError {
1850 fn webgpu_error_type(&self) -> ErrorType {
1851 let e: &dyn WebGpuError = match self {
1852 Self::Device(e) => e,
1853 Self::MissingFeatures(e) => e,
1854
1855 Self::TooManyQueries { .. } | Self::ZeroCount => return ErrorType::Validation,
1856 };
1857 e.webgpu_error_type()
1858 }
1859}
1860
1861pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
1862
1863#[derive(Debug)]
1864pub struct QuerySet {
1865 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynQuerySet>>,
1866 pub(crate) device: Arc<Device>,
1867 pub(crate) label: String,
1869 pub(crate) tracking_data: TrackingData,
1870 pub(crate) desc: wgt::QuerySetDescriptor<()>,
1871}
1872
1873impl Drop for QuerySet {
1874 fn drop(&mut self) {
1875 resource_log!("Destroy raw {}", self.error_ident());
1876 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1878 unsafe {
1879 self.device.raw().destroy_query_set(raw);
1880 }
1881 }
1882}
1883
1884crate::impl_resource_type!(QuerySet);
1885crate::impl_labeled!(QuerySet);
1886crate::impl_parent_device!(QuerySet);
1887crate::impl_storage_item!(QuerySet);
1888crate::impl_trackable!(QuerySet);
1889
1890impl QuerySet {
1891 pub(crate) fn raw(&self) -> &dyn hal::DynQuerySet {
1892 self.raw.as_ref()
1893 }
1894}
1895
1896pub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;
1897pub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;
1898
1899pub type BlasPrepareCompactResult = Result<(), BlasPrepareCompactError>;
1900
1901#[cfg(send_sync)]
1902pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + Send + 'static>;
1903#[cfg(not(send_sync))]
1904pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + 'static>;
1905
1906pub(crate) struct BlasPendingCompact {
1907 pub(crate) op: Option<BlasCompactCallback>,
1908 pub(crate) _parent_blas: Arc<Blas>,
1910}
1911
1912impl fmt::Debug for BlasPendingCompact {
1913 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1914 f.debug_struct("BlasPendingCompact")
1915 .field("op", &())
1916 .field("_parent_blas", &self._parent_blas)
1917 .finish()
1918 }
1919}
1920
1921#[derive(Debug)]
1922pub(crate) enum BlasCompactState {
1923 Compacted,
1925 Waiting(BlasPendingCompact),
1927 Ready { size: wgt::BufferAddress },
1929 Idle,
1931}
1932
1933#[cfg(send_sync)]
1934unsafe impl Send for BlasCompactState {}
1935#[cfg(send_sync)]
1936unsafe impl Sync for BlasCompactState {}
1937
1938#[derive(Debug)]
1939pub struct Blas {
1940 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
1941 pub(crate) device: Arc<Device>,
1942 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
1943 pub(crate) sizes: wgt::BlasGeometrySizeDescriptors,
1944 pub(crate) flags: wgt::AccelerationStructureFlags,
1945 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
1946 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
1947 pub(crate) handle: u64,
1948 pub(crate) label: String,
1950 pub(crate) tracking_data: TrackingData,
1951 pub(crate) compaction_buffer: Option<ManuallyDrop<Box<dyn hal::DynBuffer>>>,
1952 pub(crate) compacted_state: Mutex<BlasCompactState>,
1953}
1954
1955impl Drop for Blas {
1956 fn drop(&mut self) {
1957 resource_log!("Destroy raw {}", self.error_ident());
1958 if let Some(raw) = self.raw.take() {
1960 unsafe {
1961 self.device.raw().destroy_acceleration_structure(raw);
1962 }
1963 }
1964 if let Some(mut raw) = self.compaction_buffer.take() {
1965 unsafe {
1966 self.device
1967 .raw()
1968 .destroy_buffer(ManuallyDrop::take(&mut raw))
1969 }
1970 }
1971 }
1972}
1973
1974impl RawResourceAccess for Blas {
1975 type DynResource = dyn hal::DynAccelerationStructure;
1976
1977 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1978 self.raw.get(guard).map(|it| it.as_ref())
1979 }
1980}
1981
1982impl Blas {
1983 pub(crate) fn prepare_compact_async(
1984 self: &Arc<Self>,
1985 op: Option<BlasCompactCallback>,
1986 ) -> Result<SubmissionIndex, (Option<BlasCompactCallback>, BlasPrepareCompactError)> {
1987 let device = &self.device;
1988 if let Err(e) = device.check_is_valid() {
1989 return Err((op, e.into()));
1990 }
1991
1992 if self.built_index.read().is_none() {
1993 return Err((op, BlasPrepareCompactError::NotBuilt));
1994 }
1995
1996 if !self
1997 .flags
1998 .contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION)
1999 {
2000 return Err((op, BlasPrepareCompactError::CompactionUnsupported));
2001 }
2002
2003 let mut state = self.compacted_state.lock();
2004 *state = match *state {
2005 BlasCompactState::Compacted => {
2006 return Err((op, BlasPrepareCompactError::DoubleCompaction))
2007 }
2008 BlasCompactState::Waiting(_) => {
2009 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2010 }
2011 BlasCompactState::Ready { .. } => {
2012 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2013 }
2014 BlasCompactState::Idle => BlasCompactState::Waiting(BlasPendingCompact {
2015 op,
2016 _parent_blas: self.clone(),
2017 }),
2018 };
2019
2020 let submit_index = if let Some(queue) = device.get_queue() {
2021 queue.lock_life().prepare_compact(self).unwrap_or(0) } else {
2023 let (mut callback, status) = self.read_back_compact_size().unwrap();
2025 if let Some(callback) = callback.take() {
2026 callback(status);
2027 }
2028 0
2029 };
2030
2031 Ok(submit_index)
2032 }
2033
2034 #[must_use]
2036 pub(crate) fn read_back_compact_size(&self) -> Option<BlasCompactReadyPendingClosure> {
2037 let mut state = self.compacted_state.lock();
2038 let pending_compact = match mem::replace(&mut *state, BlasCompactState::Idle) {
2039 BlasCompactState::Waiting(pending_mapping) => pending_mapping,
2040 BlasCompactState::Idle => return None,
2042 BlasCompactState::Ready { .. } => {
2043 unreachable!("This should be validated out by `prepare_for_compaction`")
2044 }
2045 _ => panic!("No pending mapping."),
2046 };
2047 let status = {
2048 let compaction_buffer = self.compaction_buffer.as_ref().unwrap().as_ref();
2049 unsafe {
2050 let map_res = self.device.raw().map_buffer(
2051 compaction_buffer,
2052 0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress,
2053 );
2054 match map_res {
2055 Ok(mapping) => {
2056 if !mapping.is_coherent {
2057 #[expect(clippy::single_range_in_vec_init)]
2060 self.device.raw().flush_mapped_ranges(
2061 compaction_buffer,
2062 &[0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress],
2063 );
2064 }
2065 let size = core::ptr::read_unaligned(
2066 mapping.ptr.as_ptr().cast::<wgt::BufferAddress>(),
2067 );
2068 self.device.raw().unmap_buffer(compaction_buffer);
2069 if self.size_info.acceleration_structure_size != 0 {
2070 debug_assert_ne!(size, 0);
2071 }
2072 *state = BlasCompactState::Ready { size };
2073 Ok(())
2074 }
2075 Err(err) => Err(BlasPrepareCompactError::from(DeviceError::from_hal(err))),
2076 }
2077 }
2078 };
2079 Some((pending_compact.op, status))
2080 }
2081}
2082
2083crate::impl_resource_type!(Blas);
2084crate::impl_labeled!(Blas);
2085crate::impl_parent_device!(Blas);
2086crate::impl_storage_item!(Blas);
2087crate::impl_trackable!(Blas);
2088
2089#[derive(Debug)]
2090pub struct Tlas {
2091 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
2092 pub(crate) device: Arc<Device>,
2093 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
2094 pub(crate) max_instance_count: u32,
2095 pub(crate) flags: wgt::AccelerationStructureFlags,
2096 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
2097 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
2098 pub(crate) dependencies: RwLock<Vec<Arc<Blas>>>,
2099 pub(crate) instance_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
2100 pub(crate) label: String,
2102 pub(crate) tracking_data: TrackingData,
2103}
2104
2105impl Drop for Tlas {
2106 fn drop(&mut self) {
2107 unsafe {
2108 resource_log!("Destroy raw {}", self.error_ident());
2109 if let Some(structure) = self.raw.take() {
2110 self.device.raw().destroy_acceleration_structure(structure);
2111 }
2112 let buffer = ManuallyDrop::take(&mut self.instance_buffer);
2113 self.device.raw().destroy_buffer(buffer);
2114 }
2115 }
2116}
2117
2118impl RawResourceAccess for Tlas {
2119 type DynResource = dyn hal::DynAccelerationStructure;
2120
2121 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2122 self.raw.get(guard).map(|raw| raw.as_ref())
2123 }
2124}
2125
2126crate::impl_resource_type!(Tlas);
2127crate::impl_labeled!(Tlas);
2128crate::impl_parent_device!(Tlas);
2129crate::impl_storage_item!(Tlas);
2130crate::impl_trackable!(Tlas);