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 api_log,
21 binding_model::{BindGroup, BindingError},
22 device::{
23 queue, resource::DeferredDestroy, BufferMapPendingClosure, Device, DeviceError,
24 DeviceMismatch, HostMap, MissingDownlevelFlags, MissingFeatures,
25 },
26 hal_label,
27 init_tracker::{BufferInitTracker, TextureInitTracker},
28 lock::{rank, Mutex, RwLock},
29 ray_tracing::{BlasCompactReadyPendingClosure, BlasPrepareCompactError},
30 resource_log,
31 snatch::{SnatchGuard, Snatchable},
32 timestamp_normalization::TimestampNormalizationBindGroup,
33 track::{SharedTrackerIndexAllocator, TrackerIndex},
34 weak_vec::WeakVec,
35 Label, LabelHelpers, SubmissionIndex,
36};
37
38#[derive(Debug)]
58pub(crate) struct TrackingData {
59 tracker_index: TrackerIndex,
60 tracker_indices: Arc<SharedTrackerIndexAllocator>,
61}
62
63impl Drop for TrackingData {
64 fn drop(&mut self) {
65 self.tracker_indices.free(self.tracker_index);
66 }
67}
68
69impl TrackingData {
70 pub(crate) fn new(tracker_indices: Arc<SharedTrackerIndexAllocator>) -> Self {
71 Self {
72 tracker_index: tracker_indices.alloc(),
73 tracker_indices,
74 }
75 }
76
77 pub(crate) fn tracker_index(&self) -> TrackerIndex {
78 self.tracker_index
79 }
80}
81
82#[derive(Clone, Debug)]
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84pub struct ResourceErrorIdent {
85 r#type: Cow<'static, str>,
86 label: String,
87}
88
89impl fmt::Display for ResourceErrorIdent {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
91 write!(f, "{} with '{}' label", self.r#type, self.label)
92 }
93}
94
95#[derive(Debug)]
96pub enum ResourceState<T> {
97 Valid(T),
98 Invalid,
99}
100
101impl<T> ResourceState<T> {
102 pub fn as_ref(&self) -> ResourceState<&T> {
103 match self {
104 ResourceState::Valid(v) => ResourceState::Valid(v),
105 ResourceState::Invalid => ResourceState::Invalid,
106 }
107 }
108
109 pub fn valid(self) -> Option<T> {
110 match self {
111 ResourceState::Valid(v) => Some(v),
112 ResourceState::Invalid => None,
113 }
114 }
115}
116
117#[derive(thiserror::Error, Debug)]
118pub enum InvalidOrDestroyedResourceError {
119 #[error(transparent)]
120 InvalidResource(#[from] InvalidResourceError),
121 #[error(transparent)]
122 DestroyedResource(#[from] DestroyedResourceError),
123}
124
125pub trait ParentDevice: Labeled {
126 fn device(&self) -> &Arc<Device>;
127
128 fn is_equal(self: &Arc<Self>, other: &Arc<Self>) -> bool {
129 Arc::ptr_eq(self, other)
130 }
131
132 fn same_device_as<O: ParentDevice>(&self, other: &O) -> Result<(), DeviceError> {
133 if Arc::ptr_eq(self.device(), other.device()) {
134 Ok(())
135 } else {
136 Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
137 res: self.error_ident(),
138 res_device: self.device().error_ident(),
139 target: Some(other.error_ident()),
140 target_device: other.device().error_ident(),
141 })))
142 }
143 }
144
145 fn same_device(&self, device: &Device) -> Result<(), DeviceError> {
146 if core::ptr::eq(&**self.device(), device) {
147 Ok(())
148 } else {
149 Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
150 res: self.error_ident(),
151 res_device: self.device().error_ident(),
152 target: None,
153 target_device: device.error_ident(),
154 })))
155 }
156 }
157}
158
159#[macro_export]
160macro_rules! impl_parent_device {
161 ($ty:ident) => {
162 impl $crate::resource::ParentDevice for $ty {
163 fn device(&self) -> &Arc<Device> {
164 &self.device
165 }
166 }
167 };
168}
169
170pub trait RawResourceAccess: ParentDevice {
172 type DynResource: hal::DynResource + ?Sized;
173
174 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource>;
179
180 fn try_raw<'a>(
185 &'a self,
186 guard: &'a SnatchGuard,
187 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
188 self.raw(guard)
189 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
190 }
191}
192
193pub trait ResourceType {
194 const TYPE: &'static str;
195}
196
197#[macro_export]
198macro_rules! impl_resource_type {
199 ($ty:ident) => {
200 impl $crate::resource::ResourceType for $ty {
201 const TYPE: &'static str = stringify!($ty);
202 }
203 };
204}
205
206pub trait Labeled: ResourceType {
207 fn label(&self) -> &str;
213
214 fn error_ident(&self) -> ResourceErrorIdent {
215 ResourceErrorIdent {
216 r#type: Cow::Borrowed(Self::TYPE),
217 label: self.label().to_owned(),
218 }
219 }
220}
221
222#[macro_export]
223macro_rules! impl_labeled {
224 ($ty:ident) => {
225 impl $crate::resource::Labeled for $ty {
226 fn label(&self) -> &str {
227 &self.label
228 }
229 }
230 };
231}
232
233pub(crate) trait Trackable {
234 fn tracker_index(&self) -> TrackerIndex;
235}
236
237#[macro_export]
238macro_rules! impl_trackable {
239 ($ty:ident) => {
240 impl $crate::resource::Trackable for $ty {
241 fn tracker_index(&self) -> $crate::track::TrackerIndex {
242 self.tracking_data.tracker_index()
243 }
244 }
245 };
246}
247
248#[derive(Debug)]
249pub(crate) enum BufferMapState {
250 Init { staging_buffer: StagingBuffer },
252 Waiting(BufferPendingMapping),
254 Active {
256 mapping: hal::BufferMapping,
257 range: hal::MemoryRange,
258 host: HostMap,
259 },
260 Idle,
262}
263
264#[cfg(send_sync)]
265unsafe impl Send for BufferMapState {}
266#[cfg(send_sync)]
267unsafe impl Sync for BufferMapState {}
268
269#[cfg(send_sync)]
270pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + Send + 'static>;
271#[cfg(not(send_sync))]
272pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + 'static>;
273
274pub struct BufferMapOperation {
275 pub host: HostMap,
276 pub callback: Option<BufferMapCallback>,
277}
278
279impl fmt::Debug for BufferMapOperation {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 f.debug_struct("BufferMapOperation")
282 .field("host", &self.host)
283 .field("callback", &self.callback.as_ref().map(|_| "?"))
284 .finish()
285 }
286}
287
288#[derive(Clone, Debug, Error)]
289#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
290#[non_exhaustive]
291pub enum BufferAccessError {
292 #[error(transparent)]
293 Device(#[from] DeviceError),
294 #[error("Buffer map failed")]
295 Failed,
296 #[error(transparent)]
297 DestroyedResource(#[from] DestroyedResourceError),
298 #[error("Buffer is already mapped")]
299 AlreadyMapped,
300 #[error("Buffer map is pending")]
301 MapAlreadyPending,
302 #[error(transparent)]
303 MissingBufferUsage(#[from] MissingBufferUsageError),
304 #[error("Buffer is not mapped")]
305 NotMapped,
306 #[error(
307 "Buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`"
308 )]
309 UnalignedRange,
310 #[error("Buffer offset invalid: offset {offset} must be multiple of 8")]
311 UnalignedOffset { offset: wgt::BufferAddress },
312 #[error("Buffer range size invalid: range_size {range_size} must be multiple of 4")]
313 UnalignedRangeSize { range_size: wgt::BufferAddress },
314 #[error("Buffer access out of bounds: index {index} would underrun the buffer (limit: {min})")]
315 OutOfBoundsStartOffsetUnderrun {
316 index: wgt::BufferAddress,
317 min: wgt::BufferAddress,
318 },
319 #[error(
320 "Buffer access out of bounds: start offset {index} would overrun the buffer (limit: {max})"
321 )]
322 OutOfBoundsStartOffsetOverrun {
323 index: wgt::BufferAddress,
324 max: wgt::BufferAddress,
325 },
326 #[error(
327 "Buffer access out of bounds: start offset {index} + size {size} would overrun the buffer (limit: {max})"
328 )]
329 OutOfBoundsEndOffsetOverrun {
330 index: wgt::BufferAddress,
331 size: wgt::BufferAddress,
332 max: wgt::BufferAddress,
333 },
334 #[error("Buffer map aborted")]
335 MapAborted,
336 #[error(transparent)]
337 InvalidResource(#[from] InvalidResourceError),
338 #[error("Map start offset ({offset}) is out-of-bounds for buffer of size {buffer_size}")]
339 MapStartOffsetOverrun {
340 offset: wgt::BufferAddress,
341 buffer_size: wgt::BufferAddress,
342 },
343 #[error(
344 "Map end offset (start at {} + size of {}) is out-of-bounds for buffer of size {}",
345 offset,
346 size,
347 buffer_size
348 )]
349 MapEndOffsetOverrun {
350 offset: wgt::BufferAddress,
351 size: wgt::BufferAddress,
352 buffer_size: wgt::BufferAddress,
353 },
354}
355
356impl WebGpuError for BufferAccessError {
357 fn webgpu_error_type(&self) -> ErrorType {
358 match self {
359 Self::Device(e) => e.webgpu_error_type(),
360 Self::InvalidResource(e) => e.webgpu_error_type(),
361 Self::DestroyedResource(e) => e.webgpu_error_type(),
362
363 Self::Failed
364 | Self::AlreadyMapped
365 | Self::MapAlreadyPending
366 | Self::MissingBufferUsage(_)
367 | Self::NotMapped
368 | Self::UnalignedRange
369 | Self::UnalignedOffset { .. }
370 | Self::UnalignedRangeSize { .. }
371 | Self::OutOfBoundsStartOffsetUnderrun { .. }
372 | Self::OutOfBoundsStartOffsetOverrun { .. }
373 | Self::OutOfBoundsEndOffsetOverrun { .. }
374 | Self::MapAborted
375 | Self::MapStartOffsetOverrun { .. }
376 | Self::MapEndOffsetOverrun { .. } => ErrorType::Validation,
377 }
378 }
379}
380
381#[derive(Clone, Debug, Error)]
382#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
383#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
384pub struct MissingBufferUsageError {
385 pub(crate) res: ResourceErrorIdent,
386 pub(crate) actual: wgt::BufferUsages,
387 pub(crate) expected: wgt::BufferUsages,
388}
389
390impl WebGpuError for MissingBufferUsageError {
391 fn webgpu_error_type(&self) -> ErrorType {
392 ErrorType::Validation
393 }
394}
395
396#[derive(Clone, Debug, Error)]
397#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
398pub struct MissingTextureUsageError {
399 pub(crate) res: ResourceErrorIdent,
400 pub(crate) actual: wgt::TextureUsages,
401 pub(crate) expected: wgt::TextureUsages,
402}
403
404impl WebGpuError for MissingTextureUsageError {
405 fn webgpu_error_type(&self) -> ErrorType {
406 ErrorType::Validation
407 }
408}
409
410#[derive(Clone, Debug, Error)]
411#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
412#[error("{0} has been destroyed")]
413pub struct DestroyedResourceError(pub ResourceErrorIdent);
414
415impl WebGpuError for DestroyedResourceError {
416 fn webgpu_error_type(&self) -> ErrorType {
417 ErrorType::Validation
418 }
419}
420
421#[derive(Clone, Debug, Error)]
422#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
423#[error("{0} is invalid")]
424pub struct InvalidResourceError(pub ResourceErrorIdent);
425
426impl WebGpuError for InvalidResourceError {
427 fn webgpu_error_type(&self) -> ErrorType {
428 ErrorType::Validation
429 }
430}
431
432pub enum Fallible<T: ParentDevice> {
433 Valid(Arc<T>),
434 Invalid(Arc<String>),
435}
436
437impl<T: ParentDevice> Fallible<T> {
438 pub fn get(self) -> Result<Arc<T>, InvalidResourceError> {
439 match self {
440 Fallible::Valid(v) => Ok(v),
441 Fallible::Invalid(label) => Err(InvalidResourceError(ResourceErrorIdent {
442 r#type: Cow::Borrowed(T::TYPE),
443 label: (*label).clone(),
444 })),
445 }
446 }
447}
448
449impl<T: ParentDevice> Clone for Fallible<T> {
450 fn clone(&self) -> Self {
451 match self {
452 Self::Valid(v) => Self::Valid(v.clone()),
453 Self::Invalid(l) => Self::Invalid(l.clone()),
454 }
455 }
456}
457
458impl<T: ParentDevice> ResourceType for Fallible<T> {
459 const TYPE: &'static str = T::TYPE;
460}
461
462impl<T: ParentDevice + crate::storage::StorageItem> crate::storage::StorageItem for Fallible<T> {
463 type Marker = T::Marker;
464}
465
466pub type BufferAccessResult = Result<(), BufferAccessError>;
467
468#[derive(Debug)]
469pub(crate) struct BufferPendingMapping {
470 pub(crate) range: Range<wgt::BufferAddress>,
471 pub(crate) op: BufferMapOperation,
472 pub(crate) _parent_buffer: Arc<Buffer>,
474}
475
476pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
477
478#[derive(Debug)]
479pub struct Buffer {
480 pub(crate) raw: Snatchable<Box<dyn hal::DynBuffer>>,
481 pub(crate) device: Arc<Device>,
482 pub(crate) usage: wgt::BufferUsages,
483 pub(crate) size: wgt::BufferAddress,
484 pub(crate) initialization_status: RwLock<BufferInitTracker>,
485 pub(crate) label: String,
487 pub(crate) tracking_data: TrackingData,
488 pub(crate) map_state: Mutex<BufferMapState>,
489 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
491 pub(crate) timestamp_normalization_bind_group: Snatchable<TimestampNormalizationBindGroup>,
492 pub(crate) indirect_validation_bind_groups: Snatchable<crate::indirect_validation::BindGroups>,
493}
494
495impl Drop for Buffer {
496 fn drop(&mut self) {
497 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
498 raw.dispose(self.device.raw());
499 }
500
501 if let Some(raw) = self.indirect_validation_bind_groups.take() {
502 raw.dispose(self.device.raw());
503 }
504
505 if let Some(raw) = self.raw.take() {
506 resource_log!("Destroy raw {}", self.error_ident());
507 unsafe {
508 self.device.raw().destroy_buffer(raw);
509 }
510 }
511 }
512}
513
514impl RawResourceAccess for Buffer {
515 type DynResource = dyn hal::DynBuffer;
516
517 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
518 self.raw.get(guard).map(|b| b.as_ref())
519 }
520}
521
522impl Buffer {
523 pub(crate) fn check_destroyed(
524 &self,
525 guard: &SnatchGuard,
526 ) -> Result<(), DestroyedResourceError> {
527 self.raw
528 .get(guard)
529 .map(|_| ())
530 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
531 }
532
533 pub(crate) fn check_usage(
536 &self,
537 expected: wgt::BufferUsages,
538 ) -> Result<(), MissingBufferUsageError> {
539 if self.usage.contains(expected) {
540 Ok(())
541 } else {
542 Err(MissingBufferUsageError {
543 res: self.error_ident(),
544 actual: self.usage,
545 expected,
546 })
547 }
548 }
549
550 pub fn resolve_binding_size(
562 &self,
563 offset: wgt::BufferAddress,
564 binding_size: Option<wgt::BufferSize>,
565 ) -> Result<u64, BindingError> {
566 let buffer_size = self.size;
567
568 match binding_size {
569 Some(binding_size) => match offset.checked_add(binding_size.get()) {
570 Some(end) if end <= buffer_size => Ok(binding_size.get()),
571 _ => Err(BindingError::BindingRangeTooLarge {
572 buffer: self.error_ident(),
573 offset,
574 binding_size: binding_size.get(),
575 buffer_size,
576 }),
577 },
578 None => {
579 buffer_size
580 .checked_sub(offset)
581 .ok_or_else(|| BindingError::BindingOffsetTooLarge {
582 buffer: self.error_ident(),
583 offset,
584 buffer_size,
585 })
586 }
587 }
588 }
589
590 pub fn binding<'a>(
611 &'a self,
612 offset: wgt::BufferAddress,
613 binding_size: Option<wgt::BufferSize>,
614 snatch_guard: &'a SnatchGuard,
615 ) -> Result<(hal::BufferBinding<'a, dyn hal::DynBuffer>, u64), BindingError> {
616 let buf_raw = self.try_raw(snatch_guard)?;
617 let resolved_size = self.resolve_binding_size(offset, binding_size)?;
618 Ok((
621 hal::BufferBinding::new_unchecked(buf_raw, offset, binding_size),
622 resolved_size,
623 ))
624 }
625
626 pub fn map_async(
630 self: &Arc<Self>,
631 offset: wgt::BufferAddress,
632 size: Option<wgt::BufferAddress>,
633 op: BufferMapOperation,
634 ) -> Result<SubmissionIndex, BufferAccessError> {
635 self.try_map_async(offset, size, op)
636 .map_err(|(mut operation, err)| {
637 if let Some(callback) = operation.callback.take() {
638 callback(Err(err.clone()));
639 }
640 err
641 })
642 }
643
644 fn try_map_async(
667 self: &Arc<Self>,
668 offset: wgt::BufferAddress,
669 size: Option<wgt::BufferAddress>,
670 op: BufferMapOperation,
671 ) -> Result<SubmissionIndex, (BufferMapOperation, BufferAccessError)> {
672 let range_size = if let Some(size) = size {
673 size
674 } else {
675 self.size.saturating_sub(offset)
676 };
677
678 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
679 return Err((op, BufferAccessError::UnalignedOffset { offset }));
680 }
681 if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
682 return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));
683 }
684
685 if offset > self.size {
686 return Err((
687 op,
688 BufferAccessError::MapStartOffsetOverrun {
689 offset,
690 buffer_size: self.size,
691 },
692 ));
693 }
694 if range_size > self.size - offset {
696 return Err((
697 op,
698 BufferAccessError::MapEndOffsetOverrun {
699 offset,
700 size: range_size,
701 buffer_size: self.size,
702 },
703 ));
704 }
705 let end_offset = offset + range_size;
706
707 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT)
708 || !end_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT)
709 {
710 return Err((op, BufferAccessError::UnalignedRange));
711 }
712
713 let (pub_usage, internal_use) = match op.host {
714 HostMap::Read => (wgt::BufferUsages::MAP_READ, wgt::BufferUses::MAP_READ),
715 HostMap::Write => (wgt::BufferUsages::MAP_WRITE, wgt::BufferUses::MAP_WRITE),
716 };
717
718 if let Err(e) = self.check_usage(pub_usage) {
719 return Err((op, e.into()));
720 }
721
722 let device = &self.device;
723 if let Err(e) = device.check_is_valid() {
724 return Err((op, e.into()));
725 }
726
727 let submit_index = {
728 let snatch_guard = device.snatchable_lock.read();
729 if let Err(e) = self.check_destroyed(&snatch_guard) {
730 return Err((op, e.into()));
731 }
732
733 {
734 let map_state = &mut *self.map_state.lock();
735 *map_state = match *map_state {
736 BufferMapState::Init { .. } | BufferMapState::Active { .. } => {
737 return Err((op, BufferAccessError::AlreadyMapped));
738 }
739 BufferMapState::Waiting(_) => {
740 return Err((op, BufferAccessError::MapAlreadyPending));
741 }
742 BufferMapState::Idle => BufferMapState::Waiting(BufferPendingMapping {
743 range: offset..end_offset,
744 op,
745 _parent_buffer: self.clone(),
746 }),
747 };
748 }
749
750 if let Some(queue) = device.get_queue().as_ref() {
751 match queue.flush_writes_for_buffer(self, snatch_guard) {
752 Err(err) => {
753 let state = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
754 let BufferMapState::Waiting(BufferPendingMapping { op, .. }) = state else {
755 unreachable!();
756 };
757 return Err((op, err));
758 }
759 Ok(()) => {
760 Some(queue.lock_life().map(self).unwrap_or(0))
769 }
770 }
771 } else {
772 None
773 }
774 };
775
776 device
784 .trackers
785 .lock()
786 .buffers
787 .set_single(self, internal_use);
788
789 if let Some(index) = submit_index {
790 Ok(index)
791 } else {
792 let (mut operation, status) = self.map(&device.snatchable_lock.read()).unwrap();
795 if let Some(callback) = operation.callback.take() {
796 callback(status);
797 }
798 Ok(0)
799 }
800 }
801
802 pub fn get_mapped_range(
803 self: &Arc<Self>,
804 offset: wgt::BufferAddress,
805 size: Option<wgt::BufferAddress>,
806 ) -> Result<(NonNull<u8>, u64), BufferAccessError> {
807 {
808 let snatch_guard = self.device.snatchable_lock.read();
809 self.check_destroyed(&snatch_guard)?;
810 }
811
812 let range_size = if let Some(size) = size {
813 size
814 } else {
815 self.size.saturating_sub(offset)
816 };
817
818 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
819 return Err(BufferAccessError::UnalignedOffset { offset });
820 }
821 if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
822 return Err(BufferAccessError::UnalignedRangeSize { range_size });
823 }
824 let map_state = &*self.map_state.lock();
825 match *map_state {
826 BufferMapState::Init { ref staging_buffer } => {
827 if offset > self.size {
828 return Err(BufferAccessError::MapStartOffsetOverrun {
829 offset,
830 buffer_size: self.size,
831 });
832 }
833 if range_size > self.size - offset {
835 return Err(BufferAccessError::MapEndOffsetOverrun {
836 offset,
837 size: range_size,
838 buffer_size: self.size,
839 });
840 }
841 let ptr = unsafe { staging_buffer.ptr() };
842 let ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().offset(offset as isize)) };
843 Ok((ptr, range_size))
844 }
845 BufferMapState::Active {
846 ref mapping,
847 ref range,
848 ..
849 } => {
850 if offset > range.end {
851 return Err(BufferAccessError::OutOfBoundsStartOffsetOverrun {
852 index: offset,
853 max: range.end,
854 });
855 }
856 if offset < range.start {
857 return Err(BufferAccessError::OutOfBoundsStartOffsetUnderrun {
858 index: offset,
859 min: range.start,
860 });
861 }
862 if range_size > range.end - offset {
863 return Err(BufferAccessError::OutOfBoundsEndOffsetOverrun {
864 index: offset,
865 size: range_size,
866 max: range.end,
867 });
868 }
869 let relative_offset = (offset - range.start) as isize;
872 unsafe {
873 Ok((
874 NonNull::new_unchecked(mapping.ptr.as_ptr().offset(relative_offset)),
875 range_size,
876 ))
877 }
878 }
879 BufferMapState::Idle | BufferMapState::Waiting(_) => Err(BufferAccessError::NotMapped),
880 }
881 }
882 #[must_use]
885 pub(crate) fn map(&self, snatch_guard: &SnatchGuard) -> Option<BufferMapPendingClosure> {
886 let mapping = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
890 let pending_mapping = match mapping {
891 BufferMapState::Waiting(pending_mapping) => pending_mapping,
892 BufferMapState::Idle => return None,
894 BufferMapState::Active { .. } => {
897 *self.map_state.lock() = mapping;
898 return None;
899 }
900 _ => panic!("No pending mapping."),
901 };
902 let status = if pending_mapping.range.start != pending_mapping.range.end {
903 let host = pending_mapping.op.host;
904 let size = pending_mapping.range.end - pending_mapping.range.start;
905 match crate::device::map_buffer(
906 self,
907 pending_mapping.range.start,
908 size,
909 host,
910 snatch_guard,
911 ) {
912 Ok(mapping) => {
913 *self.map_state.lock() = BufferMapState::Active {
914 mapping,
915 range: pending_mapping.range.clone(),
916 host,
917 };
918 Ok(())
919 }
920 Err(e) => Err(e),
921 }
922 } else {
923 *self.map_state.lock() = BufferMapState::Active {
924 mapping: hal::BufferMapping {
925 ptr: NonNull::dangling(),
926 is_coherent: true,
927 },
928 range: pending_mapping.range,
929 host: pending_mapping.op.host,
930 };
931 Ok(())
932 };
933 Some((pending_mapping.op, status))
934 }
935
936 pub fn unmap(self: &Arc<Self>) -> Result<(), BufferAccessError> {
938 if let Some((mut operation, status)) = self.unmap_inner()? {
939 if let Some(callback) = operation.callback.take() {
940 callback(status);
941 }
942 }
943
944 Ok(())
945 }
946
947 fn unmap_inner(self: &Arc<Self>) -> Result<Option<BufferMapPendingClosure>, BufferAccessError> {
948 let device = &self.device;
949 let snatch_guard = device.snatchable_lock.read();
950 let raw_buf = self.try_raw(&snatch_guard)?;
951 let map_state = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
952 match map_state {
953 BufferMapState::Init { staging_buffer } => {
954 #[cfg(feature = "trace")]
955 if let Some(ref mut trace) = *device.trace.lock() {
956 use crate::device::trace::{DataKind, IntoTrace};
957
958 let data = trace.make_binary(DataKind::Bin, staging_buffer.get_data());
959 trace.add(trace::Action::WriteBuffer {
960 id: self.to_trace(),
961 data,
962 offset: 0,
964 size: self.size,
965 queued: true,
966 });
967 }
968
969 let staging_buffer = staging_buffer.flush();
970
971 if let Some(queue) = device.get_queue() {
972 let region = wgt::BufferSize::new(self.size).map(|size| hal::BufferCopy {
973 src_offset: 0,
974 dst_offset: 0,
975 size,
976 });
977 let transition_src = hal::BufferBarrier {
978 buffer: staging_buffer.raw(),
979 usage: hal::StateTransition {
980 from: wgt::BufferUses::MAP_WRITE,
981 to: wgt::BufferUses::COPY_SRC,
982 },
983 };
984 let transition_dst = hal::BufferBarrier::<dyn hal::DynBuffer> {
985 buffer: raw_buf,
986 usage: hal::StateTransition {
987 from: wgt::BufferUses::empty(),
988 to: wgt::BufferUses::COPY_DST,
989 },
990 };
991 let mut pending_writes = queue.pending_writes.lock();
992 let encoder = pending_writes.activate();
993 unsafe {
994 encoder.transition_buffers(&[transition_src, transition_dst]);
995 if self.size > 0 {
996 encoder.copy_buffer_to_buffer(
997 staging_buffer.raw(),
998 raw_buf,
999 region.as_slice(),
1000 );
1001 }
1002 }
1003 pending_writes.consume(staging_buffer);
1004 pending_writes.insert_buffer(self);
1005 }
1006 }
1007 BufferMapState::Idle => {
1008 return Err(BufferAccessError::NotMapped);
1009 }
1010 BufferMapState::Waiting(pending) => {
1011 return Ok(Some((pending.op, Err(BufferAccessError::MapAborted))));
1012 }
1013 BufferMapState::Active {
1014 mapping,
1015 range,
1016 host,
1017 } => {
1018 if host == HostMap::Write {
1019 #[cfg(feature = "trace")]
1020 if let Some(ref mut trace) = *device.trace.lock() {
1021 use crate::device::trace::{DataKind, IntoTrace};
1022
1023 let size = range.end - range.start;
1024 let data = trace.make_binary(DataKind::Bin, unsafe {
1025 core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize)
1026 });
1027 trace.add(trace::Action::WriteBuffer {
1028 id: self.to_trace(),
1029 data,
1030 offset: range.start,
1031 size,
1032 queued: false,
1033 });
1034 }
1035 if !mapping.is_coherent {
1036 unsafe { device.raw().flush_mapped_ranges(raw_buf, &[range]) };
1037 }
1038 }
1039 unsafe { device.raw().unmap_buffer(raw_buf) };
1040 }
1041 }
1042 Ok(None)
1043 }
1044
1045 pub fn destroy(self: &Arc<Self>) {
1046 let device = &self.device;
1047
1048 let temp = {
1049 let mut snatch_guard = device.snatchable_lock.write();
1050
1051 let raw = match self.raw.snatch(&mut snatch_guard) {
1052 Some(raw) => raw,
1053 None => {
1054 return;
1056 }
1057 };
1058
1059 let timestamp_normalization_bind_group = self
1060 .timestamp_normalization_bind_group
1061 .snatch(&mut snatch_guard);
1062
1063 let indirect_validation_bind_groups = self
1064 .indirect_validation_bind_groups
1065 .snatch(&mut snatch_guard);
1066
1067 drop(snatch_guard);
1068
1069 let bind_groups = {
1070 let mut guard = self.bind_groups.lock();
1071 mem::take(&mut *guard)
1072 };
1073
1074 queue::TempResource::DestroyedBuffer(DestroyedBuffer {
1075 raw: ManuallyDrop::new(raw),
1076 device: Arc::clone(&self.device),
1077 label: self.label().to_owned(),
1078 bind_groups,
1079 timestamp_normalization_bind_group,
1080 indirect_validation_bind_groups,
1081 })
1082 };
1083
1084 let Some(queue) = device.get_queue() else {
1085 return;
1086 };
1087
1088 {
1089 let mut pending_writes = queue.pending_writes.lock();
1090 if pending_writes.contains_buffer(self) {
1091 pending_writes.consume_temp(temp);
1092 return;
1093 }
1094 }
1095
1096 let mut life_lock = queue.lock_life();
1097 let last_submit_index = life_lock.get_buffer_latest_submission_index(self);
1098 if let Some(last_submit_index) = last_submit_index {
1099 life_lock.schedule_resource_destruction(temp, last_submit_index);
1100 }
1101 }
1102}
1103
1104#[derive(Clone, Debug, Error)]
1105#[non_exhaustive]
1106pub enum CreateBufferError {
1107 #[error(transparent)]
1108 Device(#[from] DeviceError),
1109 #[error("Failed to map buffer while creating: {0}")]
1110 AccessError(#[from] BufferAccessError),
1111 #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
1112 UnalignedSize,
1113 #[error("Invalid usage flags {0:?}")]
1114 InvalidUsage(wgt::BufferUsages),
1115 #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
1116 UsageMismatch(wgt::BufferUsages),
1117 #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
1118 MaxBufferSize { requested: u64, maximum: u64 },
1119 #[error(transparent)]
1120 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1121 #[error(transparent)]
1122 MissingFeatures(#[from] MissingFeatures),
1123 #[error("Failed to create bind group for indirect buffer validation: {0}")]
1124 IndirectValidationBindGroup(DeviceError),
1125}
1126
1127crate::impl_resource_type!(Buffer);
1128crate::impl_labeled!(Buffer);
1129crate::impl_parent_device!(Buffer);
1130crate::impl_storage_item!(Buffer);
1131crate::impl_trackable!(Buffer);
1132
1133impl WebGpuError for CreateBufferError {
1134 fn webgpu_error_type(&self) -> ErrorType {
1135 match self {
1136 Self::Device(e) => e.webgpu_error_type(),
1137 Self::AccessError(e) => e.webgpu_error_type(),
1138 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1139 Self::IndirectValidationBindGroup(e) => e.webgpu_error_type(),
1140 Self::MissingFeatures(e) => e.webgpu_error_type(),
1141
1142 Self::UnalignedSize
1143 | Self::InvalidUsage(_)
1144 | Self::UsageMismatch(_)
1145 | Self::MaxBufferSize { .. } => ErrorType::Validation,
1146 }
1147 }
1148}
1149
1150#[derive(Debug)]
1152pub struct DestroyedBuffer {
1153 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1154 device: Arc<Device>,
1155 label: String,
1156 bind_groups: WeakVec<BindGroup>,
1157 timestamp_normalization_bind_group: Option<TimestampNormalizationBindGroup>,
1158 indirect_validation_bind_groups: Option<crate::indirect_validation::BindGroups>,
1159}
1160
1161impl DestroyedBuffer {
1162 pub fn label(&self) -> &dyn fmt::Debug {
1163 &self.label
1164 }
1165}
1166
1167impl Drop for DestroyedBuffer {
1168 fn drop(&mut self) {
1169 let mut deferred = self.device.deferred_destroy.lock();
1170 deferred.push(DeferredDestroy::BindGroups(mem::take(
1171 &mut self.bind_groups,
1172 )));
1173 drop(deferred);
1174
1175 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
1176 raw.dispose(self.device.raw());
1177 }
1178
1179 if let Some(raw) = self.indirect_validation_bind_groups.take() {
1180 raw.dispose(self.device.raw());
1181 }
1182
1183 resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
1184 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1186 unsafe {
1187 hal::DynDevice::destroy_buffer(self.device.raw(), raw);
1188 }
1189 }
1190}
1191
1192#[cfg(send_sync)]
1193unsafe impl Send for StagingBuffer {}
1194#[cfg(send_sync)]
1195unsafe impl Sync for StagingBuffer {}
1196
1197#[derive(Debug)]
1217pub struct StagingBuffer {
1218 raw: Box<dyn hal::DynBuffer>,
1219 device: Arc<Device>,
1220 pub(crate) size: wgt::BufferSize,
1221 is_coherent: bool,
1222 ptr: NonNull<u8>,
1223}
1224
1225impl StagingBuffer {
1226 pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {
1227 profiling::scope!("StagingBuffer::new");
1228 let stage_desc = hal::BufferDescriptor {
1229 label: hal_label(Some("(wgpu internal) Staging"), device.instance_flags),
1230 size: size.get(),
1231 usage: wgt::BufferUses::MAP_WRITE | wgt::BufferUses::COPY_SRC,
1232 memory_flags: hal::MemoryFlags::TRANSIENT,
1233 };
1234
1235 let raw = unsafe { device.raw().create_buffer(&stage_desc) }
1236 .map_err(|e| device.handle_hal_error(e))?;
1237 let mapping = unsafe { device.raw().map_buffer(raw.as_ref(), 0..size.get()) }
1238 .map_err(|e| device.handle_hal_error(e))?;
1239
1240 let staging_buffer = StagingBuffer {
1241 raw,
1242 device: device.clone(),
1243 size,
1244 is_coherent: mapping.is_coherent,
1245 ptr: mapping.ptr,
1246 };
1247
1248 Ok(staging_buffer)
1249 }
1250
1251 pub(crate) unsafe fn ptr(&self) -> NonNull<u8> {
1254 self.ptr
1255 }
1256
1257 #[cfg(feature = "trace")]
1258 pub(crate) fn get_data(&self) -> &[u8] {
1259 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.size.get() as usize) }
1260 }
1261
1262 pub(crate) fn write_zeros(&mut self) {
1263 unsafe { core::ptr::write_bytes(self.ptr.as_ptr(), 0, self.size.get() as usize) };
1264 }
1265
1266 pub(crate) fn write(&mut self, data: &[u8]) {
1267 assert!(data.len() >= self.size.get() as usize);
1268 unsafe {
1271 core::ptr::copy_nonoverlapping(
1272 data.as_ptr(),
1273 self.ptr.as_ptr(),
1274 self.size.get() as usize,
1275 );
1276 }
1277 }
1278
1279 pub(crate) unsafe fn write_with_offset(
1281 &mut self,
1282 data: &[u8],
1283 src_offset: isize,
1284 dst_offset: isize,
1285 size: usize,
1286 ) {
1287 unsafe {
1288 debug_assert!(
1289 (src_offset + size as isize) as usize <= data.len(),
1290 "src_offset + size must be in-bounds: src_offset = {}, size = {}, data.len() = {}",
1291 src_offset,
1292 size,
1293 data.len()
1294 );
1295 core::ptr::copy_nonoverlapping(
1296 data.as_ptr().offset(src_offset),
1297 self.ptr.as_ptr().offset(dst_offset),
1298 size,
1299 );
1300 }
1301 }
1302
1303 pub(crate) fn flush(self) -> FlushedStagingBuffer {
1304 let device = self.device.raw();
1305 if !self.is_coherent {
1306 #[allow(clippy::single_range_in_vec_init)]
1307 unsafe {
1308 device.flush_mapped_ranges(self.raw.as_ref(), &[0..self.size.get()])
1309 };
1310 }
1311 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1312
1313 let StagingBuffer {
1314 raw, device, size, ..
1315 } = self;
1316
1317 FlushedStagingBuffer {
1318 raw: ManuallyDrop::new(raw),
1319 device,
1320 size,
1321 }
1322 }
1323}
1324
1325crate::impl_resource_type!(StagingBuffer);
1326crate::impl_storage_item!(StagingBuffer);
1327
1328#[derive(Debug)]
1329pub struct FlushedStagingBuffer {
1330 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1331 device: Arc<Device>,
1332 pub(crate) size: wgt::BufferSize,
1333}
1334
1335impl FlushedStagingBuffer {
1336 pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {
1337 self.raw.as_ref()
1338 }
1339}
1340
1341impl Drop for FlushedStagingBuffer {
1342 fn drop(&mut self) {
1343 resource_log!("Destroy raw StagingBuffer");
1344 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1346 unsafe { self.device.raw().destroy_buffer(raw) };
1347 }
1348}
1349
1350pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
1351
1352#[derive(Debug)]
1353pub(crate) enum TextureInner {
1354 Native {
1355 raw: Box<dyn hal::DynTexture>,
1356 },
1357 Surface {
1358 raw: Box<dyn hal::DynSurfaceTexture>,
1359 },
1360}
1361
1362impl TextureInner {
1363 pub(crate) fn raw(&self) -> &dyn hal::DynTexture {
1364 match self {
1365 Self::Native { raw } => raw.as_ref(),
1366 Self::Surface { raw, .. } => raw.as_ref().borrow(),
1367 }
1368 }
1369}
1370
1371#[derive(Debug)]
1372pub enum TextureClearMode {
1373 BufferCopy,
1374 RenderPass {
1376 clear_views: SmallVec<[ManuallyDrop<Box<dyn hal::DynTextureView>>; 1]>,
1377 is_color: bool,
1378 },
1379 Surface {
1380 clear_view: ManuallyDrop<Box<dyn hal::DynTextureView>>,
1381 },
1382 None,
1385}
1386
1387#[derive(Debug)]
1388pub struct TextureState {
1389 pub(crate) inner: Snatchable<TextureInner>,
1390}
1391
1392#[derive(Debug)]
1393pub struct Texture {
1394 pub(crate) state: ResourceState<TextureState>,
1395 pub(crate) device: Arc<Device>,
1396 pub(crate) desc: wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
1397 pub(crate) _hal_usage: wgt::TextureUses,
1398 pub(crate) format_features: wgt::TextureFormatFeatures,
1399 pub(crate) initialization_status: RwLock<TextureInitTracker>,
1400 pub(crate) full_range: TextureSelector,
1401 pub(crate) tracking_data: TrackingData,
1402 pub(crate) clear_mode: RwLock<TextureClearMode>,
1403 pub(crate) views: Mutex<WeakVec<TextureView>>,
1404 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
1406}
1407
1408impl Texture {
1409 pub(crate) fn new(
1410 device: &Arc<Device>,
1411 inner: TextureInner,
1412 hal_usage: wgt::TextureUses,
1413 desc: &TextureDescriptor,
1414 format_features: wgt::TextureFormatFeatures,
1415 clear_mode: TextureClearMode,
1416 init: bool,
1417 ) -> Self {
1418 Texture {
1419 state: ResourceState::Valid(TextureState {
1420 inner: Snatchable::new(inner),
1421 }),
1422 device: device.clone(),
1423 desc: desc.map_label(|label| label.to_string()),
1424 _hal_usage: hal_usage,
1425 format_features,
1426 initialization_status: RwLock::new(
1427 rank::TEXTURE_INITIALIZATION_STATUS,
1428 if init {
1429 TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count())
1430 } else {
1431 TextureInitTracker::new(desc.mip_level_count, 0)
1432 },
1433 ),
1434 full_range: TextureSelector {
1435 mips: 0..desc.mip_level_count,
1436 layers: 0..desc.array_layer_count(),
1437 },
1438 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1439 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
1440 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1441 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1442 }
1443 }
1444
1445 pub(crate) fn invalid(device: &Arc<Device>, desc: &TextureDescriptor) -> Self {
1446 Texture {
1447 state: ResourceState::Invalid,
1448 device: device.clone(),
1449 desc: desc.map_label(|label| label.to_string()),
1450 _hal_usage: wgt::TextureUses::empty(),
1451 format_features: wgt::TextureFormatFeatures {
1452 allowed_usages: wgt::TextureUsages::empty(),
1453 flags: wgt::TextureFormatFeatureFlags::empty(),
1454 },
1455 initialization_status: RwLock::new(
1456 rank::TEXTURE_INITIALIZATION_STATUS,
1457 TextureInitTracker::new(0, 0),
1458 ),
1459 full_range: TextureSelector {
1460 mips: 0..desc.mip_level_count,
1461 layers: 0..desc.array_layer_count(),
1462 },
1463 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1464 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, TextureClearMode::None),
1465 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1466 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1467 }
1468 }
1469
1470 pub(crate) fn check_usage(
1473 &self,
1474 expected: wgt::TextureUsages,
1475 ) -> Result<(), MissingTextureUsageError> {
1476 if self.desc.usage.contains(expected) {
1477 Ok(())
1478 } else {
1479 Err(MissingTextureUsageError {
1480 res: self.error_ident(),
1481 actual: self.desc.usage,
1482 expected,
1483 })
1484 }
1485 }
1486}
1487
1488impl Drop for Texture {
1489 fn drop(&mut self) {
1490 #[cfg(feature = "trace")]
1491 {
1492 let mut t = self.device.trace.lock();
1493 if let Some(t) = t.as_mut() {
1494 use crate::device::trace::to_trace;
1495
1496 t.add(trace::Action::DropTexture(unsafe { to_trace(self) }));
1498 }
1499 }
1500 match *self.clear_mode.write() {
1501 TextureClearMode::Surface {
1502 ref mut clear_view, ..
1503 } => {
1504 let raw = unsafe { ManuallyDrop::take(clear_view) };
1506 unsafe {
1507 self.device.raw().destroy_texture_view(raw);
1508 }
1509 }
1510 TextureClearMode::RenderPass {
1511 ref mut clear_views,
1512 ..
1513 } => {
1514 clear_views.iter_mut().for_each(|clear_view| {
1515 let raw = unsafe { ManuallyDrop::take(clear_view) };
1517 unsafe {
1518 self.device.raw().destroy_texture_view(raw);
1519 }
1520 });
1521 }
1522 _ => {}
1523 };
1524
1525 let ResourceState::Valid(state) = &mut self.state else {
1526 return;
1527 };
1528 if let Some(TextureInner::Native { raw }) = state.inner.take() {
1529 resource_log!("Destroy raw {}", self.error_ident());
1530 unsafe {
1531 self.device.raw().destroy_texture(raw);
1532 }
1533 }
1534 }
1535}
1536
1537impl RawResourceAccess for Texture {
1538 type DynResource = dyn hal::DynTexture;
1539
1540 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1541 self.state
1542 .as_ref()
1543 .valid()
1544 .and_then(|t| t.inner.get(guard).map(|t| t.raw()))
1545 }
1546}
1547
1548impl Texture {
1549 pub(crate) fn state(&self) -> Result<&TextureState, InvalidResourceError> {
1550 match &self.state {
1551 ResourceState::Valid(state) => Ok(state),
1552 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
1553 }
1554 }
1555
1556 pub(crate) fn check_destroyed(
1557 &self,
1558 guard: &SnatchGuard,
1559 ) -> Result<(), DestroyedResourceError> {
1560 let Ok(state) = self.state() else {
1561 return Ok(());
1562 };
1563 state
1564 .inner
1565 .get(guard)
1566 .map(|_| ())
1567 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1568 }
1569
1570 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
1571 self.state().map(|_| ())
1572 }
1573
1574 pub(crate) fn try_inner<'a>(
1575 &'a self,
1576 guard: &'a SnatchGuard,
1577 ) -> Result<&'a TextureInner, InvalidOrDestroyedResourceError> {
1578 self.state()?
1579 .inner
1580 .get(guard)
1581 .ok_or_else(|| DestroyedResourceError(self.error_ident()).into())
1582 }
1583
1584 pub(crate) fn get_clear_view<'a>(
1585 clear_mode: &'a TextureClearMode,
1586 desc: &'a wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
1587 mip_level: u32,
1588 depth_or_layer: u32,
1589 ) -> &'a dyn hal::DynTextureView {
1590 match *clear_mode {
1591 TextureClearMode::BufferCopy => {
1592 panic!("Given texture is cleared with buffer copies, not render passes")
1593 }
1594 TextureClearMode::None => {
1595 panic!("Given texture can't be cleared")
1596 }
1597 TextureClearMode::Surface { ref clear_view, .. } => clear_view.as_ref(),
1598 TextureClearMode::RenderPass {
1599 ref clear_views, ..
1600 } => {
1601 let index = if desc.dimension == wgt::TextureDimension::D3 {
1602 (0..mip_level).fold(0, |acc, mip| {
1603 acc + (desc.size.depth_or_array_layers >> mip).max(1)
1604 })
1605 } else {
1606 mip_level * desc.size.depth_or_array_layers
1607 } + depth_or_layer;
1608 clear_views[index as usize].as_ref()
1609 }
1610 }
1611 }
1612
1613 pub fn destroy(self: &Arc<Self>) {
1614 let device = &self.device;
1615
1616 let ResourceState::Valid(state) = &self.state else {
1617 return;
1618 };
1619
1620 let temp = {
1621 let raw = match state.inner.snatch(&mut device.snatchable_lock.write()) {
1622 Some(TextureInner::Native { raw }) => raw,
1623 Some(TextureInner::Surface { .. }) => {
1624 return;
1625 }
1626 None => {
1627 return;
1629 }
1630 };
1631
1632 let views = {
1633 let mut guard = self.views.lock();
1634 mem::take(&mut *guard)
1635 };
1636
1637 let bind_groups = {
1638 let mut guard = self.bind_groups.lock();
1639 mem::take(&mut *guard)
1640 };
1641
1642 queue::TempResource::DestroyedTexture(DestroyedTexture {
1643 raw: ManuallyDrop::new(raw),
1644 views,
1645 clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None),
1646 bind_groups,
1647 device: Arc::clone(&self.device),
1648 label: self.label().to_owned(),
1649 })
1650 };
1651
1652 let Some(queue) = device.get_queue() else {
1653 return;
1654 };
1655
1656 {
1657 let mut pending_writes = queue.pending_writes.lock();
1658 if pending_writes.contains_texture(self) {
1659 pending_writes.consume_temp(temp);
1660 return;
1661 }
1662 }
1663
1664 let mut life_lock = queue.lock_life();
1665 let last_submit_index = life_lock.get_texture_latest_submission_index(self);
1666 if let Some(last_submit_index) = last_submit_index {
1667 life_lock.schedule_resource_destruction(temp, last_submit_index);
1668 }
1669 }
1670}
1671
1672#[derive(Debug)]
1674pub struct DestroyedTexture {
1675 raw: ManuallyDrop<Box<dyn hal::DynTexture>>,
1676 views: WeakVec<TextureView>,
1677 clear_mode: TextureClearMode,
1678 bind_groups: WeakVec<BindGroup>,
1679 device: Arc<Device>,
1680 label: String,
1681}
1682
1683impl DestroyedTexture {
1684 pub fn label(&self) -> &dyn fmt::Debug {
1685 &self.label
1686 }
1687}
1688
1689impl Drop for DestroyedTexture {
1690 fn drop(&mut self) {
1691 let device = &self.device;
1692
1693 let mut deferred = device.deferred_destroy.lock();
1694 deferred.push(DeferredDestroy::TextureViews(mem::take(&mut self.views)));
1695 deferred.push(DeferredDestroy::BindGroups(mem::take(
1696 &mut self.bind_groups,
1697 )));
1698 drop(deferred);
1699
1700 match mem::replace(&mut self.clear_mode, TextureClearMode::None) {
1701 TextureClearMode::RenderPass { clear_views, .. } => {
1702 for clear_view in clear_views {
1703 let raw = ManuallyDrop::into_inner(clear_view);
1704 unsafe { self.device.raw().destroy_texture_view(raw) };
1705 }
1706 }
1707 TextureClearMode::Surface { clear_view } => {
1708 let raw = ManuallyDrop::into_inner(clear_view);
1709 unsafe { self.device.raw().destroy_texture_view(raw) };
1710 }
1711 _ => (),
1712 }
1713
1714 resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
1715 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1717 unsafe {
1718 self.device.raw().destroy_texture(raw);
1719 }
1720 }
1721}
1722
1723#[derive(Clone, Copy, Debug)]
1724pub enum TextureErrorDimension {
1725 X,
1726 Y,
1727 Z,
1728}
1729
1730#[derive(Clone, Debug, Error)]
1731#[non_exhaustive]
1732pub enum TextureDimensionError {
1733 #[error("Dimension {0:?} is zero")]
1734 Zero(TextureErrorDimension),
1735 #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
1736 LimitExceeded {
1737 dim: TextureErrorDimension,
1738 given: u32,
1739 limit: u32,
1740 },
1741 #[error("Sample count {0} is invalid")]
1742 InvalidSampleCount(u32),
1743 #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
1744 NotMultipleOfBlockWidth {
1745 width: u32,
1746 block_width: u32,
1747 format: wgt::TextureFormat,
1748 },
1749 #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
1750 NotMultipleOfBlockHeight {
1751 height: u32,
1752 block_height: u32,
1753 format: wgt::TextureFormat,
1754 },
1755 #[error(
1756 "Width {width} is not a multiple of {format:?}'s width multiple requirement ({multiple})"
1757 )]
1758 WidthNotMultipleOf {
1759 width: u32,
1760 multiple: u32,
1761 format: wgt::TextureFormat,
1762 },
1763 #[error("Height {height} is not a multiple of {format:?}'s height multiple requirement ({multiple})")]
1764 HeightNotMultipleOf {
1765 height: u32,
1766 multiple: u32,
1767 format: wgt::TextureFormat,
1768 },
1769 #[error("Multisampled texture depth or array layers must be 1, got {0}")]
1770 MultisampledDepthOrArrayLayer(u32),
1771}
1772
1773impl WebGpuError for TextureDimensionError {
1774 fn webgpu_error_type(&self) -> ErrorType {
1775 ErrorType::Validation
1776 }
1777}
1778
1779#[derive(Clone, Debug, Error)]
1780#[non_exhaustive]
1781pub enum CreateTextureError {
1782 #[error(transparent)]
1783 Device(#[from] DeviceError),
1784 #[error(transparent)]
1785 CreateTextureView(#[from] CreateTextureViewError),
1786 #[error("Invalid usage flags {0:?}")]
1787 InvalidUsage(wgt::TextureUsages),
1788 #[error(transparent)]
1789 InvalidDimension(#[from] TextureDimensionError),
1790 #[error("Depth texture ({1:?}) can't be created as {0:?}")]
1791 InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
1792 #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
1793 InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
1794 #[error(
1795 "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
1796 )]
1797 InvalidMipLevelCount { requested: u32, maximum: u32 },
1798 #[error(
1799 "Texture usages {0:?} are not allowed on a texture of type {1:?}{downlevel_suffix}",
1800 downlevel_suffix = if *.2 { " due to downlevel restrictions" } else { "" }
1801 )]
1802 InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
1803 #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
1804 InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
1805 #[error("Transient texture usage must be equal to `TRANSIENT_ATTACHMENT | RENDER_ATTACHMENT`, but got `{0:?}`")]
1806 InvalidTransientTextureUsage(wgt::TextureUsages),
1807 #[error("Transient texture view formats must be empty")]
1808 InvalidTransientTextureViewFormats,
1809 #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
1810 InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
1811 #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
1812 InvalidMultisampledStorageBinding,
1813 #[error("Format {0:?} does not support multisampling")]
1814 InvalidMultisampledFormat(wgt::TextureFormat),
1815 #[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:?}.")]
1816 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
1817 #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
1818 MultisampledNotRenderAttachment,
1819 #[error("Transient texture mip level count ({0}) must be 1")]
1820 InvalidTransientTextureMipLevelCount(u32),
1821 #[error("Transient texture layer count ({0}) must be 1")]
1822 InvalidTransientTextureLayerCount(u32),
1823 #[error("Texture format {0:?} can't be used due to missing features")]
1824 MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
1825 #[error(transparent)]
1826 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1827}
1828
1829crate::impl_resource_type!(Texture);
1830impl Labeled for Texture {
1831 fn label(&self) -> &str {
1832 &self.desc.label
1833 }
1834}
1835crate::impl_parent_device!(Texture);
1836crate::impl_storage_item!(Texture);
1837crate::impl_trackable!(Texture);
1838
1839impl Borrow<TextureSelector> for Texture {
1840 fn borrow(&self) -> &TextureSelector {
1841 &self.full_range
1842 }
1843}
1844
1845impl WebGpuError for CreateTextureError {
1846 fn webgpu_error_type(&self) -> ErrorType {
1847 match self {
1848 Self::Device(e) => e.webgpu_error_type(),
1849 Self::CreateTextureView(e) => e.webgpu_error_type(),
1850 Self::InvalidDimension(e) => e.webgpu_error_type(),
1851 Self::MissingFeatures(_, e) => e.webgpu_error_type(),
1852 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1853
1854 Self::InvalidUsage(_)
1855 | Self::InvalidDepthDimension(_, _)
1856 | Self::InvalidCompressedDimension(_, _)
1857 | Self::InvalidMipLevelCount { .. }
1858 | Self::InvalidFormatUsages(_, _, _)
1859 | Self::InvalidViewFormat(_, _)
1860 | Self::InvalidDimensionUsages(_, _)
1861 | Self::InvalidMultisampledStorageBinding
1862 | Self::InvalidMultisampledFormat(_)
1863 | Self::InvalidSampleCount(..)
1864 | Self::InvalidTransientTextureUsage(_)
1865 | Self::InvalidTransientTextureMipLevelCount(_)
1866 | Self::InvalidTransientTextureLayerCount(_)
1867 | Self::InvalidTransientTextureViewFormats
1868 | Self::MultisampledNotRenderAttachment => ErrorType::Validation,
1869 }
1870 }
1871}
1872
1873#[derive(Clone, Debug, Default, Eq, PartialEq)]
1875#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1876#[cfg_attr(feature = "serde", serde(default))]
1877pub struct TextureViewDescriptor<'a> {
1878 pub label: Label<'a>,
1882 pub format: Option<wgt::TextureFormat>,
1887 pub dimension: Option<wgt::TextureViewDimension>,
1893 pub usage: Option<wgt::TextureUsages>,
1896 pub range: wgt::ImageSubresourceRange,
1898}
1899
1900#[derive(Debug)]
1901pub(crate) struct HalTextureViewDescriptor {
1902 pub texture_format: wgt::TextureFormat,
1903 pub format: wgt::TextureFormat,
1904 pub usage: wgt::TextureUsages,
1905 pub dimension: wgt::TextureViewDimension,
1906 pub range: wgt::ImageSubresourceRange,
1907}
1908
1909impl HalTextureViewDescriptor {
1910 pub fn aspects(&self) -> hal::FormatAspects {
1911 hal::FormatAspects::new(self.texture_format, self.range.aspect)
1912 }
1913}
1914
1915#[derive(Debug, Copy, Clone, Error)]
1916pub enum TextureViewNotRenderableReason {
1917 #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
1918 Usage(wgt::TextureUsages),
1919 #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
1920 Dimension(wgt::TextureViewDimension),
1921 #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
1922 MipLevelCount(u32),
1923 #[error("This texture view has more than one array layer. View array layers: {0:?}")]
1924 ArrayLayerCount(u32),
1925 #[error(
1926 "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
1927 )]
1928 Aspects(hal::FormatAspects),
1929}
1930
1931#[derive(Debug)]
1932pub struct TextureViewState {
1933 pub(crate) raw: Snatchable<Box<dyn hal::DynTextureView>>,
1934 pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
1936}
1937
1938#[derive(Debug)]
1939pub struct TextureView {
1940 pub(crate) state: ResourceState<TextureViewState>,
1941 pub(crate) parent: Arc<Texture>,
1943 pub(crate) device: Arc<Device>,
1944 pub(crate) desc: HalTextureViewDescriptor,
1945 pub(crate) format_features: wgt::TextureFormatFeatures,
1946 pub(crate) samples: u32,
1947 pub(crate) selector: TextureSelector,
1948 pub(crate) label: String,
1950}
1951
1952impl Drop for TextureView {
1953 #[expect(trivial_casts)]
1954 fn drop(&mut self) {
1955 profiling::scope!("TextureView::drop");
1956 api_log!("TextureView::drop {:?}", self as *const _);
1957 #[cfg(feature = "trace")]
1958 if let Some(t) = self.device.trace.lock().as_mut() {
1959 t.add(trace::Action::DropTextureView(unsafe {
1960 trace::to_trace(self)
1961 }));
1962 }
1963 let ResourceState::Valid(state) = &mut self.state else {
1964 return;
1965 };
1966
1967 if let Some(raw) = state.raw.take() {
1968 resource_log!("Destroy raw {}", self.error_ident());
1969 unsafe {
1970 self.device.raw().destroy_texture_view(raw);
1971 }
1972 }
1973 }
1974}
1975
1976impl RawResourceAccess for TextureView {
1977 type DynResource = dyn hal::DynTextureView;
1978
1979 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1980 self.state()
1981 .ok()
1982 .and_then(|state| state.raw.get(guard).map(|it| it.as_ref()))
1983 }
1984
1985 fn try_raw<'a>(
1986 &'a self,
1987 guard: &'a SnatchGuard,
1988 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
1989 self.parent.check_destroyed(guard)?;
1990
1991 self.raw(guard)
1992 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1993 }
1994}
1995
1996impl TextureView {
1997 pub(crate) fn check_usage(
2000 &self,
2001 expected: wgt::TextureUsages,
2002 ) -> Result<(), MissingTextureUsageError> {
2003 if self.desc.usage.contains(expected) {
2004 Ok(())
2005 } else {
2006 Err(MissingTextureUsageError {
2007 res: self.error_ident(),
2008 actual: self.desc.usage,
2009 expected,
2010 })
2011 }
2012 }
2013
2014 pub(crate) fn state(&self) -> Result<&TextureViewState, InvalidResourceError> {
2015 match &self.state {
2016 ResourceState::Valid(state) => Ok(state),
2017 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2018 }
2019 }
2020
2021 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
2022 self.state().map(|_| ())
2023 }
2024
2025 pub(crate) fn invalid(
2026 device: &Arc<Device>,
2027 texture: &Arc<Texture>,
2028 desc: &TextureViewDescriptor,
2029 ) -> Arc<Self> {
2030 Arc::new(TextureView {
2032 state: ResourceState::Invalid,
2033 parent: texture.clone(),
2034 device: device.clone(),
2035 desc: HalTextureViewDescriptor {
2036 texture_format: texture.desc.format,
2037 format: desc.format.unwrap_or(texture.desc.format),
2038 usage: desc.usage.unwrap_or(texture.desc.usage),
2039 dimension: desc.dimension.unwrap_or(match texture.desc.dimension {
2040 wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1,
2041 wgt::TextureDimension::D2 => wgt::TextureViewDimension::D2,
2042 wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3,
2043 }),
2044 range: desc.range,
2045 },
2046 format_features: texture.format_features,
2047 samples: texture.desc.sample_count,
2048 selector: TextureSelector {
2049 mips: desc.range.base_mip_level
2050 ..(desc.range.base_mip_level + desc.range.mip_level_count.unwrap_or_default()),
2051 layers: desc.range.base_array_layer
2052 ..(desc.range.base_array_layer
2053 + desc.range.array_layer_count.unwrap_or_default()),
2054 },
2055 label: desc.label.to_string(),
2056 })
2057 }
2058}
2059
2060#[derive(Clone, Debug, Error)]
2061#[non_exhaustive]
2062pub enum CreateTextureViewError {
2063 #[error(transparent)]
2064 Device(#[from] DeviceError),
2065 #[error(transparent)]
2066 DestroyedResource(#[from] DestroyedResourceError),
2067 #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
2068 InvalidTextureViewDimension {
2069 view: wgt::TextureViewDimension,
2070 texture: wgt::TextureDimension,
2071 },
2072 #[error("Texture view format `{0:?}` cannot be used as a render attachment. Make sure the format supports RENDER_ATTACHMENT usage and required device features are enabled.")]
2073 TextureViewFormatNotRenderable(wgt::TextureFormat),
2074 #[error("Texture view format `{0:?}` cannot be used as a storage binding. Make sure the format supports STORAGE usage and required device features are enabled.")]
2075 TextureViewFormatNotStorage(wgt::TextureFormat),
2076 #[error("Texture view usages (`{view:?}`) must be a subset of the texture's original usages (`{texture:?}`)")]
2077 InvalidTextureViewUsage {
2078 view: wgt::TextureUsages,
2079 texture: wgt::TextureUsages,
2080 },
2081 #[error("Texture view dimension `{0:?}` cannot be used with a multisampled texture")]
2082 InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
2083 #[error(
2084 "TextureView has an arrayLayerCount of {depth}. Views of type `Cube` must have arrayLayerCount of 6."
2085 )]
2086 InvalidCubemapTextureDepth { depth: u32 },
2087 #[error("TextureView has an arrayLayerCount of {depth}. Views of type `CubeArray` must have an arrayLayerCount that is a multiple of 6.")]
2088 InvalidCubemapArrayTextureDepth { depth: u32 },
2089 #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
2090 InvalidCubeTextureViewSize,
2091 #[error("Mip level count is 0")]
2092 ZeroMipLevelCount,
2093 #[error("Array layer count is 0")]
2094 ZeroArrayLayerCount,
2095 #[error(
2096 "`TextureView` starts at mip level {base_mip_level} and spans {mip_level_count} mip \
2097 levels, but the texture view only has {total} total mip level(s)"
2098 )]
2099 TooManyMipLevels {
2100 base_mip_level: u32,
2101 mip_level_count: u32,
2102 total: u32,
2103 },
2104 #[error(
2105 "`TextureView` starts at array layer {base_array_layer} and spans {array_layer_count}) \
2106 array layers, but the texture view only has {total} total layer(s)"
2107 )]
2108 TooManyArrayLayers {
2109 base_array_layer: u32,
2110 array_layer_count: u32,
2111 total: u32,
2112 },
2113 #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
2114 InvalidArrayLayerCount {
2115 requested: u32,
2116 dim: wgt::TextureViewDimension,
2117 },
2118 #[error(
2119 "Aspect {requested_aspect:?} is not a valid aspect of the source texture format {texture_format:?}"
2120 )]
2121 InvalidAspect {
2122 texture_format: wgt::TextureFormat,
2123 requested_aspect: wgt::TextureAspect,
2124 },
2125 #[error(
2126 "Trying to create a view of format {view:?} of a texture with format {texture:?}, \
2127 but this view format is not present in the texture's viewFormat array"
2128 )]
2129 FormatReinterpretation {
2130 texture: wgt::TextureFormat,
2131 view: wgt::TextureFormat,
2132 },
2133 #[error(
2134 "The texture view (`{view:?}`) from transient texture (`{texture:?}`) must have the same usage"
2135 )]
2136 InvalidTransientTextureViewUsage {
2137 texture: wgt::TextureUsages,
2138 view: wgt::TextureUsages,
2139 },
2140 #[error(transparent)]
2141 InvalidResource(#[from] InvalidResourceError),
2142 #[error(transparent)]
2143 MissingFeatures(#[from] MissingFeatures),
2144}
2145
2146impl From<InvalidOrDestroyedResourceError> for CreateTextureViewError {
2147 fn from(value: InvalidOrDestroyedResourceError) -> Self {
2148 match value {
2149 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
2150 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
2151 }
2152 }
2153}
2154
2155impl WebGpuError for CreateTextureViewError {
2156 fn webgpu_error_type(&self) -> ErrorType {
2157 match self {
2158 Self::Device(e) => e.webgpu_error_type(),
2159
2160 Self::InvalidTextureViewDimension { .. }
2161 | Self::InvalidResource(_)
2162 | Self::InvalidMultisampledTextureViewDimension(_)
2163 | Self::InvalidCubemapTextureDepth { .. }
2164 | Self::InvalidCubemapArrayTextureDepth { .. }
2165 | Self::InvalidCubeTextureViewSize
2166 | Self::ZeroMipLevelCount
2167 | Self::ZeroArrayLayerCount
2168 | Self::TooManyMipLevels { .. }
2169 | Self::TooManyArrayLayers { .. }
2170 | Self::InvalidArrayLayerCount { .. }
2171 | Self::InvalidAspect { .. }
2172 | Self::FormatReinterpretation { .. }
2173 | Self::DestroyedResource(_)
2174 | Self::TextureViewFormatNotRenderable(_)
2175 | Self::TextureViewFormatNotStorage(_)
2176 | Self::InvalidTextureViewUsage { .. }
2177 | Self::InvalidTransientTextureViewUsage { .. }
2178 | Self::MissingFeatures(_) => ErrorType::Validation,
2179 }
2180 }
2181}
2182
2183crate::impl_resource_type!(TextureView);
2184crate::impl_labeled!(TextureView);
2185crate::impl_parent_device!(TextureView);
2186crate::impl_storage_item!(TextureView);
2187
2188pub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;
2189
2190#[derive(Debug)]
2191pub struct ExternalTexture {
2192 pub(crate) device: Arc<Device>,
2193 pub(crate) planes: arrayvec::ArrayVec<Arc<TextureView>, 3>,
2195 pub(crate) params: Arc<Buffer>,
2198 pub(crate) label: String,
2200 pub(crate) tracking_data: TrackingData,
2201}
2202
2203impl Drop for ExternalTexture {
2204 fn drop(&mut self) {
2205 resource_log!("Destroy raw {}", self.error_ident());
2206 }
2207}
2208
2209impl ExternalTexture {
2210 pub fn destroy(self: &Arc<Self>) {
2211 self.params.destroy();
2212 }
2213}
2214
2215#[derive(Clone, Debug, Error)]
2216#[non_exhaustive]
2217pub enum CreateExternalTextureError {
2218 #[error(transparent)]
2219 Device(#[from] DeviceError),
2220 #[error(transparent)]
2221 MissingFeatures(#[from] MissingFeatures),
2222 #[error(transparent)]
2223 InvalidResource(#[from] InvalidResourceError),
2224 #[error(transparent)]
2225 CreateBuffer(#[from] CreateBufferError),
2226 #[error(transparent)]
2227 QueueWrite(#[from] queue::QueueWriteError),
2228 #[error("External texture format {format:?} expects {expected} planes, but given {provided}")]
2229 IncorrectPlaneCount {
2230 format: wgt::ExternalTextureFormat,
2231 expected: usize,
2232 provided: usize,
2233 },
2234 #[error("External texture planes cannot be multisampled, but given view with samples = {0}")]
2235 InvalidPlaneMultisample(u32),
2236 #[error("External texture planes expect a filterable float sample type, but given view with format {format:?} (sample type {sample_type:?})")]
2237 InvalidPlaneSampleType {
2238 format: wgt::TextureFormat,
2239 sample_type: wgt::TextureSampleType,
2240 },
2241 #[error("External texture planes expect 2D dimension, but given view with dimension = {0:?}")]
2242 InvalidPlaneDimension(wgt::TextureViewDimension),
2243 #[error(transparent)]
2244 MissingTextureUsage(#[from] MissingTextureUsageError),
2245 #[error("External texture format {format:?} plane {plane} expects format with {expected} components but given view with format {provided:?} ({} components)",
2246 provided.components())]
2247 InvalidPlaneFormat {
2248 format: wgt::ExternalTextureFormat,
2249 plane: usize,
2250 expected: u8,
2251 provided: wgt::TextureFormat,
2252 },
2253}
2254
2255impl WebGpuError for CreateExternalTextureError {
2256 fn webgpu_error_type(&self) -> ErrorType {
2257 match self {
2258 CreateExternalTextureError::Device(e) => e.webgpu_error_type(),
2259 CreateExternalTextureError::MissingFeatures(e) => e.webgpu_error_type(),
2260 CreateExternalTextureError::InvalidResource(e) => e.webgpu_error_type(),
2261 CreateExternalTextureError::CreateBuffer(e) => e.webgpu_error_type(),
2262 CreateExternalTextureError::QueueWrite(e) => e.webgpu_error_type(),
2263 CreateExternalTextureError::MissingTextureUsage(e) => e.webgpu_error_type(),
2264 CreateExternalTextureError::IncorrectPlaneCount { .. }
2265 | CreateExternalTextureError::InvalidPlaneMultisample(_)
2266 | CreateExternalTextureError::InvalidPlaneSampleType { .. }
2267 | CreateExternalTextureError::InvalidPlaneDimension(_)
2268 | CreateExternalTextureError::InvalidPlaneFormat { .. } => ErrorType::Validation,
2269 }
2270 }
2271}
2272
2273crate::impl_resource_type!(ExternalTexture);
2274crate::impl_labeled!(ExternalTexture);
2275crate::impl_parent_device!(ExternalTexture);
2276crate::impl_storage_item!(ExternalTexture);
2277crate::impl_trackable!(ExternalTexture);
2278
2279#[derive(Clone, Debug, PartialEq)]
2281#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2282pub struct SamplerDescriptor<'a> {
2283 pub label: Label<'a>,
2287 pub address_modes: [wgt::AddressMode; 3],
2289 pub mag_filter: wgt::FilterMode,
2291 pub min_filter: wgt::FilterMode,
2293 pub mipmap_filter: wgt::MipmapFilterMode,
2295 pub lod_min_clamp: f32,
2297 pub lod_max_clamp: f32,
2299 pub compare: Option<wgt::CompareFunction>,
2301 pub anisotropy_clamp: u16,
2303 pub border_color: Option<wgt::SamplerBorderColor>,
2306}
2307
2308#[derive(Debug)]
2309pub struct Sampler {
2310 pub(crate) raw: ResourceState<Box<dyn hal::DynSampler>>,
2311 pub(crate) device: Arc<Device>,
2312 pub(crate) label: String,
2314 pub(crate) tracking_data: TrackingData,
2315 pub(crate) comparison: bool,
2317 pub(crate) filtering: bool,
2319}
2320
2321impl Drop for Sampler {
2322 #[allow(trivial_casts)]
2323 fn drop(&mut self) {
2324 profiling::scope!("Sampler::drop");
2325 api_log!("Sampler::drop {:?}", self as *const _);
2326 #[cfg(feature = "trace")]
2327 if let Some(t) = self.device.trace.lock().as_mut() {
2328 t.add(trace::Action::DropSampler(unsafe { trace::to_trace(self) }));
2329 }
2330 resource_log!("Destroy raw {}", self.error_ident());
2331 if let ResourceState::Valid(raw) = mem::replace(&mut self.raw, ResourceState::Invalid) {
2332 unsafe {
2333 self.device.raw().destroy_sampler(raw);
2334 }
2335 }
2336 }
2337}
2338
2339impl Sampler {
2340 pub(crate) fn raw(&self) -> Result<&dyn hal::DynSampler, InvalidResourceError> {
2341 self.raw
2342 .as_ref()
2343 .valid()
2344 .map(|raw| raw.as_ref())
2345 .ok_or_else(|| InvalidResourceError(self.error_ident()))
2346 }
2347
2348 pub(crate) fn invalid(device: Arc<Device>, desc: &SamplerDescriptor) -> Arc<Self> {
2349 Arc::new(Sampler {
2350 raw: ResourceState::Invalid,
2351 label: desc.label.to_string(),
2352 tracking_data: TrackingData::new(device.tracker_indices.samplers.clone()),
2353 device,
2354 comparison: desc.compare.is_some(),
2355 filtering: desc.mag_filter == wgt::FilterMode::Linear
2356 || desc.min_filter == wgt::FilterMode::Linear
2357 || desc.mipmap_filter == wgt::MipmapFilterMode::Linear,
2358 })
2359 }
2360}
2361
2362#[derive(Copy, Clone)]
2363pub enum SamplerFilterErrorType {
2364 MagFilter,
2365 MinFilter,
2366 MipmapFilter,
2367}
2368
2369impl fmt::Debug for SamplerFilterErrorType {
2370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2371 match *self {
2372 SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
2373 SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
2374 SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
2375 }
2376 }
2377}
2378
2379#[derive(Clone, Debug, Error)]
2380#[non_exhaustive]
2381pub enum CreateSamplerError {
2382 #[error(transparent)]
2383 Device(#[from] DeviceError),
2384 #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
2385 InvalidLodMinClamp(f32),
2386 #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
2387 InvalidLodMaxClamp {
2388 lod_min_clamp: f32,
2389 lod_max_clamp: f32,
2390 },
2391 #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
2392 InvalidAnisotropy(u16),
2393 #[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.")]
2394 InvalidFilterModeWithAnisotropy {
2395 filter_type: SamplerFilterErrorType,
2396 filter_mode: wgt::FilterMode,
2397 anisotropic_clamp: u16,
2398 },
2399 #[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.")]
2400 InvalidMipmapFilterModeWithAnisotropy {
2401 filter_type: SamplerFilterErrorType,
2402 filter_mode: wgt::MipmapFilterMode,
2403 anisotropic_clamp: u16,
2404 },
2405 #[error(transparent)]
2406 MissingFeatures(#[from] MissingFeatures),
2407}
2408
2409crate::impl_resource_type!(Sampler);
2410crate::impl_labeled!(Sampler);
2411crate::impl_parent_device!(Sampler);
2412crate::impl_storage_item!(Sampler);
2413crate::impl_trackable!(Sampler);
2414
2415impl WebGpuError for CreateSamplerError {
2416 fn webgpu_error_type(&self) -> ErrorType {
2417 match self {
2418 Self::Device(e) => e.webgpu_error_type(),
2419 Self::MissingFeatures(e) => e.webgpu_error_type(),
2420
2421 Self::InvalidLodMinClamp(_)
2422 | Self::InvalidLodMaxClamp { .. }
2423 | Self::InvalidAnisotropy(_)
2424 | Self::InvalidFilterModeWithAnisotropy { .. }
2425 | Self::InvalidMipmapFilterModeWithAnisotropy { .. } => ErrorType::Validation,
2426 }
2427 }
2428}
2429
2430#[derive(Clone, Debug, Error)]
2431#[non_exhaustive]
2432pub enum CreateQuerySetError {
2433 #[error(transparent)]
2434 Device(#[from] DeviceError),
2435 #[error("QuerySets cannot be made with zero queries")]
2436 ZeroCount,
2437 #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
2438 TooManyQueries { count: u32, maximum: u32 },
2439 #[error(transparent)]
2440 MissingFeatures(#[from] MissingFeatures),
2441}
2442
2443impl WebGpuError for CreateQuerySetError {
2444 fn webgpu_error_type(&self) -> ErrorType {
2445 match self {
2446 Self::Device(e) => e.webgpu_error_type(),
2447 Self::MissingFeatures(e) => e.webgpu_error_type(),
2448
2449 Self::TooManyQueries { .. } | Self::ZeroCount => ErrorType::Validation,
2450 }
2451 }
2452}
2453
2454pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
2455
2456#[derive(Debug)]
2457pub(crate) struct QuerySetState {
2458 pub(crate) raw: Snatchable<Box<dyn hal::DynQuerySet>>,
2459}
2460
2461#[derive(Debug)]
2462pub struct QuerySet {
2463 pub(crate) state: ResourceState<QuerySetState>,
2464 pub(crate) device: Arc<Device>,
2465 pub(crate) label: String,
2467 pub(crate) tracking_data: TrackingData,
2468 pub(crate) desc: wgt::QuerySetDescriptor<()>,
2469 pub(crate) initialized_slots: Mutex<bit_vec::BitVec>,
2470}
2471
2472impl RawResourceAccess for QuerySet {
2473 type DynResource = dyn hal::DynQuerySet;
2474
2475 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2476 self.state().ok()?.raw.get(guard).map(|b| b.as_ref())
2477 }
2478}
2479
2480impl QuerySet {
2481 pub(crate) fn state(&self) -> Result<&QuerySetState, InvalidResourceError> {
2482 match &self.state {
2483 ResourceState::Valid(state) => Ok(state),
2484 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2485 }
2486 }
2487
2488 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
2489 self.state().map(|_| ())
2490 }
2491
2492 pub fn invalid(device: Arc<Device>, desc: &QuerySetDescriptor) -> Arc<Self> {
2493 Arc::new(QuerySet {
2494 state: ResourceState::Invalid,
2495 label: desc.label.to_string(),
2496 tracking_data: TrackingData::new(device.tracker_indices.query_sets.clone()),
2497 desc: desc.clone().map_label(|_| ()),
2498 initialized_slots: Mutex::new(
2499 rank::QUERY_SET_INITIALIZED_SLOTS,
2500 bit_vec::BitVec::from_elem(desc.count as usize, false),
2501 ),
2502 device,
2503 })
2504 }
2505
2506 pub fn destroy(self: &Arc<Self>) {
2507 let device = &self.device;
2508
2509 profiling::scope!("QuerySet::destroy");
2510 api_log!("QuerySet::destroy {:?}", Arc::as_ptr(self));
2511
2512 #[cfg(feature = "trace")]
2513 if let Some(trace) = device.trace.lock().as_mut() {
2514 use crate::device::trace::IntoTrace as _;
2515
2516 trace.add(trace::Action::DestroyQuerySet(self.to_trace()));
2517 };
2518
2519 let ResourceState::Valid(state) = &self.state else {
2520 return;
2521 };
2522
2523 let temp = {
2524 let mut snatch_guard = self.device.snatchable_lock.write();
2525
2526 let raw = match state.raw.snatch(&mut snatch_guard) {
2527 Some(raw) => raw,
2528 None => {
2529 return;
2531 }
2532 };
2533
2534 drop(snatch_guard);
2535
2536 queue::TempResource::DestroyedQuerySet(DestroyedQuerySet {
2537 raw: ManuallyDrop::new(raw),
2538 device: Arc::clone(&self.device),
2539 label: self.label().to_owned(),
2540 })
2541 };
2542
2543 let Some(queue) = device.get_queue() else {
2544 return;
2545 };
2546
2547 let mut life_lock = queue.lock_life();
2548 let last_submit_index = life_lock.get_query_set_latest_submission_index(self);
2549 if let Some(last_submit_index) = last_submit_index {
2550 life_lock.schedule_resource_destruction(temp, last_submit_index);
2551 }
2552 }
2553}
2554
2555impl Drop for QuerySet {
2556 fn drop(&mut self) {
2557 resource_log!("Destroy raw {}", self.error_ident());
2558 #[cfg(feature = "trace")]
2559 if let Some(trace) = self.device.trace.lock().as_mut() {
2560 use crate::device::trace::to_trace;
2561
2562 trace.add(trace::Action::DropQuerySet(unsafe { to_trace(self) }));
2563 }
2564 let ResourceState::Valid(state) = &mut self.state else {
2565 return;
2566 };
2567 if let Some(raw) = state.raw.take() {
2568 unsafe {
2570 self.device.raw().destroy_query_set(raw);
2571 }
2572 }
2573 }
2574}
2575
2576crate::impl_resource_type!(QuerySet);
2577crate::impl_labeled!(QuerySet);
2578crate::impl_parent_device!(QuerySet);
2579crate::impl_storage_item!(QuerySet);
2580crate::impl_trackable!(QuerySet);
2581
2582#[derive(Debug)]
2584pub struct DestroyedQuerySet {
2585 raw: ManuallyDrop<Box<dyn hal::DynQuerySet>>,
2586 device: Arc<Device>,
2587 label: String,
2588}
2589
2590impl DestroyedQuerySet {
2591 pub fn label(&self) -> &dyn fmt::Debug {
2592 &self.label
2593 }
2594}
2595
2596impl Drop for DestroyedQuerySet {
2597 fn drop(&mut self) {
2598 resource_log!("Destroy raw QuerySet (destroyed) {:?}", self.label());
2599 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
2601 unsafe {
2602 hal::DynDevice::destroy_query_set(self.device.raw(), raw);
2603 }
2604 }
2605}
2606
2607pub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;
2608pub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;
2609
2610pub type BlasPrepareCompactResult = Result<(), BlasPrepareCompactError>;
2611
2612#[cfg(send_sync)]
2613pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + Send + 'static>;
2614#[cfg(not(send_sync))]
2615pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + 'static>;
2616
2617pub(crate) struct BlasPendingCompact {
2618 pub(crate) op: Option<BlasCompactCallback>,
2619 pub(crate) _parent_blas: Arc<Blas>,
2621}
2622
2623impl fmt::Debug for BlasPendingCompact {
2624 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2625 f.debug_struct("BlasPendingCompact")
2626 .field("op", &())
2627 .field("_parent_blas", &self._parent_blas)
2628 .finish()
2629 }
2630}
2631
2632#[derive(Debug)]
2633pub(crate) enum BlasCompactState {
2634 Compacted,
2636 Waiting(BlasPendingCompact),
2638 Ready { size: wgt::BufferAddress },
2640 Idle,
2642}
2643
2644#[cfg(send_sync)]
2645unsafe impl Send for BlasCompactState {}
2646#[cfg(send_sync)]
2647unsafe impl Sync for BlasCompactState {}
2648
2649#[derive(Debug)]
2650pub struct Blas {
2651 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
2652 pub(crate) device: Arc<Device>,
2653 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
2654 pub(crate) sizes: wgt::BlasGeometrySizeDescriptors,
2655 pub(crate) flags: wgt::AccelerationStructureFlags,
2656 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
2657 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
2658 pub(crate) handle: u64,
2659 pub(crate) label: String,
2661 pub(crate) tracking_data: TrackingData,
2662 pub(crate) compaction_buffer: Option<ManuallyDrop<Box<dyn hal::DynBuffer>>>,
2663 pub(crate) compacted_state: Mutex<BlasCompactState>,
2664}
2665
2666impl Drop for Blas {
2667 fn drop(&mut self) {
2668 resource_log!("Destroy raw {}", self.error_ident());
2669 if let Some(raw) = self.raw.take() {
2671 unsafe {
2672 self.device.raw().destroy_acceleration_structure(raw);
2673 }
2674 }
2675 if let Some(mut raw) = self.compaction_buffer.take() {
2676 unsafe {
2677 self.device
2678 .raw()
2679 .destroy_buffer(ManuallyDrop::take(&mut raw))
2680 }
2681 }
2682 }
2683}
2684
2685impl RawResourceAccess for Blas {
2686 type DynResource = dyn hal::DynAccelerationStructure;
2687
2688 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2689 self.raw.get(guard).map(|it| it.as_ref())
2690 }
2691}
2692
2693impl Blas {
2694 pub(crate) fn prepare_compact_async(
2695 self: &Arc<Self>,
2696 op: Option<BlasCompactCallback>,
2697 ) -> Result<SubmissionIndex, (Option<BlasCompactCallback>, BlasPrepareCompactError)> {
2698 let device = &self.device;
2699 if let Err(e) = device.check_is_valid() {
2700 return Err((op, e.into()));
2701 }
2702
2703 if self.built_index.read().is_none() {
2704 return Err((op, BlasPrepareCompactError::NotBuilt));
2705 }
2706
2707 if !self
2708 .flags
2709 .contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION)
2710 {
2711 return Err((op, BlasPrepareCompactError::CompactionUnsupported));
2712 }
2713
2714 let mut state = self.compacted_state.lock();
2715 *state = match *state {
2716 BlasCompactState::Compacted => {
2717 return Err((op, BlasPrepareCompactError::DoubleCompaction))
2718 }
2719 BlasCompactState::Waiting(_) => {
2720 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2721 }
2722 BlasCompactState::Ready { .. } => {
2723 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2724 }
2725 BlasCompactState::Idle => BlasCompactState::Waiting(BlasPendingCompact {
2726 op,
2727 _parent_blas: self.clone(),
2728 }),
2729 };
2730
2731 let submit_index = if let Some(queue) = device.get_queue() {
2732 queue.lock_life().prepare_compact(self).unwrap_or(0) } else {
2734 let (mut callback, status) = self.read_back_compact_size().unwrap();
2736 if let Some(callback) = callback.take() {
2737 callback(status);
2738 }
2739 0
2740 };
2741
2742 Ok(submit_index)
2743 }
2744
2745 #[must_use]
2747 pub(crate) fn read_back_compact_size(&self) -> Option<BlasCompactReadyPendingClosure> {
2748 let mut state = self.compacted_state.lock();
2749 let pending_compact = match mem::replace(&mut *state, BlasCompactState::Idle) {
2750 BlasCompactState::Waiting(pending_mapping) => pending_mapping,
2751 BlasCompactState::Idle => return None,
2753 BlasCompactState::Ready { .. } => {
2754 unreachable!("This should be validated out by `prepare_for_compaction`")
2755 }
2756 _ => panic!("No pending mapping."),
2757 };
2758 let status = {
2759 let compaction_buffer = self.compaction_buffer.as_ref().unwrap().as_ref();
2760 unsafe {
2761 let map_res = self.device.raw().map_buffer(
2762 compaction_buffer,
2763 0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress,
2764 );
2765 match map_res {
2766 Ok(mapping) => {
2767 if !mapping.is_coherent {
2768 #[expect(clippy::single_range_in_vec_init, reason = "intentional")]
2769 self.device.raw().invalidate_mapped_ranges(
2770 compaction_buffer,
2771 &[0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress],
2772 );
2773 }
2774 let size = core::ptr::read_unaligned(
2775 mapping.ptr.as_ptr().cast::<wgt::BufferAddress>(),
2776 );
2777 self.device.raw().unmap_buffer(compaction_buffer);
2778 if self.size_info.acceleration_structure_size != 0 {
2779 debug_assert_ne!(size, 0);
2780 }
2781 *state = BlasCompactState::Ready { size };
2782 Ok(())
2783 }
2784 Err(err) => Err(BlasPrepareCompactError::from(DeviceError::from_hal(err))),
2785 }
2786 }
2787 };
2788 Some((pending_compact.op, status))
2789 }
2790}
2791
2792crate::impl_resource_type!(Blas);
2793crate::impl_labeled!(Blas);
2794crate::impl_parent_device!(Blas);
2795crate::impl_storage_item!(Blas);
2796crate::impl_trackable!(Blas);
2797
2798#[derive(Debug)]
2799pub struct Tlas {
2800 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
2801 pub(crate) device: Arc<Device>,
2802 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
2803 pub(crate) max_instance_count: u32,
2804 pub(crate) flags: wgt::AccelerationStructureFlags,
2805 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
2806 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
2807 pub(crate) dependencies: RwLock<Vec<Arc<Blas>>>,
2808 pub(crate) instance_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
2809 pub(crate) label: String,
2811 pub(crate) tracking_data: TrackingData,
2812}
2813
2814impl Drop for Tlas {
2815 fn drop(&mut self) {
2816 unsafe {
2817 resource_log!("Destroy raw {}", self.error_ident());
2818 if let Some(structure) = self.raw.take() {
2819 self.device.raw().destroy_acceleration_structure(structure);
2820 }
2821 let buffer = ManuallyDrop::take(&mut self.instance_buffer);
2822 self.device.raw().destroy_buffer(buffer);
2823 }
2824 }
2825}
2826
2827impl RawResourceAccess for Tlas {
2828 type DynResource = dyn hal::DynAccelerationStructure;
2829
2830 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2831 self.raw.get(guard).map(|raw| raw.as_ref())
2832 }
2833}
2834
2835crate::impl_resource_type!(Tlas);
2836crate::impl_labeled!(Tlas);
2837crate::impl_parent_device!(Tlas);
2838crate::impl_storage_item!(Tlas);
2839crate::impl_trackable!(Tlas);