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