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