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 type BufferAccessResult = Result<(), BufferAccessError>;
433
434#[derive(Debug)]
435pub(crate) struct BufferPendingMapping {
436 pub(crate) range: Range<wgt::BufferAddress>,
437 pub(crate) op: BufferMapOperation,
438 pub(crate) _parent_buffer: Arc<Buffer>,
440}
441
442pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
443
444#[derive(Debug)]
445pub(crate) struct BufferState {
446 pub(crate) raw: Snatchable<Box<dyn hal::DynBuffer>>,
447}
448
449#[derive(Debug)]
450pub struct Buffer {
451 pub(crate) state: ResourceState<BufferState>,
452 pub(crate) device: Arc<Device>,
453 pub(crate) usage: wgt::BufferUsages,
454 pub(crate) size: wgt::BufferAddress,
455 pub(crate) initialization_status: RwLock<BufferInitTracker>,
456 pub(crate) label: String,
458 pub(crate) tracking_data: TrackingData,
459 pub(crate) map_state: Mutex<BufferMapState>,
460 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
462 pub(crate) timestamp_normalization_bind_group: Snatchable<TimestampNormalizationBindGroup>,
463 pub(crate) indirect_validation_bind_groups: Snatchable<crate::indirect_validation::BindGroups>,
464}
465
466impl Drop for Buffer {
467 #[allow(trivial_casts)]
468 fn drop(&mut self) {
469 profiling::scope!("Buffer::drop");
470 api_log!("Buffer::drop {:?}", self as *const _);
471 #[cfg(feature = "trace")]
472 if let Some(t) = self.device.trace.lock().as_mut() {
473 t.add(trace::Action::DropBuffer(unsafe { trace::to_trace(self) }));
474 }
475
476 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
477 raw.dispose(self.device.raw());
478 }
479
480 if let Some(raw) = self.indirect_validation_bind_groups.take() {
481 raw.dispose(self.device.raw());
482 }
483
484 let map_state = mem::replace(self.map_state.get_mut(), BufferMapState::Idle);
485 let active_map = match map_state {
486 BufferMapState::Init { staging_buffer } => {
487 staging_buffer.dispose();
488 false
489 }
490 BufferMapState::Waiting(buffer_pending_mapping) => {
491 if buffer_pending_mapping.op.callback.is_some() {
492 let result = Err(BufferAccessError::DestroyedResource(
493 DestroyedResourceError(self.error_ident()),
494 ));
495 self.device
496 .deferred_buffer_map_pending_closures
497 .push((buffer_pending_mapping.op, result));
498 }
499 false
500 }
501 BufferMapState::Active { .. } => true,
502 BufferMapState::Idle => false,
503 };
504
505 let ResourceState::Valid(state) = &mut self.state else {
506 return;
507 };
508
509 if let Some(raw) = state.raw.take() {
510 if active_map {
511 unsafe { self.device.raw().unmap_buffer(raw.as_ref()) }
512 }
513 resource_log!("Destroy raw {}", self.error_ident());
514 unsafe {
515 self.device.raw().destroy_buffer(raw);
516 }
517 }
518 }
519}
520
521impl RawResourceAccess for Buffer {
522 type DynResource = dyn hal::DynBuffer;
523
524 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
525 self.state()
526 .ok()
527 .and_then(|state| state.raw.get(guard).map(|b| b.as_ref()))
528 }
529}
530
531impl Buffer {
532 pub(crate) fn check_destroyed(
533 &self,
534 guard: &SnatchGuard,
535 ) -> Result<(), DestroyedResourceError> {
536 let ResourceState::Valid(state) = &self.state else {
537 return Ok(());
538 };
539 state
540 .raw
541 .get(guard)
542 .map(|_| ())
543 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
544 }
545
546 pub(crate) fn check_usage(
549 &self,
550 expected: wgt::BufferUsages,
551 ) -> Result<(), MissingBufferUsageError> {
552 if self.usage.contains(expected) {
553 Ok(())
554 } else {
555 Err(MissingBufferUsageError {
556 res: self.error_ident(),
557 actual: self.usage,
558 expected,
559 })
560 }
561 }
562
563 pub(crate) fn state(&self) -> Result<&BufferState, InvalidResourceError> {
564 match &self.state {
565 ResourceState::Valid(state) => Ok(state),
566 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
567 }
568 }
569
570 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
571 self.state().map(|_| ())
572 }
573
574 pub fn invalid(device: Arc<Device>, desc: &BufferDescriptor) -> Arc<Self> {
575 Arc::new(Buffer {
576 state: ResourceState::Invalid,
577 usage: desc.usage,
578 size: desc.size,
579 initialization_status: RwLock::new(
580 rank::BUFFER_INITIALIZATION_STATUS,
581 BufferInitTracker::new(0),
582 ),
583 map_state: Mutex::new(rank::BUFFER_MAP_STATE, BufferMapState::Idle),
584 label: desc.label.to_string(),
585 tracking_data: TrackingData::new(device.tracker_indices.buffers.clone()),
586 bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
587 timestamp_normalization_bind_group: Snatchable::empty(),
588 indirect_validation_bind_groups: Snatchable::empty(),
589 device,
590 })
591 }
592
593 pub fn resolve_binding_size(
605 &self,
606 offset: wgt::BufferAddress,
607 binding_size: Option<wgt::BufferSize>,
608 ) -> Result<u64, BindingError> {
609 let buffer_size = self.size;
610
611 match binding_size {
612 Some(binding_size) => match offset.checked_add(binding_size.get()) {
613 Some(end) if end <= buffer_size => Ok(binding_size.get()),
614 _ => Err(BindingError::BindingRangeTooLarge {
615 buffer: self.error_ident(),
616 offset,
617 binding_size: binding_size.get(),
618 buffer_size,
619 }),
620 },
621 None => {
622 buffer_size
623 .checked_sub(offset)
624 .ok_or_else(|| BindingError::BindingOffsetTooLarge {
625 buffer: self.error_ident(),
626 offset,
627 buffer_size,
628 })
629 }
630 }
631 }
632
633 pub fn binding<'a>(
654 &'a self,
655 offset: wgt::BufferAddress,
656 binding_size: Option<wgt::BufferSize>,
657 snatch_guard: &'a SnatchGuard,
658 ) -> Result<(hal::BufferBinding<'a, dyn hal::DynBuffer>, u64), BindingError> {
659 let buf_raw = self.try_raw(snatch_guard)?;
660 let resolved_size = self.resolve_binding_size(offset, binding_size)?;
661 Ok((
664 hal::BufferBinding::new_unchecked(buf_raw, offset, binding_size),
665 resolved_size,
666 ))
667 }
668
669 pub fn map_async(
673 self: &Arc<Self>,
674 offset: wgt::BufferAddress,
675 size: Option<wgt::BufferAddress>,
676 op: BufferMapOperation,
677 ) -> Result<SubmissionIndex, BufferAccessError> {
678 profiling::scope!("Buffer::map_async");
679 api_log!(
680 "Buffer::map_async {:?} offset {offset:?} size {size:?} op: {op:?}",
681 Arc::as_ptr(self)
682 );
683
684 self.try_map_async(offset, size, op)
685 .map_err(|(mut operation, err)| {
686 if let Some(callback) = operation.callback.take() {
687 callback(Err(err.clone()));
688 }
689 err
690 })
691 }
692
693 fn try_map_async(
716 self: &Arc<Self>,
717 offset: wgt::BufferAddress,
718 size: Option<wgt::BufferAddress>,
719 op: BufferMapOperation,
720 ) -> Result<SubmissionIndex, (BufferMapOperation, BufferAccessError)> {
721 let range_size = if let Some(size) = size {
722 size
723 } else {
724 self.size.saturating_sub(offset)
725 };
726
727 if let Err(e) = self.check_is_valid() {
728 return Err((op, e.into()));
729 }
730
731 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
732 return Err((op, BufferAccessError::UnalignedOffset { offset }));
733 }
734 if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
735 return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));
736 }
737
738 if offset > self.size {
739 return Err((
740 op,
741 BufferAccessError::MapStartOffsetOverrun {
742 offset,
743 buffer_size: self.size,
744 },
745 ));
746 }
747 if range_size > self.size - offset {
749 return Err((
750 op,
751 BufferAccessError::MapEndOffsetOverrun {
752 offset,
753 size: range_size,
754 buffer_size: self.size,
755 },
756 ));
757 }
758 let end_offset = offset + range_size;
759
760 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT)
761 || !end_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT)
762 {
763 return Err((op, BufferAccessError::UnalignedRange));
764 }
765
766 let (pub_usage, internal_use) = match op.host {
767 HostMap::Read => (wgt::BufferUsages::MAP_READ, wgt::BufferUses::MAP_READ),
768 HostMap::Write => (wgt::BufferUsages::MAP_WRITE, wgt::BufferUses::MAP_WRITE),
769 };
770
771 if let Err(e) = self.check_usage(pub_usage) {
772 return Err((op, e.into()));
773 }
774
775 let device = &self.device;
776 if let Err(e) = device.check_is_valid() {
777 return Err((op, e.into()));
778 }
779
780 let submit_index = {
781 let snatch_guard = device.snatchable_lock.read();
782 if let Err(e) = self.check_destroyed(&snatch_guard) {
783 return Err((op, e.into()));
784 }
785
786 {
787 let map_state = &mut *self.map_state.lock();
788 *map_state = match *map_state {
789 BufferMapState::Init { .. } | BufferMapState::Active { .. } => {
790 return Err((op, BufferAccessError::AlreadyMapped));
791 }
792 BufferMapState::Waiting(_) => {
793 return Err((op, BufferAccessError::MapAlreadyPending));
794 }
795 BufferMapState::Idle => BufferMapState::Waiting(BufferPendingMapping {
796 range: offset..end_offset,
797 op,
798 _parent_buffer: self.clone(),
799 }),
800 };
801 }
802
803 if let Some(queue) = device.get_queue().as_ref() {
804 match queue.flush_writes_for_buffer(self, snatch_guard) {
805 Err(err) => {
806 let state = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
807 let BufferMapState::Waiting(BufferPendingMapping { op, .. }) = state else {
808 unreachable!();
809 };
810 return Err((op, err));
811 }
812 Ok(()) => {
813 Some(queue.lock_life().map(self).unwrap_or(0))
822 }
823 }
824 } else {
825 None
826 }
827 };
828
829 device
837 .trackers
838 .lock()
839 .buffers
840 .set_single(self, internal_use);
841
842 if let Some(index) = submit_index {
843 Ok(index)
844 } else {
845 let (mut operation, status) = self.map(&device.snatchable_lock.read()).unwrap();
848 if let Some(callback) = operation.callback.take() {
849 callback(status);
850 }
851 Ok(0)
852 }
853 }
854
855 pub fn get_mapped_range(
856 self: &Arc<Self>,
857 offset: wgt::BufferAddress,
858 size: Option<wgt::BufferAddress>,
859 ) -> Result<(NonNull<u8>, u64), BufferAccessError> {
860 profiling::scope!("Buffer::get_mapped_range");
861 api_log!(
862 "Buffer::get_mapped_range {:?} offset {offset:?} size {size:?}",
863 Arc::as_ptr(self)
864 );
865
866 self.check_is_valid()?;
867 {
868 let snatch_guard = self.device.snatchable_lock.read();
869 self.check_destroyed(&snatch_guard)?;
870 }
871
872 let range_size = if let Some(size) = size {
873 size
874 } else {
875 self.size.saturating_sub(offset)
876 };
877
878 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
879 return Err(BufferAccessError::UnalignedOffset { offset });
880 }
881 if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
882 return Err(BufferAccessError::UnalignedRangeSize { range_size });
883 }
884 let map_state = &*self.map_state.lock();
885 match *map_state {
886 BufferMapState::Init { ref staging_buffer } => {
887 if offset > self.size {
888 return Err(BufferAccessError::MapStartOffsetOverrun {
889 offset,
890 buffer_size: self.size,
891 });
892 }
893 if range_size > self.size - offset {
895 return Err(BufferAccessError::MapEndOffsetOverrun {
896 offset,
897 size: range_size,
898 buffer_size: self.size,
899 });
900 }
901 let ptr = unsafe { staging_buffer.ptr() };
902 let ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().offset(offset as isize)) };
903 Ok((ptr, range_size))
904 }
905 BufferMapState::Active {
906 ref mapping,
907 ref range,
908 ..
909 } => {
910 if offset > range.end {
911 return Err(BufferAccessError::OutOfBoundsStartOffsetOverrun {
912 index: offset,
913 max: range.end,
914 });
915 }
916 if offset < range.start {
917 return Err(BufferAccessError::OutOfBoundsStartOffsetUnderrun {
918 index: offset,
919 min: range.start,
920 });
921 }
922 if range_size > range.end - offset {
923 return Err(BufferAccessError::OutOfBoundsEndOffsetOverrun {
924 index: offset,
925 size: range_size,
926 max: range.end,
927 });
928 }
929 let relative_offset = (offset - range.start) as isize;
932 unsafe {
933 Ok((
934 NonNull::new_unchecked(mapping.ptr.as_ptr().offset(relative_offset)),
935 range_size,
936 ))
937 }
938 }
939 BufferMapState::Idle | BufferMapState::Waiting(_) => Err(BufferAccessError::NotMapped),
940 }
941 }
942 #[must_use]
945 pub(crate) fn map(&self, snatch_guard: &SnatchGuard) -> Option<BufferMapPendingClosure> {
946 let mapping = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
950 let pending_mapping = match mapping {
951 BufferMapState::Waiting(pending_mapping) => pending_mapping,
952 BufferMapState::Idle => return None,
954 BufferMapState::Active { .. } => {
957 *self.map_state.lock() = mapping;
958 return None;
959 }
960 _ => panic!("No pending mapping."),
961 };
962 let status = if pending_mapping.range.start != pending_mapping.range.end {
963 let host = pending_mapping.op.host;
964 let size = pending_mapping.range.end - pending_mapping.range.start;
965 match crate::device::map_buffer(
966 self,
967 pending_mapping.range.start,
968 size,
969 host,
970 snatch_guard,
971 ) {
972 Ok(mapping) => {
973 *self.map_state.lock() = BufferMapState::Active {
974 mapping,
975 range: pending_mapping.range.clone(),
976 host,
977 };
978 Ok(())
979 }
980 Err(e) => Err(e),
981 }
982 } else {
983 *self.map_state.lock() = BufferMapState::Active {
984 mapping: hal::BufferMapping {
985 ptr: NonNull::dangling(),
986 is_coherent: true,
987 },
988 range: pending_mapping.range,
989 host: pending_mapping.op.host,
990 };
991 Ok(())
992 };
993 Some((pending_mapping.op, status))
994 }
995
996 pub fn unmap(self: &Arc<Self>) -> Result<(), BufferAccessError> {
998 profiling::scope!("unmap", "Buffer");
999 api_log!("Buffer::unmap {:?}", Arc::as_ptr(self));
1000 if let Some((mut operation, status)) = self.unmap_inner()? {
1001 if let Some(callback) = operation.callback.take() {
1002 callback(status);
1003 }
1004 }
1005
1006 Ok(())
1007 }
1008
1009 fn unmap_inner(self: &Arc<Self>) -> Result<Option<BufferMapPendingClosure>, BufferAccessError> {
1010 let device = &self.device;
1011 self.check_is_valid()?;
1012 self.device.check_is_valid()?;
1013 let snatch_guard = device.snatchable_lock.read();
1014 self.check_destroyed(&snatch_guard)?;
1015 let raw_buf = self.try_raw(&snatch_guard)?;
1016 let map_state = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
1017 match map_state {
1018 BufferMapState::Init { staging_buffer } => {
1019 #[cfg(feature = "trace")]
1020 if let Some(ref mut trace) = *device.trace.lock() {
1021 use crate::device::trace::{DataKind, IntoTrace};
1022
1023 let data = trace.make_binary(DataKind::Bin, staging_buffer.get_data());
1024 trace.add(trace::Action::WriteBuffer {
1025 id: self.to_trace(),
1026 data,
1027 offset: 0,
1029 size: self.size,
1030 queued: true,
1031 });
1032 }
1033
1034 let staging_buffer = staging_buffer.flush();
1035
1036 if let Some(queue) = device.get_queue() {
1037 let region = Some(hal::BufferCopy {
1040 src_offset: 0,
1041 dst_offset: 0,
1042 size: staging_buffer.size,
1043 });
1044 let transition_src = hal::BufferBarrier {
1045 buffer: staging_buffer.raw(),
1046 usage: hal::StateTransition {
1047 from: wgt::BufferUses::MAP_WRITE,
1048 to: wgt::BufferUses::COPY_SRC,
1049 },
1050 };
1051 let transition_dst = hal::BufferBarrier::<dyn hal::DynBuffer> {
1052 buffer: raw_buf,
1053 usage: hal::StateTransition {
1054 from: wgt::BufferUses::empty(),
1055 to: wgt::BufferUses::COPY_DST,
1056 },
1057 };
1058 let mut pending_writes = queue.pending_writes.lock();
1059 let encoder = pending_writes.activate();
1060 unsafe {
1061 encoder.transition_buffers(&[transition_src, transition_dst]);
1062 encoder.copy_buffer_to_buffer(
1065 staging_buffer.raw(),
1066 raw_buf,
1067 region.as_slice(),
1068 );
1069 }
1070 pending_writes.consume(staging_buffer);
1071 pending_writes.insert_buffer(self);
1072 }
1073 }
1074 BufferMapState::Idle => {
1075 return Err(BufferAccessError::NotMapped);
1076 }
1077 BufferMapState::Waiting(pending) => {
1078 return Ok(Some((pending.op, Err(BufferAccessError::MapAborted))));
1079 }
1080 BufferMapState::Active {
1081 mapping,
1082 range,
1083 host,
1084 } => {
1085 if host == HostMap::Write {
1086 #[cfg(feature = "trace")]
1087 if let Some(ref mut trace) = *device.trace.lock() {
1088 use crate::device::trace::{DataKind, IntoTrace};
1089
1090 let size = range.end - range.start;
1091 let data = trace.make_binary(DataKind::Bin, unsafe {
1092 core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize)
1093 });
1094 trace.add(trace::Action::WriteBuffer {
1095 id: self.to_trace(),
1096 data,
1097 offset: range.start,
1098 size,
1099 queued: false,
1100 });
1101 }
1102 if !mapping.is_coherent {
1103 unsafe { device.raw().flush_mapped_ranges(raw_buf, &[range]) };
1104 }
1105 }
1106 unsafe { device.raw().unmap_buffer(raw_buf) };
1107 }
1108 }
1109 Ok(None)
1110 }
1111
1112 pub fn destroy(self: &Arc<Self>) {
1113 profiling::scope!("Buffer::destroy");
1114 api_log!("Buffer::destroy {:?}", Arc::as_ptr(self));
1115
1116 let device = &self.device;
1117
1118 #[cfg(feature = "trace")]
1119 if let Some(trace) = device.trace.lock().as_mut() {
1120 use crate::device::trace::IntoTrace;
1121 trace.add(trace::Action::DestroyBuffer(self.to_trace()));
1122 }
1123
1124 let ResourceState::Valid(state) = &self.state else {
1125 return;
1126 };
1127
1128 let _ = self.unmap();
1129
1130 let temp = {
1131 let mut snatch_guard = device.snatchable_lock.write();
1132
1133 let raw = match state.raw.snatch(&mut snatch_guard) {
1134 Some(raw) => raw,
1135 None => {
1136 return;
1138 }
1139 };
1140
1141 let timestamp_normalization_bind_group = self
1142 .timestamp_normalization_bind_group
1143 .snatch(&mut snatch_guard);
1144
1145 let indirect_validation_bind_groups = self
1146 .indirect_validation_bind_groups
1147 .snatch(&mut snatch_guard);
1148
1149 drop(snatch_guard);
1150
1151 let bind_groups = {
1152 let mut guard = self.bind_groups.lock();
1153 mem::take(&mut *guard)
1154 };
1155
1156 queue::TempResource::DestroyedBuffer(DestroyedBuffer {
1157 raw: ManuallyDrop::new(raw),
1158 device: Arc::clone(&self.device),
1159 label: self.label().to_owned(),
1160 bind_groups,
1161 timestamp_normalization_bind_group,
1162 indirect_validation_bind_groups,
1163 })
1164 };
1165
1166 let Some(queue) = device.get_queue() else {
1167 return;
1168 };
1169
1170 {
1171 let mut pending_writes = queue.pending_writes.lock();
1172 if pending_writes.contains_buffer(self) {
1173 pending_writes.consume_temp(temp);
1174 return;
1175 }
1176 }
1177
1178 let mut life_lock = queue.lock_life();
1179 let last_submit_index = life_lock.get_buffer_latest_submission_index(self);
1180 if let Some(last_submit_index) = last_submit_index {
1181 life_lock.schedule_resource_destruction(temp, last_submit_index);
1182 }
1183 }
1184}
1185
1186#[derive(Clone, Debug, Error)]
1187#[non_exhaustive]
1188pub enum CreateBufferError {
1189 #[error(transparent)]
1190 Device(#[from] DeviceError),
1191 #[error("Failed to map buffer while creating: {0}")]
1192 AccessError(#[from] BufferAccessError),
1193 #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
1194 UnalignedSize,
1195 #[error("Invalid usage flags {0:?}")]
1196 InvalidUsage(wgt::BufferUsages),
1197 #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
1198 UsageMismatch(wgt::BufferUsages),
1199 #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
1200 MaxBufferSize { requested: u64, maximum: u64 },
1201 #[error(transparent)]
1202 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1203 #[error(transparent)]
1204 MissingFeatures(#[from] MissingFeatures),
1205 #[error("Failed to create bind group for indirect buffer validation: {0}")]
1206 IndirectValidationBindGroup(DeviceError),
1207 #[error("Error initializing buffer: {0}")]
1208 QueueWrite(#[from] queue::QueueWriteError),
1209}
1210
1211crate::impl_resource_type!(Buffer);
1212crate::impl_labeled!(Buffer);
1213crate::impl_parent_device!(Buffer);
1214crate::impl_storage_item!(Buffer);
1215crate::impl_trackable!(Buffer);
1216
1217impl WebGpuError for CreateBufferError {
1218 fn webgpu_error_type(&self) -> ErrorType {
1219 match self {
1220 Self::Device(e) => e.webgpu_error_type(),
1221 Self::AccessError(e) => e.webgpu_error_type(),
1222 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1223 Self::IndirectValidationBindGroup(e) => e.webgpu_error_type(),
1224 Self::MissingFeatures(e) => e.webgpu_error_type(),
1225 Self::QueueWrite(e) => e.webgpu_error_type(),
1226
1227 Self::UnalignedSize
1228 | Self::InvalidUsage(_)
1229 | Self::UsageMismatch(_)
1230 | Self::MaxBufferSize { .. } => ErrorType::Validation,
1231 }
1232 }
1233}
1234
1235#[derive(Debug)]
1237pub struct DestroyedBuffer {
1238 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1239 device: Arc<Device>,
1240 label: String,
1241 bind_groups: WeakVec<BindGroup>,
1242 timestamp_normalization_bind_group: Option<TimestampNormalizationBindGroup>,
1243 indirect_validation_bind_groups: Option<crate::indirect_validation::BindGroups>,
1244}
1245
1246impl DestroyedBuffer {
1247 pub fn label(&self) -> &dyn fmt::Debug {
1248 &self.label
1249 }
1250}
1251
1252impl Drop for DestroyedBuffer {
1253 fn drop(&mut self) {
1254 let mut deferred = self.device.deferred_destroy.lock();
1255 deferred.push(DeferredDestroy::BindGroups(mem::take(
1256 &mut self.bind_groups,
1257 )));
1258 drop(deferred);
1259
1260 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
1261 raw.dispose(self.device.raw());
1262 }
1263
1264 if let Some(raw) = self.indirect_validation_bind_groups.take() {
1265 raw.dispose(self.device.raw());
1266 }
1267
1268 resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
1269 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1271 unsafe {
1272 hal::DynDevice::destroy_buffer(self.device.raw(), raw);
1273 }
1274 }
1275}
1276
1277#[cfg(send_sync)]
1278unsafe impl Send for StagingBuffer {}
1279#[cfg(send_sync)]
1280unsafe impl Sync for StagingBuffer {}
1281
1282#[derive(Debug)]
1302pub struct StagingBuffer {
1303 raw: Box<dyn hal::DynBuffer>,
1304 device: Arc<Device>,
1305 pub(crate) size: wgt::BufferSize,
1306 is_coherent: bool,
1307 ptr: NonNull<u8>,
1308}
1309
1310impl StagingBuffer {
1311 pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {
1312 profiling::scope!("StagingBuffer::new");
1313 let stage_desc = hal::BufferDescriptor {
1314 label: hal_label(Some("(wgpu internal) Staging"), device.instance_flags),
1315 size: size.get(),
1316 usage: wgt::BufferUses::MAP_WRITE | wgt::BufferUses::COPY_SRC,
1317 memory_flags: hal::MemoryFlags::TRANSIENT,
1318 };
1319
1320 let raw = unsafe { device.raw().create_buffer(&stage_desc) }
1321 .map_err(|e| device.handle_hal_error(e))?;
1322 let mapping = unsafe { device.raw().map_buffer(raw.as_ref(), 0..size.get()) }
1323 .map_err(|e| device.handle_hal_error(e))?;
1324
1325 let staging_buffer = StagingBuffer {
1326 raw,
1327 device: device.clone(),
1328 size,
1329 is_coherent: mapping.is_coherent,
1330 ptr: mapping.ptr,
1331 };
1332
1333 Ok(staging_buffer)
1334 }
1335
1336 pub(crate) unsafe fn ptr(&self) -> NonNull<u8> {
1339 self.ptr
1340 }
1341
1342 #[cfg(feature = "trace")]
1343 pub(crate) fn get_data(&self) -> &[u8] {
1344 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.size.get() as usize) }
1345 }
1346
1347 pub(crate) fn write_zeros(&mut self) {
1348 unsafe { core::ptr::write_bytes(self.ptr.as_ptr(), 0, self.size.get() as usize) };
1349 }
1350
1351 pub(crate) fn write(&mut self, data: &[u8]) {
1352 assert!(data.len() >= self.size.get() as usize);
1353 unsafe {
1356 core::ptr::copy_nonoverlapping(
1357 data.as_ptr(),
1358 self.ptr.as_ptr(),
1359 self.size.get() as usize,
1360 );
1361 }
1362 }
1363
1364 pub(crate) unsafe fn write_with_offset(
1366 &mut self,
1367 data: &[u8],
1368 src_offset: isize,
1369 dst_offset: isize,
1370 size: usize,
1371 ) {
1372 unsafe {
1373 debug_assert!(
1374 (src_offset + size as isize) as usize <= data.len(),
1375 "src_offset + size must be in-bounds: src_offset = {}, size = {}, data.len() = {}",
1376 src_offset,
1377 size,
1378 data.len()
1379 );
1380 core::ptr::copy_nonoverlapping(
1381 data.as_ptr().offset(src_offset),
1382 self.ptr.as_ptr().offset(dst_offset),
1383 size,
1384 );
1385 }
1386 }
1387
1388 pub(crate) fn flush(self) -> FlushedStagingBuffer {
1389 let device = self.device.raw();
1390 if !self.is_coherent {
1391 #[allow(clippy::single_range_in_vec_init)]
1392 unsafe {
1393 device.flush_mapped_ranges(self.raw.as_ref(), &[0..self.size.get()])
1394 };
1395 }
1396 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1397
1398 let StagingBuffer {
1399 raw, device, size, ..
1400 } = self;
1401
1402 FlushedStagingBuffer {
1403 raw: ManuallyDrop::new(raw),
1404 device,
1405 size,
1406 }
1407 }
1408
1409 pub(crate) fn dispose(self) {
1410 let device = self.device.raw();
1411 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1412 unsafe { device.destroy_buffer(self.raw) };
1413 }
1414}
1415
1416crate::impl_resource_type!(StagingBuffer);
1417crate::impl_storage_item!(StagingBuffer);
1418
1419#[derive(Debug)]
1420pub struct FlushedStagingBuffer {
1421 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1422 device: Arc<Device>,
1423 pub(crate) size: wgt::BufferSize,
1424}
1425
1426impl FlushedStagingBuffer {
1427 pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {
1428 self.raw.as_ref()
1429 }
1430}
1431
1432impl Drop for FlushedStagingBuffer {
1433 fn drop(&mut self) {
1434 resource_log!("Destroy raw StagingBuffer");
1435 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1437 unsafe { self.device.raw().destroy_buffer(raw) };
1438 }
1439}
1440
1441pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
1442
1443#[derive(Debug)]
1444pub(crate) enum TextureInner {
1445 Native {
1446 raw: Box<dyn hal::DynTexture>,
1447 },
1448 Surface {
1449 raw: Box<dyn hal::DynSurfaceTexture>,
1450 },
1451}
1452
1453impl TextureInner {
1454 pub(crate) fn raw(&self) -> &dyn hal::DynTexture {
1455 match self {
1456 Self::Native { raw } => raw.as_ref(),
1457 Self::Surface { raw, .. } => raw.as_ref().borrow(),
1458 }
1459 }
1460}
1461
1462#[derive(Debug)]
1463pub enum TextureClearMode {
1464 BufferCopy,
1465 RenderPass {
1467 clear_views: SmallVec<[ManuallyDrop<Box<dyn hal::DynTextureView>>; 1]>,
1468 is_color: bool,
1469 },
1470 Surface {
1471 clear_view: ManuallyDrop<Box<dyn hal::DynTextureView>>,
1472 },
1473 None,
1476}
1477
1478#[derive(Debug)]
1479pub struct TextureState {
1480 pub(crate) inner: Snatchable<TextureInner>,
1481}
1482
1483#[derive(Debug)]
1484pub struct Texture {
1485 pub(crate) state: ResourceState<TextureState>,
1486 pub(crate) device: Arc<Device>,
1487 pub(crate) desc: wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
1488 pub(crate) _hal_usage: wgt::TextureUses,
1489 pub(crate) format_features: wgt::TextureFormatFeatures,
1490 pub(crate) initialization_status: RwLock<TextureInitTracker>,
1491 pub(crate) full_range: TextureSelector,
1492 pub(crate) tracking_data: TrackingData,
1493 pub(crate) clear_mode: RwLock<TextureClearMode>,
1494 pub(crate) views: Mutex<WeakVec<TextureView>>,
1495 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
1497}
1498
1499impl Texture {
1500 pub(crate) fn new(
1501 device: &Arc<Device>,
1502 inner: TextureInner,
1503 hal_usage: wgt::TextureUses,
1504 desc: &TextureDescriptor,
1505 format_features: wgt::TextureFormatFeatures,
1506 clear_mode: TextureClearMode,
1507 init: bool,
1508 ) -> Self {
1509 Texture {
1510 state: ResourceState::Valid(TextureState {
1511 inner: Snatchable::new(inner),
1512 }),
1513 device: device.clone(),
1514 desc: desc.map_label(|label| label.to_string()),
1515 _hal_usage: hal_usage,
1516 format_features,
1517 initialization_status: RwLock::new(
1518 rank::TEXTURE_INITIALIZATION_STATUS,
1519 if init {
1520 TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count())
1521 } else {
1522 TextureInitTracker::new(desc.mip_level_count, 0)
1523 },
1524 ),
1525 full_range: TextureSelector {
1526 mips: 0..desc.mip_level_count,
1527 layers: 0..desc.array_layer_count(),
1528 },
1529 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1530 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
1531 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1532 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1533 }
1534 }
1535
1536 pub fn invalid(device: &Arc<Device>, desc: &TextureDescriptor) -> Arc<Self> {
1537 Arc::new(Texture {
1538 state: ResourceState::Invalid,
1539 device: device.clone(),
1540 desc: desc.map_label(|label| label.to_string()),
1541 _hal_usage: wgt::TextureUses::empty(),
1542 format_features: wgt::TextureFormatFeatures {
1543 allowed_usages: wgt::TextureUsages::empty(),
1544 flags: wgt::TextureFormatFeatureFlags::empty(),
1545 },
1546 initialization_status: RwLock::new(
1547 rank::TEXTURE_INITIALIZATION_STATUS,
1548 TextureInitTracker::new(0, 0),
1549 ),
1550 full_range: TextureSelector {
1551 mips: 0..desc.mip_level_count,
1552 layers: 0..desc.array_layer_count(),
1553 },
1554 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1555 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, TextureClearMode::None),
1556 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1557 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1558 })
1559 }
1560
1561 pub(crate) fn check_usage(
1564 &self,
1565 expected: wgt::TextureUsages,
1566 ) -> Result<(), MissingTextureUsageError> {
1567 if self.desc.usage.contains(expected) {
1568 Ok(())
1569 } else {
1570 Err(MissingTextureUsageError {
1571 res: self.error_ident(),
1572 actual: self.desc.usage,
1573 expected,
1574 })
1575 }
1576 }
1577}
1578
1579impl Drop for Texture {
1580 #[allow(trivial_casts)]
1581 fn drop(&mut self) {
1582 profiling::scope!("Texture::drop");
1583 api_log!("Texture::drop {:?}", self as *const _);
1584
1585 #[cfg(feature = "trace")]
1586 {
1587 let mut t = self.device.trace.lock();
1588 if let Some(t) = t.as_mut() {
1589 use crate::device::trace::to_trace;
1590
1591 t.add(trace::Action::DropTexture(unsafe { to_trace(self) }));
1593 }
1594 }
1595 match *self.clear_mode.write() {
1596 TextureClearMode::Surface {
1597 ref mut clear_view, ..
1598 } => {
1599 let raw = unsafe { ManuallyDrop::take(clear_view) };
1601 unsafe {
1602 self.device.raw().destroy_texture_view(raw);
1603 }
1604 }
1605 TextureClearMode::RenderPass {
1606 ref mut clear_views,
1607 ..
1608 } => {
1609 clear_views.iter_mut().for_each(|clear_view| {
1610 let raw = unsafe { ManuallyDrop::take(clear_view) };
1612 unsafe {
1613 self.device.raw().destroy_texture_view(raw);
1614 }
1615 });
1616 }
1617 _ => {}
1618 };
1619
1620 let ResourceState::Valid(state) = &mut self.state else {
1621 return;
1622 };
1623 if let Some(TextureInner::Native { raw }) = state.inner.take() {
1624 resource_log!("Destroy raw {}", self.error_ident());
1625 unsafe {
1626 self.device.raw().destroy_texture(raw);
1627 }
1628 }
1629 }
1630}
1631
1632impl RawResourceAccess for Texture {
1633 type DynResource = dyn hal::DynTexture;
1634
1635 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1636 self.state
1637 .as_ref()
1638 .valid()
1639 .and_then(|t| t.inner.get(guard).map(|t| t.raw()))
1640 }
1641}
1642
1643impl Texture {
1644 pub(crate) fn state(&self) -> Result<&TextureState, InvalidResourceError> {
1645 match &self.state {
1646 ResourceState::Valid(state) => Ok(state),
1647 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
1648 }
1649 }
1650
1651 pub(crate) fn check_destroyed(
1652 &self,
1653 guard: &SnatchGuard,
1654 ) -> Result<(), DestroyedResourceError> {
1655 let Ok(state) = self.state() else {
1656 return Ok(());
1657 };
1658 state
1659 .inner
1660 .get(guard)
1661 .map(|_| ())
1662 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1663 }
1664
1665 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
1666 self.state().map(|_| ())
1667 }
1668
1669 pub(crate) fn try_inner<'a>(
1670 &'a self,
1671 guard: &'a SnatchGuard,
1672 ) -> Result<&'a TextureInner, InvalidOrDestroyedResourceError> {
1673 self.state()?
1674 .inner
1675 .get(guard)
1676 .ok_or_else(|| DestroyedResourceError(self.error_ident()).into())
1677 }
1678
1679 pub(crate) fn get_clear_view<'a>(
1680 clear_mode: &'a TextureClearMode,
1681 desc: &'a wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
1682 mip_level: u32,
1683 depth_or_layer: u32,
1684 ) -> &'a dyn hal::DynTextureView {
1685 match *clear_mode {
1686 TextureClearMode::BufferCopy => {
1687 panic!("Given texture is cleared with buffer copies, not render passes")
1688 }
1689 TextureClearMode::None => {
1690 panic!("Given texture can't be cleared")
1691 }
1692 TextureClearMode::Surface { ref clear_view, .. } => clear_view.as_ref(),
1693 TextureClearMode::RenderPass {
1694 ref clear_views, ..
1695 } => {
1696 let index = if desc.dimension == wgt::TextureDimension::D3 {
1697 (0..mip_level).fold(0, |acc, mip| {
1698 acc + (desc.size.depth_or_array_layers >> mip).max(1)
1699 })
1700 } else {
1701 mip_level * desc.size.depth_or_array_layers
1702 } + depth_or_layer;
1703 clear_views[index as usize].as_ref()
1704 }
1705 }
1706 }
1707
1708 pub fn destroy(self: &Arc<Self>) {
1709 profiling::scope!("Texture::destroy");
1710 api_log!("Texture::destroy {:?}", Arc::as_ptr(self));
1711
1712 #[cfg(feature = "trace")]
1713 if let Some(trace) = self.device.trace.lock().as_mut() {
1714 use crate::device::trace::IntoTrace as _;
1715
1716 trace.add(trace::Action::DestroyTexture(self.to_trace()));
1717 }
1718
1719 let device = &self.device;
1720
1721 let ResourceState::Valid(state) = &self.state else {
1722 return;
1723 };
1724
1725 let temp = {
1726 let raw = match state.inner.snatch(&mut device.snatchable_lock.write()) {
1727 Some(TextureInner::Native { raw }) => raw,
1728 Some(TextureInner::Surface { .. }) => {
1729 return;
1730 }
1731 None => {
1732 return;
1734 }
1735 };
1736
1737 let views = {
1738 let mut guard = self.views.lock();
1739 mem::take(&mut *guard)
1740 };
1741
1742 let bind_groups = {
1743 let mut guard = self.bind_groups.lock();
1744 mem::take(&mut *guard)
1745 };
1746
1747 queue::TempResource::DestroyedTexture(DestroyedTexture {
1748 raw: ManuallyDrop::new(raw),
1749 views,
1750 clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None),
1751 bind_groups,
1752 device: Arc::clone(&self.device),
1753 label: self.label().to_owned(),
1754 })
1755 };
1756
1757 let Some(queue) = device.get_queue() else {
1758 return;
1759 };
1760
1761 {
1762 let mut pending_writes = queue.pending_writes.lock();
1763 if pending_writes.contains_texture(self) {
1764 pending_writes.consume_temp(temp);
1765 return;
1766 }
1767 }
1768
1769 let mut life_lock = queue.lock_life();
1770 let last_submit_index = life_lock.get_texture_latest_submission_index(self);
1771 if let Some(last_submit_index) = last_submit_index {
1772 life_lock.schedule_resource_destruction(temp, last_submit_index);
1773 }
1774 }
1775
1776 fn create_view_inner(
1777 self: &Arc<Self>,
1778 desc: &TextureViewDescriptor,
1779 ) -> Result<Arc<TextureView>, CreateTextureViewError> {
1780 let device = &self.device;
1781 device.check_is_valid()?;
1782
1783 let snatch_guard = device.snatchable_lock.read();
1784
1785 let texture_raw = self.try_inner(&snatch_guard)?.raw();
1786
1787 let resolved_format = desc.format.unwrap_or_else(|| {
1790 self.desc
1791 .format
1792 .aspect_specific_format(desc.range.aspect)
1793 .unwrap_or(self.desc.format)
1794 });
1795
1796 let resolved_dimension = desc.dimension.unwrap_or_else(|| match self.desc.dimension {
1797 wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1,
1798 wgt::TextureDimension::D2 => {
1799 if self.desc.array_layer_count() == 1 {
1800 wgt::TextureViewDimension::D2
1801 } else {
1802 wgt::TextureViewDimension::D2Array
1803 }
1804 }
1805 wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3,
1806 });
1807
1808 let resolved_mip_level_count = desc.range.mip_level_count.unwrap_or_else(|| {
1809 self.desc
1810 .mip_level_count
1811 .saturating_sub(desc.range.base_mip_level)
1812 });
1813
1814 let resolved_array_layer_count =
1815 desc.range
1816 .array_layer_count
1817 .unwrap_or_else(|| match resolved_dimension {
1818 wgt::TextureViewDimension::D1
1819 | wgt::TextureViewDimension::D2
1820 | wgt::TextureViewDimension::D3 => 1,
1821 wgt::TextureViewDimension::Cube => 6,
1822 wgt::TextureViewDimension::D2Array | wgt::TextureViewDimension::CubeArray => {
1823 self.desc
1824 .array_layer_count()
1825 .saturating_sub(desc.range.base_array_layer)
1826 }
1827 });
1828
1829 let resolved_usage = {
1830 let usage = desc.usage.unwrap_or(wgt::TextureUsages::empty());
1831 if usage.is_empty() {
1832 self.desc.usage
1833 } else if self.desc.usage.contains(usage) {
1834 if self
1836 .desc
1837 .usage
1838 .contains(wgt::TextureUsages::TRANSIENT_ATTACHMENT)
1839 && self.desc.usage != usage
1840 {
1841 return Err(CreateTextureViewError::InvalidTransientTextureViewUsage {
1842 texture: self.desc.usage,
1843 view: usage,
1844 });
1845 }
1846
1847 usage
1848 } else {
1849 return Err(CreateTextureViewError::InvalidTextureViewUsage {
1850 view: usage,
1851 texture: self.desc.usage,
1852 });
1853 }
1854 };
1855
1856 let format_features = device.describe_format_features(resolved_format)?;
1857 let allowed_format_usages = format_features.allowed_usages;
1858 if resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
1859 && !allowed_format_usages.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
1860 {
1861 return Err(CreateTextureViewError::TextureViewFormatNotRenderable(
1862 resolved_format,
1863 ));
1864 }
1865
1866 if resolved_usage.contains(wgt::TextureUsages::STORAGE_BINDING)
1867 && !allowed_format_usages.contains(wgt::TextureUsages::STORAGE_BINDING)
1868 {
1869 return Err(CreateTextureViewError::TextureViewFormatNotStorage(
1870 resolved_format,
1871 ));
1872 }
1873
1874 let aspects = hal::FormatAspects::new(self.desc.format, desc.range.aspect);
1877 if aspects.is_empty() {
1878 return Err(CreateTextureViewError::InvalidAspect {
1879 texture_format: self.desc.format,
1880 requested_aspect: desc.range.aspect,
1881 });
1882 }
1883
1884 if desc.range.aspect == wgt::TextureAspect::All && resolved_format.is_multi_planar_format()
1885 {
1886 return Err(CreateTextureViewError::MultiplanarFullTexture(
1887 resolved_format,
1888 ));
1889 }
1890
1891 let format_is_good = if desc.range.aspect == wgt::TextureAspect::All {
1892 resolved_format == self.desc.format || self.desc.view_formats.contains(&resolved_format)
1893 } else {
1894 Some(resolved_format) == self.desc.format.aspect_specific_format(desc.range.aspect)
1895 };
1896 if !format_is_good {
1897 return Err(CreateTextureViewError::FormatReinterpretation {
1898 texture: self.desc.format,
1899 view: resolved_format,
1900 });
1901 }
1902
1903 if self.desc.sample_count > 1 && resolved_dimension != wgt::TextureViewDimension::D2 {
1905 let multisample_array_exception = resolved_dimension
1907 == wgt::TextureViewDimension::D2Array
1908 && device.features.contains(wgt::Features::MULTISAMPLE_ARRAY);
1909
1910 if !multisample_array_exception {
1911 return Err(
1912 CreateTextureViewError::InvalidMultisampledTextureViewDimension(
1913 resolved_dimension,
1914 ),
1915 );
1916 }
1917 }
1918
1919 if self.desc.dimension != resolved_dimension.compatible_texture_dimension() {
1921 return Err(CreateTextureViewError::InvalidTextureViewDimension {
1922 view: resolved_dimension,
1923 texture: self.desc.dimension,
1924 });
1925 }
1926
1927 match resolved_dimension {
1928 wgt::TextureViewDimension::D1
1929 | wgt::TextureViewDimension::D2
1930 | wgt::TextureViewDimension::D3 => {
1931 if resolved_array_layer_count != 1 {
1932 return Err(CreateTextureViewError::InvalidArrayLayerCount {
1933 requested: resolved_array_layer_count,
1934 dim: resolved_dimension,
1935 });
1936 }
1937 }
1938 wgt::TextureViewDimension::Cube => {
1939 if resolved_array_layer_count != 6 {
1940 return Err(CreateTextureViewError::InvalidCubemapTextureDepth {
1941 depth: resolved_array_layer_count,
1942 });
1943 }
1944 }
1945 wgt::TextureViewDimension::CubeArray => {
1946 if !resolved_array_layer_count.is_multiple_of(6) {
1947 return Err(CreateTextureViewError::InvalidCubemapArrayTextureDepth {
1948 depth: resolved_array_layer_count,
1949 });
1950 }
1951 }
1952 _ => {}
1953 }
1954
1955 match resolved_dimension {
1956 wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => {
1957 if self.desc.size.width != self.desc.size.height {
1958 return Err(CreateTextureViewError::InvalidCubeTextureViewSize);
1959 }
1960 }
1961 _ => {}
1962 }
1963
1964 if resolved_mip_level_count == 0 {
1965 return Err(CreateTextureViewError::ZeroMipLevelCount);
1966 }
1967
1968 let mip_level_end = desc
1969 .range
1970 .base_mip_level
1971 .saturating_add(resolved_mip_level_count);
1972
1973 let level_end = self.desc.mip_level_count;
1974 if mip_level_end > level_end {
1975 return Err(CreateTextureViewError::TooManyMipLevels {
1976 base_mip_level: desc.range.base_mip_level,
1977 mip_level_count: resolved_mip_level_count,
1978 total: level_end,
1979 });
1980 }
1981
1982 if resolved_array_layer_count == 0 {
1983 return Err(CreateTextureViewError::ZeroArrayLayerCount);
1984 }
1985
1986 let array_layer_end = desc
1987 .range
1988 .base_array_layer
1989 .saturating_add(resolved_array_layer_count);
1990
1991 let layer_end = self.desc.array_layer_count();
1992 if array_layer_end > layer_end {
1993 return Err(CreateTextureViewError::TooManyArrayLayers {
1994 base_array_layer: desc.range.base_array_layer,
1995 array_layer_count: resolved_array_layer_count,
1996 total: layer_end,
1997 });
1998 };
1999
2000 let render_extent = 'error: {
2002 if !resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
2003 break 'error Err(TextureViewNotRenderableReason::Usage(resolved_usage));
2004 }
2005
2006 let allowed_view_dimensions = [
2007 wgt::TextureViewDimension::D2,
2008 wgt::TextureViewDimension::D2Array,
2009 wgt::TextureViewDimension::D3,
2010 ];
2011 if !allowed_view_dimensions.contains(&resolved_dimension) {
2012 break 'error Err(TextureViewNotRenderableReason::Dimension(
2013 resolved_dimension,
2014 ));
2015 }
2016
2017 if resolved_mip_level_count != 1 {
2018 break 'error Err(TextureViewNotRenderableReason::MipLevelCount(
2019 resolved_mip_level_count,
2020 ));
2021 }
2022
2023 if resolved_array_layer_count != 1
2024 && !(device.features.contains(wgt::Features::MULTIVIEW))
2025 {
2026 break 'error Err(TextureViewNotRenderableReason::ArrayLayerCount(
2027 resolved_array_layer_count,
2028 ));
2029 }
2030
2031 if !self.desc.format.is_multi_planar_format()
2032 && aspects != hal::FormatAspects::from(self.desc.format)
2033 {
2034 break 'error Err(TextureViewNotRenderableReason::Aspects(aspects));
2035 }
2036
2037 Ok(self
2038 .desc
2039 .compute_render_extent(desc.range.base_mip_level, desc.range.aspect.to_plane()))
2040 };
2041
2042 let usage = {
2044 let resolved_hal_usage = crate::conv::map_texture_usage(
2045 resolved_usage,
2046 resolved_format.into(),
2047 format_features.flags,
2048 );
2049 let mask_copy = !(wgt::TextureUses::COPY_SRC | wgt::TextureUses::COPY_DST);
2050 let mask_dimension = match resolved_dimension {
2051 wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => {
2052 wgt::TextureUses::RESOURCE
2053 }
2054 wgt::TextureViewDimension::D3 => {
2055 wgt::TextureUses::RESOURCE
2056 | wgt::TextureUses::STORAGE_READ_ONLY
2057 | wgt::TextureUses::STORAGE_WRITE_ONLY
2058 | wgt::TextureUses::STORAGE_READ_WRITE
2059 }
2060 _ => wgt::TextureUses::all(),
2061 };
2062 let mask_mip_level = if resolved_mip_level_count == 1 {
2063 wgt::TextureUses::all()
2064 } else {
2065 wgt::TextureUses::RESOURCE
2066 };
2067 resolved_hal_usage & mask_copy & mask_dimension & mask_mip_level
2068 };
2069
2070 let format = if resolved_format.is_depth_stencil_component(self.desc.format) {
2072 self.desc.format
2073 } else {
2074 resolved_format
2075 };
2076
2077 let resolved_range = wgt::ImageSubresourceRange {
2078 aspect: desc.range.aspect,
2079 base_mip_level: desc.range.base_mip_level,
2080 mip_level_count: Some(resolved_mip_level_count),
2081 base_array_layer: desc.range.base_array_layer,
2082 array_layer_count: Some(resolved_array_layer_count),
2083 };
2084
2085 let hal_desc = hal::TextureViewDescriptor {
2086 label: desc.label.to_hal(device.instance_flags),
2087 format,
2088 dimension: resolved_dimension,
2089 usage,
2090 range: resolved_range,
2091 };
2092
2093 let raw = unsafe { device.raw().create_texture_view(texture_raw, &hal_desc) }
2094 .map_err(|e| device.handle_hal_error(e))?;
2095
2096 let selector = TextureSelector {
2097 mips: desc.range.base_mip_level..mip_level_end,
2098 layers: desc.range.base_array_layer..array_layer_end,
2099 };
2100
2101 let view = TextureView {
2102 state: ResourceState::Valid(TextureViewState {
2103 raw: Snatchable::new(raw),
2104 render_extent,
2105 }),
2106 parent: self.clone(),
2107 device: device.clone(),
2108 desc: HalTextureViewDescriptor {
2109 texture_format: self.desc.format,
2110 format: resolved_format,
2111 dimension: resolved_dimension,
2112 usage: resolved_usage,
2113 range: resolved_range,
2114 },
2115 format_features: self.format_features,
2116 samples: self.desc.sample_count,
2117 selector,
2118 label: desc.label.to_string(),
2119 };
2120
2121 let view = Arc::new(view);
2122
2123 {
2124 let mut views = self.views.lock();
2125 views.push(Arc::downgrade(&view));
2126 }
2127
2128 Ok(view)
2129 }
2130
2131 pub fn create_view(
2132 self: &Arc<Self>,
2133 desc: &TextureViewDescriptor,
2134 ) -> (Arc<TextureView>, Option<CreateTextureViewError>) {
2135 profiling::scope!("Texture::create_view");
2136
2137 let (view, error) = match self.create_view_inner(desc) {
2138 Ok(view) => (view, None),
2139 Err(e) => (TextureView::invalid(&self.device, self, desc), Some(e)),
2140 };
2141
2142 api_log!(
2143 "Texture::create_view({:?}) -> {:?}",
2144 Arc::as_ptr(self),
2145 Arc::as_ptr(&view)
2146 );
2147
2148 #[cfg(feature = "trace")]
2149 if let Some(ref mut trace) = *self.device.trace.lock() {
2150 use crate::device::trace;
2151 use trace::IntoTrace as _;
2152 trace.add(trace::Action::CreateTextureView {
2153 id: view.to_trace(),
2154 parent: self.to_trace(),
2155 desc: desc.clone(),
2156 });
2157 }
2158
2159 (view, error)
2160 }
2161}
2162
2163#[derive(Debug)]
2165pub struct DestroyedTexture {
2166 raw: ManuallyDrop<Box<dyn hal::DynTexture>>,
2167 views: WeakVec<TextureView>,
2168 clear_mode: TextureClearMode,
2169 bind_groups: WeakVec<BindGroup>,
2170 device: Arc<Device>,
2171 label: String,
2172}
2173
2174impl DestroyedTexture {
2175 pub fn label(&self) -> &dyn fmt::Debug {
2176 &self.label
2177 }
2178}
2179
2180impl Drop for DestroyedTexture {
2181 fn drop(&mut self) {
2182 let device = &self.device;
2183
2184 let mut deferred = device.deferred_destroy.lock();
2185 deferred.push(DeferredDestroy::TextureViews(mem::take(&mut self.views)));
2186 deferred.push(DeferredDestroy::BindGroups(mem::take(
2187 &mut self.bind_groups,
2188 )));
2189 drop(deferred);
2190
2191 match mem::replace(&mut self.clear_mode, TextureClearMode::None) {
2192 TextureClearMode::RenderPass { clear_views, .. } => {
2193 for clear_view in clear_views {
2194 let raw = ManuallyDrop::into_inner(clear_view);
2195 unsafe { self.device.raw().destroy_texture_view(raw) };
2196 }
2197 }
2198 TextureClearMode::Surface { clear_view } => {
2199 let raw = ManuallyDrop::into_inner(clear_view);
2200 unsafe { self.device.raw().destroy_texture_view(raw) };
2201 }
2202 _ => (),
2203 }
2204
2205 resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
2206 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
2208 unsafe {
2209 self.device.raw().destroy_texture(raw);
2210 }
2211 }
2212}
2213
2214#[derive(Clone, Copy, Debug)]
2215pub enum TextureErrorDimension {
2216 X,
2217 Y,
2218 Z,
2219}
2220
2221#[derive(Clone, Debug, Error)]
2222#[non_exhaustive]
2223pub enum TextureDimensionError {
2224 #[error("Dimension {0:?} is zero")]
2225 Zero(TextureErrorDimension),
2226 #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
2227 LimitExceeded {
2228 dim: TextureErrorDimension,
2229 given: u32,
2230 limit: u32,
2231 },
2232 #[error("Sample count {0} is invalid")]
2233 InvalidSampleCount(u32),
2234 #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
2235 NotMultipleOfBlockWidth {
2236 width: u32,
2237 block_width: u32,
2238 format: wgt::TextureFormat,
2239 },
2240 #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
2241 NotMultipleOfBlockHeight {
2242 height: u32,
2243 block_height: u32,
2244 format: wgt::TextureFormat,
2245 },
2246 #[error(
2247 "Width {width} is not a multiple of {format:?}'s width multiple requirement ({multiple})"
2248 )]
2249 WidthNotMultipleOf {
2250 width: u32,
2251 multiple: u32,
2252 format: wgt::TextureFormat,
2253 },
2254 #[error("Height {height} is not a multiple of {format:?}'s height multiple requirement ({multiple})")]
2255 HeightNotMultipleOf {
2256 height: u32,
2257 multiple: u32,
2258 format: wgt::TextureFormat,
2259 },
2260 #[error("Multisampled texture depth or array layers must be 1, got {0}")]
2261 MultisampledDepthOrArrayLayer(u32),
2262}
2263
2264impl WebGpuError for TextureDimensionError {
2265 fn webgpu_error_type(&self) -> ErrorType {
2266 ErrorType::Validation
2267 }
2268}
2269
2270#[derive(Clone, Debug, Error)]
2271#[non_exhaustive]
2272pub enum CreateTextureError {
2273 #[error(transparent)]
2274 Device(#[from] DeviceError),
2275 #[error(transparent)]
2276 CreateTextureView(#[from] CreateTextureViewError),
2277 #[error("Invalid usage flags {0:?}")]
2278 InvalidUsage(wgt::TextureUsages),
2279 #[error(transparent)]
2280 InvalidDimension(#[from] TextureDimensionError),
2281 #[error("Depth texture ({1:?}) can't be created as {0:?}")]
2282 InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
2283 #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
2284 InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
2285 #[error(
2286 "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
2287 )]
2288 InvalidMipLevelCount { requested: u32, maximum: u32 },
2289 #[error(
2290 "Texture usages {0:?} are not allowed on a texture of type {1:?}{downlevel_suffix}",
2291 downlevel_suffix = if *.2 { " due to downlevel restrictions" } else { "" }
2292 )]
2293 InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
2294 #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
2295 InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
2296 #[error("Transient texture usage must be equal to `TRANSIENT_ATTACHMENT | RENDER_ATTACHMENT`, but got `{0:?}`")]
2297 InvalidTransientTextureUsage(wgt::TextureUsages),
2298 #[error("Transient texture view formats must be empty")]
2299 InvalidTransientTextureViewFormats,
2300 #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
2301 InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
2302 #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
2303 InvalidMultisampledStorageBinding,
2304 #[error("Format {0:?} does not support multisampling")]
2305 InvalidMultisampledFormat(wgt::TextureFormat),
2306 #[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:?}.")]
2307 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
2308 #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
2309 MultisampledNotRenderAttachment,
2310 #[error("Transient texture mip level count ({0}) must be 1")]
2311 InvalidTransientTextureMipLevelCount(u32),
2312 #[error("Transient texture layer count ({0}) must be 1")]
2313 InvalidTransientTextureLayerCount(u32),
2314 #[error("Texture format {0:?} can't be used due to missing features")]
2315 MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
2316 #[error(transparent)]
2317 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
2318}
2319
2320crate::impl_resource_type!(Texture);
2321impl Labeled for Texture {
2322 fn label(&self) -> &str {
2323 &self.desc.label
2324 }
2325}
2326crate::impl_parent_device!(Texture);
2327crate::impl_storage_item!(Texture);
2328crate::impl_trackable!(Texture);
2329
2330impl Borrow<TextureSelector> for Texture {
2331 fn borrow(&self) -> &TextureSelector {
2332 &self.full_range
2333 }
2334}
2335
2336impl WebGpuError for CreateTextureError {
2337 fn webgpu_error_type(&self) -> ErrorType {
2338 match self {
2339 Self::Device(e) => e.webgpu_error_type(),
2340 Self::CreateTextureView(e) => e.webgpu_error_type(),
2341 Self::InvalidDimension(e) => e.webgpu_error_type(),
2342 Self::MissingFeatures(_, e) => e.webgpu_error_type(),
2343 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
2344
2345 Self::InvalidUsage(_)
2346 | Self::InvalidDepthDimension(_, _)
2347 | Self::InvalidCompressedDimension(_, _)
2348 | Self::InvalidMipLevelCount { .. }
2349 | Self::InvalidFormatUsages(_, _, _)
2350 | Self::InvalidViewFormat(_, _)
2351 | Self::InvalidDimensionUsages(_, _)
2352 | Self::InvalidMultisampledStorageBinding
2353 | Self::InvalidMultisampledFormat(_)
2354 | Self::InvalidSampleCount(..)
2355 | Self::InvalidTransientTextureUsage(_)
2356 | Self::InvalidTransientTextureMipLevelCount(_)
2357 | Self::InvalidTransientTextureLayerCount(_)
2358 | Self::InvalidTransientTextureViewFormats
2359 | Self::MultisampledNotRenderAttachment => ErrorType::Validation,
2360 }
2361 }
2362}
2363
2364#[derive(Clone, Debug, Default, Eq, PartialEq)]
2366#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2367#[cfg_attr(feature = "serde", serde(default))]
2368pub struct TextureViewDescriptor<'a> {
2369 pub label: Label<'a>,
2373 pub format: Option<wgt::TextureFormat>,
2378 pub dimension: Option<wgt::TextureViewDimension>,
2384 pub usage: Option<wgt::TextureUsages>,
2387 pub range: wgt::ImageSubresourceRange,
2389}
2390
2391#[derive(Debug)]
2392pub(crate) struct HalTextureViewDescriptor {
2393 pub texture_format: wgt::TextureFormat,
2394 pub format: wgt::TextureFormat,
2395 pub usage: wgt::TextureUsages,
2396 pub dimension: wgt::TextureViewDimension,
2397 pub range: wgt::ImageSubresourceRange,
2398}
2399
2400impl HalTextureViewDescriptor {
2401 pub fn aspects(&self) -> hal::FormatAspects {
2402 hal::FormatAspects::new(self.texture_format, self.range.aspect)
2403 }
2404}
2405
2406#[derive(Debug, Copy, Clone, Error)]
2407pub enum TextureViewNotRenderableReason {
2408 #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
2409 Usage(wgt::TextureUsages),
2410 #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
2411 Dimension(wgt::TextureViewDimension),
2412 #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
2413 MipLevelCount(u32),
2414 #[error("This texture view has more than one array layer. View array layers: {0:?}")]
2415 ArrayLayerCount(u32),
2416 #[error(
2417 "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
2418 )]
2419 Aspects(hal::FormatAspects),
2420}
2421
2422#[derive(Debug)]
2423pub struct TextureViewState {
2424 pub(crate) raw: Snatchable<Box<dyn hal::DynTextureView>>,
2425 pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
2427}
2428
2429#[derive(Debug)]
2430pub struct TextureView {
2431 pub(crate) state: ResourceState<TextureViewState>,
2432 pub(crate) parent: Arc<Texture>,
2434 pub(crate) device: Arc<Device>,
2435 pub(crate) desc: HalTextureViewDescriptor,
2436 pub(crate) format_features: wgt::TextureFormatFeatures,
2437 pub(crate) samples: u32,
2438 pub(crate) selector: TextureSelector,
2439 pub(crate) label: String,
2441}
2442
2443impl Drop for TextureView {
2444 #[expect(trivial_casts)]
2445 fn drop(&mut self) {
2446 profiling::scope!("TextureView::drop");
2447 api_log!("TextureView::drop {:?}", self as *const _);
2448 #[cfg(feature = "trace")]
2449 if let Some(t) = self.device.trace.lock().as_mut() {
2450 t.add(trace::Action::DropTextureView(unsafe {
2451 trace::to_trace(self)
2452 }));
2453 }
2454 let ResourceState::Valid(state) = &mut self.state else {
2455 return;
2456 };
2457
2458 if let Some(raw) = state.raw.take() {
2459 resource_log!("Destroy raw {}", self.error_ident());
2460 unsafe {
2461 self.device.raw().destroy_texture_view(raw);
2462 }
2463 }
2464 }
2465}
2466
2467impl RawResourceAccess for TextureView {
2468 type DynResource = dyn hal::DynTextureView;
2469
2470 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2471 self.state()
2472 .ok()
2473 .and_then(|state| state.raw.get(guard).map(|it| it.as_ref()))
2474 }
2475
2476 fn try_raw<'a>(
2477 &'a self,
2478 guard: &'a SnatchGuard,
2479 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
2480 self.parent.check_destroyed(guard)?;
2481
2482 self.raw(guard)
2483 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
2484 }
2485}
2486
2487impl TextureView {
2488 pub(crate) fn check_usage(
2491 &self,
2492 expected: wgt::TextureUsages,
2493 ) -> Result<(), MissingTextureUsageError> {
2494 if self.desc.usage.contains(expected) {
2495 Ok(())
2496 } else {
2497 Err(MissingTextureUsageError {
2498 res: self.error_ident(),
2499 actual: self.desc.usage,
2500 expected,
2501 })
2502 }
2503 }
2504
2505 pub(crate) fn state(&self) -> Result<&TextureViewState, InvalidResourceError> {
2506 match &self.state {
2507 ResourceState::Valid(state) => Ok(state),
2508 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2509 }
2510 }
2511
2512 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
2513 self.state().map(|_| ())
2514 }
2515
2516 pub(crate) fn invalid(
2517 device: &Arc<Device>,
2518 texture: &Arc<Texture>,
2519 desc: &TextureViewDescriptor,
2520 ) -> Arc<Self> {
2521 Arc::new(TextureView {
2523 state: ResourceState::Invalid,
2524 parent: texture.clone(),
2525 device: device.clone(),
2526 desc: HalTextureViewDescriptor {
2527 texture_format: texture.desc.format,
2528 format: desc.format.unwrap_or(texture.desc.format),
2529 usage: desc.usage.unwrap_or(texture.desc.usage),
2530 dimension: desc.dimension.unwrap_or(match texture.desc.dimension {
2531 wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1,
2532 wgt::TextureDimension::D2 => wgt::TextureViewDimension::D2,
2533 wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3,
2534 }),
2535 range: desc.range,
2536 },
2537 format_features: texture.format_features,
2538 samples: texture.desc.sample_count,
2539 selector: TextureSelector {
2540 mips: desc.range.mip_range(texture.desc.mip_level_count),
2541 layers: desc.range.layer_range(texture.desc.array_layer_count()),
2542 },
2543 label: desc.label.to_string(),
2544 })
2545 }
2546}
2547
2548#[derive(Clone, Debug, Error)]
2549#[non_exhaustive]
2550pub enum CreateTextureViewError {
2551 #[error(transparent)]
2552 Device(#[from] DeviceError),
2553 #[error(transparent)]
2554 DestroyedResource(#[from] DestroyedResourceError),
2555 #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
2556 InvalidTextureViewDimension {
2557 view: wgt::TextureViewDimension,
2558 texture: wgt::TextureDimension,
2559 },
2560 #[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.")]
2561 TextureViewFormatNotRenderable(wgt::TextureFormat),
2562 #[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.")]
2563 TextureViewFormatNotStorage(wgt::TextureFormat),
2564 #[error("Texture view usages (`{view:?}`) must be a subset of the texture's original usages (`{texture:?}`)")]
2565 InvalidTextureViewUsage {
2566 view: wgt::TextureUsages,
2567 texture: wgt::TextureUsages,
2568 },
2569 #[error("Texture view dimension `{0:?}` cannot be used with a multisampled texture")]
2570 InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
2571 #[error(
2572 "TextureView has an arrayLayerCount of {depth}. Views of type `Cube` must have arrayLayerCount of 6."
2573 )]
2574 InvalidCubemapTextureDepth { depth: u32 },
2575 #[error("TextureView has an arrayLayerCount of {depth}. Views of type `CubeArray` must have an arrayLayerCount that is a multiple of 6.")]
2576 InvalidCubemapArrayTextureDepth { depth: u32 },
2577 #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
2578 InvalidCubeTextureViewSize,
2579 #[error("Mip level count is 0")]
2580 ZeroMipLevelCount,
2581 #[error("Array layer count is 0")]
2582 ZeroArrayLayerCount,
2583 #[error(
2584 "`TextureView` starts at mip level {base_mip_level} and spans {mip_level_count} mip \
2585 levels, but the texture view only has {total} total mip level(s)"
2586 )]
2587 TooManyMipLevels {
2588 base_mip_level: u32,
2589 mip_level_count: u32,
2590 total: u32,
2591 },
2592 #[error(
2593 "`TextureView` starts at array layer {base_array_layer} and spans {array_layer_count}) \
2594 array layers, but the texture view only has {total} total layer(s)"
2595 )]
2596 TooManyArrayLayers {
2597 base_array_layer: u32,
2598 array_layer_count: u32,
2599 total: u32,
2600 },
2601 #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
2602 InvalidArrayLayerCount {
2603 requested: u32,
2604 dim: wgt::TextureViewDimension,
2605 },
2606 #[error(
2607 "Aspect {requested_aspect:?} is not a valid aspect of the source texture format {texture_format:?}"
2608 )]
2609 InvalidAspect {
2610 texture_format: wgt::TextureFormat,
2611 requested_aspect: wgt::TextureAspect,
2612 },
2613 #[error(
2614 "Trying to create a view of format {view:?} of a texture with format {texture:?}, \
2615 but this view format is not present in the texture's viewFormat array"
2616 )]
2617 FormatReinterpretation {
2618 texture: wgt::TextureFormat,
2619 view: wgt::TextureFormat,
2620 },
2621 #[error(
2622 "The texture view (`{view:?}`) from transient texture (`{texture:?}`) must have the same usage"
2623 )]
2624 InvalidTransientTextureViewUsage {
2625 texture: wgt::TextureUsages,
2626 view: wgt::TextureUsages,
2627 },
2628 #[error(transparent)]
2629 InvalidResource(#[from] InvalidResourceError),
2630 #[error(transparent)]
2631 MissingFeatures(#[from] MissingFeatures),
2632 #[error("TextureAspect::All cannot be used in texture views on multi-planar formats")]
2633 MultiplanarFullTexture(wgt::TextureFormat),
2634}
2635
2636impl From<InvalidOrDestroyedResourceError> for CreateTextureViewError {
2637 fn from(value: InvalidOrDestroyedResourceError) -> Self {
2638 match value {
2639 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
2640 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
2641 }
2642 }
2643}
2644
2645impl WebGpuError for CreateTextureViewError {
2646 fn webgpu_error_type(&self) -> ErrorType {
2647 match self {
2648 Self::Device(e) => e.webgpu_error_type(),
2649
2650 Self::InvalidTextureViewDimension { .. }
2651 | Self::InvalidResource(_)
2652 | Self::InvalidMultisampledTextureViewDimension(_)
2653 | Self::InvalidCubemapTextureDepth { .. }
2654 | Self::InvalidCubemapArrayTextureDepth { .. }
2655 | Self::InvalidCubeTextureViewSize
2656 | Self::ZeroMipLevelCount
2657 | Self::ZeroArrayLayerCount
2658 | Self::TooManyMipLevels { .. }
2659 | Self::TooManyArrayLayers { .. }
2660 | Self::InvalidArrayLayerCount { .. }
2661 | Self::InvalidAspect { .. }
2662 | Self::FormatReinterpretation { .. }
2663 | Self::DestroyedResource(_)
2664 | Self::TextureViewFormatNotRenderable(_)
2665 | Self::TextureViewFormatNotStorage(_)
2666 | Self::InvalidTextureViewUsage { .. }
2667 | Self::InvalidTransientTextureViewUsage { .. }
2668 | Self::MissingFeatures(_)
2669 | Self::MultiplanarFullTexture(_) => ErrorType::Validation,
2670 }
2671 }
2672}
2673
2674crate::impl_resource_type!(TextureView);
2675crate::impl_labeled!(TextureView);
2676crate::impl_parent_device!(TextureView);
2677crate::impl_storage_item!(TextureView);
2678
2679pub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;
2680
2681#[derive(Debug)]
2682pub(crate) struct ExternalTextureState {
2683 pub(crate) params: Arc<Buffer>,
2686}
2687
2688#[derive(Debug)]
2689pub struct ExternalTexture {
2690 pub(crate) state: ResourceState<ExternalTextureState>,
2691 pub(crate) device: Arc<Device>,
2692 pub(crate) planes: arrayvec::ArrayVec<Arc<TextureView>, 3>,
2694 pub(crate) label: String,
2696 pub(crate) tracking_data: TrackingData,
2697}
2698
2699impl Drop for ExternalTexture {
2700 #[allow(trivial_casts)]
2701 fn drop(&mut self) {
2702 profiling::scope!("ExternalTexture::drop");
2703 api_log!("ExternalTexture::drop {:?}", self as *const _);
2704
2705 resource_log!("Destroy raw {}", self.error_ident());
2706 #[cfg(feature = "trace")]
2707 if let Some(t) = self.device.trace.lock().as_mut() {
2708 t.add(trace::Action::DropExternalTexture(unsafe {
2709 trace::to_trace(self)
2710 }));
2711 }
2712 }
2713}
2714
2715impl ExternalTexture {
2716 pub(crate) fn state(&self) -> Result<&ExternalTextureState, InvalidResourceError> {
2717 match &self.state {
2718 ResourceState::Valid(state) => Ok(state),
2719 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2720 }
2721 }
2722
2723 pub fn destroy(self: &Arc<Self>) {
2724 profiling::scope!("ExternalTexture::destroy");
2725 api_log!("ExternalTexture::destroy {:?}", Arc::as_ptr(self));
2726
2727 #[cfg(feature = "trace")]
2728 if let Some(trace) = self.device.trace.lock().as_mut() {
2729 use crate::device::trace::IntoTrace as _;
2730
2731 trace.add(trace::Action::DestroyExternalTexture(self.to_trace()));
2732 }
2733 if let Ok(state) = self.state() {
2734 state.params.destroy();
2735 }
2736 }
2737
2738 pub fn invalid(device: Arc<Device>, desc: &ExternalTextureDescriptor) -> Arc<Self> {
2739 Arc::new(ExternalTexture {
2740 state: ResourceState::Invalid,
2741 planes: arrayvec::ArrayVec::new(),
2742 label: desc.label.to_string(),
2743 tracking_data: TrackingData::new(device.tracker_indices.external_textures.clone()),
2744 device,
2745 })
2746 }
2747}
2748
2749#[derive(Clone, Debug, Error)]
2750#[non_exhaustive]
2751pub enum CreateExternalTextureError {
2752 #[error(transparent)]
2753 Device(#[from] DeviceError),
2754 #[error(transparent)]
2755 MissingFeatures(#[from] MissingFeatures),
2756 #[error(transparent)]
2757 InvalidResource(#[from] InvalidResourceError),
2758 #[error(transparent)]
2759 CreateBuffer(#[from] CreateBufferError),
2760 #[error(transparent)]
2761 QueueWrite(#[from] queue::QueueWriteError),
2762 #[error("External texture format {format:?} expects {expected} planes, but given {provided}")]
2763 IncorrectPlaneCount {
2764 format: wgt::ExternalTextureFormat,
2765 expected: usize,
2766 provided: usize,
2767 },
2768 #[error("External texture planes cannot be multisampled, but given view with samples = {0}")]
2769 InvalidPlaneMultisample(u32),
2770 #[error("External texture planes expect a filterable float sample type, but given view with format {format:?} (sample type {sample_type:?})")]
2771 InvalidPlaneSampleType {
2772 format: wgt::TextureFormat,
2773 sample_type: wgt::TextureSampleType,
2774 },
2775 #[error("External texture planes expect 2D dimension, but given view with dimension = {0:?}")]
2776 InvalidPlaneDimension(wgt::TextureViewDimension),
2777 #[error(transparent)]
2778 MissingTextureUsage(#[from] MissingTextureUsageError),
2779 #[error("External texture format {format:?} plane {plane} expects format with {expected} components but given view with format {provided:?} ({} components)",
2780 provided.components())]
2781 InvalidPlaneFormat {
2782 format: wgt::ExternalTextureFormat,
2783 plane: usize,
2784 expected: u8,
2785 provided: wgt::TextureFormat,
2786 },
2787}
2788
2789impl WebGpuError for CreateExternalTextureError {
2790 fn webgpu_error_type(&self) -> ErrorType {
2791 match self {
2792 CreateExternalTextureError::Device(e) => e.webgpu_error_type(),
2793 CreateExternalTextureError::MissingFeatures(e) => e.webgpu_error_type(),
2794 CreateExternalTextureError::InvalidResource(e) => e.webgpu_error_type(),
2795 CreateExternalTextureError::CreateBuffer(e) => e.webgpu_error_type(),
2796 CreateExternalTextureError::QueueWrite(e) => e.webgpu_error_type(),
2797 CreateExternalTextureError::MissingTextureUsage(e) => e.webgpu_error_type(),
2798 CreateExternalTextureError::IncorrectPlaneCount { .. }
2799 | CreateExternalTextureError::InvalidPlaneMultisample(_)
2800 | CreateExternalTextureError::InvalidPlaneSampleType { .. }
2801 | CreateExternalTextureError::InvalidPlaneDimension(_)
2802 | CreateExternalTextureError::InvalidPlaneFormat { .. } => ErrorType::Validation,
2803 }
2804 }
2805}
2806
2807crate::impl_resource_type!(ExternalTexture);
2808crate::impl_labeled!(ExternalTexture);
2809crate::impl_parent_device!(ExternalTexture);
2810crate::impl_storage_item!(ExternalTexture);
2811crate::impl_trackable!(ExternalTexture);
2812
2813#[derive(Clone, Debug, PartialEq)]
2815#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2816pub struct SamplerDescriptor<'a> {
2817 pub label: Label<'a>,
2821 pub address_modes: [wgt::AddressMode; 3],
2823 pub mag_filter: wgt::FilterMode,
2825 pub min_filter: wgt::FilterMode,
2827 pub mipmap_filter: wgt::MipmapFilterMode,
2829 pub lod_min_clamp: f32,
2831 pub lod_max_clamp: f32,
2833 pub compare: Option<wgt::CompareFunction>,
2835 pub anisotropy_clamp: u16,
2837 pub border_color: Option<wgt::SamplerBorderColor>,
2840}
2841
2842#[derive(Debug)]
2843pub struct Sampler {
2844 pub(crate) raw: ResourceState<Box<dyn hal::DynSampler>>,
2845 pub(crate) device: Arc<Device>,
2846 pub(crate) label: String,
2848 pub(crate) tracking_data: TrackingData,
2849 pub(crate) comparison: bool,
2851 pub(crate) filtering: bool,
2853}
2854
2855impl Drop for Sampler {
2856 #[allow(trivial_casts)]
2857 fn drop(&mut self) {
2858 profiling::scope!("Sampler::drop");
2859 api_log!("Sampler::drop {:?}", self as *const _);
2860 #[cfg(feature = "trace")]
2861 if let Some(t) = self.device.trace.lock().as_mut() {
2862 t.add(trace::Action::DropSampler(unsafe { trace::to_trace(self) }));
2863 }
2864 resource_log!("Destroy raw {}", self.error_ident());
2865 if let ResourceState::Valid(raw) = mem::replace(&mut self.raw, ResourceState::Invalid) {
2866 unsafe {
2867 self.device.raw().destroy_sampler(raw);
2868 }
2869 }
2870 }
2871}
2872
2873impl Sampler {
2874 pub(crate) fn raw(&self) -> Result<&dyn hal::DynSampler, InvalidResourceError> {
2875 self.raw
2876 .as_ref()
2877 .valid()
2878 .map(|raw| raw.as_ref())
2879 .ok_or_else(|| InvalidResourceError(self.error_ident()))
2880 }
2881
2882 pub(crate) fn invalid(device: Arc<Device>, desc: &SamplerDescriptor) -> Arc<Self> {
2883 Arc::new(Sampler {
2884 raw: ResourceState::Invalid,
2885 label: desc.label.to_string(),
2886 tracking_data: TrackingData::new(device.tracker_indices.samplers.clone()),
2887 device,
2888 comparison: desc.compare.is_some(),
2889 filtering: desc.mag_filter == wgt::FilterMode::Linear
2890 || desc.min_filter == wgt::FilterMode::Linear
2891 || desc.mipmap_filter == wgt::MipmapFilterMode::Linear,
2892 })
2893 }
2894}
2895
2896#[derive(Copy, Clone)]
2897pub enum SamplerFilterErrorType {
2898 MagFilter,
2899 MinFilter,
2900 MipmapFilter,
2901}
2902
2903impl fmt::Debug for SamplerFilterErrorType {
2904 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2905 match *self {
2906 SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
2907 SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
2908 SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
2909 }
2910 }
2911}
2912
2913#[derive(Clone, Debug, Error)]
2914#[non_exhaustive]
2915pub enum CreateSamplerError {
2916 #[error(transparent)]
2917 Device(#[from] DeviceError),
2918 #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
2919 InvalidLodMinClamp(f32),
2920 #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
2921 InvalidLodMaxClamp {
2922 lod_min_clamp: f32,
2923 lod_max_clamp: f32,
2924 },
2925 #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
2926 InvalidAnisotropy(u16),
2927 #[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.")]
2928 InvalidFilterModeWithAnisotropy {
2929 filter_type: SamplerFilterErrorType,
2930 filter_mode: wgt::FilterMode,
2931 anisotropic_clamp: u16,
2932 },
2933 #[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.")]
2934 InvalidMipmapFilterModeWithAnisotropy {
2935 filter_type: SamplerFilterErrorType,
2936 filter_mode: wgt::MipmapFilterMode,
2937 anisotropic_clamp: u16,
2938 },
2939 #[error(transparent)]
2940 MissingFeatures(#[from] MissingFeatures),
2941}
2942
2943crate::impl_resource_type!(Sampler);
2944crate::impl_labeled!(Sampler);
2945crate::impl_parent_device!(Sampler);
2946crate::impl_storage_item!(Sampler);
2947crate::impl_trackable!(Sampler);
2948
2949impl WebGpuError for CreateSamplerError {
2950 fn webgpu_error_type(&self) -> ErrorType {
2951 match self {
2952 Self::Device(e) => e.webgpu_error_type(),
2953 Self::MissingFeatures(e) => e.webgpu_error_type(),
2954
2955 Self::InvalidLodMinClamp(_)
2956 | Self::InvalidLodMaxClamp { .. }
2957 | Self::InvalidAnisotropy(_)
2958 | Self::InvalidFilterModeWithAnisotropy { .. }
2959 | Self::InvalidMipmapFilterModeWithAnisotropy { .. } => ErrorType::Validation,
2960 }
2961 }
2962}
2963
2964#[derive(Clone, Debug, Error)]
2965#[non_exhaustive]
2966pub enum CreateQuerySetError {
2967 #[error(transparent)]
2968 Device(#[from] DeviceError),
2969 #[error("QuerySets cannot be made with zero queries")]
2970 ZeroCount,
2971 #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
2972 TooManyQueries { count: u32, maximum: u32 },
2973 #[error(transparent)]
2974 MissingFeatures(#[from] MissingFeatures),
2975}
2976
2977impl WebGpuError for CreateQuerySetError {
2978 fn webgpu_error_type(&self) -> ErrorType {
2979 match self {
2980 Self::Device(e) => e.webgpu_error_type(),
2981 Self::MissingFeatures(e) => e.webgpu_error_type(),
2982
2983 Self::TooManyQueries { .. } | Self::ZeroCount => ErrorType::Validation,
2984 }
2985 }
2986}
2987
2988pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
2989
2990#[derive(Debug)]
2991pub(crate) struct QuerySetState {
2992 pub(crate) raw: Snatchable<Box<dyn hal::DynQuerySet>>,
2993}
2994
2995#[derive(Debug)]
2996pub struct QuerySet {
2997 pub(crate) state: ResourceState<QuerySetState>,
2998 pub(crate) device: Arc<Device>,
2999 pub(crate) label: String,
3001 pub(crate) tracking_data: TrackingData,
3002 pub(crate) desc: wgt::QuerySetDescriptor<()>,
3003 pub(crate) initialized_slots: Mutex<bit_vec::BitVec>,
3004}
3005
3006impl RawResourceAccess for QuerySet {
3007 type DynResource = dyn hal::DynQuerySet;
3008
3009 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
3010 self.state().ok()?.raw.get(guard).map(|b| b.as_ref())
3011 }
3012}
3013
3014impl QuerySet {
3015 pub(crate) fn state(&self) -> Result<&QuerySetState, InvalidResourceError> {
3016 match &self.state {
3017 ResourceState::Valid(state) => Ok(state),
3018 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3019 }
3020 }
3021
3022 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
3023 self.state().map(|_| ())
3024 }
3025
3026 pub fn invalid(device: Arc<Device>, desc: &QuerySetDescriptor) -> Arc<Self> {
3027 Arc::new(QuerySet {
3028 state: ResourceState::Invalid,
3029 label: desc.label.to_string(),
3030 tracking_data: TrackingData::new(device.tracker_indices.query_sets.clone()),
3031 desc: desc.clone().map_label(|_| ()),
3032 initialized_slots: Mutex::new(
3033 rank::QUERY_SET_INITIALIZED_SLOTS,
3034 bit_vec::BitVec::new(),
3035 ),
3036 device,
3037 })
3038 }
3039
3040 pub fn destroy(self: &Arc<Self>) {
3041 let device = &self.device;
3042
3043 profiling::scope!("QuerySet::destroy");
3044 api_log!("QuerySet::destroy {:?}", Arc::as_ptr(self));
3045
3046 #[cfg(feature = "trace")]
3047 if let Some(trace) = device.trace.lock().as_mut() {
3048 use crate::device::trace::IntoTrace as _;
3049
3050 trace.add(trace::Action::DestroyQuerySet(self.to_trace()));
3051 };
3052
3053 let ResourceState::Valid(state) = &self.state else {
3054 return;
3055 };
3056
3057 let temp = {
3058 let mut snatch_guard = self.device.snatchable_lock.write();
3059
3060 let raw = match state.raw.snatch(&mut snatch_guard) {
3061 Some(raw) => raw,
3062 None => {
3063 return;
3065 }
3066 };
3067
3068 drop(snatch_guard);
3069
3070 queue::TempResource::DestroyedQuerySet(DestroyedQuerySet {
3071 raw: ManuallyDrop::new(raw),
3072 device: Arc::clone(&self.device),
3073 label: self.label().to_owned(),
3074 })
3075 };
3076
3077 let Some(queue) = device.get_queue() else {
3078 return;
3079 };
3080
3081 let mut life_lock = queue.lock_life();
3082 let last_submit_index = life_lock.get_query_set_latest_submission_index(self);
3083 if let Some(last_submit_index) = last_submit_index {
3084 life_lock.schedule_resource_destruction(temp, last_submit_index);
3085 }
3086 }
3087}
3088
3089impl Drop for QuerySet {
3090 #[allow(trivial_casts)]
3091 fn drop(&mut self) {
3092 profiling::scope!("QuerySet::drop");
3093 api_log!("QuerySet::drop {:?}", self as *const _);
3094 resource_log!("Destroy raw {}", self.error_ident());
3095 #[cfg(feature = "trace")]
3096 if let Some(trace) = self.device.trace.lock().as_mut() {
3097 use crate::device::trace::to_trace;
3098
3099 trace.add(trace::Action::DropQuerySet(unsafe { to_trace(self) }));
3100 }
3101 let ResourceState::Valid(state) = &mut self.state else {
3102 return;
3103 };
3104 if let Some(raw) = state.raw.take() {
3105 unsafe {
3107 self.device.raw().destroy_query_set(raw);
3108 }
3109 }
3110 }
3111}
3112
3113crate::impl_resource_type!(QuerySet);
3114crate::impl_labeled!(QuerySet);
3115crate::impl_parent_device!(QuerySet);
3116crate::impl_storage_item!(QuerySet);
3117crate::impl_trackable!(QuerySet);
3118
3119#[derive(Debug)]
3121pub struct DestroyedQuerySet {
3122 raw: ManuallyDrop<Box<dyn hal::DynQuerySet>>,
3123 device: Arc<Device>,
3124 label: String,
3125}
3126
3127impl DestroyedQuerySet {
3128 pub fn label(&self) -> &dyn fmt::Debug {
3129 &self.label
3130 }
3131}
3132
3133impl Drop for DestroyedQuerySet {
3134 fn drop(&mut self) {
3135 resource_log!("Destroy raw QuerySet (destroyed) {:?}", self.label());
3136 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
3138 unsafe {
3139 hal::DynDevice::destroy_query_set(self.device.raw(), raw);
3140 }
3141 }
3142}
3143
3144pub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;
3145pub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;
3146
3147pub type BlasPrepareCompactResult = Result<(), BlasPrepareCompactError>;
3148
3149#[cfg(send_sync)]
3150pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + Send + 'static>;
3151#[cfg(not(send_sync))]
3152pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + 'static>;
3153
3154pub(crate) struct BlasPendingCompact {
3155 pub(crate) op: Option<BlasCompactCallback>,
3156 pub(crate) _parent_blas: Arc<Blas>,
3158}
3159
3160impl fmt::Debug for BlasPendingCompact {
3161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3162 f.debug_struct("BlasPendingCompact")
3163 .field("op", &())
3164 .field("_parent_blas", &self._parent_blas)
3165 .finish()
3166 }
3167}
3168
3169#[derive(Debug)]
3170pub(crate) enum BlasCompactState {
3171 Compacted,
3173 Waiting(BlasPendingCompact),
3175 Ready { size: wgt::BufferAddress },
3177 Idle,
3179}
3180
3181#[cfg(send_sync)]
3182unsafe impl Send for BlasCompactState {}
3183#[cfg(send_sync)]
3184unsafe impl Sync for BlasCompactState {}
3185
3186#[derive(Debug)]
3187pub(crate) struct BlasState {
3188 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
3189}
3190
3191#[derive(Debug)]
3192pub struct Blas {
3193 pub(crate) state: ResourceState<BlasState>,
3194 pub(crate) device: Arc<Device>,
3195 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
3196 pub(crate) sizes: wgt::BlasGeometrySizeDescriptors,
3197 pub(crate) flags: wgt::AccelerationStructureFlags,
3198 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
3199 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
3200 pub(crate) handle: u64,
3201 pub(crate) label: String,
3203 pub(crate) tracking_data: TrackingData,
3204 pub(crate) compaction_buffer: Option<ManuallyDrop<Box<dyn hal::DynBuffer>>>,
3205 pub(crate) compacted_state: Mutex<BlasCompactState>,
3206}
3207
3208impl Drop for Blas {
3209 #[allow(trivial_casts)]
3210 fn drop(&mut self) {
3211 profiling::scope!("Blas::drop");
3212 api_log!("Blas::drop {:?}", self as *const _);
3213 #[cfg(feature = "trace")]
3214 if let Some(t) = self.device.trace.lock().as_mut() {
3215 use crate::device::trace::{to_trace, Action};
3216 t.add(Action::DropBlas(unsafe { to_trace(self) }));
3217 }
3218 resource_log!("Destroy raw {}", self.error_ident());
3219 if let ResourceState::Valid(state) = &mut self.state {
3221 if let Some(raw) = state.raw.take() {
3222 unsafe {
3223 self.device.raw().destroy_acceleration_structure(raw);
3224 }
3225 }
3226 }
3227 if let Some(mut raw) = self.compaction_buffer.take() {
3228 unsafe {
3229 self.device
3230 .raw()
3231 .destroy_buffer(ManuallyDrop::take(&mut raw))
3232 }
3233 }
3234 }
3235}
3236
3237impl RawResourceAccess for Blas {
3238 type DynResource = dyn hal::DynAccelerationStructure;
3239
3240 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
3241 self.state().ok()?.raw.get(guard).map(|it| it.as_ref())
3242 }
3243}
3244
3245impl Blas {
3246 pub(crate) fn state(&self) -> Result<&BlasState, InvalidResourceError> {
3247 match &self.state {
3248 ResourceState::Valid(state) => Ok(state),
3249 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3250 }
3251 }
3252
3253 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
3254 self.state().map(|_| ())
3255 }
3256
3257 pub(crate) fn invalid(device: Arc<Device>, desc: &BlasDescriptor) -> Arc<Self> {
3258 Arc::new(Blas {
3259 state: ResourceState::Invalid,
3260 size_info: hal::AccelerationStructureBuildSizes {
3261 acceleration_structure_size: 0,
3262 update_scratch_size: 0,
3263 build_scratch_size: 0,
3264 },
3265 sizes: wgt::BlasGeometrySizeDescriptors::Triangles {
3266 descriptors: Vec::new(),
3267 },
3268 flags: desc.flags,
3269 update_mode: desc.update_mode,
3270 built_index: RwLock::new(rank::BLAS_BUILT_INDEX, None),
3271 handle: 0,
3272 label: desc.label.to_string(),
3273 tracking_data: TrackingData::new(device.tracker_indices.blas_s.clone()),
3274 device,
3275 compaction_buffer: None,
3276 compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Idle),
3277 })
3278 }
3279
3280 pub fn handle(&self) -> Option<u64> {
3281 Some(self.handle)
3282 }
3283
3284 pub fn ready_for_compaction(self: &Arc<Self>) -> Result<bool, InvalidResourceError> {
3285 profiling::scope!("Blas::prepare_compact_async");
3286 api_log!("Blas::prepare_compact_async {:?}", Arc::as_ptr(self));
3287
3288 self.check_is_valid()?;
3289 let state = self.compacted_state.lock();
3290 Ok(matches!(*state, BlasCompactState::Ready { .. }))
3291 }
3292
3293 pub fn prepare_compact_async(
3294 self: &Arc<Self>,
3295 callback: Option<BlasCompactCallback>,
3296 ) -> Result<SubmissionIndex, BlasPrepareCompactError> {
3297 profiling::scope!("Blas::prepare_compact_async");
3298 api_log!("Blas::prepare_compact_async {:?}", Arc::as_ptr(self));
3299
3300 let compact_result = self.prepare_compact_async_inner(callback);
3301
3302 match compact_result {
3303 Ok(submission_index) => Ok(submission_index),
3304 Err((mut callback, err)) => {
3305 if let Some(callback) = callback.take() {
3306 callback(Err(err.clone()));
3307 }
3308 Err(err)
3309 }
3310 }
3311 }
3312
3313 fn prepare_compact_async_inner(
3314 self: &Arc<Self>,
3315 op: Option<BlasCompactCallback>,
3316 ) -> Result<SubmissionIndex, (Option<BlasCompactCallback>, BlasPrepareCompactError)> {
3317 let device = &self.device;
3318 if let Err(e) = device.check_is_valid() {
3319 return Err((op, e.into()));
3320 }
3321
3322 if let Err(e) = self.check_is_valid() {
3323 return Err((op, e.into()));
3324 }
3325
3326 if self.built_index.read().is_none() {
3327 return Err((op, BlasPrepareCompactError::NotBuilt));
3328 }
3329
3330 if !self
3331 .flags
3332 .contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION)
3333 {
3334 return Err((op, BlasPrepareCompactError::CompactionUnsupported));
3335 }
3336
3337 let mut state = self.compacted_state.lock();
3338 *state = match *state {
3339 BlasCompactState::Compacted => {
3340 return Err((op, BlasPrepareCompactError::DoubleCompaction))
3341 }
3342 BlasCompactState::Waiting(_) => {
3343 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
3344 }
3345 BlasCompactState::Ready { .. } => {
3346 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
3347 }
3348 BlasCompactState::Idle => BlasCompactState::Waiting(BlasPendingCompact {
3349 op,
3350 _parent_blas: self.clone(),
3351 }),
3352 };
3353
3354 let submit_index = if let Some(queue) = device.get_queue() {
3355 queue.lock_life().prepare_compact(self).unwrap_or(0) } else {
3357 let (mut callback, status) = self.read_back_compact_size().unwrap();
3359 if let Some(callback) = callback.take() {
3360 callback(status);
3361 }
3362 0
3363 };
3364
3365 Ok(submit_index)
3366 }
3367
3368 #[must_use]
3370 pub(crate) fn read_back_compact_size(&self) -> Option<BlasCompactReadyPendingClosure> {
3371 let mut state = self.compacted_state.lock();
3372 let pending_compact = match mem::replace(&mut *state, BlasCompactState::Idle) {
3373 BlasCompactState::Waiting(pending_mapping) => pending_mapping,
3374 BlasCompactState::Idle => return None,
3376 BlasCompactState::Ready { .. } => {
3377 unreachable!("This should be validated out by `prepare_for_compaction`")
3378 }
3379 _ => panic!("No pending mapping."),
3380 };
3381 let status = {
3382 let compaction_buffer = self.compaction_buffer.as_ref().unwrap().as_ref();
3383 unsafe {
3384 let map_res = self.device.raw().map_buffer(
3385 compaction_buffer,
3386 0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress,
3387 );
3388 match map_res {
3389 Ok(mapping) => {
3390 if !mapping.is_coherent {
3391 #[expect(clippy::single_range_in_vec_init, reason = "intentional")]
3392 self.device.raw().invalidate_mapped_ranges(
3393 compaction_buffer,
3394 &[0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress],
3395 );
3396 }
3397 let size = core::ptr::read_unaligned(
3398 mapping.ptr.as_ptr().cast::<wgt::BufferAddress>(),
3399 );
3400 self.device.raw().unmap_buffer(compaction_buffer);
3401 if self.size_info.acceleration_structure_size != 0 {
3402 debug_assert_ne!(size, 0);
3403 }
3404 *state = BlasCompactState::Ready { size };
3405 Ok(())
3406 }
3407 Err(err) => Err(BlasPrepareCompactError::from(DeviceError::from_hal(err))),
3408 }
3409 }
3410 };
3411 Some((pending_compact.op, status))
3412 }
3413}
3414
3415crate::impl_resource_type!(Blas);
3416crate::impl_labeled!(Blas);
3417crate::impl_parent_device!(Blas);
3418crate::impl_storage_item!(Blas);
3419crate::impl_trackable!(Blas);
3420
3421#[derive(Debug)]
3422pub(crate) struct TlasState {
3423 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
3424 pub(crate) instance_buffer: Box<dyn hal::DynBuffer>,
3425}
3426
3427#[derive(Debug)]
3428pub struct Tlas {
3429 pub(crate) state: ResourceState<TlasState>,
3430 pub(crate) device: Arc<Device>,
3431 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
3432 pub(crate) max_instance_count: u32,
3433 pub(crate) flags: wgt::AccelerationStructureFlags,
3434 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
3435 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
3436 pub(crate) dependencies: RwLock<Vec<Arc<Blas>>>,
3437 pub(crate) label: String,
3439 pub(crate) tracking_data: TrackingData,
3440}
3441
3442impl Drop for Tlas {
3443 #[allow(trivial_casts)]
3444 fn drop(&mut self) {
3445 profiling::scope!("Tlas::drop");
3446 api_log!("Tlas::drop {:?}", self as *const _);
3447
3448 #[cfg(feature = "trace")]
3449 if let Some(t) = self.device.trace.lock().as_mut() {
3450 use crate::device::trace::{to_trace, Action};
3451 t.add(Action::DropTlas(unsafe { to_trace(self) }));
3452 }
3453
3454 resource_log!("Destroy raw {}", self.error_ident());
3455 let ResourceState::Valid(mut state) = mem::replace(&mut self.state, ResourceState::Invalid)
3456 else {
3457 return;
3458 };
3459 if let Some(structure) = state.raw.take() {
3460 unsafe { self.device.raw().destroy_acceleration_structure(structure) };
3461 }
3462 unsafe { self.device.raw().destroy_buffer(state.instance_buffer) };
3463 }
3464}
3465
3466impl Tlas {
3467 pub(crate) fn state(&self) -> Result<&TlasState, InvalidResourceError> {
3468 match &self.state {
3469 ResourceState::Valid(state) => Ok(state),
3470 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3471 }
3472 }
3473
3474 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
3475 self.state().map(|_| ())
3476 }
3477
3478 pub(crate) fn invalid(device: Arc<Device>, desc: &TlasDescriptor) -> Arc<Self> {
3479 Arc::new(Self {
3480 state: ResourceState::Invalid,
3481 label: desc.label.to_string(),
3482 tracking_data: TrackingData::new(device.tracker_indices.tlas_s.clone()),
3483 size_info: hal::AccelerationStructureBuildSizes {
3484 acceleration_structure_size: 0,
3485 update_scratch_size: 0,
3486 build_scratch_size: 0,
3487 },
3488 max_instance_count: desc.max_instances,
3489 flags: desc.flags,
3490 update_mode: desc.update_mode,
3491 built_index: RwLock::new(rank::TLAS_BUILT_INDEX, None),
3492 dependencies: RwLock::new(rank::TLAS_DEPENDENCIES, Vec::new()),
3493 device,
3494 })
3495 }
3496}
3497
3498impl RawResourceAccess for Tlas {
3499 type DynResource = dyn hal::DynAccelerationStructure;
3500
3501 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
3502 self.state().ok()?.raw.get(guard).map(|raw| raw.as_ref())
3503 }
3504}
3505
3506crate::impl_resource_type!(Tlas);
3507crate::impl_labeled!(Tlas);
3508crate::impl_parent_device!(Tlas);
3509crate::impl_storage_item!(Tlas);
3510crate::impl_trackable!(Tlas);