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 if host == HostMap::Write {
879 #[cfg(feature = "trace")]
880 if let Some(ref mut trace) = *device.trace.lock() {
881 use crate::device::trace::IntoTrace;
882
883 let size = range.end - range.start;
884 let data = trace.make_binary("bin", unsafe {
885 core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize)
886 });
887 trace.add(trace::Action::WriteBuffer {
888 id: self.to_trace(),
889 data,
890 range: range.clone(),
891 queued: false,
892 });
893 }
894 if !mapping.is_coherent {
895 unsafe { device.raw().flush_mapped_ranges(raw_buf, &[range]) };
896 }
897 }
898 unsafe { device.raw().unmap_buffer(raw_buf) };
899 }
900 }
901 Ok(None)
902 }
903
904 pub fn destroy(self: &Arc<Self>) {
905 let device = &self.device;
906
907 let temp = {
908 let mut snatch_guard = device.snatchable_lock.write();
909
910 let raw = match self.raw.snatch(&mut snatch_guard) {
911 Some(raw) => raw,
912 None => {
913 return;
915 }
916 };
917
918 let timestamp_normalization_bind_group = self
919 .timestamp_normalization_bind_group
920 .snatch(&mut snatch_guard);
921
922 let indirect_validation_bind_groups = self
923 .indirect_validation_bind_groups
924 .snatch(&mut snatch_guard);
925
926 drop(snatch_guard);
927
928 let bind_groups = {
929 let mut guard = self.bind_groups.lock();
930 mem::take(&mut *guard)
931 };
932
933 queue::TempResource::DestroyedBuffer(DestroyedBuffer {
934 raw: ManuallyDrop::new(raw),
935 device: Arc::clone(&self.device),
936 label: self.label().to_owned(),
937 bind_groups,
938 timestamp_normalization_bind_group,
939 indirect_validation_bind_groups,
940 })
941 };
942
943 if let Some(queue) = device.get_queue() {
944 let mut pending_writes = queue.pending_writes.lock();
945 if pending_writes.contains_buffer(self) {
946 pending_writes.consume_temp(temp);
947 } else {
948 let mut life_lock = queue.lock_life();
949 let last_submit_index = life_lock.get_buffer_latest_submission_index(self);
950 if let Some(last_submit_index) = last_submit_index {
951 life_lock.schedule_resource_destruction(temp, last_submit_index);
952 }
953 }
954 }
955 }
956}
957
958#[derive(Clone, Debug, Error)]
959#[non_exhaustive]
960pub enum CreateBufferError {
961 #[error(transparent)]
962 Device(#[from] DeviceError),
963 #[error("Failed to map buffer while creating: {0}")]
964 AccessError(#[from] BufferAccessError),
965 #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
966 UnalignedSize,
967 #[error("Invalid usage flags {0:?}")]
968 InvalidUsage(wgt::BufferUsages),
969 #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
970 UsageMismatch(wgt::BufferUsages),
971 #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
972 MaxBufferSize { requested: u64, maximum: u64 },
973 #[error(transparent)]
974 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
975 #[error(transparent)]
976 MissingFeatures(#[from] MissingFeatures),
977 #[error("Failed to create bind group for indirect buffer validation: {0}")]
978 IndirectValidationBindGroup(DeviceError),
979}
980
981crate::impl_resource_type!(Buffer);
982crate::impl_labeled!(Buffer);
983crate::impl_parent_device!(Buffer);
984crate::impl_storage_item!(Buffer);
985crate::impl_trackable!(Buffer);
986
987impl WebGpuError for CreateBufferError {
988 fn webgpu_error_type(&self) -> ErrorType {
989 let e: &dyn WebGpuError = match self {
990 Self::Device(e) => e,
991 Self::AccessError(e) => e,
992 Self::MissingDownlevelFlags(e) => e,
993 Self::IndirectValidationBindGroup(e) => e,
994 Self::MissingFeatures(e) => e,
995
996 Self::UnalignedSize
997 | Self::InvalidUsage(_)
998 | Self::UsageMismatch(_)
999 | Self::MaxBufferSize { .. } => return ErrorType::Validation,
1000 };
1001 e.webgpu_error_type()
1002 }
1003}
1004
1005#[derive(Debug)]
1007pub struct DestroyedBuffer {
1008 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1009 device: Arc<Device>,
1010 label: String,
1011 bind_groups: WeakVec<BindGroup>,
1012 timestamp_normalization_bind_group: Option<TimestampNormalizationBindGroup>,
1013 indirect_validation_bind_groups: Option<crate::indirect_validation::BindGroups>,
1014}
1015
1016impl DestroyedBuffer {
1017 pub fn label(&self) -> &dyn fmt::Debug {
1018 &self.label
1019 }
1020}
1021
1022impl Drop for DestroyedBuffer {
1023 fn drop(&mut self) {
1024 let mut deferred = self.device.deferred_destroy.lock();
1025 deferred.push(DeferredDestroy::BindGroups(mem::take(
1026 &mut self.bind_groups,
1027 )));
1028 drop(deferred);
1029
1030 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
1031 raw.dispose(self.device.raw());
1032 }
1033
1034 if let Some(raw) = self.indirect_validation_bind_groups.take() {
1035 raw.dispose(self.device.raw());
1036 }
1037
1038 resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
1039 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1041 unsafe {
1042 hal::DynDevice::destroy_buffer(self.device.raw(), raw);
1043 }
1044 }
1045}
1046
1047#[cfg(send_sync)]
1048unsafe impl Send for StagingBuffer {}
1049#[cfg(send_sync)]
1050unsafe impl Sync for StagingBuffer {}
1051
1052#[derive(Debug)]
1072pub struct StagingBuffer {
1073 raw: Box<dyn hal::DynBuffer>,
1074 device: Arc<Device>,
1075 pub(crate) size: wgt::BufferSize,
1076 is_coherent: bool,
1077 ptr: NonNull<u8>,
1078}
1079
1080impl StagingBuffer {
1081 pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {
1082 profiling::scope!("StagingBuffer::new");
1083 let stage_desc = hal::BufferDescriptor {
1084 label: hal_label(Some("(wgpu internal) Staging"), device.instance_flags),
1085 size: size.get(),
1086 usage: wgt::BufferUses::MAP_WRITE | wgt::BufferUses::COPY_SRC,
1087 memory_flags: hal::MemoryFlags::TRANSIENT,
1088 };
1089
1090 let raw = unsafe { device.raw().create_buffer(&stage_desc) }
1091 .map_err(|e| device.handle_hal_error(e))?;
1092 let mapping = unsafe { device.raw().map_buffer(raw.as_ref(), 0..size.get()) }
1093 .map_err(|e| device.handle_hal_error(e))?;
1094
1095 let staging_buffer = StagingBuffer {
1096 raw,
1097 device: device.clone(),
1098 size,
1099 is_coherent: mapping.is_coherent,
1100 ptr: mapping.ptr,
1101 };
1102
1103 Ok(staging_buffer)
1104 }
1105
1106 pub(crate) unsafe fn ptr(&self) -> NonNull<u8> {
1109 self.ptr
1110 }
1111
1112 #[cfg(feature = "trace")]
1113 pub(crate) fn get_data(&self) -> &[u8] {
1114 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.size.get() as usize) }
1115 }
1116
1117 pub(crate) fn write_zeros(&mut self) {
1118 unsafe { core::ptr::write_bytes(self.ptr.as_ptr(), 0, self.size.get() as usize) };
1119 }
1120
1121 pub(crate) fn write(&mut self, data: &[u8]) {
1122 assert!(data.len() >= self.size.get() as usize);
1123 unsafe {
1126 core::ptr::copy_nonoverlapping(
1127 data.as_ptr(),
1128 self.ptr.as_ptr(),
1129 self.size.get() as usize,
1130 );
1131 }
1132 }
1133
1134 pub(crate) unsafe fn write_with_offset(
1136 &mut self,
1137 data: &[u8],
1138 src_offset: isize,
1139 dst_offset: isize,
1140 size: usize,
1141 ) {
1142 unsafe {
1143 debug_assert!(
1144 (src_offset + size as isize) as usize <= data.len(),
1145 "src_offset + size must be in-bounds: src_offset = {}, size = {}, data.len() = {}",
1146 src_offset,
1147 size,
1148 data.len()
1149 );
1150 core::ptr::copy_nonoverlapping(
1151 data.as_ptr().offset(src_offset),
1152 self.ptr.as_ptr().offset(dst_offset),
1153 size,
1154 );
1155 }
1156 }
1157
1158 pub(crate) fn flush(self) -> FlushedStagingBuffer {
1159 let device = self.device.raw();
1160 if !self.is_coherent {
1161 #[allow(clippy::single_range_in_vec_init)]
1162 unsafe {
1163 device.flush_mapped_ranges(self.raw.as_ref(), &[0..self.size.get()])
1164 };
1165 }
1166 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1167
1168 let StagingBuffer {
1169 raw, device, size, ..
1170 } = self;
1171
1172 FlushedStagingBuffer {
1173 raw: ManuallyDrop::new(raw),
1174 device,
1175 size,
1176 }
1177 }
1178}
1179
1180crate::impl_resource_type!(StagingBuffer);
1181crate::impl_storage_item!(StagingBuffer);
1182
1183#[derive(Debug)]
1184pub struct FlushedStagingBuffer {
1185 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1186 device: Arc<Device>,
1187 pub(crate) size: wgt::BufferSize,
1188}
1189
1190impl FlushedStagingBuffer {
1191 pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {
1192 self.raw.as_ref()
1193 }
1194}
1195
1196impl Drop for FlushedStagingBuffer {
1197 fn drop(&mut self) {
1198 resource_log!("Destroy raw StagingBuffer");
1199 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1201 unsafe { self.device.raw().destroy_buffer(raw) };
1202 }
1203}
1204
1205pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
1206
1207#[derive(Debug)]
1208pub(crate) enum TextureInner {
1209 Native {
1210 raw: Box<dyn hal::DynTexture>,
1211 },
1212 Surface {
1213 raw: Box<dyn hal::DynSurfaceTexture>,
1214 },
1215}
1216
1217impl TextureInner {
1218 pub(crate) fn raw(&self) -> &dyn hal::DynTexture {
1219 match self {
1220 Self::Native { raw } => raw.as_ref(),
1221 Self::Surface { raw, .. } => raw.as_ref().borrow(),
1222 }
1223 }
1224}
1225
1226#[derive(Debug)]
1227pub enum TextureClearMode {
1228 BufferCopy,
1229 RenderPass {
1231 clear_views: SmallVec<[ManuallyDrop<Box<dyn hal::DynTextureView>>; 1]>,
1232 is_color: bool,
1233 },
1234 Surface {
1235 clear_view: ManuallyDrop<Box<dyn hal::DynTextureView>>,
1236 },
1237 None,
1240}
1241
1242#[derive(Debug)]
1243pub struct Texture {
1244 pub(crate) inner: Snatchable<TextureInner>,
1245 pub(crate) device: Arc<Device>,
1246 pub(crate) desc: wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
1247 pub(crate) _hal_usage: wgt::TextureUses,
1248 pub(crate) format_features: wgt::TextureFormatFeatures,
1249 pub(crate) initialization_status: RwLock<TextureInitTracker>,
1250 pub(crate) full_range: TextureSelector,
1251 pub(crate) label: String,
1253 pub(crate) tracking_data: TrackingData,
1254 pub(crate) clear_mode: RwLock<TextureClearMode>,
1255 pub(crate) views: Mutex<WeakVec<TextureView>>,
1256 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
1257}
1258
1259impl Texture {
1260 pub(crate) fn new(
1261 device: &Arc<Device>,
1262 inner: TextureInner,
1263 hal_usage: wgt::TextureUses,
1264 desc: &TextureDescriptor,
1265 format_features: wgt::TextureFormatFeatures,
1266 clear_mode: TextureClearMode,
1267 init: bool,
1268 ) -> Self {
1269 Texture {
1270 inner: Snatchable::new(inner),
1271 device: device.clone(),
1272 desc: desc.map_label(|_| ()),
1273 _hal_usage: hal_usage,
1274 format_features,
1275 initialization_status: RwLock::new(
1276 rank::TEXTURE_INITIALIZATION_STATUS,
1277 if init {
1278 TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count())
1279 } else {
1280 TextureInitTracker::new(desc.mip_level_count, 0)
1281 },
1282 ),
1283 full_range: TextureSelector {
1284 mips: 0..desc.mip_level_count,
1285 layers: 0..desc.array_layer_count(),
1286 },
1287 label: desc.label.to_string(),
1288 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1289 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
1290 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1291 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1292 }
1293 }
1294
1295 pub(crate) fn check_usage(
1298 &self,
1299 expected: wgt::TextureUsages,
1300 ) -> Result<(), MissingTextureUsageError> {
1301 if self.desc.usage.contains(expected) {
1302 Ok(())
1303 } else {
1304 Err(MissingTextureUsageError {
1305 res: self.error_ident(),
1306 actual: self.desc.usage,
1307 expected,
1308 })
1309 }
1310 }
1311}
1312
1313impl Drop for Texture {
1314 fn drop(&mut self) {
1315 match *self.clear_mode.write() {
1316 TextureClearMode::Surface {
1317 ref mut clear_view, ..
1318 } => {
1319 let raw = unsafe { ManuallyDrop::take(clear_view) };
1321 unsafe {
1322 self.device.raw().destroy_texture_view(raw);
1323 }
1324 }
1325 TextureClearMode::RenderPass {
1326 ref mut clear_views,
1327 ..
1328 } => {
1329 clear_views.iter_mut().for_each(|clear_view| {
1330 let raw = unsafe { ManuallyDrop::take(clear_view) };
1332 unsafe {
1333 self.device.raw().destroy_texture_view(raw);
1334 }
1335 });
1336 }
1337 _ => {}
1338 };
1339
1340 if let Some(TextureInner::Native { raw }) = self.inner.take() {
1341 resource_log!("Destroy raw {}", self.error_ident());
1342 unsafe {
1343 self.device.raw().destroy_texture(raw);
1344 }
1345 }
1346 }
1347}
1348
1349impl RawResourceAccess for Texture {
1350 type DynResource = dyn hal::DynTexture;
1351
1352 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1353 self.inner.get(guard).map(|t| t.raw())
1354 }
1355}
1356
1357impl Texture {
1358 pub(crate) fn try_inner<'a>(
1359 &'a self,
1360 guard: &'a SnatchGuard,
1361 ) -> Result<&'a TextureInner, DestroyedResourceError> {
1362 self.inner
1363 .get(guard)
1364 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1365 }
1366
1367 pub(crate) fn check_destroyed(
1368 &self,
1369 guard: &SnatchGuard,
1370 ) -> Result<(), DestroyedResourceError> {
1371 self.inner
1372 .get(guard)
1373 .map(|_| ())
1374 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1375 }
1376
1377 pub(crate) fn get_clear_view<'a>(
1378 clear_mode: &'a TextureClearMode,
1379 desc: &'a wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
1380 mip_level: u32,
1381 depth_or_layer: u32,
1382 ) -> &'a dyn hal::DynTextureView {
1383 match *clear_mode {
1384 TextureClearMode::BufferCopy => {
1385 panic!("Given texture is cleared with buffer copies, not render passes")
1386 }
1387 TextureClearMode::None => {
1388 panic!("Given texture can't be cleared")
1389 }
1390 TextureClearMode::Surface { ref clear_view, .. } => clear_view.as_ref(),
1391 TextureClearMode::RenderPass {
1392 ref clear_views, ..
1393 } => {
1394 let index = if desc.dimension == wgt::TextureDimension::D3 {
1395 (0..mip_level).fold(0, |acc, mip| {
1396 acc + (desc.size.depth_or_array_layers >> mip).max(1)
1397 })
1398 } else {
1399 mip_level * desc.size.depth_or_array_layers
1400 } + depth_or_layer;
1401 clear_views[index as usize].as_ref()
1402 }
1403 }
1404 }
1405
1406 pub fn destroy(self: &Arc<Self>) {
1407 let device = &self.device;
1408
1409 let temp = {
1410 let raw = match self.inner.snatch(&mut device.snatchable_lock.write()) {
1411 Some(TextureInner::Native { raw }) => raw,
1412 Some(TextureInner::Surface { .. }) => {
1413 return;
1414 }
1415 None => {
1416 return;
1418 }
1419 };
1420
1421 let views = {
1422 let mut guard = self.views.lock();
1423 mem::take(&mut *guard)
1424 };
1425
1426 let bind_groups = {
1427 let mut guard = self.bind_groups.lock();
1428 mem::take(&mut *guard)
1429 };
1430
1431 queue::TempResource::DestroyedTexture(DestroyedTexture {
1432 raw: ManuallyDrop::new(raw),
1433 views,
1434 clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None),
1435 bind_groups,
1436 device: Arc::clone(&self.device),
1437 label: self.label().to_owned(),
1438 })
1439 };
1440
1441 if let Some(queue) = device.get_queue() {
1442 let mut pending_writes = queue.pending_writes.lock();
1443 if pending_writes.contains_texture(self) {
1444 pending_writes.consume_temp(temp);
1445 } else {
1446 let mut life_lock = queue.lock_life();
1447 let last_submit_index = life_lock.get_texture_latest_submission_index(self);
1448 if let Some(last_submit_index) = last_submit_index {
1449 life_lock.schedule_resource_destruction(temp, last_submit_index);
1450 }
1451 }
1452 }
1453 }
1454}
1455
1456#[derive(Debug)]
1458pub struct DestroyedTexture {
1459 raw: ManuallyDrop<Box<dyn hal::DynTexture>>,
1460 views: WeakVec<TextureView>,
1461 clear_mode: TextureClearMode,
1462 bind_groups: WeakVec<BindGroup>,
1463 device: Arc<Device>,
1464 label: String,
1465}
1466
1467impl DestroyedTexture {
1468 pub fn label(&self) -> &dyn fmt::Debug {
1469 &self.label
1470 }
1471}
1472
1473impl Drop for DestroyedTexture {
1474 fn drop(&mut self) {
1475 let device = &self.device;
1476
1477 let mut deferred = device.deferred_destroy.lock();
1478 deferred.push(DeferredDestroy::TextureViews(mem::take(&mut self.views)));
1479 deferred.push(DeferredDestroy::BindGroups(mem::take(
1480 &mut self.bind_groups,
1481 )));
1482 drop(deferred);
1483
1484 match mem::replace(&mut self.clear_mode, TextureClearMode::None) {
1485 TextureClearMode::RenderPass { clear_views, .. } => {
1486 for clear_view in clear_views {
1487 let raw = ManuallyDrop::into_inner(clear_view);
1488 unsafe { self.device.raw().destroy_texture_view(raw) };
1489 }
1490 }
1491 TextureClearMode::Surface { clear_view } => {
1492 let raw = ManuallyDrop::into_inner(clear_view);
1493 unsafe { self.device.raw().destroy_texture_view(raw) };
1494 }
1495 _ => (),
1496 }
1497
1498 resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
1499 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1501 unsafe {
1502 self.device.raw().destroy_texture(raw);
1503 }
1504 }
1505}
1506
1507#[derive(Clone, Copy, Debug)]
1508pub enum TextureErrorDimension {
1509 X,
1510 Y,
1511 Z,
1512}
1513
1514#[derive(Clone, Debug, Error)]
1515#[non_exhaustive]
1516pub enum TextureDimensionError {
1517 #[error("Dimension {0:?} is zero")]
1518 Zero(TextureErrorDimension),
1519 #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
1520 LimitExceeded {
1521 dim: TextureErrorDimension,
1522 given: u32,
1523 limit: u32,
1524 },
1525 #[error("Sample count {0} is invalid")]
1526 InvalidSampleCount(u32),
1527 #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
1528 NotMultipleOfBlockWidth {
1529 width: u32,
1530 block_width: u32,
1531 format: wgt::TextureFormat,
1532 },
1533 #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
1534 NotMultipleOfBlockHeight {
1535 height: u32,
1536 block_height: u32,
1537 format: wgt::TextureFormat,
1538 },
1539 #[error(
1540 "Width {width} is not a multiple of {format:?}'s width multiple requirement ({multiple})"
1541 )]
1542 WidthNotMultipleOf {
1543 width: u32,
1544 multiple: u32,
1545 format: wgt::TextureFormat,
1546 },
1547 #[error("Height {height} is not a multiple of {format:?}'s height multiple requirement ({multiple})")]
1548 HeightNotMultipleOf {
1549 height: u32,
1550 multiple: u32,
1551 format: wgt::TextureFormat,
1552 },
1553 #[error("Multisampled texture depth or array layers must be 1, got {0}")]
1554 MultisampledDepthOrArrayLayer(u32),
1555}
1556
1557impl WebGpuError for TextureDimensionError {
1558 fn webgpu_error_type(&self) -> ErrorType {
1559 ErrorType::Validation
1560 }
1561}
1562
1563#[derive(Clone, Debug, Error)]
1564#[non_exhaustive]
1565pub enum CreateTextureError {
1566 #[error(transparent)]
1567 Device(#[from] DeviceError),
1568 #[error(transparent)]
1569 CreateTextureView(#[from] CreateTextureViewError),
1570 #[error("Invalid usage flags {0:?}")]
1571 InvalidUsage(wgt::TextureUsages),
1572 #[error("Texture usage {0:?} is not compatible with texture usage {1:?}")]
1573 IncompatibleUsage(wgt::TextureUsages, wgt::TextureUsages),
1574 #[error(transparent)]
1575 InvalidDimension(#[from] TextureDimensionError),
1576 #[error("Depth texture ({1:?}) can't be created as {0:?}")]
1577 InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
1578 #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
1579 InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
1580 #[error(
1581 "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
1582 )]
1583 InvalidMipLevelCount { requested: u32, maximum: u32 },
1584 #[error(
1585 "Texture usages {0:?} are not allowed on a texture of type {1:?}{downlevel_suffix}",
1586 downlevel_suffix = if *.2 { " due to downlevel restrictions" } else { "" }
1587 )]
1588 InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
1589 #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
1590 InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
1591 #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
1592 InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
1593 #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
1594 InvalidMultisampledStorageBinding,
1595 #[error("Format {0:?} does not support multisampling")]
1596 InvalidMultisampledFormat(wgt::TextureFormat),
1597 #[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:?}.")]
1598 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
1599 #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
1600 MultisampledNotRenderAttachment,
1601 #[error("Texture format {0:?} can't be used due to missing features")]
1602 MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
1603 #[error(transparent)]
1604 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1605}
1606
1607crate::impl_resource_type!(Texture);
1608crate::impl_labeled!(Texture);
1609crate::impl_parent_device!(Texture);
1610crate::impl_storage_item!(Texture);
1611crate::impl_trackable!(Texture);
1612
1613impl Borrow<TextureSelector> for Texture {
1614 fn borrow(&self) -> &TextureSelector {
1615 &self.full_range
1616 }
1617}
1618
1619impl WebGpuError for CreateTextureError {
1620 fn webgpu_error_type(&self) -> ErrorType {
1621 let e: &dyn WebGpuError = match self {
1622 Self::Device(e) => e,
1623 Self::CreateTextureView(e) => e,
1624 Self::InvalidDimension(e) => e,
1625 Self::MissingFeatures(_, e) => e,
1626 Self::MissingDownlevelFlags(e) => e,
1627
1628 Self::InvalidUsage(_)
1629 | Self::IncompatibleUsage(_, _)
1630 | Self::InvalidDepthDimension(_, _)
1631 | Self::InvalidCompressedDimension(_, _)
1632 | Self::InvalidMipLevelCount { .. }
1633 | Self::InvalidFormatUsages(_, _, _)
1634 | Self::InvalidViewFormat(_, _)
1635 | Self::InvalidDimensionUsages(_, _)
1636 | Self::InvalidMultisampledStorageBinding
1637 | Self::InvalidMultisampledFormat(_)
1638 | Self::InvalidSampleCount(..)
1639 | Self::MultisampledNotRenderAttachment => return ErrorType::Validation,
1640 };
1641 e.webgpu_error_type()
1642 }
1643}
1644
1645#[derive(Clone, Debug, Default, Eq, PartialEq)]
1647#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1648#[cfg_attr(feature = "serde", serde(default))]
1649pub struct TextureViewDescriptor<'a> {
1650 pub label: Label<'a>,
1654 pub format: Option<wgt::TextureFormat>,
1659 pub dimension: Option<wgt::TextureViewDimension>,
1665 pub usage: Option<wgt::TextureUsages>,
1668 pub range: wgt::ImageSubresourceRange,
1670}
1671
1672#[derive(Debug)]
1673pub(crate) struct HalTextureViewDescriptor {
1674 pub texture_format: wgt::TextureFormat,
1675 pub format: wgt::TextureFormat,
1676 pub usage: wgt::TextureUsages,
1677 pub dimension: wgt::TextureViewDimension,
1678 pub range: wgt::ImageSubresourceRange,
1679}
1680
1681impl HalTextureViewDescriptor {
1682 pub fn aspects(&self) -> hal::FormatAspects {
1683 hal::FormatAspects::new(self.texture_format, self.range.aspect)
1684 }
1685}
1686
1687#[derive(Debug, Copy, Clone, Error)]
1688pub enum TextureViewNotRenderableReason {
1689 #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
1690 Usage(wgt::TextureUsages),
1691 #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
1692 Dimension(wgt::TextureViewDimension),
1693 #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
1694 MipLevelCount(u32),
1695 #[error("This texture view has more than one array layer. View array layers: {0:?}")]
1696 ArrayLayerCount(u32),
1697 #[error(
1698 "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
1699 )]
1700 Aspects(hal::FormatAspects),
1701}
1702
1703#[derive(Debug)]
1704pub struct TextureView {
1705 pub(crate) raw: Snatchable<Box<dyn hal::DynTextureView>>,
1706 pub(crate) parent: Arc<Texture>,
1708 pub(crate) device: Arc<Device>,
1709 pub(crate) desc: HalTextureViewDescriptor,
1710 pub(crate) format_features: wgt::TextureFormatFeatures,
1711 pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
1713 pub(crate) samples: u32,
1714 pub(crate) selector: TextureSelector,
1715 pub(crate) label: String,
1717}
1718
1719impl Drop for TextureView {
1720 fn drop(&mut self) {
1721 if let Some(raw) = self.raw.take() {
1722 resource_log!("Destroy raw {}", self.error_ident());
1723 unsafe {
1724 self.device.raw().destroy_texture_view(raw);
1725 }
1726 }
1727 }
1728}
1729
1730impl RawResourceAccess for TextureView {
1731 type DynResource = dyn hal::DynTextureView;
1732
1733 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1734 self.raw.get(guard).map(|it| it.as_ref())
1735 }
1736
1737 fn try_raw<'a>(
1738 &'a self,
1739 guard: &'a SnatchGuard,
1740 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
1741 self.parent.check_destroyed(guard)?;
1742
1743 self.raw(guard)
1744 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1745 }
1746}
1747
1748impl TextureView {
1749 pub(crate) fn check_usage(
1752 &self,
1753 expected: wgt::TextureUsages,
1754 ) -> Result<(), MissingTextureUsageError> {
1755 if self.desc.usage.contains(expected) {
1756 Ok(())
1757 } else {
1758 Err(MissingTextureUsageError {
1759 res: self.error_ident(),
1760 actual: self.desc.usage,
1761 expected,
1762 })
1763 }
1764 }
1765}
1766
1767#[derive(Clone, Debug, Error)]
1768#[non_exhaustive]
1769pub enum CreateTextureViewError {
1770 #[error(transparent)]
1771 Device(#[from] DeviceError),
1772 #[error(transparent)]
1773 DestroyedResource(#[from] DestroyedResourceError),
1774 #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
1775 InvalidTextureViewDimension {
1776 view: wgt::TextureViewDimension,
1777 texture: wgt::TextureDimension,
1778 },
1779 #[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.")]
1780 TextureViewFormatNotRenderable(wgt::TextureFormat),
1781 #[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.")]
1782 TextureViewFormatNotStorage(wgt::TextureFormat),
1783 #[error("Texture view usages (`{view:?}`) must be a subset of the texture's original usages (`{texture:?}`)")]
1784 InvalidTextureViewUsage {
1785 view: wgt::TextureUsages,
1786 texture: wgt::TextureUsages,
1787 },
1788 #[error("Texture view dimension `{0:?}` cannot be used with a multisampled texture")]
1789 InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
1790 #[error(
1791 "TextureView has an arrayLayerCount of {depth}. Views of type `Cube` must have arrayLayerCount of 6."
1792 )]
1793 InvalidCubemapTextureDepth { depth: u32 },
1794 #[error("TextureView has an arrayLayerCount of {depth}. Views of type `CubeArray` must have an arrayLayerCount that is a multiple of 6.")]
1795 InvalidCubemapArrayTextureDepth { depth: u32 },
1796 #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
1797 InvalidCubeTextureViewSize,
1798 #[error("Mip level count is 0")]
1799 ZeroMipLevelCount,
1800 #[error("Array layer count is 0")]
1801 ZeroArrayLayerCount,
1802 #[error(
1803 "TextureView spans mip levels [{base_mip_level}, {end_mip_level}) \
1804 (mipLevelCount {mip_level_count}) but the texture view only has {total} total mip levels",
1805 end_mip_level = base_mip_level + mip_level_count
1806 )]
1807 TooManyMipLevels {
1808 base_mip_level: u32,
1809 mip_level_count: u32,
1810 total: u32,
1811 },
1812 #[error(
1813 "TextureView spans array layers [{base_array_layer}, {end_array_layer}) \
1814 (arrayLayerCount {array_layer_count}) but the texture view only has {total} total layers",
1815 end_array_layer = base_array_layer + array_layer_count
1816 )]
1817 TooManyArrayLayers {
1818 base_array_layer: u32,
1819 array_layer_count: u32,
1820 total: u32,
1821 },
1822 #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
1823 InvalidArrayLayerCount {
1824 requested: u32,
1825 dim: wgt::TextureViewDimension,
1826 },
1827 #[error(
1828 "Aspect {requested_aspect:?} is not a valid aspect of the source texture format {texture_format:?}"
1829 )]
1830 InvalidAspect {
1831 texture_format: wgt::TextureFormat,
1832 requested_aspect: wgt::TextureAspect,
1833 },
1834 #[error(
1835 "Trying to create a view of format {view:?} of a texture with format {texture:?}, \
1836 but this view format is not present in the texture's viewFormat array"
1837 )]
1838 FormatReinterpretation {
1839 texture: wgt::TextureFormat,
1840 view: wgt::TextureFormat,
1841 },
1842 #[error(transparent)]
1843 InvalidResource(#[from] InvalidResourceError),
1844 #[error(transparent)]
1845 MissingFeatures(#[from] MissingFeatures),
1846}
1847
1848impl WebGpuError for CreateTextureViewError {
1849 fn webgpu_error_type(&self) -> ErrorType {
1850 match self {
1851 Self::Device(e) => e.webgpu_error_type(),
1852
1853 Self::InvalidTextureViewDimension { .. }
1854 | Self::InvalidResource(_)
1855 | Self::InvalidMultisampledTextureViewDimension(_)
1856 | Self::InvalidCubemapTextureDepth { .. }
1857 | Self::InvalidCubemapArrayTextureDepth { .. }
1858 | Self::InvalidCubeTextureViewSize
1859 | Self::ZeroMipLevelCount
1860 | Self::ZeroArrayLayerCount
1861 | Self::TooManyMipLevels { .. }
1862 | Self::TooManyArrayLayers { .. }
1863 | Self::InvalidArrayLayerCount { .. }
1864 | Self::InvalidAspect { .. }
1865 | Self::FormatReinterpretation { .. }
1866 | Self::DestroyedResource(_)
1867 | Self::TextureViewFormatNotRenderable(_)
1868 | Self::TextureViewFormatNotStorage(_)
1869 | Self::InvalidTextureViewUsage { .. }
1870 | Self::MissingFeatures(_) => ErrorType::Validation,
1871 }
1872 }
1873}
1874
1875#[derive(Clone, Debug, Error)]
1876#[non_exhaustive]
1877pub enum TextureViewDestroyError {}
1878
1879crate::impl_resource_type!(TextureView);
1880crate::impl_labeled!(TextureView);
1881crate::impl_parent_device!(TextureView);
1882crate::impl_storage_item!(TextureView);
1883
1884pub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;
1885
1886#[derive(Debug)]
1887pub struct ExternalTexture {
1888 pub(crate) device: Arc<Device>,
1889 pub(crate) planes: arrayvec::ArrayVec<Arc<TextureView>, 3>,
1891 pub(crate) params: Arc<Buffer>,
1894 pub(crate) label: String,
1896 pub(crate) tracking_data: TrackingData,
1897}
1898
1899impl Drop for ExternalTexture {
1900 fn drop(&mut self) {
1901 resource_log!("Destroy raw {}", self.error_ident());
1902 }
1903}
1904
1905impl ExternalTexture {
1906 pub fn destroy(self: &Arc<Self>) {
1907 self.params.destroy();
1908 }
1909}
1910
1911#[derive(Clone, Debug, Error)]
1912#[non_exhaustive]
1913pub enum CreateExternalTextureError {
1914 #[error(transparent)]
1915 Device(#[from] DeviceError),
1916 #[error(transparent)]
1917 MissingFeatures(#[from] MissingFeatures),
1918 #[error(transparent)]
1919 InvalidResource(#[from] InvalidResourceError),
1920 #[error(transparent)]
1921 CreateBuffer(#[from] CreateBufferError),
1922 #[error(transparent)]
1923 QueueWrite(#[from] queue::QueueWriteError),
1924 #[error("External texture format {format:?} expects {expected} planes, but given {provided}")]
1925 IncorrectPlaneCount {
1926 format: wgt::ExternalTextureFormat,
1927 expected: usize,
1928 provided: usize,
1929 },
1930 #[error("External texture planes cannot be multisampled, but given view with samples = {0}")]
1931 InvalidPlaneMultisample(u32),
1932 #[error("External texture planes expect a filterable float sample type, but given view with format {format:?} (sample type {sample_type:?})")]
1933 InvalidPlaneSampleType {
1934 format: wgt::TextureFormat,
1935 sample_type: wgt::TextureSampleType,
1936 },
1937 #[error("External texture planes expect 2D dimension, but given view with dimension = {0:?}")]
1938 InvalidPlaneDimension(wgt::TextureViewDimension),
1939 #[error(transparent)]
1940 MissingTextureUsage(#[from] MissingTextureUsageError),
1941 #[error("External texture format {format:?} plane {plane} expects format with {expected} components but given view with format {provided:?} ({} components)",
1942 provided.components())]
1943 InvalidPlaneFormat {
1944 format: wgt::ExternalTextureFormat,
1945 plane: usize,
1946 expected: u8,
1947 provided: wgt::TextureFormat,
1948 },
1949}
1950
1951impl WebGpuError for CreateExternalTextureError {
1952 fn webgpu_error_type(&self) -> ErrorType {
1953 let e: &dyn WebGpuError = match self {
1954 CreateExternalTextureError::Device(e) => e,
1955 CreateExternalTextureError::MissingFeatures(e) => e,
1956 CreateExternalTextureError::InvalidResource(e) => e,
1957 CreateExternalTextureError::CreateBuffer(e) => e,
1958 CreateExternalTextureError::QueueWrite(e) => e,
1959 CreateExternalTextureError::MissingTextureUsage(e) => e,
1960 CreateExternalTextureError::IncorrectPlaneCount { .. }
1961 | CreateExternalTextureError::InvalidPlaneMultisample(_)
1962 | CreateExternalTextureError::InvalidPlaneSampleType { .. }
1963 | CreateExternalTextureError::InvalidPlaneDimension(_)
1964 | CreateExternalTextureError::InvalidPlaneFormat { .. } => {
1965 return ErrorType::Validation
1966 }
1967 };
1968 e.webgpu_error_type()
1969 }
1970}
1971
1972crate::impl_resource_type!(ExternalTexture);
1973crate::impl_labeled!(ExternalTexture);
1974crate::impl_parent_device!(ExternalTexture);
1975crate::impl_storage_item!(ExternalTexture);
1976crate::impl_trackable!(ExternalTexture);
1977
1978#[derive(Clone, Debug, PartialEq)]
1980#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1981pub struct SamplerDescriptor<'a> {
1982 pub label: Label<'a>,
1986 pub address_modes: [wgt::AddressMode; 3],
1988 pub mag_filter: wgt::FilterMode,
1990 pub min_filter: wgt::FilterMode,
1992 pub mipmap_filter: wgt::MipmapFilterMode,
1994 pub lod_min_clamp: f32,
1996 pub lod_max_clamp: f32,
1998 pub compare: Option<wgt::CompareFunction>,
2000 pub anisotropy_clamp: u16,
2002 pub border_color: Option<wgt::SamplerBorderColor>,
2005}
2006
2007#[derive(Debug)]
2008pub struct Sampler {
2009 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynSampler>>,
2010 pub(crate) device: Arc<Device>,
2011 pub(crate) label: String,
2013 pub(crate) tracking_data: TrackingData,
2014 pub(crate) comparison: bool,
2016 pub(crate) filtering: bool,
2018}
2019
2020impl Drop for Sampler {
2021 fn drop(&mut self) {
2022 resource_log!("Destroy raw {}", self.error_ident());
2023 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
2025 unsafe {
2026 self.device.raw().destroy_sampler(raw);
2027 }
2028 }
2029}
2030
2031impl Sampler {
2032 pub(crate) fn raw(&self) -> &dyn hal::DynSampler {
2033 self.raw.as_ref()
2034 }
2035}
2036
2037#[derive(Copy, Clone)]
2038pub enum SamplerFilterErrorType {
2039 MagFilter,
2040 MinFilter,
2041 MipmapFilter,
2042}
2043
2044impl fmt::Debug for SamplerFilterErrorType {
2045 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2046 match *self {
2047 SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
2048 SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
2049 SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
2050 }
2051 }
2052}
2053
2054#[derive(Clone, Debug, Error)]
2055#[non_exhaustive]
2056pub enum CreateSamplerError {
2057 #[error(transparent)]
2058 Device(#[from] DeviceError),
2059 #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
2060 InvalidLodMinClamp(f32),
2061 #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
2062 InvalidLodMaxClamp {
2063 lod_min_clamp: f32,
2064 lod_max_clamp: f32,
2065 },
2066 #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
2067 InvalidAnisotropy(u16),
2068 #[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.")]
2069 InvalidFilterModeWithAnisotropy {
2070 filter_type: SamplerFilterErrorType,
2071 filter_mode: wgt::FilterMode,
2072 anisotropic_clamp: u16,
2073 },
2074 #[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.")]
2075 InvalidMipmapFilterModeWithAnisotropy {
2076 filter_type: SamplerFilterErrorType,
2077 filter_mode: wgt::MipmapFilterMode,
2078 anisotropic_clamp: u16,
2079 },
2080 #[error(transparent)]
2081 MissingFeatures(#[from] MissingFeatures),
2082}
2083
2084crate::impl_resource_type!(Sampler);
2085crate::impl_labeled!(Sampler);
2086crate::impl_parent_device!(Sampler);
2087crate::impl_storage_item!(Sampler);
2088crate::impl_trackable!(Sampler);
2089
2090impl WebGpuError for CreateSamplerError {
2091 fn webgpu_error_type(&self) -> ErrorType {
2092 let e: &dyn WebGpuError = match self {
2093 Self::Device(e) => e,
2094 Self::MissingFeatures(e) => e,
2095
2096 Self::InvalidLodMinClamp(_)
2097 | Self::InvalidLodMaxClamp { .. }
2098 | Self::InvalidAnisotropy(_)
2099 | Self::InvalidFilterModeWithAnisotropy { .. }
2100 | Self::InvalidMipmapFilterModeWithAnisotropy { .. } => return ErrorType::Validation,
2101 };
2102 e.webgpu_error_type()
2103 }
2104}
2105
2106#[derive(Clone, Debug, Error)]
2107#[non_exhaustive]
2108pub enum CreateQuerySetError {
2109 #[error(transparent)]
2110 Device(#[from] DeviceError),
2111 #[error("QuerySets cannot be made with zero queries")]
2112 ZeroCount,
2113 #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
2114 TooManyQueries { count: u32, maximum: u32 },
2115 #[error(transparent)]
2116 MissingFeatures(#[from] MissingFeatures),
2117}
2118
2119impl WebGpuError for CreateQuerySetError {
2120 fn webgpu_error_type(&self) -> ErrorType {
2121 let e: &dyn WebGpuError = match self {
2122 Self::Device(e) => e,
2123 Self::MissingFeatures(e) => e,
2124
2125 Self::TooManyQueries { .. } | Self::ZeroCount => return ErrorType::Validation,
2126 };
2127 e.webgpu_error_type()
2128 }
2129}
2130
2131pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
2132
2133#[derive(Debug)]
2134pub struct QuerySet {
2135 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynQuerySet>>,
2136 pub(crate) device: Arc<Device>,
2137 pub(crate) label: String,
2139 pub(crate) tracking_data: TrackingData,
2140 pub(crate) desc: wgt::QuerySetDescriptor<()>,
2141}
2142
2143impl Drop for QuerySet {
2144 fn drop(&mut self) {
2145 resource_log!("Destroy raw {}", self.error_ident());
2146 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
2148 unsafe {
2149 self.device.raw().destroy_query_set(raw);
2150 }
2151 }
2152}
2153
2154crate::impl_resource_type!(QuerySet);
2155crate::impl_labeled!(QuerySet);
2156crate::impl_parent_device!(QuerySet);
2157crate::impl_storage_item!(QuerySet);
2158crate::impl_trackable!(QuerySet);
2159
2160impl QuerySet {
2161 pub(crate) fn raw(&self) -> &dyn hal::DynQuerySet {
2162 self.raw.as_ref()
2163 }
2164}
2165
2166pub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;
2167pub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;
2168
2169pub type BlasPrepareCompactResult = Result<(), BlasPrepareCompactError>;
2170
2171#[cfg(send_sync)]
2172pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + Send + 'static>;
2173#[cfg(not(send_sync))]
2174pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + 'static>;
2175
2176pub(crate) struct BlasPendingCompact {
2177 pub(crate) op: Option<BlasCompactCallback>,
2178 pub(crate) _parent_blas: Arc<Blas>,
2180}
2181
2182impl fmt::Debug for BlasPendingCompact {
2183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2184 f.debug_struct("BlasPendingCompact")
2185 .field("op", &())
2186 .field("_parent_blas", &self._parent_blas)
2187 .finish()
2188 }
2189}
2190
2191#[derive(Debug)]
2192pub(crate) enum BlasCompactState {
2193 Compacted,
2195 Waiting(BlasPendingCompact),
2197 Ready { size: wgt::BufferAddress },
2199 Idle,
2201}
2202
2203#[cfg(send_sync)]
2204unsafe impl Send for BlasCompactState {}
2205#[cfg(send_sync)]
2206unsafe impl Sync for BlasCompactState {}
2207
2208#[derive(Debug)]
2209pub struct Blas {
2210 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
2211 pub(crate) device: Arc<Device>,
2212 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
2213 pub(crate) sizes: wgt::BlasGeometrySizeDescriptors,
2214 pub(crate) flags: wgt::AccelerationStructureFlags,
2215 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
2216 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
2217 pub(crate) handle: u64,
2218 pub(crate) label: String,
2220 pub(crate) tracking_data: TrackingData,
2221 pub(crate) compaction_buffer: Option<ManuallyDrop<Box<dyn hal::DynBuffer>>>,
2222 pub(crate) compacted_state: Mutex<BlasCompactState>,
2223}
2224
2225impl Drop for Blas {
2226 fn drop(&mut self) {
2227 resource_log!("Destroy raw {}", self.error_ident());
2228 if let Some(raw) = self.raw.take() {
2230 unsafe {
2231 self.device.raw().destroy_acceleration_structure(raw);
2232 }
2233 }
2234 if let Some(mut raw) = self.compaction_buffer.take() {
2235 unsafe {
2236 self.device
2237 .raw()
2238 .destroy_buffer(ManuallyDrop::take(&mut raw))
2239 }
2240 }
2241 }
2242}
2243
2244impl RawResourceAccess for Blas {
2245 type DynResource = dyn hal::DynAccelerationStructure;
2246
2247 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2248 self.raw.get(guard).map(|it| it.as_ref())
2249 }
2250}
2251
2252impl Blas {
2253 pub(crate) fn prepare_compact_async(
2254 self: &Arc<Self>,
2255 op: Option<BlasCompactCallback>,
2256 ) -> Result<SubmissionIndex, (Option<BlasCompactCallback>, BlasPrepareCompactError)> {
2257 let device = &self.device;
2258 if let Err(e) = device.check_is_valid() {
2259 return Err((op, e.into()));
2260 }
2261
2262 if self.built_index.read().is_none() {
2263 return Err((op, BlasPrepareCompactError::NotBuilt));
2264 }
2265
2266 if !self
2267 .flags
2268 .contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION)
2269 {
2270 return Err((op, BlasPrepareCompactError::CompactionUnsupported));
2271 }
2272
2273 let mut state = self.compacted_state.lock();
2274 *state = match *state {
2275 BlasCompactState::Compacted => {
2276 return Err((op, BlasPrepareCompactError::DoubleCompaction))
2277 }
2278 BlasCompactState::Waiting(_) => {
2279 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2280 }
2281 BlasCompactState::Ready { .. } => {
2282 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2283 }
2284 BlasCompactState::Idle => BlasCompactState::Waiting(BlasPendingCompact {
2285 op,
2286 _parent_blas: self.clone(),
2287 }),
2288 };
2289
2290 let submit_index = if let Some(queue) = device.get_queue() {
2291 queue.lock_life().prepare_compact(self).unwrap_or(0) } else {
2293 let (mut callback, status) = self.read_back_compact_size().unwrap();
2295 if let Some(callback) = callback.take() {
2296 callback(status);
2297 }
2298 0
2299 };
2300
2301 Ok(submit_index)
2302 }
2303
2304 #[must_use]
2306 pub(crate) fn read_back_compact_size(&self) -> Option<BlasCompactReadyPendingClosure> {
2307 let mut state = self.compacted_state.lock();
2308 let pending_compact = match mem::replace(&mut *state, BlasCompactState::Idle) {
2309 BlasCompactState::Waiting(pending_mapping) => pending_mapping,
2310 BlasCompactState::Idle => return None,
2312 BlasCompactState::Ready { .. } => {
2313 unreachable!("This should be validated out by `prepare_for_compaction`")
2314 }
2315 _ => panic!("No pending mapping."),
2316 };
2317 let status = {
2318 let compaction_buffer = self.compaction_buffer.as_ref().unwrap().as_ref();
2319 unsafe {
2320 let map_res = self.device.raw().map_buffer(
2321 compaction_buffer,
2322 0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress,
2323 );
2324 match map_res {
2325 Ok(mapping) => {
2326 if !mapping.is_coherent {
2327 #[expect(clippy::single_range_in_vec_init)]
2330 self.device.raw().flush_mapped_ranges(
2331 compaction_buffer,
2332 &[0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress],
2333 );
2334 }
2335 let size = core::ptr::read_unaligned(
2336 mapping.ptr.as_ptr().cast::<wgt::BufferAddress>(),
2337 );
2338 self.device.raw().unmap_buffer(compaction_buffer);
2339 if self.size_info.acceleration_structure_size != 0 {
2340 debug_assert_ne!(size, 0);
2341 }
2342 *state = BlasCompactState::Ready { size };
2343 Ok(())
2344 }
2345 Err(err) => Err(BlasPrepareCompactError::from(DeviceError::from_hal(err))),
2346 }
2347 }
2348 };
2349 Some((pending_compact.op, status))
2350 }
2351}
2352
2353crate::impl_resource_type!(Blas);
2354crate::impl_labeled!(Blas);
2355crate::impl_parent_device!(Blas);
2356crate::impl_storage_item!(Blas);
2357crate::impl_trackable!(Blas);
2358
2359#[derive(Debug)]
2360pub struct Tlas {
2361 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
2362 pub(crate) device: Arc<Device>,
2363 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
2364 pub(crate) max_instance_count: u32,
2365 pub(crate) flags: wgt::AccelerationStructureFlags,
2366 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
2367 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
2368 pub(crate) dependencies: RwLock<Vec<Arc<Blas>>>,
2369 pub(crate) instance_buffer: ManuallyDrop<Box<dyn hal::DynBuffer>>,
2370 pub(crate) label: String,
2372 pub(crate) tracking_data: TrackingData,
2373}
2374
2375impl Drop for Tlas {
2376 fn drop(&mut self) {
2377 unsafe {
2378 resource_log!("Destroy raw {}", self.error_ident());
2379 if let Some(structure) = self.raw.take() {
2380 self.device.raw().destroy_acceleration_structure(structure);
2381 }
2382 let buffer = ManuallyDrop::take(&mut self.instance_buffer);
2383 self.device.raw().destroy_buffer(buffer);
2384 }
2385 }
2386}
2387
2388impl RawResourceAccess for Tlas {
2389 type DynResource = dyn hal::DynAccelerationStructure;
2390
2391 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2392 self.raw.get(guard).map(|raw| raw.as_ref())
2393 }
2394}
2395
2396crate::impl_resource_type!(Tlas);
2397crate::impl_labeled!(Tlas);
2398crate::impl_parent_device!(Tlas);
2399crate::impl_storage_item!(Tlas);
2400crate::impl_trackable!(Tlas);