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