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