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