1use alloc::{
2 borrow::{Cow, ToOwned as _},
3 boxed::Box,
4 string::String,
5 sync::Arc,
6 vec::Vec,
7};
8use core::{
9 borrow::Borrow,
10 fmt,
11 mem::{self, size_of, ManuallyDrop},
12 num::NonZeroU64,
13 ops::Range,
14 ptr::NonNull,
15};
16use smallvec::SmallVec;
17use thiserror::Error;
18use wgt::{
19 error::{ErrorType, WebGpuError},
20 math::align_to,
21 MapMode, TextureSelector, WriteOnly,
22};
23
24#[cfg(feature = "trace")]
25use crate::device::trace;
26use crate::{
27 api_log,
28 binding_model::{BindGroup, BindingError},
29 device::{
30 queue, resource::DeferredDestroy, BufferMapPendingClosure, Device, DeviceError,
31 DeviceMismatch, MissingDownlevelFlags, MissingFeatures,
32 },
33 hal_label,
34 init_tracker::{BufferInitTracker, TextureInitTracker},
35 lock::{rank, Mutex, MutexGuard, RwLock},
36 ray_tracing::{BlasCompactReadyPendingClosure, BlasPrepareCompactError},
37 resource_log,
38 snatch::{SnatchGuard, Snatchable},
39 timestamp_normalization::TimestampNormalizationBindGroup,
40 track::{SharedTrackerIndexAllocator, TrackerIndex},
41 weak_vec::WeakVec,
42 Label, LabelHelpers, SubmissionIndex,
43};
44
45#[derive(Debug)]
65pub(crate) struct TrackingData {
66 tracker_index: TrackerIndex,
67 tracker_indices: Arc<SharedTrackerIndexAllocator>,
68}
69
70impl Drop for TrackingData {
71 fn drop(&mut self) {
72 self.tracker_indices.free(self.tracker_index);
73 }
74}
75
76impl TrackingData {
77 pub(crate) fn new(tracker_indices: Arc<SharedTrackerIndexAllocator>) -> Self {
78 Self {
79 tracker_index: tracker_indices.alloc(),
80 tracker_indices,
81 }
82 }
83
84 pub(crate) fn tracker_index(&self) -> TrackerIndex {
85 self.tracker_index
86 }
87}
88
89#[derive(Clone, Debug)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91pub struct ResourceErrorIdent {
92 r#type: Cow<'static, str>,
93 label: String,
94}
95
96impl fmt::Display for ResourceErrorIdent {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
98 write!(f, "{} with '{}' label", self.r#type, self.label)
99 }
100}
101
102#[derive(Debug)]
103pub enum ResourceState<T> {
104 Valid(T),
105 Invalid,
106}
107
108impl<T> ResourceState<T> {
109 pub fn as_ref(&self) -> ResourceState<&T> {
110 match self {
111 ResourceState::Valid(v) => ResourceState::Valid(v),
112 ResourceState::Invalid => ResourceState::Invalid,
113 }
114 }
115
116 pub fn valid(self) -> Option<T> {
117 match self {
118 ResourceState::Valid(v) => Some(v),
119 ResourceState::Invalid => None,
120 }
121 }
122}
123
124#[derive(thiserror::Error, Debug)]
125pub enum InvalidOrDestroyedResourceError {
126 #[error(transparent)]
127 InvalidResource(#[from] InvalidResourceError),
128 #[error(transparent)]
129 DestroyedResource(#[from] DestroyedResourceError),
130}
131
132pub trait ParentDevice: Labeled {
133 fn device(&self) -> &Arc<Device>;
134
135 fn is_equal(self: &Arc<Self>, other: &Arc<Self>) -> bool {
136 Arc::ptr_eq(self, other)
137 }
138
139 fn same_device_as<O: ParentDevice>(&self, other: &O) -> Result<(), DeviceError> {
140 if Arc::ptr_eq(self.device(), other.device()) {
141 Ok(())
142 } else {
143 Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
144 res: self.error_ident(),
145 res_device: self.device().error_ident(),
146 target: Some(other.error_ident()),
147 target_device: other.device().error_ident(),
148 })))
149 }
150 }
151
152 fn same_device(&self, device: &Device) -> Result<(), DeviceError> {
153 if core::ptr::eq(&**self.device(), device) {
154 Ok(())
155 } else {
156 Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
157 res: self.error_ident(),
158 res_device: self.device().error_ident(),
159 target: None,
160 target_device: device.error_ident(),
161 })))
162 }
163 }
164}
165
166#[macro_export]
167macro_rules! impl_parent_device {
168 ($ty:ident) => {
169 impl $crate::resource::ParentDevice for $ty {
170 fn device(&self) -> &Arc<Device> {
171 &self.device
172 }
173 }
174 };
175}
176
177pub trait RawResourceAccess: ParentDevice {
179 type DynResource: hal::DynResource + ?Sized;
180
181 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource>;
186
187 fn try_raw<'a>(
192 &'a self,
193 guard: &'a SnatchGuard,
194 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
195 self.raw(guard)
196 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
197 }
198}
199
200pub trait ResourceType {
201 const TYPE: &'static str;
202}
203
204#[macro_export]
205macro_rules! impl_resource_type {
206 ($ty:ident) => {
207 impl $crate::resource::ResourceType for $ty {
208 const TYPE: &'static str = stringify!($ty);
209 }
210 };
211}
212
213pub trait Labeled: ResourceType {
214 fn label(&self) -> &str;
220
221 fn error_ident(&self) -> ResourceErrorIdent {
222 ResourceErrorIdent {
223 r#type: Cow::Borrowed(Self::TYPE),
224 label: self.label().to_owned(),
225 }
226 }
227}
228
229#[macro_export]
230macro_rules! impl_labeled {
231 ($ty:ident) => {
232 impl $crate::resource::Labeled for $ty {
233 fn label(&self) -> &str {
234 &self.label
235 }
236 }
237 };
238}
239
240pub(crate) trait Trackable {
241 fn tracker_index(&self) -> TrackerIndex;
242}
243
244#[macro_export]
245macro_rules! impl_trackable {
246 ($ty:ident) => {
247 impl $crate::resource::Trackable for $ty {
248 fn tracker_index(&self) -> $crate::track::TrackerIndex {
249 self.tracking_data.tracker_index()
250 }
251 }
252 };
253}
254
255#[derive(Debug)]
256pub(crate) enum BufferMapState {
257 Init { staging_buffer: StagingBuffer },
259 Waiting(BufferPendingMapping),
261 Active {
263 mapping: hal::BufferMapping,
264 range: hal::MemoryRange,
265 host: MapMode,
266 },
267 Idle,
269}
270
271#[cfg(send_sync)]
272unsafe impl Send for BufferMapState {}
273#[cfg(send_sync)]
274unsafe impl Sync for BufferMapState {}
275
276#[derive(Debug)]
280pub struct BufferMapping {
281 ptr: NonNull<u8>,
282 len: u64,
283 mode: MapMode,
284 _guard: wgpu_sync::RwLockReadGuard<'static, BufferMapState>,
286 _lock: Arc<RwLock<BufferMapState>>,
287}
288
289impl BufferMapping {
290 fn new(ptr: NonNull<u8>, len: u64, mode: MapMode, lock: Arc<RwLock<BufferMapState>>) -> Self {
291 let guard = unsafe { lock.underlying() }.read();
294 Self {
295 ptr,
296 len,
297 mode,
298 _guard: unsafe {
301 mem::transmute::<
302 wgpu_sync::RwLockReadGuard<'_, BufferMapState>,
303 wgpu_sync::RwLockReadGuard<'static, BufferMapState>,
304 >(guard)
305 },
306 _lock: lock,
307 }
308 }
309
310 pub fn ptr(&self) -> NonNull<u8> {
319 self.ptr
320 }
321
322 pub fn mode(&self) -> MapMode {
323 self.mode
324 }
325
326 #[expect(clippy::len_without_is_empty)]
327 pub fn len(&self) -> u64 {
328 self.len
329 }
330
331 pub fn read_into_vec(&self, offset: usize) -> Vec<u8> {
334 assert!(self.mode == MapMode::Read);
335 assert!(offset <= self.len as usize);
336 let mut vec = alloc::vec![0u8; self.len as usize - offset];
337 self.read(&mut vec, offset);
338 vec
339 }
340
341 pub fn read(&self, dst: &mut [u8], offset: usize) {
344 assert!(self.mode == MapMode::Read);
345 assert!(offset <= self.len as usize);
346 let size = self.len - offset as u64;
347 assert!(dst.len() <= size as usize);
348 unsafe {
349 core::ptr::copy_nonoverlapping(
350 self.ptr.as_ptr().add(offset),
351 dst.as_mut_ptr(),
352 dst.len(),
353 )
354 };
355 }
356
357 pub fn write_slice(&'_ self) -> WriteOnly<'_, [u8]> {
360 assert!(self.mode == MapMode::Write);
361 unsafe { WriteOnly::new(NonNull::slice_from_raw_parts(self.ptr, self.len as usize)) }
362 }
363}
364
365#[cfg(send_sync)]
366pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + Send + 'static>;
367#[cfg(not(send_sync))]
368pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + 'static>;
369
370pub struct BufferMapOperation {
371 pub mode: MapMode,
372 pub callback: Option<BufferMapCallback>,
373}
374
375impl fmt::Debug for BufferMapOperation {
376 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377 f.debug_struct("BufferMapOperation")
378 .field("mode", &self.mode)
379 .field("callback", &self.callback.as_ref().map(|_| "?"))
380 .finish()
381 }
382}
383
384#[derive(Clone, Debug, Error)]
385#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
386#[non_exhaustive]
387pub enum BufferAccessError {
388 #[error(transparent)]
389 Device(#[from] DeviceError),
390 #[error("Buffer map failed")]
391 Failed,
392 #[error(transparent)]
393 DestroyedResource(#[from] DestroyedResourceError),
394 #[error("Buffer is already mapped")]
395 AlreadyMapped,
396 #[error("Buffer map is pending")]
397 MapAlreadyPending,
398 #[error(transparent)]
399 MissingBufferUsage(#[from] MissingBufferUsageError),
400 #[error("Buffer is not mapped")]
401 NotMapped,
402 #[error(
403 "Buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`"
404 )]
405 UnalignedRange,
406 #[error("Buffer offset invalid: offset {offset} must be multiple of 8")]
407 UnalignedOffset { offset: wgt::BufferAddress },
408 #[error("Buffer range size invalid: range_size {range_size} must be multiple of 4")]
409 UnalignedRangeSize { range_size: wgt::BufferAddress },
410 #[error("Buffer access out of bounds: index {index} would underrun the buffer (limit: {min})")]
411 OutOfBoundsStartOffsetUnderrun {
412 index: wgt::BufferAddress,
413 min: wgt::BufferAddress,
414 },
415 #[error(
416 "Buffer access out of bounds: start offset {index} would overrun the buffer (limit: {max})"
417 )]
418 OutOfBoundsStartOffsetOverrun {
419 index: wgt::BufferAddress,
420 max: wgt::BufferAddress,
421 },
422 #[error(
423 "Buffer access out of bounds: start offset {index} + size {size} would overrun the buffer (limit: {max})"
424 )]
425 OutOfBoundsEndOffsetOverrun {
426 index: wgt::BufferAddress,
427 size: wgt::BufferAddress,
428 max: wgt::BufferAddress,
429 },
430 #[error("Buffer map aborted")]
431 MapAborted,
432 #[error(transparent)]
433 InvalidResource(#[from] InvalidResourceError),
434 #[error("Map start offset ({offset}) is out-of-bounds for buffer of size {buffer_size}")]
435 MapStartOffsetOverrun {
436 offset: wgt::BufferAddress,
437 buffer_size: wgt::BufferAddress,
438 },
439 #[error(
440 "Map end offset (start at {} + size of {}) is out-of-bounds for buffer of size {}",
441 offset,
442 size,
443 buffer_size
444 )]
445 MapEndOffsetOverrun {
446 offset: wgt::BufferAddress,
447 size: wgt::BufferAddress,
448 buffer_size: wgt::BufferAddress,
449 },
450}
451
452impl WebGpuError for BufferAccessError {
453 fn webgpu_error_type(&self) -> ErrorType {
454 match self {
455 Self::Device(e) => e.webgpu_error_type(),
456 Self::InvalidResource(e) => e.webgpu_error_type(),
457 Self::DestroyedResource(e) => e.webgpu_error_type(),
458
459 Self::Failed
460 | Self::AlreadyMapped
461 | Self::MapAlreadyPending
462 | Self::MissingBufferUsage(_)
463 | Self::NotMapped
464 | Self::UnalignedRange
465 | Self::UnalignedOffset { .. }
466 | Self::UnalignedRangeSize { .. }
467 | Self::OutOfBoundsStartOffsetUnderrun { .. }
468 | Self::OutOfBoundsStartOffsetOverrun { .. }
469 | Self::OutOfBoundsEndOffsetOverrun { .. }
470 | Self::MapAborted
471 | Self::MapStartOffsetOverrun { .. }
472 | Self::MapEndOffsetOverrun { .. } => ErrorType::Validation,
473 }
474 }
475}
476
477#[derive(Clone, Debug, Error)]
478#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
479#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
480pub struct MissingBufferUsageError {
481 pub(crate) res: ResourceErrorIdent,
482 pub(crate) actual: wgt::BufferUsages,
483 pub(crate) expected: wgt::BufferUsages,
484}
485
486impl WebGpuError for MissingBufferUsageError {
487 fn webgpu_error_type(&self) -> ErrorType {
488 ErrorType::Validation
489 }
490}
491
492#[derive(Clone, Debug, Error)]
493#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
494pub struct MissingTextureUsageError {
495 pub(crate) res: ResourceErrorIdent,
496 pub(crate) actual: wgt::TextureUsages,
497 pub(crate) expected: wgt::TextureUsages,
498}
499
500impl WebGpuError for MissingTextureUsageError {
501 fn webgpu_error_type(&self) -> ErrorType {
502 ErrorType::Validation
503 }
504}
505
506#[derive(Clone, Debug, Error)]
507#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
508#[error("{0} has been destroyed")]
509pub struct DestroyedResourceError(pub ResourceErrorIdent);
510
511impl WebGpuError for DestroyedResourceError {
512 fn webgpu_error_type(&self) -> ErrorType {
513 ErrorType::Validation
514 }
515}
516
517#[derive(Clone, Debug, Error)]
518#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
519#[error("{0} is invalid")]
520pub struct InvalidResourceError(pub ResourceErrorIdent);
521
522impl WebGpuError for InvalidResourceError {
523 fn webgpu_error_type(&self) -> ErrorType {
524 ErrorType::Validation
525 }
526}
527
528pub type BufferAccessResult = Result<(), BufferAccessError>;
529
530#[derive(Debug)]
531pub(crate) struct BufferPendingMapping {
532 pub(crate) range: Range<wgt::BufferAddress>,
533 pub(crate) op: BufferMapOperation,
534 pub(crate) _parent_buffer: Arc<Buffer>,
536}
537
538pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
539
540#[derive(Debug)]
541pub(crate) struct BufferState {
542 pub(crate) raw: Snatchable<Box<dyn hal::DynBuffer>>,
543}
544
545#[derive(Debug)]
546pub struct Buffer {
547 pub(crate) state: ResourceState<BufferState>,
548 pub(crate) device: Arc<Device>,
549 pub(crate) usage: wgt::BufferUsages,
550 pub(crate) size: wgt::BufferAddress,
551 pub(crate) initialization_status: RwLock<BufferInitTracker>,
552 pub(crate) label: String,
554 pub(crate) tracking_data: TrackingData,
555 pub(crate) map_state: Arc<RwLock<BufferMapState>>,
556 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
558 pub(crate) timestamp_normalization_bind_group: Snatchable<TimestampNormalizationBindGroup>,
559 pub(crate) indirect_validation_bind_groups: Snatchable<crate::indirect_validation::BindGroups>,
560}
561
562impl Drop for Buffer {
563 #[allow(trivial_casts)]
564 fn drop(&mut self) {
565 profiling::scope!("Buffer::drop");
566 api_log!("Buffer::drop {:?}", self as *const _);
567 #[cfg(feature = "trace")]
568 if let Some(t) = self.device.trace.lock().as_mut() {
569 t.add(trace::Action::DropBuffer(unsafe { trace::to_trace(self) }));
570 }
571
572 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
573 raw.dispose(self.device.raw());
574 }
575
576 if let Some(raw) = self.indirect_validation_bind_groups.take() {
577 raw.dispose(self.device.raw());
578 }
579
580 let map_state = mem::replace(&mut *self.map_state.write(), BufferMapState::Idle);
581 let active_map = match map_state {
582 BufferMapState::Init { staging_buffer } => {
583 staging_buffer.dispose();
584 false
585 }
586 BufferMapState::Waiting(buffer_pending_mapping) => {
587 if buffer_pending_mapping.op.callback.is_some() {
588 let result = Err(BufferAccessError::DestroyedResource(
589 DestroyedResourceError(self.error_ident()),
590 ));
591 self.device
592 .deferred_buffer_map_pending_closures
593 .push((buffer_pending_mapping.op, result));
594 }
595 false
596 }
597 BufferMapState::Active { .. } => true,
598 BufferMapState::Idle => false,
599 };
600
601 let ResourceState::Valid(state) = &mut self.state else {
602 return;
603 };
604
605 if let Some(raw) = state.raw.take() {
606 if active_map {
607 unsafe { self.device.raw().unmap_buffer(raw.as_ref()) }
608 }
609 resource_log!("Destroy raw {}", self.error_ident());
610 unsafe {
611 self.device.raw().destroy_buffer(raw);
612 }
613 }
614 }
615}
616
617impl RawResourceAccess for Buffer {
618 type DynResource = dyn hal::DynBuffer;
619
620 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
621 self.state()
622 .ok()
623 .and_then(|state| state.raw.get(guard).map(|b| b.as_ref()))
624 }
625}
626
627impl Buffer {
628 pub(crate) fn check_destroyed(
629 &self,
630 guard: &SnatchGuard,
631 ) -> Result<(), DestroyedResourceError> {
632 let ResourceState::Valid(state) = &self.state else {
633 return Ok(());
634 };
635 state
636 .raw
637 .get(guard)
638 .map(|_| ())
639 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
640 }
641
642 pub(crate) fn check_usage(
645 &self,
646 expected: wgt::BufferUsages,
647 ) -> Result<(), MissingBufferUsageError> {
648 if self.usage.contains(expected) {
649 Ok(())
650 } else {
651 Err(MissingBufferUsageError {
652 res: self.error_ident(),
653 actual: self.usage,
654 expected,
655 })
656 }
657 }
658
659 pub(crate) fn state(&self) -> Result<&BufferState, InvalidResourceError> {
660 match &self.state {
661 ResourceState::Valid(state) => Ok(state),
662 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
663 }
664 }
665
666 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
667 self.state().map(|_| ())
668 }
669
670 pub fn invalid(device: Arc<Device>, desc: &BufferDescriptor) -> Arc<Self> {
671 Arc::new(Buffer {
672 state: ResourceState::Invalid,
673 usage: desc.usage,
674 size: desc.size,
675 initialization_status: RwLock::new(
676 rank::BUFFER_INITIALIZATION_STATUS,
677 BufferInitTracker::new(0),
678 ),
679 map_state: Arc::new(RwLock::new(rank::BUFFER_MAP_STATE, BufferMapState::Idle)),
680 label: desc.label.to_string(),
681 tracking_data: TrackingData::new(device.tracker_indices.buffers.clone()),
682 bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
683 timestamp_normalization_bind_group: Snatchable::empty(),
684 indirect_validation_bind_groups: Snatchable::empty(),
685 device,
686 })
687 }
688
689 fn resolve_binding_size<S: Copy + Into<wgt::BufferAddress> + TryFrom<wgt::BufferAddress>>(
701 &self,
702 offset: wgt::BufferAddress,
703 binding_size: Option<S>,
704 ) -> Result<S, BindingError> {
705 let buffer_size = self.size;
706
707 match binding_size {
708 Some(binding_size) => match offset.checked_add(binding_size.into()) {
709 Some(end) if end <= buffer_size => Ok(binding_size),
710 _ => Err(BindingError::BindingRangeTooLarge {
711 buffer: self.error_ident(),
712 offset,
713 binding_size: binding_size.into(),
714 buffer_size,
715 }),
716 },
717 None => buffer_size
718 .checked_sub(offset)
719 .and_then(|remaining| S::try_from(remaining).ok())
720 .ok_or_else(|| {
721 if offset <= buffer_size {
722 debug_assert_eq!(offset, buffer_size);
723 BindingError::BindingOffsetEqualsSize {
724 buffer: self.error_ident(),
725 offset,
726 buffer_size,
727 }
728 } else {
729 BindingError::BindingOffsetTooLarge {
730 buffer: self.error_ident(),
731 offset,
732 buffer_size,
733 }
734 }
735 }),
736 }
737 }
738
739 pub fn resolve_vertex_or_index_binding_range(
747 &self,
748 offset: wgt::BufferAddress,
749 size: Option<wgt::BufferAddress>,
750 ) -> Result<Range<wgt::BufferAddress>, BindingError> {
751 let resolved_size = self.resolve_binding_size(offset, size)?;
752 if resolved_size != 0 {
753 Ok(offset..offset + resolved_size)
754 } else {
755 const _: () = {
759 assert!(wgt::VERTEX_ALIGNMENT == wgt::COPY_BUFFER_ALIGNMENT);
760 };
761 let target = align_to(self.size, wgt::VERTEX_ALIGNMENT);
762 Ok(target..target)
763 }
764 }
765
766 pub fn binding<'a>(
778 &'a self,
779 offset: wgt::BufferAddress,
780 binding_size: Option<wgt::BufferSize>,
781 snatch_guard: &'a SnatchGuard,
782 ) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer, wgt::BufferSize>, BindingError> {
783 let buf_raw = self.try_raw(snatch_guard)?;
784 let resolved_size = self.resolve_binding_size(offset, binding_size)?;
785 Ok(hal::BufferBinding::new_unchecked(
788 buf_raw,
789 offset,
790 resolved_size,
791 ))
792 }
793
794 pub fn map_async(
798 self: &Arc<Self>,
799 offset: wgt::BufferAddress,
800 size: Option<wgt::BufferAddress>,
801 op: BufferMapOperation,
802 ) -> Option<SubmissionIndex> {
803 profiling::scope!("Buffer::map_async");
804 api_log!(
805 "Buffer::map_async {:?} offset {offset:?} size {size:?} op: {op:?}",
806 Arc::as_ptr(self)
807 );
808
809 self.try_map_async(offset, size, op)
810 .map_err(|(mut operation, err)| {
811 self.device
812 .handle_error(err.clone(), Some(&self.label), "Buffer::map_async");
813 if let Some(callback) = operation.callback.take() {
814 callback(Err(err));
815 }
816 })
817 .ok()
818 }
819
820 fn try_map_async(
843 self: &Arc<Self>,
844 offset: wgt::BufferAddress,
845 size: Option<wgt::BufferAddress>,
846 op: BufferMapOperation,
847 ) -> Result<SubmissionIndex, (BufferMapOperation, BufferAccessError)> {
848 let range_size = if let Some(size) = size {
849 size
850 } else {
851 self.size.saturating_sub(offset)
852 };
853
854 if let Err(e) = self.check_is_valid() {
855 return Err((op, e.into()));
856 }
857
858 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
859 return Err((op, BufferAccessError::UnalignedOffset { offset }));
860 }
861 if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
862 return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));
863 }
864
865 if offset > self.size {
866 return Err((
867 op,
868 BufferAccessError::MapStartOffsetOverrun {
869 offset,
870 buffer_size: self.size,
871 },
872 ));
873 }
874 if range_size > self.size - offset {
876 return Err((
877 op,
878 BufferAccessError::MapEndOffsetOverrun {
879 offset,
880 size: range_size,
881 buffer_size: self.size,
882 },
883 ));
884 }
885 let end_offset = offset + range_size;
886
887 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT)
888 || !end_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT)
889 {
890 return Err((op, BufferAccessError::UnalignedRange));
891 }
892
893 let (pub_usage, internal_use) = match op.mode {
894 MapMode::Read => (wgt::BufferUsages::MAP_READ, wgt::BufferUses::MAP_READ),
895 MapMode::Write => (wgt::BufferUsages::MAP_WRITE, wgt::BufferUses::MAP_WRITE),
896 };
897
898 if let Err(e) = self.check_usage(pub_usage) {
899 return Err((op, e.into()));
900 }
901
902 let device = &self.device;
903 if let Err(e) = device.check_is_valid() {
904 return Err((op, e.into()));
905 }
906
907 let submit_index = {
908 let snatch_guard = device.snatchable_lock.read();
909 if let Err(e) = self.check_destroyed(&snatch_guard) {
910 return Err((op, e.into()));
911 }
912
913 {
914 let map_state = &mut *self.map_state.write();
915 *map_state = match *map_state {
916 BufferMapState::Init { .. } | BufferMapState::Active { .. } => {
917 return Err((op, BufferAccessError::AlreadyMapped));
918 }
919 BufferMapState::Waiting(_) => {
920 return Err((op, BufferAccessError::MapAlreadyPending));
921 }
922 BufferMapState::Idle => BufferMapState::Waiting(BufferPendingMapping {
923 range: offset..end_offset,
924 op,
925 _parent_buffer: self.clone(),
926 }),
927 };
928 }
929
930 if let Some(queue) = device.get_queue().as_ref() {
931 match queue.flush_writes_for_buffer(self, snatch_guard) {
932 Err(err) => {
933 let state =
934 mem::replace(&mut *self.map_state.write(), BufferMapState::Idle);
935 let BufferMapState::Waiting(BufferPendingMapping { op, .. }) = state else {
936 unreachable!();
937 };
938 return Err((op, err));
939 }
940 Ok(()) => {
941 Some(queue.lock_life().map(self).unwrap_or(0))
950 }
951 }
952 } else {
953 None
954 }
955 };
956
957 device
965 .trackers
966 .lock()
967 .buffers
968 .set_single(self, internal_use);
969
970 if let Some(index) = submit_index {
971 Ok(index)
972 } else {
973 let (mut operation, status) = self.map(&device.snatchable_lock.read()).unwrap();
976 if let Some(callback) = operation.callback.take() {
977 callback(status);
978 }
979 Ok(0)
980 }
981 }
982
983 pub fn get_mapped_range(
988 self: &Arc<Self>,
989 offset: wgt::BufferAddress,
990 size: Option<wgt::BufferAddress>,
991 ) -> Result<BufferMapping, BufferAccessError> {
992 profiling::scope!("Buffer::get_mapped_range");
993 api_log!(
994 "Buffer::get_mapped_range {:?} offset {offset:?} size {size:?}",
995 Arc::as_ptr(self)
996 );
997
998 self.check_is_valid()?;
999 {
1000 let snatch_guard = self.device.snatchable_lock.read();
1001 self.check_destroyed(&snatch_guard)?;
1002 }
1003
1004 let range_size = if let Some(size) = size {
1005 size
1006 } else {
1007 self.size.saturating_sub(offset)
1008 };
1009
1010 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
1011 return Err(BufferAccessError::UnalignedOffset { offset });
1012 }
1013 if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1014 return Err(BufferAccessError::UnalignedRangeSize { range_size });
1015 }
1016 let map_state = &*self.map_state.read();
1017 match *map_state {
1018 BufferMapState::Init { ref staging_buffer } => {
1019 if offset > self.size {
1020 return Err(BufferAccessError::MapStartOffsetOverrun {
1021 offset,
1022 buffer_size: self.size,
1023 });
1024 }
1025 if range_size > self.size - offset {
1027 return Err(BufferAccessError::MapEndOffsetOverrun {
1028 offset,
1029 size: range_size,
1030 buffer_size: self.size,
1031 });
1032 }
1033 let ptr = unsafe { staging_buffer.ptr() };
1034 let ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().offset(offset as isize)) };
1035 Ok(BufferMapping::new(
1036 ptr,
1037 range_size,
1038 MapMode::Write,
1039 self.map_state.clone(),
1040 ))
1041 }
1042 BufferMapState::Active {
1043 ref mapping,
1044 ref range,
1045 ref host,
1046 } => {
1047 if offset > range.end {
1048 return Err(BufferAccessError::OutOfBoundsStartOffsetOverrun {
1049 index: offset,
1050 max: range.end,
1051 });
1052 }
1053 if offset < range.start {
1054 return Err(BufferAccessError::OutOfBoundsStartOffsetUnderrun {
1055 index: offset,
1056 min: range.start,
1057 });
1058 }
1059 if range_size > range.end - offset {
1060 return Err(BufferAccessError::OutOfBoundsEndOffsetOverrun {
1061 index: offset,
1062 size: range_size,
1063 max: range.end,
1064 });
1065 }
1066 let relative_offset = (offset - range.start) as isize;
1069 unsafe {
1070 Ok(BufferMapping::new(
1071 NonNull::new_unchecked(mapping.ptr.as_ptr().offset(relative_offset)),
1072 range_size,
1073 *host,
1074 self.map_state.clone(),
1075 ))
1076 }
1077 }
1078 BufferMapState::Idle | BufferMapState::Waiting(_) => Err(BufferAccessError::NotMapped),
1079 }
1080 }
1081 #[must_use]
1084 pub(crate) fn map(&self, snatch_guard: &SnatchGuard) -> Option<BufferMapPendingClosure> {
1085 let mut map_state = self.map_state.write();
1094 let pending_mapping = match mem::replace(&mut *map_state, BufferMapState::Idle) {
1095 BufferMapState::Waiting(pending_mapping) => pending_mapping,
1096 BufferMapState::Idle => return None,
1098 mapping @ BufferMapState::Active { .. } => {
1101 *map_state = mapping;
1102 return None;
1103 }
1104 _ => panic!("No pending mapping."),
1105 };
1106 let status = if let Err(error) = self.device.check_is_valid() {
1107 Err(error.into())
1108 } else if pending_mapping.range.start != pending_mapping.range.end {
1109 let host = pending_mapping.op.mode;
1110 let size = pending_mapping.range.end - pending_mapping.range.start;
1111 match crate::device::map_buffer(
1112 self,
1113 pending_mapping.range.start,
1114 size,
1115 host,
1116 snatch_guard,
1117 ) {
1118 Ok(mapping) => {
1119 *map_state = BufferMapState::Active {
1120 mapping,
1121 range: pending_mapping.range.clone(),
1122 host,
1123 };
1124 Ok(())
1125 }
1126 Err(e) => Err(e),
1127 }
1128 } else {
1129 *map_state = BufferMapState::Active {
1130 mapping: hal::BufferMapping {
1131 ptr: NonNull::dangling(),
1132 is_coherent: true,
1133 },
1134 range: pending_mapping.range,
1135 host: pending_mapping.op.mode,
1136 };
1137 Ok(())
1138 };
1139 Some((pending_mapping.op, status))
1140 }
1141
1142 pub fn unmap(self: &Arc<Self>) {
1144 profiling::scope!("unmap", "Buffer");
1145 api_log!("Buffer::unmap {:?}", Arc::as_ptr(self));
1146 if let Some((mut operation, status)) = self.unmap_inner() {
1147 if let Some(callback) = operation.callback.take() {
1148 callback(status);
1149 }
1150 }
1151 }
1152
1153 fn unmap_inner(self: &Arc<Self>) -> Option<BufferMapPendingClosure> {
1162 let device = &self.device;
1163 let snatch_guard = device.snatchable_lock.read();
1164 let raw_buf = self.try_raw(&snatch_guard).ok()?;
1168 let map_state = mem::replace(&mut *self.map_state.write(), BufferMapState::Idle);
1169 match map_state {
1170 BufferMapState::Init { staging_buffer } => {
1171 if !self.device.is_valid() {
1172 staging_buffer.dispose();
1173 return None;
1174 }
1175 #[cfg(feature = "trace")]
1176 if let Some(ref mut trace) = *device.trace.lock() {
1177 use crate::device::trace::{DataKind, IntoTrace};
1178
1179 let data = trace.make_binary(DataKind::Bin, staging_buffer.get_data());
1180 trace.add(trace::Action::WriteBuffer {
1181 id: self.to_trace(),
1182 data,
1183 offset: 0,
1185 size: self.size,
1186 queued: true,
1187 });
1188 }
1189
1190 let staging_buffer = staging_buffer.flush();
1191
1192 if let Some(queue) = device.get_queue() {
1193 let region = Some(hal::BufferCopy {
1196 src_offset: 0,
1197 dst_offset: 0,
1198 size: staging_buffer.size,
1199 });
1200 let transition_src = hal::BufferBarrier {
1201 buffer: staging_buffer.raw(),
1202 usage: hal::StateTransition {
1203 from: wgt::BufferUses::MAP_WRITE,
1204 to: wgt::BufferUses::COPY_SRC,
1205 },
1206 };
1207 let transition_dst = hal::BufferBarrier::<dyn hal::DynBuffer> {
1208 buffer: raw_buf,
1209 usage: hal::StateTransition {
1210 from: wgt::BufferUses::empty(),
1211 to: wgt::BufferUses::COPY_DST,
1212 },
1213 };
1214 let mut pending_writes = queue.pending_writes.lock();
1215 let encoder = pending_writes.activate();
1216 unsafe {
1217 encoder.transition_buffers(&[transition_src, transition_dst]);
1218 encoder.copy_buffer_to_buffer(
1221 staging_buffer.raw(),
1222 raw_buf,
1223 region.as_slice(),
1224 );
1225 }
1226 pending_writes.consume(staging_buffer);
1227 pending_writes.insert_buffer(self);
1228 }
1229 None
1230 }
1231 BufferMapState::Idle => None,
1232 BufferMapState::Waiting(pending) => {
1233 Some((pending.op, Err(BufferAccessError::MapAborted)))
1234 }
1235 BufferMapState::Active {
1236 mapping,
1237 range,
1238 host,
1239 } => {
1240 self.device.check_is_valid().ok()?;
1241 if host == MapMode::Write {
1242 #[cfg(feature = "trace")]
1243 if let Some(ref mut trace) = *device.trace.lock() {
1244 use crate::device::trace::{DataKind, IntoTrace};
1245
1246 let size = range.end - range.start;
1247 let data = trace.make_binary(DataKind::Bin, unsafe {
1248 core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize)
1249 });
1250 trace.add(trace::Action::WriteBuffer {
1251 id: self.to_trace(),
1252 data,
1253 offset: range.start,
1254 size,
1255 queued: false,
1256 });
1257 }
1258 if !mapping.is_coherent {
1259 unsafe { device.raw().flush_mapped_ranges(raw_buf, &[range]) };
1260 }
1261 }
1262 unsafe { device.raw().unmap_buffer(raw_buf) };
1263 None
1264 }
1265 }
1266 }
1267
1268 pub fn destroy(self: &Arc<Self>) {
1269 profiling::scope!("Buffer::destroy");
1270 api_log!("Buffer::destroy {:?}", Arc::as_ptr(self));
1271
1272 let device = &self.device;
1273
1274 #[cfg(feature = "trace")]
1275 if let Some(trace) = device.trace.lock().as_mut() {
1276 use crate::device::trace::IntoTrace;
1277 trace.add(trace::Action::DestroyBuffer(self.to_trace()));
1278 }
1279
1280 let ResourceState::Valid(state) = &self.state else {
1281 return;
1282 };
1283
1284 self.unmap();
1285
1286 let temp = {
1287 let mut snatch_guard = device.snatchable_lock.write();
1288
1289 let raw = match state.raw.snatch(&mut snatch_guard) {
1290 Some(raw) => raw,
1291 None => {
1292 return;
1294 }
1295 };
1296
1297 let timestamp_normalization_bind_group = self
1298 .timestamp_normalization_bind_group
1299 .snatch(&mut snatch_guard);
1300
1301 let indirect_validation_bind_groups = self
1302 .indirect_validation_bind_groups
1303 .snatch(&mut snatch_guard);
1304
1305 drop(snatch_guard);
1306
1307 let bind_groups = {
1308 let mut guard = self.bind_groups.lock();
1309 mem::take(&mut *guard)
1310 };
1311
1312 queue::TempResource::DestroyedBuffer(DestroyedBuffer {
1313 raw: ManuallyDrop::new(raw),
1314 device: Arc::clone(&self.device),
1315 label: self.label().to_owned(),
1316 bind_groups,
1317 timestamp_normalization_bind_group,
1318 indirect_validation_bind_groups,
1319 })
1320 };
1321
1322 let Some(queue) = device.get_queue() else {
1323 return;
1324 };
1325
1326 {
1327 let mut pending_writes = queue.pending_writes.lock();
1328 if pending_writes.contains_buffer(self) {
1329 pending_writes.consume_temp(temp);
1330 return;
1331 }
1332 }
1333
1334 let mut life_lock = queue.lock_life();
1335 let last_submit_index = life_lock.get_buffer_latest_submission_index(self);
1336 if let Some(last_submit_index) = last_submit_index {
1337 life_lock.schedule_resource_destruction(temp, last_submit_index);
1338 }
1339 }
1340
1341 pub fn size(&self) -> wgt::BufferAddress {
1342 self.size
1343 }
1344
1345 pub fn usage(&self) -> wgt::BufferUsages {
1346 self.usage
1347 }
1348}
1349
1350#[derive(Clone, Debug, Error)]
1351#[non_exhaustive]
1352pub enum CreateBufferError {
1353 #[error(transparent)]
1354 Device(#[from] DeviceError),
1355 #[error("Failed to map buffer while creating: {0}")]
1356 AccessError(#[from] BufferAccessError),
1357 #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
1358 UnalignedSize,
1359 #[error("Invalid usage flags {0:?}")]
1360 InvalidUsage(wgt::BufferUsages),
1361 #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
1362 UsageMismatch(wgt::BufferUsages),
1363 #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
1364 MaxBufferSize { requested: u64, maximum: u64 },
1365 #[error(transparent)]
1366 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1367 #[error(transparent)]
1368 MissingFeatures(#[from] MissingFeatures),
1369 #[error("Failed to create bind group for indirect buffer validation: {0}")]
1370 IndirectValidationBindGroup(DeviceError),
1371 #[error("Error initializing buffer: {0}")]
1372 QueueWrite(#[from] queue::QueueWriteError),
1373}
1374
1375crate::impl_resource_type!(Buffer);
1376crate::impl_labeled!(Buffer);
1377crate::impl_parent_device!(Buffer);
1378crate::impl_storage_item!(Buffer);
1379crate::impl_trackable!(Buffer);
1380
1381impl WebGpuError for CreateBufferError {
1382 fn webgpu_error_type(&self) -> ErrorType {
1383 match self {
1384 Self::Device(e) => e.webgpu_error_type(),
1385 Self::AccessError(e) => e.webgpu_error_type(),
1386 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1387 Self::IndirectValidationBindGroup(e) => e.webgpu_error_type(),
1388 Self::MissingFeatures(e) => e.webgpu_error_type(),
1389 Self::QueueWrite(e) => e.webgpu_error_type(),
1390
1391 Self::UnalignedSize
1392 | Self::InvalidUsage(_)
1393 | Self::UsageMismatch(_)
1394 | Self::MaxBufferSize { .. } => ErrorType::Validation,
1395 }
1396 }
1397}
1398
1399#[derive(Debug)]
1401pub struct DestroyedBuffer {
1402 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1403 device: Arc<Device>,
1404 label: String,
1405 bind_groups: WeakVec<BindGroup>,
1406 timestamp_normalization_bind_group: Option<TimestampNormalizationBindGroup>,
1407 indirect_validation_bind_groups: Option<crate::indirect_validation::BindGroups>,
1408}
1409
1410impl DestroyedBuffer {
1411 pub fn label(&self) -> &dyn fmt::Debug {
1412 &self.label
1413 }
1414}
1415
1416impl Drop for DestroyedBuffer {
1417 fn drop(&mut self) {
1418 let mut deferred = self.device.deferred_destroy.lock();
1419 deferred.push(DeferredDestroy::BindGroups(mem::take(
1420 &mut self.bind_groups,
1421 )));
1422 drop(deferred);
1423
1424 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
1425 raw.dispose(self.device.raw());
1426 }
1427
1428 if let Some(raw) = self.indirect_validation_bind_groups.take() {
1429 raw.dispose(self.device.raw());
1430 }
1431
1432 resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
1433 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1435 unsafe {
1436 hal::DynDevice::destroy_buffer(self.device.raw(), raw);
1437 }
1438 }
1439}
1440
1441#[cfg(send_sync)]
1442unsafe impl Send for StagingBuffer {}
1443#[cfg(send_sync)]
1444unsafe impl Sync for StagingBuffer {}
1445
1446#[derive(Debug)]
1466pub struct StagingBuffer {
1467 raw: Box<dyn hal::DynBuffer>,
1468 device: Arc<Device>,
1469 pub(crate) size: wgt::BufferSize,
1470 is_coherent: bool,
1471 ptr: NonNull<u8>,
1472}
1473
1474impl StagingBuffer {
1475 pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {
1476 profiling::scope!("StagingBuffer::new");
1477 let stage_desc = hal::BufferDescriptor {
1478 label: hal_label(Some("(wgpu internal) Staging"), device.instance_flags),
1479 size: size.get(),
1480 usage: wgt::BufferUses::MAP_WRITE | wgt::BufferUses::COPY_SRC,
1481 memory_flags: hal::MemoryFlags::TRANSIENT,
1482 };
1483
1484 let (raw, _) = unsafe { device.raw().create_buffer(&stage_desc) }
1485 .map_err(|e| device.handle_hal_error(e))?;
1486 let mapping = unsafe { device.raw().map_buffer(raw.as_ref(), 0..size.get()) }
1487 .map_err(|e| device.handle_hal_error(e))?;
1488
1489 let staging_buffer = StagingBuffer {
1490 raw,
1491 device: device.clone(),
1492 size,
1493 is_coherent: mapping.is_coherent,
1494 ptr: mapping.ptr,
1495 };
1496
1497 Ok(staging_buffer)
1498 }
1499
1500 pub unsafe fn ptr(&self) -> NonNull<u8> {
1504 self.ptr
1505 }
1506
1507 pub fn size(&self) -> wgt::BufferSize {
1508 self.size
1509 }
1510
1511 #[cfg(feature = "trace")]
1512 pub(crate) fn get_data(&self) -> &[u8] {
1513 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.size.get() as usize) }
1514 }
1515
1516 pub(crate) fn write_zeros(&mut self) {
1517 unsafe { core::ptr::write_bytes(self.ptr.as_ptr(), 0, self.size.get() as usize) };
1518 }
1519
1520 pub(crate) fn write_exact(&mut self, data: &[u8]) {
1526 assert_eq!(data.len(), self.size.get() as usize);
1527 unsafe {
1530 core::ptr::copy_nonoverlapping(
1531 data.as_ptr(),
1532 self.ptr.as_ptr(),
1533 self.size.get() as usize,
1534 );
1535 }
1536 }
1537
1538 pub(crate) fn write_with_zero_padding(&mut self, data: &[u8]) {
1544 assert!(data.len() <= self.size.get() as usize);
1545 unsafe {
1548 core::ptr::copy_nonoverlapping(data.as_ptr(), self.ptr.as_ptr(), data.len());
1549 core::ptr::write_bytes(
1550 self.ptr.as_ptr().add(data.len()),
1551 0,
1552 self.size.get() as usize - data.len(),
1553 );
1554 }
1555 }
1556
1557 pub(crate) unsafe fn write_with_offset(
1559 &mut self,
1560 data: &[u8],
1561 src_offset: isize,
1562 dst_offset: isize,
1563 size: usize,
1564 ) {
1565 unsafe {
1566 debug_assert!(
1567 (src_offset + size as isize) as usize <= data.len(),
1568 "src_offset + size must be in-bounds: src_offset = {}, size = {}, data.len() = {}",
1569 src_offset,
1570 size,
1571 data.len()
1572 );
1573 core::ptr::copy_nonoverlapping(
1574 data.as_ptr().offset(src_offset),
1575 self.ptr.as_ptr().offset(dst_offset),
1576 size,
1577 );
1578 }
1579 }
1580
1581 pub(crate) fn flush(self) -> FlushedStagingBuffer {
1582 let device = self.device.raw();
1583 if !self.is_coherent {
1584 #[allow(clippy::single_range_in_vec_init)]
1585 unsafe {
1586 device.flush_mapped_ranges(self.raw.as_ref(), &[0..self.size.get()])
1587 };
1588 }
1589 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1590
1591 let StagingBuffer {
1592 raw, device, size, ..
1593 } = self;
1594
1595 FlushedStagingBuffer {
1596 raw: ManuallyDrop::new(raw),
1597 device,
1598 size,
1599 }
1600 }
1601
1602 pub(crate) fn dispose(self) {
1603 let device = self.device.raw();
1604 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1605 unsafe { device.destroy_buffer(self.raw) };
1606 }
1607}
1608
1609crate::impl_resource_type!(StagingBuffer);
1610crate::impl_storage_item!(StagingBuffer);
1611
1612#[derive(Debug)]
1613pub struct FlushedStagingBuffer {
1614 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1615 device: Arc<Device>,
1616 pub(crate) size: wgt::BufferSize,
1617}
1618
1619impl FlushedStagingBuffer {
1620 pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {
1621 self.raw.as_ref()
1622 }
1623}
1624
1625impl Drop for FlushedStagingBuffer {
1626 fn drop(&mut self) {
1627 resource_log!("Destroy raw StagingBuffer");
1628 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1630 unsafe { self.device.raw().destroy_buffer(raw) };
1631 }
1632}
1633
1634pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
1635
1636#[derive(Debug)]
1637pub(crate) enum TextureInner {
1638 Native {
1639 raw: Box<dyn hal::DynTexture>,
1640 },
1641 Surface {
1642 raw: Box<dyn hal::DynSurfaceTexture>,
1643 },
1644}
1645
1646impl TextureInner {
1647 pub(crate) fn raw(&self) -> &dyn hal::DynTexture {
1648 match self {
1649 Self::Native { raw } => raw.as_ref(),
1650 Self::Surface { raw, .. } => raw.as_ref().borrow(),
1651 }
1652 }
1653}
1654
1655#[derive(Debug)]
1656pub enum TextureClearMode {
1657 BufferCopy,
1658 RenderPass {
1660 clear_views: SmallVec<[ManuallyDrop<Box<dyn hal::DynTextureView>>; 1]>,
1661 is_color: bool,
1662 },
1663 Surface {
1664 clear_view: ManuallyDrop<Box<dyn hal::DynTextureView>>,
1665 },
1666 None,
1669}
1670
1671#[derive(Debug)]
1672pub struct TextureState {
1673 pub(crate) inner: Snatchable<TextureInner>,
1674}
1675
1676#[derive(Debug)]
1677pub struct Texture {
1678 pub(crate) state: ResourceState<TextureState>,
1679 pub(crate) device: Arc<Device>,
1680 pub(crate) desc: wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
1681 pub(crate) _hal_usage: wgt::TextureUses,
1682 pub(crate) format_features: wgt::TextureFormatFeatures,
1683 pub(crate) initialization_status: RwLock<TextureInitTracker>,
1684 pub(crate) full_range: TextureSelector,
1685 pub(crate) tracking_data: TrackingData,
1686 pub(crate) clear_mode: RwLock<TextureClearMode>,
1687 pub(crate) views: Mutex<WeakVec<TextureView>>,
1688 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
1690}
1691
1692impl Texture {
1693 pub(crate) fn new(
1694 device: &Arc<Device>,
1695 inner: TextureInner,
1696 hal_usage: wgt::TextureUses,
1697 desc: &TextureDescriptor,
1698 format_features: wgt::TextureFormatFeatures,
1699 clear_mode: TextureClearMode,
1700 init: bool,
1701 ) -> Self {
1702 Texture {
1703 state: ResourceState::Valid(TextureState {
1704 inner: Snatchable::new(inner),
1705 }),
1706 device: device.clone(),
1707 desc: desc.map_label(|label| label.to_string()),
1708 _hal_usage: hal_usage,
1709 format_features,
1710 initialization_status: RwLock::new(
1711 rank::TEXTURE_INITIALIZATION_STATUS,
1712 if init {
1713 TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count())
1714 } else {
1715 TextureInitTracker::new(desc.mip_level_count, 0)
1716 },
1717 ),
1718 full_range: TextureSelector {
1719 mips: 0..desc.mip_level_count,
1720 layers: 0..desc.array_layer_count(),
1721 },
1722 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1723 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
1724 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1725 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1726 }
1727 }
1728
1729 pub fn invalid(device: &Arc<Device>, desc: &TextureDescriptor) -> Arc<Self> {
1730 Arc::new(Texture {
1731 state: ResourceState::Invalid,
1732 device: device.clone(),
1733 desc: desc.map_label(|label| label.to_string()),
1734 _hal_usage: wgt::TextureUses::empty(),
1735 format_features: wgt::TextureFormatFeatures {
1736 allowed_usages: wgt::TextureUsages::empty(),
1737 flags: wgt::TextureFormatFeatureFlags::empty(),
1738 },
1739 initialization_status: RwLock::new(
1740 rank::TEXTURE_INITIALIZATION_STATUS,
1741 TextureInitTracker::new(0, 0),
1742 ),
1743 full_range: TextureSelector {
1744 mips: 0..desc.mip_level_count,
1745 layers: 0..desc.array_layer_count(),
1746 },
1747 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1748 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, TextureClearMode::None),
1749 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1750 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1751 })
1752 }
1753
1754 pub(crate) fn check_usage(
1757 &self,
1758 expected: wgt::TextureUsages,
1759 ) -> Result<(), MissingTextureUsageError> {
1760 if self.desc.usage.contains(expected) {
1761 Ok(())
1762 } else {
1763 Err(MissingTextureUsageError {
1764 res: self.error_ident(),
1765 actual: self.desc.usage,
1766 expected,
1767 })
1768 }
1769 }
1770}
1771
1772impl Drop for Texture {
1773 #[allow(trivial_casts)]
1774 fn drop(&mut self) {
1775 profiling::scope!("Texture::drop");
1776 api_log!("Texture::drop {:?}", self as *const _);
1777
1778 #[cfg(feature = "trace")]
1779 {
1780 let mut t = self.device.trace.lock();
1781 if let Some(t) = t.as_mut() {
1782 use crate::device::trace::to_trace;
1783
1784 t.add(trace::Action::DropTexture(unsafe { to_trace(self) }));
1786 }
1787 }
1788 match *self.clear_mode.write() {
1789 TextureClearMode::Surface {
1790 ref mut clear_view, ..
1791 } => {
1792 let raw = unsafe { ManuallyDrop::take(clear_view) };
1794 unsafe {
1795 self.device.raw().destroy_texture_view(raw);
1796 }
1797 }
1798 TextureClearMode::RenderPass {
1799 ref mut clear_views,
1800 ..
1801 } => {
1802 clear_views.iter_mut().for_each(|clear_view| {
1803 let raw = unsafe { ManuallyDrop::take(clear_view) };
1805 unsafe {
1806 self.device.raw().destroy_texture_view(raw);
1807 }
1808 });
1809 }
1810 _ => {}
1811 };
1812
1813 let ResourceState::Valid(state) = &mut self.state else {
1814 return;
1815 };
1816 if let Some(TextureInner::Native { raw }) = state.inner.take() {
1817 resource_log!("Destroy raw {}", self.error_ident());
1818 unsafe {
1819 self.device.raw().destroy_texture(raw);
1820 }
1821 }
1822 }
1823}
1824
1825impl RawResourceAccess for Texture {
1826 type DynResource = dyn hal::DynTexture;
1827
1828 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1829 self.state
1830 .as_ref()
1831 .valid()
1832 .and_then(|t| t.inner.get(guard).map(|t| t.raw()))
1833 }
1834}
1835
1836impl Texture {
1837 pub(crate) fn state(&self) -> Result<&TextureState, InvalidResourceError> {
1838 match &self.state {
1839 ResourceState::Valid(state) => Ok(state),
1840 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
1841 }
1842 }
1843
1844 pub(crate) fn check_destroyed(
1845 &self,
1846 guard: &SnatchGuard,
1847 ) -> Result<(), DestroyedResourceError> {
1848 let Ok(state) = self.state() else {
1849 return Ok(());
1850 };
1851 state
1852 .inner
1853 .get(guard)
1854 .map(|_| ())
1855 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1856 }
1857
1858 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
1859 self.state().map(|_| ())
1860 }
1861
1862 pub(crate) fn try_inner<'a>(
1863 &'a self,
1864 guard: &'a SnatchGuard,
1865 ) -> Result<&'a TextureInner, InvalidOrDestroyedResourceError> {
1866 self.state()?
1867 .inner
1868 .get(guard)
1869 .ok_or_else(|| DestroyedResourceError(self.error_ident()).into())
1870 }
1871
1872 pub(crate) fn get_clear_view<'a>(
1873 clear_mode: &'a TextureClearMode,
1874 desc: &'a wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
1875 mip_level: u32,
1876 depth_or_layer: u32,
1877 ) -> &'a dyn hal::DynTextureView {
1878 match *clear_mode {
1879 TextureClearMode::BufferCopy => {
1880 panic!("Given texture is cleared with buffer copies, not render passes")
1881 }
1882 TextureClearMode::None => {
1883 panic!("Given texture can't be cleared")
1884 }
1885 TextureClearMode::Surface { ref clear_view, .. } => clear_view.as_ref(),
1886 TextureClearMode::RenderPass {
1887 ref clear_views, ..
1888 } => {
1889 let index = if desc.dimension == wgt::TextureDimension::D3 {
1890 (0..mip_level).fold(0, |acc, mip| {
1891 acc + (desc.size.depth_or_array_layers >> mip).max(1)
1892 })
1893 } else {
1894 mip_level * desc.size.depth_or_array_layers
1895 } + depth_or_layer;
1896 clear_views[index as usize].as_ref()
1897 }
1898 }
1899 }
1900
1901 pub fn destroy(self: &Arc<Self>) {
1902 profiling::scope!("Texture::destroy");
1903 api_log!("Texture::destroy {:?}", Arc::as_ptr(self));
1904
1905 #[cfg(feature = "trace")]
1906 if let Some(trace) = self.device.trace.lock().as_mut() {
1907 use crate::device::trace::IntoTrace as _;
1908
1909 trace.add(trace::Action::DestroyTexture(self.to_trace()));
1910 }
1911
1912 let device = &self.device;
1913
1914 let ResourceState::Valid(state) = &self.state else {
1915 return;
1916 };
1917
1918 let temp = {
1919 let raw = match state.inner.snatch(&mut device.snatchable_lock.write()) {
1920 Some(TextureInner::Native { raw }) => raw,
1921 Some(TextureInner::Surface { .. }) => {
1922 return;
1923 }
1924 None => {
1925 return;
1927 }
1928 };
1929
1930 let views = {
1931 let mut guard = self.views.lock();
1932 mem::take(&mut *guard)
1933 };
1934
1935 let bind_groups = {
1936 let mut guard = self.bind_groups.lock();
1937 mem::take(&mut *guard)
1938 };
1939
1940 queue::TempResource::DestroyedTexture(DestroyedTexture {
1941 raw: ManuallyDrop::new(raw),
1942 views,
1943 clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None),
1944 bind_groups,
1945 device: Arc::clone(&self.device),
1946 label: self.label().to_owned(),
1947 })
1948 };
1949
1950 let Some(queue) = device.get_queue() else {
1951 return;
1952 };
1953
1954 {
1955 let mut pending_writes = queue.pending_writes.lock();
1956 if pending_writes.contains_texture(self) {
1957 pending_writes.consume_temp(temp);
1958 return;
1959 }
1960 }
1961
1962 let mut life_lock = queue.lock_life();
1963 let last_submit_index = life_lock.get_texture_latest_submission_index(self);
1964 if let Some(last_submit_index) = last_submit_index {
1965 life_lock.schedule_resource_destruction(temp, last_submit_index);
1966 }
1967 }
1968
1969 fn create_view_inner(
1970 self: &Arc<Self>,
1971 desc: &TextureViewDescriptor,
1972 ) -> Result<Arc<TextureView>, CreateTextureViewError> {
1973 let device = &self.device;
1974 device.check_is_valid()?;
1975
1976 if desc.swizzle != wgt::TextureComponentSwizzle::default() {
1977 self.device
1978 .require_features(wgt::Features::TEXTURE_COMPONENT_SWIZZLE)?;
1979 }
1980
1981 let snatch_guard = device.snatchable_lock.read();
1982
1983 let texture_raw = self.try_inner(&snatch_guard)?.raw();
1984
1985 let resolved_format = desc.format.unwrap_or_else(|| {
1988 self.desc
1989 .format
1990 .aspect_specific_format(desc.range.aspect)
1991 .unwrap_or(self.desc.format)
1992 });
1993
1994 let resolved_dimension = desc.dimension.unwrap_or_else(|| match self.desc.dimension {
1995 wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1,
1996 wgt::TextureDimension::D2 => {
1997 if self.desc.array_layer_count() == 1 {
1998 wgt::TextureViewDimension::D2
1999 } else {
2000 wgt::TextureViewDimension::D2Array
2001 }
2002 }
2003 wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3,
2004 });
2005
2006 let resolved_mip_level_count = desc.range.mip_level_count.unwrap_or_else(|| {
2007 self.desc
2008 .mip_level_count
2009 .saturating_sub(desc.range.base_mip_level)
2010 });
2011
2012 let resolved_array_layer_count =
2013 desc.range
2014 .array_layer_count
2015 .unwrap_or_else(|| match resolved_dimension {
2016 wgt::TextureViewDimension::D1
2017 | wgt::TextureViewDimension::D2
2018 | wgt::TextureViewDimension::D3 => 1,
2019 wgt::TextureViewDimension::Cube => 6,
2020 wgt::TextureViewDimension::D2Array | wgt::TextureViewDimension::CubeArray => {
2021 self.desc
2022 .array_layer_count()
2023 .saturating_sub(desc.range.base_array_layer)
2024 }
2025 });
2026
2027 let resolved_usage = {
2028 let usage = desc.usage.unwrap_or(wgt::TextureUsages::empty());
2029 if usage.is_empty() {
2030 self.desc.usage
2031 } else if self.desc.usage.contains(usage) {
2032 if self
2034 .desc
2035 .usage
2036 .contains(wgt::TextureUsages::TRANSIENT_ATTACHMENT)
2037 && self.desc.usage != usage
2038 {
2039 return Err(CreateTextureViewError::InvalidTransientTextureViewUsage {
2040 texture: self.desc.usage,
2041 view: usage,
2042 });
2043 }
2044
2045 usage
2046 } else {
2047 return Err(CreateTextureViewError::InvalidTextureViewUsage {
2048 view: usage,
2049 texture: self.desc.usage,
2050 });
2051 }
2052 };
2053
2054 let format_features = device.describe_format_features(resolved_format)?;
2055 let allowed_format_usages = format_features.allowed_usages;
2056 if resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
2057 && !allowed_format_usages.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
2058 {
2059 return Err(CreateTextureViewError::TextureViewFormatNotRenderable(
2060 resolved_format,
2061 ));
2062 }
2063
2064 if resolved_usage.contains(wgt::TextureUsages::STORAGE_BINDING)
2065 && !allowed_format_usages.contains(wgt::TextureUsages::STORAGE_BINDING)
2066 {
2067 return Err(CreateTextureViewError::TextureViewFormatNotStorage(
2068 resolved_format,
2069 ));
2070 }
2071
2072 let aspects = hal::FormatAspects::new(self.desc.format, desc.range.aspect);
2075 if aspects.is_empty() {
2076 return Err(CreateTextureViewError::InvalidAspect {
2077 texture_format: self.desc.format,
2078 requested_aspect: desc.range.aspect,
2079 });
2080 }
2081
2082 if desc.range.aspect == wgt::TextureAspect::All && resolved_format.is_multi_planar_format()
2083 {
2084 return Err(CreateTextureViewError::MultiplanarFullTexture(
2085 resolved_format,
2086 ));
2087 }
2088
2089 if desc.range.aspect == wgt::TextureAspect::All {
2090 if resolved_format != self.desc.format
2091 && !self.desc.view_formats.contains(&resolved_format)
2092 {
2093 return Err(CreateTextureViewError::FormatReinterpretation {
2094 texture: self.desc.format,
2095 view: resolved_format,
2096 });
2097 }
2098 } else {
2099 let aspect_format = self.desc.format.aspect_specific_format(desc.range.aspect);
2100 match aspect_format {
2101 Some(aspect_format) if aspect_format == resolved_format => (),
2102 Some(aspect_format) => {
2103 return Err(CreateTextureViewError::WrongAspectReinterpretation {
2104 texture: self.desc.format,
2105 aspect: desc.range.aspect,
2106 aspect_format,
2107 requested_format: resolved_format,
2108 })
2109 }
2110 None => {
2111 unreachable!()
2114 }
2115 }
2116 }
2117
2118 if self.desc.sample_count > 1 && resolved_dimension != wgt::TextureViewDimension::D2 {
2120 let multisample_array_exception = resolved_dimension
2122 == wgt::TextureViewDimension::D2Array
2123 && device.features.contains(wgt::Features::MULTISAMPLE_ARRAY);
2124
2125 if !multisample_array_exception {
2126 return Err(
2127 CreateTextureViewError::InvalidMultisampledTextureViewDimension(
2128 resolved_dimension,
2129 ),
2130 );
2131 }
2132 }
2133
2134 if self.desc.dimension != resolved_dimension.compatible_texture_dimension() {
2136 return Err(CreateTextureViewError::InvalidTextureViewDimension {
2137 view: resolved_dimension,
2138 texture: self.desc.dimension,
2139 });
2140 }
2141
2142 match resolved_dimension {
2143 wgt::TextureViewDimension::D1
2144 | wgt::TextureViewDimension::D2
2145 | wgt::TextureViewDimension::D3
2146 if resolved_array_layer_count != 1 =>
2147 {
2148 return Err(CreateTextureViewError::InvalidArrayLayerCount {
2149 requested: resolved_array_layer_count,
2150 dim: resolved_dimension,
2151 });
2152 }
2153 wgt::TextureViewDimension::Cube if resolved_array_layer_count != 6 => {
2154 return Err(CreateTextureViewError::InvalidCubemapTextureDepth {
2155 depth: resolved_array_layer_count,
2156 });
2157 }
2158 wgt::TextureViewDimension::CubeArray
2159 if !resolved_array_layer_count.is_multiple_of(6) =>
2160 {
2161 return Err(CreateTextureViewError::InvalidCubemapArrayTextureDepth {
2162 depth: resolved_array_layer_count,
2163 });
2164 }
2165 _ => {}
2166 }
2167
2168 match resolved_dimension {
2169 wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray
2170 if self.desc.size.width != self.desc.size.height =>
2171 {
2172 return Err(CreateTextureViewError::InvalidCubeTextureViewSize);
2173 }
2174 _ => {}
2175 }
2176
2177 if resolved_mip_level_count == 0 {
2178 return Err(CreateTextureViewError::ZeroMipLevelCount);
2179 }
2180
2181 let mip_level_end = desc
2182 .range
2183 .base_mip_level
2184 .saturating_add(resolved_mip_level_count);
2185
2186 let level_end = self.desc.mip_level_count;
2187 if mip_level_end > level_end {
2188 return Err(CreateTextureViewError::TooManyMipLevels {
2189 base_mip_level: desc.range.base_mip_level,
2190 mip_level_count: resolved_mip_level_count,
2191 total: level_end,
2192 });
2193 }
2194
2195 if resolved_array_layer_count == 0 {
2196 return Err(CreateTextureViewError::ZeroArrayLayerCount);
2197 }
2198
2199 let array_layer_end = desc
2200 .range
2201 .base_array_layer
2202 .saturating_add(resolved_array_layer_count);
2203
2204 let layer_end = self.desc.array_layer_count();
2205 if array_layer_end > layer_end {
2206 return Err(CreateTextureViewError::TooManyArrayLayers {
2207 base_array_layer: desc.range.base_array_layer,
2208 array_layer_count: resolved_array_layer_count,
2209 total: layer_end,
2210 });
2211 };
2212
2213 let render_extent = 'error: {
2215 if !resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
2216 break 'error Err(TextureViewNotRenderableReason::Usage(resolved_usage));
2217 }
2218
2219 let allowed_view_dimensions = [
2220 wgt::TextureViewDimension::D2,
2221 wgt::TextureViewDimension::D2Array,
2222 wgt::TextureViewDimension::D3,
2223 ];
2224 if !allowed_view_dimensions.contains(&resolved_dimension) {
2225 break 'error Err(TextureViewNotRenderableReason::Dimension(
2226 resolved_dimension,
2227 ));
2228 }
2229
2230 if resolved_mip_level_count != 1 {
2231 break 'error Err(TextureViewNotRenderableReason::MipLevelCount(
2232 resolved_mip_level_count,
2233 ));
2234 }
2235
2236 if resolved_array_layer_count != 1
2237 && !(device.features.contains(wgt::Features::MULTIVIEW))
2238 {
2239 break 'error Err(TextureViewNotRenderableReason::ArrayLayerCount(
2240 resolved_array_layer_count,
2241 ));
2242 }
2243
2244 if !self.desc.format.is_multi_planar_format()
2245 && aspects != hal::FormatAspects::from(self.desc.format)
2246 {
2247 break 'error Err(TextureViewNotRenderableReason::Aspects(aspects));
2248 }
2249
2250 if desc.swizzle != wgt::TextureComponentSwizzle::default() {
2251 break 'error Err(TextureViewNotRenderableReason::Swizzle(desc.swizzle));
2252 }
2253
2254 Ok(self
2255 .desc
2256 .compute_render_extent(desc.range.base_mip_level, desc.range.aspect.to_plane()))
2257 };
2258
2259 let usage = {
2261 let resolved_hal_usage = crate::conv::map_texture_usage(
2262 resolved_usage,
2263 resolved_format.into(),
2264 format_features.flags,
2265 );
2266 let mask_copy = !(wgt::TextureUses::COPY_SRC | wgt::TextureUses::COPY_DST);
2267 let mask_dimension = match resolved_dimension {
2268 wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => {
2269 wgt::TextureUses::RESOURCE
2270 }
2271 wgt::TextureViewDimension::D3 => {
2272 wgt::TextureUses::RESOURCE
2273 | wgt::TextureUses::STORAGE_READ_ONLY
2274 | wgt::TextureUses::STORAGE_WRITE_ONLY
2275 | wgt::TextureUses::STORAGE_READ_WRITE
2276 }
2277 _ => wgt::TextureUses::all(),
2278 };
2279 let mask_mip_level = if resolved_mip_level_count == 1 {
2280 wgt::TextureUses::all()
2281 } else {
2282 wgt::TextureUses::RESOURCE
2283 };
2284 resolved_hal_usage & mask_copy & mask_dimension & mask_mip_level
2285 };
2286
2287 let format = if resolved_format.is_depth_stencil_component(self.desc.format) {
2289 self.desc.format
2290 } else {
2291 resolved_format
2292 };
2293
2294 let resolved_range = wgt::ImageSubresourceRange {
2295 aspect: desc.range.aspect,
2296 base_mip_level: desc.range.base_mip_level,
2297 mip_level_count: Some(resolved_mip_level_count),
2298 base_array_layer: desc.range.base_array_layer,
2299 array_layer_count: Some(resolved_array_layer_count),
2300 };
2301
2302 let hal_desc = hal::TextureViewDescriptor {
2303 label: desc.label.to_hal(device.instance_flags),
2304 format,
2305 dimension: resolved_dimension,
2306 usage,
2307 range: resolved_range,
2308 swizzle: desc.swizzle,
2309 };
2310
2311 let raw = unsafe { device.raw().create_texture_view(texture_raw, &hal_desc) }
2312 .map_err(|e| device.handle_hal_error(e))?;
2313
2314 let selector = TextureSelector {
2315 mips: desc.range.base_mip_level..mip_level_end,
2316 layers: desc.range.base_array_layer..array_layer_end,
2317 };
2318
2319 let view = TextureView {
2320 state: ResourceState::Valid(TextureViewState {
2321 raw: Snatchable::new(raw),
2322 render_extent,
2323 }),
2324 parent: self.clone(),
2325 device: device.clone(),
2326 desc: HalTextureViewDescriptor {
2327 texture_format: self.desc.format,
2328 format: resolved_format,
2329 dimension: resolved_dimension,
2330 usage: resolved_usage,
2331 range: resolved_range,
2332 swizzle: desc.swizzle,
2333 },
2334 format_features: self.format_features,
2335 samples: self.desc.sample_count,
2336 selector,
2337 label: desc.label.to_string(),
2338 };
2339
2340 let view = Arc::new(view);
2341
2342 {
2343 let mut views = self.views.lock();
2344 views.push(Arc::downgrade(&view));
2345 }
2346
2347 Ok(view)
2348 }
2349
2350 pub fn create_view(self: &Arc<Self>, desc: &TextureViewDescriptor) -> Arc<TextureView> {
2351 profiling::scope!("Texture::create_view");
2352
2353 let view = self.create_view_inner(desc).unwrap_or_else(|err| {
2354 self.device
2355 .handle_error(err, desc.label.as_deref(), "Texture::create_view failed");
2356 TextureView::invalid(&self.device, self, desc)
2357 });
2358
2359 api_log!(
2360 "Texture::create_view({:?}) -> {:?}",
2361 Arc::as_ptr(self),
2362 Arc::as_ptr(&view)
2363 );
2364
2365 #[cfg(feature = "trace")]
2366 if let Some(ref mut trace) = *self.device.trace.lock() {
2367 use crate::device::trace;
2368 use trace::IntoTrace as _;
2369 trace.add(trace::Action::CreateTextureView {
2370 id: view.to_trace(),
2371 parent: self.to_trace(),
2372 desc: desc.clone(),
2373 });
2374 }
2375
2376 view
2377 }
2378
2379 pub fn descriptor(&self) -> &wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>> {
2380 &self.desc
2381 }
2382
2383 pub unsafe fn mark_externally_initialized(&self) {
2390 let mut initialization_status = self.initialization_status.write();
2391 for mip_tracker in initialization_status.mips.iter_mut() {
2392 mip_tracker.drain(0..self.desc.array_layer_count());
2393 }
2394 }
2395}
2396
2397#[derive(Debug)]
2399pub struct DestroyedTexture {
2400 raw: ManuallyDrop<Box<dyn hal::DynTexture>>,
2401 views: WeakVec<TextureView>,
2402 clear_mode: TextureClearMode,
2403 bind_groups: WeakVec<BindGroup>,
2404 device: Arc<Device>,
2405 label: String,
2406}
2407
2408impl DestroyedTexture {
2409 pub fn label(&self) -> &dyn fmt::Debug {
2410 &self.label
2411 }
2412}
2413
2414impl Drop for DestroyedTexture {
2415 fn drop(&mut self) {
2416 let device = &self.device;
2417
2418 let mut deferred = device.deferred_destroy.lock();
2419 deferred.push(DeferredDestroy::TextureViews(mem::take(&mut self.views)));
2420 deferred.push(DeferredDestroy::BindGroups(mem::take(
2421 &mut self.bind_groups,
2422 )));
2423 drop(deferred);
2424
2425 match mem::replace(&mut self.clear_mode, TextureClearMode::None) {
2426 TextureClearMode::RenderPass { clear_views, .. } => {
2427 for clear_view in clear_views {
2428 let raw = ManuallyDrop::into_inner(clear_view);
2429 unsafe { self.device.raw().destroy_texture_view(raw) };
2430 }
2431 }
2432 TextureClearMode::Surface { clear_view } => {
2433 let raw = ManuallyDrop::into_inner(clear_view);
2434 unsafe { self.device.raw().destroy_texture_view(raw) };
2435 }
2436 _ => (),
2437 }
2438
2439 resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
2440 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
2442 unsafe {
2443 self.device.raw().destroy_texture(raw);
2444 }
2445 }
2446}
2447
2448#[derive(Clone, Copy, Debug)]
2449pub enum TextureErrorDimension {
2450 X,
2451 Y,
2452 Z,
2453}
2454
2455#[derive(Clone, Debug, Error)]
2456#[non_exhaustive]
2457pub enum TextureDimensionError {
2458 #[error("Dimension {0:?} is zero")]
2459 Zero(TextureErrorDimension),
2460 #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
2461 LimitExceeded {
2462 dim: TextureErrorDimension,
2463 given: u32,
2464 limit: u32,
2465 },
2466 #[error("Sample count {0} is invalid")]
2467 InvalidSampleCount(u32),
2468 #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
2469 NotMultipleOfBlockWidth {
2470 width: u32,
2471 block_width: u32,
2472 format: wgt::TextureFormat,
2473 },
2474 #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
2475 NotMultipleOfBlockHeight {
2476 height: u32,
2477 block_height: u32,
2478 format: wgt::TextureFormat,
2479 },
2480 #[error(
2481 "Width {width} is not a multiple of {format:?}'s width multiple requirement ({multiple})"
2482 )]
2483 WidthNotMultipleOf {
2484 width: u32,
2485 multiple: u32,
2486 format: wgt::TextureFormat,
2487 },
2488 #[error("Height {height} is not a multiple of {format:?}'s height multiple requirement ({multiple})")]
2489 HeightNotMultipleOf {
2490 height: u32,
2491 multiple: u32,
2492 format: wgt::TextureFormat,
2493 },
2494 #[error("Multisampled texture depth or array layers must be 1, got {0}")]
2495 MultisampledDepthOrArrayLayer(u32),
2496}
2497
2498impl WebGpuError for TextureDimensionError {
2499 fn webgpu_error_type(&self) -> ErrorType {
2500 ErrorType::Validation
2501 }
2502}
2503
2504#[derive(Clone, Debug, Error)]
2505#[non_exhaustive]
2506pub enum CreateTextureError {
2507 #[error(transparent)]
2508 Device(#[from] DeviceError),
2509 #[error(transparent)]
2510 CreateTextureView(#[from] CreateTextureViewError),
2511 #[error("Invalid usage flags {0:?}")]
2512 InvalidUsage(wgt::TextureUsages),
2513 #[error(transparent)]
2514 InvalidDimension(#[from] TextureDimensionError),
2515 #[error("Depth texture ({1:?}) can't be created as {0:?}")]
2516 InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
2517 #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
2518 InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
2519 #[error(
2520 "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
2521 )]
2522 InvalidMipLevelCount { requested: u32, maximum: u32 },
2523 #[error(
2524 "Texture usages {0:?} are not allowed on a texture of type {1:?}{downlevel_suffix}",
2525 downlevel_suffix = if *.2 { " due to downlevel restrictions" } else { "" }
2526 )]
2527 InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
2528 #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
2529 InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
2530 #[error("Transient texture usage must be equal to `TRANSIENT_ATTACHMENT | RENDER_ATTACHMENT`, but got `{0:?}`")]
2531 InvalidTransientTextureUsage(wgt::TextureUsages),
2532 #[error("Transient texture view formats must be empty")]
2533 InvalidTransientTextureViewFormats,
2534 #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
2535 InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
2536 #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
2537 InvalidMultisampledStorageBinding,
2538 #[error("Format {0:?} does not support multisampling")]
2539 InvalidMultisampledFormat(wgt::TextureFormat),
2540 #[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:?}.")]
2541 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
2542 #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
2543 MultisampledNotRenderAttachment,
2544 #[error("Transient texture mip level count ({0}) must be 1")]
2545 InvalidTransientTextureMipLevelCount(u32),
2546 #[error("Transient texture layer count ({0}) must be 1")]
2547 InvalidTransientTextureLayerCount(u32),
2548 #[error("Texture format {0:?} can't be used due to missing features")]
2549 MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
2550 #[error(transparent)]
2551 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
2552}
2553
2554crate::impl_resource_type!(Texture);
2555impl Labeled for Texture {
2556 fn label(&self) -> &str {
2557 &self.desc.label
2558 }
2559}
2560crate::impl_parent_device!(Texture);
2561crate::impl_storage_item!(Texture);
2562crate::impl_trackable!(Texture);
2563
2564impl Borrow<TextureSelector> for Texture {
2565 fn borrow(&self) -> &TextureSelector {
2566 &self.full_range
2567 }
2568}
2569
2570impl WebGpuError for CreateTextureError {
2571 fn webgpu_error_type(&self) -> ErrorType {
2572 match self {
2573 Self::Device(e) => e.webgpu_error_type(),
2574 Self::CreateTextureView(e) => e.webgpu_error_type(),
2575 Self::InvalidDimension(e) => e.webgpu_error_type(),
2576 Self::MissingFeatures(_, e) => e.webgpu_error_type(),
2577 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
2578
2579 Self::InvalidUsage(_)
2580 | Self::InvalidDepthDimension(_, _)
2581 | Self::InvalidCompressedDimension(_, _)
2582 | Self::InvalidMipLevelCount { .. }
2583 | Self::InvalidFormatUsages(_, _, _)
2584 | Self::InvalidViewFormat(_, _)
2585 | Self::InvalidDimensionUsages(_, _)
2586 | Self::InvalidMultisampledStorageBinding
2587 | Self::InvalidMultisampledFormat(_)
2588 | Self::InvalidSampleCount(..)
2589 | Self::InvalidTransientTextureUsage(_)
2590 | Self::InvalidTransientTextureMipLevelCount(_)
2591 | Self::InvalidTransientTextureLayerCount(_)
2592 | Self::InvalidTransientTextureViewFormats
2593 | Self::MultisampledNotRenderAttachment => ErrorType::Validation,
2594 }
2595 }
2596}
2597
2598#[derive(Clone, Debug, Default, Eq, PartialEq)]
2600#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2601#[cfg_attr(feature = "serde", serde(default))]
2602pub struct TextureViewDescriptor<'a> {
2603 pub label: Label<'a>,
2607 pub format: Option<wgt::TextureFormat>,
2612 pub dimension: Option<wgt::TextureViewDimension>,
2618 pub usage: Option<wgt::TextureUsages>,
2621 pub range: wgt::ImageSubresourceRange,
2623 pub swizzle: wgt::TextureComponentSwizzle,
2627}
2628
2629#[derive(Debug)]
2630pub(crate) struct HalTextureViewDescriptor {
2631 pub texture_format: wgt::TextureFormat,
2632 pub format: wgt::TextureFormat,
2633 pub usage: wgt::TextureUsages,
2634 pub dimension: wgt::TextureViewDimension,
2635 pub range: wgt::ImageSubresourceRange,
2636 pub swizzle: wgt::TextureComponentSwizzle,
2637}
2638
2639impl HalTextureViewDescriptor {
2640 pub fn aspects(&self) -> hal::FormatAspects {
2641 hal::FormatAspects::new(self.texture_format, self.range.aspect)
2642 }
2643}
2644
2645#[derive(Debug, Copy, Clone, Error)]
2646pub enum TextureViewNotRenderableReason {
2647 #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
2648 Usage(wgt::TextureUsages),
2649 #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
2650 Dimension(wgt::TextureViewDimension),
2651 #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
2652 MipLevelCount(u32),
2653 #[error("This texture view has more than one array layer. View array layers: {0:?}")]
2654 ArrayLayerCount(u32),
2655 #[error(
2656 "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
2657 )]
2658 Aspects(hal::FormatAspects),
2659 #[error("The texture view swizzle must be identity. View swizzle: {0:?}")]
2660 Swizzle(wgt::TextureComponentSwizzle),
2661}
2662
2663#[derive(Debug)]
2664pub struct TextureViewState {
2665 pub(crate) raw: Snatchable<Box<dyn hal::DynTextureView>>,
2666 pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
2668}
2669
2670#[derive(Debug)]
2671pub struct TextureView {
2672 pub(crate) state: ResourceState<TextureViewState>,
2673 pub(crate) parent: Arc<Texture>,
2675 pub(crate) device: Arc<Device>,
2676 pub(crate) desc: HalTextureViewDescriptor,
2677 pub(crate) format_features: wgt::TextureFormatFeatures,
2678 pub(crate) samples: u32,
2679 pub(crate) selector: TextureSelector,
2680 pub(crate) label: String,
2682}
2683
2684impl Drop for TextureView {
2685 #[expect(trivial_casts)]
2686 fn drop(&mut self) {
2687 profiling::scope!("TextureView::drop");
2688 api_log!("TextureView::drop {:?}", self as *const _);
2689 #[cfg(feature = "trace")]
2690 if let Some(t) = self.device.trace.lock().as_mut() {
2691 t.add(trace::Action::DropTextureView(unsafe {
2692 trace::to_trace(self)
2693 }));
2694 }
2695 let ResourceState::Valid(state) = &mut self.state else {
2696 return;
2697 };
2698
2699 if let Some(raw) = state.raw.take() {
2700 resource_log!("Destroy raw {}", self.error_ident());
2701 unsafe {
2702 self.device.raw().destroy_texture_view(raw);
2703 }
2704 }
2705 }
2706}
2707
2708impl RawResourceAccess for TextureView {
2709 type DynResource = dyn hal::DynTextureView;
2710
2711 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2712 self.state()
2713 .ok()
2714 .and_then(|state| state.raw.get(guard).map(|it| it.as_ref()))
2715 }
2716
2717 fn try_raw<'a>(
2718 &'a self,
2719 guard: &'a SnatchGuard,
2720 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
2721 self.parent.check_destroyed(guard)?;
2722
2723 self.raw(guard)
2724 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
2725 }
2726}
2727
2728impl TextureView {
2729 pub(crate) fn check_usage(
2732 &self,
2733 expected: wgt::TextureUsages,
2734 ) -> Result<(), MissingTextureUsageError> {
2735 if self.desc.usage.contains(expected) {
2736 Ok(())
2737 } else {
2738 Err(MissingTextureUsageError {
2739 res: self.error_ident(),
2740 actual: self.desc.usage,
2741 expected,
2742 })
2743 }
2744 }
2745
2746 pub(crate) fn state(&self) -> Result<&TextureViewState, InvalidResourceError> {
2747 match &self.state {
2748 ResourceState::Valid(state) => Ok(state),
2749 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2750 }
2751 }
2752
2753 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
2754 self.state().map(|_| ())
2755 }
2756
2757 pub(crate) fn invalid(
2758 device: &Arc<Device>,
2759 texture: &Arc<Texture>,
2760 desc: &TextureViewDescriptor,
2761 ) -> Arc<Self> {
2762 Arc::new(TextureView {
2764 state: ResourceState::Invalid,
2765 parent: texture.clone(),
2766 device: device.clone(),
2767 desc: HalTextureViewDescriptor {
2768 texture_format: texture.desc.format,
2769 format: desc.format.unwrap_or(texture.desc.format),
2770 usage: desc.usage.unwrap_or(texture.desc.usage),
2771 dimension: desc.dimension.unwrap_or(match texture.desc.dimension {
2772 wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1,
2773 wgt::TextureDimension::D2 => wgt::TextureViewDimension::D2,
2774 wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3,
2775 }),
2776 range: desc.range,
2777 swizzle: desc.swizzle,
2778 },
2779 format_features: texture.format_features,
2780 samples: texture.desc.sample_count,
2781 selector: TextureSelector {
2782 mips: desc.range.mip_range(texture.desc.mip_level_count),
2783 layers: desc.range.layer_range(texture.desc.array_layer_count()),
2784 },
2785 label: desc.label.to_string(),
2786 })
2787 }
2788
2789 pub fn texture(&self) -> &Arc<Texture> {
2790 &self.parent
2791 }
2792}
2793
2794#[derive(Clone, Debug, Error)]
2795#[non_exhaustive]
2796pub enum CreateTextureViewError {
2797 #[error(transparent)]
2798 Device(#[from] DeviceError),
2799 #[error(transparent)]
2800 DestroyedResource(#[from] DestroyedResourceError),
2801 #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
2802 InvalidTextureViewDimension {
2803 view: wgt::TextureViewDimension,
2804 texture: wgt::TextureDimension,
2805 },
2806 #[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.")]
2807 TextureViewFormatNotRenderable(wgt::TextureFormat),
2808 #[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.")]
2809 TextureViewFormatNotStorage(wgt::TextureFormat),
2810 #[error("Texture view usages (`{view:?}`) must be a subset of the texture's original usages (`{texture:?}`)")]
2811 InvalidTextureViewUsage {
2812 view: wgt::TextureUsages,
2813 texture: wgt::TextureUsages,
2814 },
2815 #[error("Texture view dimension `{0:?}` cannot be used with a multisampled texture")]
2816 InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
2817 #[error(
2818 "TextureView has an arrayLayerCount of {depth}. Views of type `Cube` must have arrayLayerCount of 6."
2819 )]
2820 InvalidCubemapTextureDepth { depth: u32 },
2821 #[error("TextureView has an arrayLayerCount of {depth}. Views of type `CubeArray` must have an arrayLayerCount that is a multiple of 6.")]
2822 InvalidCubemapArrayTextureDepth { depth: u32 },
2823 #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
2824 InvalidCubeTextureViewSize,
2825 #[error("Mip level count is 0")]
2826 ZeroMipLevelCount,
2827 #[error("Array layer count is 0")]
2828 ZeroArrayLayerCount,
2829 #[error(
2830 "`TextureView` starts at mip level {base_mip_level} and spans {mip_level_count} mip \
2831 levels, but the texture view only has {total} total mip level(s)"
2832 )]
2833 TooManyMipLevels {
2834 base_mip_level: u32,
2835 mip_level_count: u32,
2836 total: u32,
2837 },
2838 #[error(
2839 "`TextureView` starts at array layer {base_array_layer} and spans {array_layer_count}) \
2840 array layers, but the texture view only has {total} total layer(s)"
2841 )]
2842 TooManyArrayLayers {
2843 base_array_layer: u32,
2844 array_layer_count: u32,
2845 total: u32,
2846 },
2847 #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
2848 InvalidArrayLayerCount {
2849 requested: u32,
2850 dim: wgt::TextureViewDimension,
2851 },
2852 #[error(
2853 "Aspect {requested_aspect:?} is not a valid aspect of the source texture format {texture_format:?}"
2854 )]
2855 InvalidAspect {
2856 texture_format: wgt::TextureFormat,
2857 requested_aspect: wgt::TextureAspect,
2858 },
2859 #[error(
2860 "Trying to create a view of format {view:?} of a texture with format {texture:?}, \
2861 but this view format is not present in the texture's viewFormat array"
2862 )]
2863 FormatReinterpretation {
2864 texture: wgt::TextureFormat,
2865 view: wgt::TextureFormat,
2866 },
2867 #[error(
2868 "The texture view (`{view:?}`) from transient texture (`{texture:?}`) must have the same usage"
2869 )]
2870 InvalidTransientTextureViewUsage {
2871 texture: wgt::TextureUsages,
2872 view: wgt::TextureUsages,
2873 },
2874 #[error(transparent)]
2875 InvalidResource(#[from] InvalidResourceError),
2876 #[error(transparent)]
2877 MissingFeatures(#[from] MissingFeatures),
2878
2879 #[error(
2880 "Trying to create a view of format {requested_format:?} on aspect {aspect:?} of format {texture:?}, \
2881 but the actual format of this aspect is {aspect_format:?}"
2882 )]
2883 WrongAspectReinterpretation {
2884 texture: wgt::TextureFormat,
2885 aspect: wgt::TextureAspect,
2886 aspect_format: wgt::TextureFormat,
2887 requested_format: wgt::TextureFormat,
2888 },
2889 #[error("TextureAspect::All cannot be used in texture views on multi-planar formats")]
2890 MultiplanarFullTexture(wgt::TextureFormat),
2891}
2892
2893impl From<InvalidOrDestroyedResourceError> for CreateTextureViewError {
2894 fn from(value: InvalidOrDestroyedResourceError) -> Self {
2895 match value {
2896 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
2897 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
2898 }
2899 }
2900}
2901
2902impl WebGpuError for CreateTextureViewError {
2903 fn webgpu_error_type(&self) -> ErrorType {
2904 match self {
2905 Self::Device(e) => e.webgpu_error_type(),
2906
2907 Self::InvalidTextureViewDimension { .. }
2908 | Self::InvalidResource(_)
2909 | Self::InvalidMultisampledTextureViewDimension(_)
2910 | Self::InvalidCubemapTextureDepth { .. }
2911 | Self::InvalidCubemapArrayTextureDepth { .. }
2912 | Self::InvalidCubeTextureViewSize
2913 | Self::ZeroMipLevelCount
2914 | Self::ZeroArrayLayerCount
2915 | Self::TooManyMipLevels { .. }
2916 | Self::TooManyArrayLayers { .. }
2917 | Self::InvalidArrayLayerCount { .. }
2918 | Self::InvalidAspect { .. }
2919 | Self::FormatReinterpretation { .. }
2920 | Self::DestroyedResource(_)
2921 | Self::TextureViewFormatNotRenderable(_)
2922 | Self::TextureViewFormatNotStorage(_)
2923 | Self::InvalidTextureViewUsage { .. }
2924 | Self::InvalidTransientTextureViewUsage { .. }
2925 | Self::MissingFeatures(_)
2926 | Self::WrongAspectReinterpretation { .. }
2927 | Self::MultiplanarFullTexture(_) => ErrorType::Validation,
2928 }
2929 }
2930}
2931
2932crate::impl_resource_type!(TextureView);
2933crate::impl_labeled!(TextureView);
2934crate::impl_parent_device!(TextureView);
2935crate::impl_storage_item!(TextureView);
2936
2937pub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;
2938
2939#[derive(Debug)]
2940pub(crate) struct ExternalTextureState {
2941 pub(crate) params: Arc<Buffer>,
2944}
2945
2946#[derive(Debug)]
2947pub struct ExternalTexture {
2948 pub(crate) state: ResourceState<ExternalTextureState>,
2949 pub(crate) device: Arc<Device>,
2950 pub(crate) planes: arrayvec::ArrayVec<Arc<TextureView>, 3>,
2952 pub(crate) label: String,
2954 pub(crate) tracking_data: TrackingData,
2955}
2956
2957impl Drop for ExternalTexture {
2958 #[allow(trivial_casts)]
2959 fn drop(&mut self) {
2960 profiling::scope!("ExternalTexture::drop");
2961 api_log!("ExternalTexture::drop {:?}", self as *const _);
2962
2963 resource_log!("Destroy raw {}", self.error_ident());
2964 #[cfg(feature = "trace")]
2965 if let Some(t) = self.device.trace.lock().as_mut() {
2966 t.add(trace::Action::DropExternalTexture(unsafe {
2967 trace::to_trace(self)
2968 }));
2969 }
2970 }
2971}
2972
2973impl ExternalTexture {
2974 pub(crate) fn state(&self) -> Result<&ExternalTextureState, InvalidResourceError> {
2975 match &self.state {
2976 ResourceState::Valid(state) => Ok(state),
2977 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2978 }
2979 }
2980
2981 pub fn destroy(self: &Arc<Self>) {
2982 profiling::scope!("ExternalTexture::destroy");
2983 api_log!("ExternalTexture::destroy {:?}", Arc::as_ptr(self));
2984
2985 #[cfg(feature = "trace")]
2986 if let Some(trace) = self.device.trace.lock().as_mut() {
2987 use crate::device::trace::IntoTrace as _;
2988
2989 trace.add(trace::Action::DestroyExternalTexture(self.to_trace()));
2990 }
2991 if let Ok(state) = self.state() {
2992 state.params.destroy();
2993 }
2994 }
2995
2996 pub fn invalid(device: Arc<Device>, desc: &ExternalTextureDescriptor) -> Arc<Self> {
2997 Arc::new(ExternalTexture {
2998 state: ResourceState::Invalid,
2999 planes: arrayvec::ArrayVec::new(),
3000 label: desc.label.to_string(),
3001 tracking_data: TrackingData::new(device.tracker_indices.external_textures.clone()),
3002 device,
3003 })
3004 }
3005}
3006
3007#[derive(Clone, Debug, Error)]
3008#[non_exhaustive]
3009pub enum CreateExternalTextureError {
3010 #[error(transparent)]
3011 Device(#[from] DeviceError),
3012 #[error(transparent)]
3013 MissingFeatures(#[from] MissingFeatures),
3014 #[error(transparent)]
3015 InvalidResource(#[from] InvalidResourceError),
3016 #[error(transparent)]
3017 CreateBuffer(#[from] CreateBufferError),
3018 #[error(transparent)]
3019 QueueWrite(#[from] queue::QueueWriteError),
3020 #[error("External texture format {format:?} expects {expected} planes, but given {provided}")]
3021 IncorrectPlaneCount {
3022 format: wgt::ExternalTextureFormat,
3023 expected: usize,
3024 provided: usize,
3025 },
3026 #[error("External texture planes cannot be multisampled, but given view with samples = {0}")]
3027 InvalidPlaneMultisample(u32),
3028 #[error("External texture planes expect a filterable float sample type, but given view with format {format:?} (sample type {sample_type:?})")]
3029 InvalidPlaneSampleType {
3030 format: wgt::TextureFormat,
3031 sample_type: wgt::TextureSampleType,
3032 },
3033 #[error("External texture planes expect 2D dimension, but given view with dimension = {0:?}")]
3034 InvalidPlaneDimension(wgt::TextureViewDimension),
3035 #[error(transparent)]
3036 MissingTextureUsage(#[from] MissingTextureUsageError),
3037 #[error("External texture format {format:?} plane {plane} expects format with {expected} components but given view with format {provided:?} ({} components)",
3038 provided.components())]
3039 InvalidPlaneFormat {
3040 format: wgt::ExternalTextureFormat,
3041 plane: usize,
3042 expected: u8,
3043 provided: wgt::TextureFormat,
3044 },
3045}
3046
3047impl WebGpuError for CreateExternalTextureError {
3048 fn webgpu_error_type(&self) -> ErrorType {
3049 match self {
3050 CreateExternalTextureError::Device(e) => e.webgpu_error_type(),
3051 CreateExternalTextureError::MissingFeatures(e) => e.webgpu_error_type(),
3052 CreateExternalTextureError::InvalidResource(e) => e.webgpu_error_type(),
3053 CreateExternalTextureError::CreateBuffer(e) => e.webgpu_error_type(),
3054 CreateExternalTextureError::QueueWrite(e) => e.webgpu_error_type(),
3055 CreateExternalTextureError::MissingTextureUsage(e) => e.webgpu_error_type(),
3056 CreateExternalTextureError::IncorrectPlaneCount { .. }
3057 | CreateExternalTextureError::InvalidPlaneMultisample(_)
3058 | CreateExternalTextureError::InvalidPlaneSampleType { .. }
3059 | CreateExternalTextureError::InvalidPlaneDimension(_)
3060 | CreateExternalTextureError::InvalidPlaneFormat { .. } => ErrorType::Validation,
3061 }
3062 }
3063}
3064
3065crate::impl_resource_type!(ExternalTexture);
3066crate::impl_labeled!(ExternalTexture);
3067crate::impl_parent_device!(ExternalTexture);
3068crate::impl_storage_item!(ExternalTexture);
3069crate::impl_trackable!(ExternalTexture);
3070
3071#[derive(Clone, Debug, PartialEq)]
3073#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3074pub struct SamplerDescriptor<'a> {
3075 pub label: Label<'a>,
3079 pub address_modes: [wgt::AddressMode; 3],
3081 pub mag_filter: wgt::FilterMode,
3083 pub min_filter: wgt::FilterMode,
3085 pub mipmap_filter: wgt::MipmapFilterMode,
3087 pub lod_min_clamp: f32,
3089 pub lod_max_clamp: f32,
3091 pub compare: Option<wgt::CompareFunction>,
3093 pub anisotropy_clamp: u16,
3095 pub border_color: Option<wgt::SamplerBorderColor>,
3098}
3099
3100#[derive(Debug)]
3101pub struct Sampler {
3102 pub(crate) raw: ResourceState<Box<dyn hal::DynSampler>>,
3103 pub(crate) device: Arc<Device>,
3104 pub(crate) label: String,
3106 pub(crate) tracking_data: TrackingData,
3107 pub(crate) comparison: bool,
3109 pub(crate) filtering: bool,
3111}
3112
3113impl Drop for Sampler {
3114 #[allow(trivial_casts)]
3115 fn drop(&mut self) {
3116 profiling::scope!("Sampler::drop");
3117 api_log!("Sampler::drop {:?}", self as *const _);
3118 #[cfg(feature = "trace")]
3119 if let Some(t) = self.device.trace.lock().as_mut() {
3120 t.add(trace::Action::DropSampler(unsafe { trace::to_trace(self) }));
3121 }
3122 resource_log!("Destroy raw {}", self.error_ident());
3123 if let ResourceState::Valid(raw) = mem::replace(&mut self.raw, ResourceState::Invalid) {
3124 unsafe {
3125 self.device.raw().destroy_sampler(raw);
3126 }
3127 }
3128 }
3129}
3130
3131impl Sampler {
3132 pub(crate) fn raw(&self) -> Result<&dyn hal::DynSampler, InvalidResourceError> {
3133 self.raw
3134 .as_ref()
3135 .valid()
3136 .map(|raw| raw.as_ref())
3137 .ok_or_else(|| InvalidResourceError(self.error_ident()))
3138 }
3139
3140 pub(crate) fn invalid(device: Arc<Device>, desc: &SamplerDescriptor) -> Arc<Self> {
3141 Arc::new(Sampler {
3142 raw: ResourceState::Invalid,
3143 label: desc.label.to_string(),
3144 tracking_data: TrackingData::new(device.tracker_indices.samplers.clone()),
3145 device,
3146 comparison: desc.compare.is_some(),
3147 filtering: desc.mag_filter == wgt::FilterMode::Linear
3148 || desc.min_filter == wgt::FilterMode::Linear
3149 || desc.mipmap_filter == wgt::MipmapFilterMode::Linear,
3150 })
3151 }
3152}
3153
3154#[derive(Copy, Clone)]
3155pub enum SamplerFilterErrorType {
3156 MagFilter,
3157 MinFilter,
3158 MipmapFilter,
3159}
3160
3161impl fmt::Debug for SamplerFilterErrorType {
3162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3163 match *self {
3164 SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
3165 SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
3166 SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
3167 }
3168 }
3169}
3170
3171#[derive(Clone, Debug, Error)]
3172#[non_exhaustive]
3173pub enum CreateSamplerError {
3174 #[error(transparent)]
3175 Device(#[from] DeviceError),
3176 #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
3177 InvalidLodMinClamp(f32),
3178 #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
3179 InvalidLodMaxClamp {
3180 lod_min_clamp: f32,
3181 lod_max_clamp: f32,
3182 },
3183 #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
3184 InvalidAnisotropy(u16),
3185 #[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.")]
3186 InvalidFilterModeWithAnisotropy {
3187 filter_type: SamplerFilterErrorType,
3188 filter_mode: wgt::FilterMode,
3189 anisotropic_clamp: u16,
3190 },
3191 #[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.")]
3192 InvalidMipmapFilterModeWithAnisotropy {
3193 filter_type: SamplerFilterErrorType,
3194 filter_mode: wgt::MipmapFilterMode,
3195 anisotropic_clamp: u16,
3196 },
3197 #[error(transparent)]
3198 MissingFeatures(#[from] MissingFeatures),
3199}
3200
3201crate::impl_resource_type!(Sampler);
3202crate::impl_labeled!(Sampler);
3203crate::impl_parent_device!(Sampler);
3204crate::impl_storage_item!(Sampler);
3205crate::impl_trackable!(Sampler);
3206
3207impl WebGpuError for CreateSamplerError {
3208 fn webgpu_error_type(&self) -> ErrorType {
3209 match self {
3210 Self::Device(e) => e.webgpu_error_type(),
3211 Self::MissingFeatures(e) => e.webgpu_error_type(),
3212
3213 Self::InvalidLodMinClamp(_)
3214 | Self::InvalidLodMaxClamp { .. }
3215 | Self::InvalidAnisotropy(_)
3216 | Self::InvalidFilterModeWithAnisotropy { .. }
3217 | Self::InvalidMipmapFilterModeWithAnisotropy { .. } => ErrorType::Validation,
3218 }
3219 }
3220}
3221
3222#[derive(Clone, Debug, Error)]
3223#[non_exhaustive]
3224pub enum CreateQuerySetError {
3225 #[error(transparent)]
3226 Device(#[from] DeviceError),
3227 #[error("QuerySets cannot be made with zero queries")]
3228 ZeroCount,
3229 #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
3230 TooManyQueries { count: u32, maximum: u32 },
3231 #[error(transparent)]
3232 MissingFeatures(#[from] MissingFeatures),
3233}
3234
3235impl WebGpuError for CreateQuerySetError {
3236 fn webgpu_error_type(&self) -> ErrorType {
3237 match self {
3238 Self::Device(e) => e.webgpu_error_type(),
3239 Self::MissingFeatures(e) => e.webgpu_error_type(),
3240
3241 Self::TooManyQueries { .. } | Self::ZeroCount => ErrorType::Validation,
3242 }
3243 }
3244}
3245
3246pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
3247
3248#[derive(Debug)]
3249pub(crate) struct QuerySetState {
3250 pub(crate) raw: Snatchable<Box<dyn hal::DynQuerySet>>,
3251}
3252
3253#[derive(Debug)]
3254pub struct QuerySet {
3255 pub(crate) state: ResourceState<QuerySetState>,
3256 pub(crate) device: Arc<Device>,
3257 pub(crate) tracking_data: TrackingData,
3258 pub(crate) desc: wgt::QuerySetDescriptor<String>,
3259 pub(crate) initialized_slots: Mutex<bit_vec::BitVec>,
3260}
3261
3262impl RawResourceAccess for QuerySet {
3263 type DynResource = dyn hal::DynQuerySet;
3264
3265 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
3266 self.state().ok()?.raw.get(guard).map(|b| b.as_ref())
3267 }
3268}
3269
3270impl QuerySet {
3271 pub(crate) fn state(&self) -> Result<&QuerySetState, InvalidResourceError> {
3272 match &self.state {
3273 ResourceState::Valid(state) => Ok(state),
3274 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3275 }
3276 }
3277
3278 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
3279 self.state().map(|_| ())
3280 }
3281
3282 pub fn invalid(device: Arc<Device>, desc: &QuerySetDescriptor) -> Arc<Self> {
3283 Arc::new(QuerySet {
3284 state: ResourceState::Invalid,
3285 tracking_data: TrackingData::new(device.tracker_indices.query_sets.clone()),
3286 desc: desc.map_label(|l| l.to_string()),
3287 initialized_slots: Mutex::new(
3288 rank::QUERY_SET_INITIALIZED_SLOTS,
3289 bit_vec::BitVec::new(),
3290 ),
3291 device,
3292 })
3293 }
3294
3295 pub fn destroy(self: &Arc<Self>) {
3296 let device = &self.device;
3297
3298 profiling::scope!("QuerySet::destroy");
3299 api_log!("QuerySet::destroy {:?}", Arc::as_ptr(self));
3300
3301 #[cfg(feature = "trace")]
3302 if let Some(trace) = device.trace.lock().as_mut() {
3303 use crate::device::trace::IntoTrace as _;
3304
3305 trace.add(trace::Action::DestroyQuerySet(self.to_trace()));
3306 };
3307
3308 let ResourceState::Valid(state) = &self.state else {
3309 return;
3310 };
3311
3312 let temp = {
3313 let mut snatch_guard = self.device.snatchable_lock.write();
3314
3315 let raw = match state.raw.snatch(&mut snatch_guard) {
3316 Some(raw) => raw,
3317 None => {
3318 return;
3320 }
3321 };
3322
3323 drop(snatch_guard);
3324
3325 queue::TempResource::DestroyedQuerySet(DestroyedQuerySet {
3326 raw: ManuallyDrop::new(raw),
3327 device: Arc::clone(&self.device),
3328 label: self.label().to_owned(),
3329 })
3330 };
3331
3332 let Some(queue) = device.get_queue() else {
3333 return;
3334 };
3335
3336 let mut life_lock = queue.lock_life();
3337 let last_submit_index = life_lock.get_query_set_latest_submission_index(self);
3338 if let Some(last_submit_index) = last_submit_index {
3339 life_lock.schedule_resource_destruction(temp, last_submit_index);
3340 }
3341 }
3342
3343 pub fn descriptor(&self) -> &wgt::QuerySetDescriptor<String> {
3344 &self.desc
3345 }
3346}
3347
3348impl Drop for QuerySet {
3349 #[allow(trivial_casts)]
3350 fn drop(&mut self) {
3351 profiling::scope!("QuerySet::drop");
3352 api_log!("QuerySet::drop {:?}", self as *const _);
3353 resource_log!("Destroy raw {}", self.error_ident());
3354 #[cfg(feature = "trace")]
3355 if let Some(trace) = self.device.trace.lock().as_mut() {
3356 use crate::device::trace::to_trace;
3357
3358 trace.add(trace::Action::DropQuerySet(unsafe { to_trace(self) }));
3359 }
3360 let ResourceState::Valid(state) = &mut self.state else {
3361 return;
3362 };
3363 if let Some(raw) = state.raw.take() {
3364 unsafe {
3366 self.device.raw().destroy_query_set(raw);
3367 }
3368 }
3369 }
3370}
3371
3372crate::impl_resource_type!(QuerySet);
3373impl Labeled for QuerySet {
3374 fn label(&self) -> &str {
3375 &self.desc.label
3376 }
3377}
3378crate::impl_parent_device!(QuerySet);
3379crate::impl_storage_item!(QuerySet);
3380crate::impl_trackable!(QuerySet);
3381
3382#[derive(Debug)]
3384pub struct DestroyedQuerySet {
3385 raw: ManuallyDrop<Box<dyn hal::DynQuerySet>>,
3386 device: Arc<Device>,
3387 label: String,
3388}
3389
3390impl DestroyedQuerySet {
3391 pub fn label(&self) -> &dyn fmt::Debug {
3392 &self.label
3393 }
3394}
3395
3396impl Drop for DestroyedQuerySet {
3397 fn drop(&mut self) {
3398 resource_log!("Destroy raw QuerySet (destroyed) {:?}", self.label());
3399 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
3401 unsafe {
3402 hal::DynDevice::destroy_query_set(self.device.raw(), raw);
3403 }
3404 }
3405}
3406
3407pub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;
3408pub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;
3409
3410pub type BlasPrepareCompactResult = Result<(), BlasPrepareCompactError>;
3411
3412#[cfg(send_sync)]
3413pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + Send + 'static>;
3414#[cfg(not(send_sync))]
3415pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + 'static>;
3416
3417pub(crate) struct BlasPendingCompact {
3418 pub(crate) op: Option<BlasCompactCallback>,
3419 pub(crate) _parent_blas: Arc<Blas>,
3421}
3422
3423impl fmt::Debug for BlasPendingCompact {
3424 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3425 f.debug_struct("BlasPendingCompact")
3426 .field("op", &())
3427 .field("_parent_blas", &self._parent_blas)
3428 .finish()
3429 }
3430}
3431
3432#[derive(Debug)]
3433pub(crate) enum BlasCompactState {
3434 Compacted,
3436 Waiting(BlasPendingCompact),
3438 Ready { size: wgt::BufferAddress },
3440 Idle,
3442}
3443
3444#[cfg(send_sync)]
3445unsafe impl Send for BlasCompactState {}
3446#[cfg(send_sync)]
3447unsafe impl Sync for BlasCompactState {}
3448
3449#[derive(Debug)]
3450pub(crate) struct BlasState {
3451 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
3452}
3453
3454#[derive(Debug)]
3455pub struct Blas {
3456 pub(crate) state: ResourceState<BlasState>,
3457 pub(crate) device: Arc<Device>,
3458 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
3459 pub(crate) sizes: wgt::BlasGeometrySizeDescriptors,
3460 pub(crate) flags: wgt::AccelerationStructureFlags,
3461 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
3462 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
3463 pub(crate) handle: u64,
3464 pub(crate) label: String,
3466 pub(crate) tracking_data: TrackingData,
3467 pub(crate) compaction_buffer: Option<ManuallyDrop<Box<dyn hal::DynBuffer>>>,
3468 pub(crate) compacted_state: Mutex<BlasCompactState>,
3469}
3470
3471impl Drop for Blas {
3472 #[allow(trivial_casts)]
3473 fn drop(&mut self) {
3474 profiling::scope!("Blas::drop");
3475 api_log!("Blas::drop {:?}", self as *const _);
3476 #[cfg(feature = "trace")]
3477 if let Some(t) = self.device.trace.lock().as_mut() {
3478 use crate::device::trace::{to_trace, Action};
3479 t.add(Action::DropBlas(unsafe { to_trace(self) }));
3480 }
3481 resource_log!("Destroy raw {}", self.error_ident());
3482 if let ResourceState::Valid(state) = &mut self.state {
3484 if let Some(raw) = state.raw.take() {
3485 unsafe {
3486 self.device.raw().destroy_acceleration_structure(raw);
3487 }
3488 }
3489 }
3490 if let Some(mut raw) = self.compaction_buffer.take() {
3491 unsafe {
3492 self.device
3493 .raw()
3494 .destroy_buffer(ManuallyDrop::take(&mut raw))
3495 }
3496 }
3497 }
3498}
3499
3500impl RawResourceAccess for Blas {
3501 type DynResource = dyn hal::DynAccelerationStructure;
3502
3503 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
3504 self.state().ok()?.raw.get(guard).map(|it| it.as_ref())
3505 }
3506}
3507
3508impl Blas {
3509 pub(crate) fn state(&self) -> Result<&BlasState, InvalidResourceError> {
3510 match &self.state {
3511 ResourceState::Valid(state) => Ok(state),
3512 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3513 }
3514 }
3515
3516 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
3517 self.state().map(|_| ())
3518 }
3519
3520 pub(crate) fn invalid(device: Arc<Device>, desc: &BlasDescriptor) -> Arc<Self> {
3521 Arc::new(Blas {
3522 state: ResourceState::Invalid,
3523 size_info: hal::AccelerationStructureBuildSizes {
3524 acceleration_structure_size: 0,
3525 update_scratch_size: 0,
3526 build_scratch_size: 0,
3527 },
3528 sizes: wgt::BlasGeometrySizeDescriptors::Triangles {
3529 descriptors: Vec::new(),
3530 },
3531 flags: desc.flags,
3532 update_mode: desc.update_mode,
3533 built_index: RwLock::new(rank::BLAS_BUILT_INDEX, None),
3534 handle: 0,
3535 label: desc.label.to_string(),
3536 tracking_data: TrackingData::new(device.tracker_indices.blas_s.clone()),
3537 device,
3538 compaction_buffer: None,
3539 compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Idle),
3540 })
3541 }
3542
3543 pub fn handle(&self) -> Option<u64> {
3544 Some(self.handle)
3545 }
3546
3547 pub fn ready_for_compaction(self: &Arc<Self>) -> Result<bool, InvalidResourceError> {
3548 profiling::scope!("Blas::prepare_compact_async");
3549 api_log!("Blas::prepare_compact_async {:?}", Arc::as_ptr(self));
3550
3551 self.check_is_valid()?;
3552 let state = self.compacted_state.lock();
3553 Ok(matches!(*state, BlasCompactState::Ready { .. }))
3554 }
3555
3556 pub fn prepare_compact_async(
3557 self: &Arc<Self>,
3558 callback: Option<BlasCompactCallback>,
3559 ) -> Result<SubmissionIndex, BlasPrepareCompactError> {
3560 profiling::scope!("Blas::prepare_compact_async");
3561 api_log!("Blas::prepare_compact_async {:?}", Arc::as_ptr(self));
3562
3563 let compact_result = self.prepare_compact_async_inner(callback);
3564
3565 match compact_result {
3566 Ok(submission_index) => Ok(submission_index),
3567 Err((mut callback, err)) => {
3568 if let Some(callback) = callback.take() {
3569 callback(Err(err.clone()));
3570 }
3571 Err(err)
3572 }
3573 }
3574 }
3575
3576 fn prepare_compact_async_inner(
3577 self: &Arc<Self>,
3578 op: Option<BlasCompactCallback>,
3579 ) -> Result<SubmissionIndex, (Option<BlasCompactCallback>, BlasPrepareCompactError)> {
3580 let device = &self.device;
3581 if let Err(e) = device.check_is_valid() {
3582 return Err((op, e.into()));
3583 }
3584
3585 if let Err(e) = self.check_is_valid() {
3586 return Err((op, e.into()));
3587 }
3588
3589 if self.built_index.read().is_none() {
3590 return Err((op, BlasPrepareCompactError::NotBuilt));
3591 }
3592
3593 if !self
3594 .flags
3595 .contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION)
3596 {
3597 return Err((op, BlasPrepareCompactError::CompactionUnsupported));
3598 }
3599
3600 let mut state = self.compacted_state.lock();
3601 *state = match *state {
3602 BlasCompactState::Compacted => {
3603 return Err((op, BlasPrepareCompactError::DoubleCompaction))
3604 }
3605 BlasCompactState::Waiting(_) => {
3606 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
3607 }
3608 BlasCompactState::Ready { .. } => {
3609 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
3610 }
3611 BlasCompactState::Idle => BlasCompactState::Waiting(BlasPendingCompact {
3612 op,
3613 _parent_blas: self.clone(),
3614 }),
3615 };
3616
3617 let submit_index = if let Some(queue) = device.get_queue() {
3618 drop(state);
3619 queue.lock_life().prepare_compact(self).unwrap_or(0) } else {
3621 let (mut callback, status) = self.read_back_compact_size(state).unwrap();
3623 if let Some(callback) = callback.take() {
3624 callback(status);
3625 }
3626 0
3627 };
3628
3629 Ok(submit_index)
3630 }
3631
3632 #[must_use]
3634 pub(crate) fn read_back_compact_size(
3635 &self,
3636 mut state: MutexGuard<'_, BlasCompactState>,
3637 ) -> Option<BlasCompactReadyPendingClosure> {
3638 let pending_compact = match mem::replace(&mut *state, BlasCompactState::Idle) {
3639 BlasCompactState::Waiting(pending_mapping) => pending_mapping,
3640 BlasCompactState::Idle => return None,
3642 BlasCompactState::Ready { .. } => {
3643 unreachable!("This should be validated out by `prepare_for_compaction`")
3644 }
3645 _ => panic!("No pending mapping."),
3646 };
3647 let status = {
3648 let compaction_buffer = self.compaction_buffer.as_ref().unwrap().as_ref();
3649 unsafe {
3650 let map_res = self.device.raw().map_buffer(
3651 compaction_buffer,
3652 0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress,
3653 );
3654 match map_res {
3655 Ok(mapping) => {
3656 if !mapping.is_coherent {
3657 #[expect(clippy::single_range_in_vec_init, reason = "intentional")]
3658 self.device.raw().invalidate_mapped_ranges(
3659 compaction_buffer,
3660 &[0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress],
3661 );
3662 }
3663 let size = core::ptr::read_unaligned(
3664 mapping.ptr.as_ptr().cast::<wgt::BufferAddress>(),
3665 );
3666 self.device.raw().unmap_buffer(compaction_buffer);
3667 if self.size_info.acceleration_structure_size != 0 {
3668 debug_assert_ne!(size, 0);
3669 }
3670 *state = BlasCompactState::Ready { size };
3671 Ok(())
3672 }
3673 Err(err) => Err(BlasPrepareCompactError::from(DeviceError::from_hal(err))),
3674 }
3675 }
3676 };
3677 Some((pending_compact.op, status))
3678 }
3679}
3680
3681crate::impl_resource_type!(Blas);
3682crate::impl_labeled!(Blas);
3683crate::impl_parent_device!(Blas);
3684crate::impl_storage_item!(Blas);
3685crate::impl_trackable!(Blas);
3686
3687#[derive(Debug)]
3688pub(crate) struct TlasState {
3689 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
3690 pub(crate) instance_buffer: Box<dyn hal::DynBuffer>,
3691}
3692
3693#[derive(Debug)]
3694pub struct Tlas {
3695 pub(crate) state: ResourceState<TlasState>,
3696 pub(crate) device: Arc<Device>,
3697 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
3698 pub(crate) max_instance_count: u32,
3699 pub(crate) flags: wgt::AccelerationStructureFlags,
3700 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
3701 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
3702 pub(crate) dependencies: RwLock<Vec<Arc<Blas>>>,
3703 pub(crate) label: String,
3705 pub(crate) tracking_data: TrackingData,
3706}
3707
3708impl Drop for Tlas {
3709 #[allow(trivial_casts)]
3710 fn drop(&mut self) {
3711 profiling::scope!("Tlas::drop");
3712 api_log!("Tlas::drop {:?}", self as *const _);
3713
3714 #[cfg(feature = "trace")]
3715 if let Some(t) = self.device.trace.lock().as_mut() {
3716 use crate::device::trace::{to_trace, Action};
3717 t.add(Action::DropTlas(unsafe { to_trace(self) }));
3718 }
3719
3720 resource_log!("Destroy raw {}", self.error_ident());
3721 let ResourceState::Valid(mut state) = mem::replace(&mut self.state, ResourceState::Invalid)
3722 else {
3723 return;
3724 };
3725 if let Some(structure) = state.raw.take() {
3726 unsafe { self.device.raw().destroy_acceleration_structure(structure) };
3727 }
3728 unsafe { self.device.raw().destroy_buffer(state.instance_buffer) };
3729 }
3730}
3731
3732impl Tlas {
3733 pub(crate) fn state(&self) -> Result<&TlasState, InvalidResourceError> {
3734 match &self.state {
3735 ResourceState::Valid(state) => Ok(state),
3736 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3737 }
3738 }
3739
3740 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
3741 self.state().map(|_| ())
3742 }
3743
3744 pub(crate) fn invalid(device: Arc<Device>, desc: &TlasDescriptor) -> Arc<Self> {
3745 Arc::new(Self {
3746 state: ResourceState::Invalid,
3747 label: desc.label.to_string(),
3748 tracking_data: TrackingData::new(device.tracker_indices.tlas_s.clone()),
3749 size_info: hal::AccelerationStructureBuildSizes {
3750 acceleration_structure_size: 0,
3751 update_scratch_size: 0,
3752 build_scratch_size: 0,
3753 },
3754 max_instance_count: desc.max_instances,
3755 flags: desc.flags,
3756 update_mode: desc.update_mode,
3757 built_index: RwLock::new(rank::TLAS_BUILT_INDEX, None),
3758 dependencies: RwLock::new(rank::TLAS_DEPENDENCIES, Vec::new()),
3759 device,
3760 })
3761 }
3762}
3763
3764impl RawResourceAccess for Tlas {
3765 type DynResource = dyn hal::DynAccelerationStructure;
3766
3767 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
3768 self.state().ok()?.raw.get(guard).map(|raw| raw.as_ref())
3769 }
3770}
3771
3772crate::impl_resource_type!(Tlas);
3773crate::impl_labeled!(Tlas);
3774crate::impl_parent_device!(Tlas);
3775crate::impl_storage_item!(Tlas);
3776crate::impl_trackable!(Tlas);