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