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