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 api_log,
21 binding_model::{BindGroup, BindingError},
22 device::{
23 queue, resource::DeferredDestroy, BufferMapPendingClosure, Device, DeviceError,
24 DeviceMismatch, HostMap, MissingDownlevelFlags, MissingFeatures,
25 },
26 hal_label,
27 init_tracker::{BufferInitTracker, TextureInitTracker},
28 lock::{rank, Mutex, RwLock},
29 ray_tracing::{BlasCompactReadyPendingClosure, BlasPrepareCompactError},
30 resource_log,
31 snatch::{SnatchGuard, Snatchable},
32 timestamp_normalization::TimestampNormalizationBindGroup,
33 track::{SharedTrackerIndexAllocator, TrackerIndex},
34 weak_vec::WeakVec,
35 Label, LabelHelpers, SubmissionIndex,
36};
37
38#[derive(Debug)]
58pub(crate) struct TrackingData {
59 tracker_index: TrackerIndex,
60 tracker_indices: Arc<SharedTrackerIndexAllocator>,
61}
62
63impl Drop for TrackingData {
64 fn drop(&mut self) {
65 self.tracker_indices.free(self.tracker_index);
66 }
67}
68
69impl TrackingData {
70 pub(crate) fn new(tracker_indices: Arc<SharedTrackerIndexAllocator>) -> Self {
71 Self {
72 tracker_index: tracker_indices.alloc(),
73 tracker_indices,
74 }
75 }
76
77 pub(crate) fn tracker_index(&self) -> TrackerIndex {
78 self.tracker_index
79 }
80}
81
82#[derive(Clone, Debug)]
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84pub struct ResourceErrorIdent {
85 r#type: Cow<'static, str>,
86 label: String,
87}
88
89impl fmt::Display for ResourceErrorIdent {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
91 write!(f, "{} with '{}' label", self.r#type, self.label)
92 }
93}
94
95#[derive(Debug)]
96pub enum ResourceState<T> {
97 Valid(T),
98 Invalid,
99}
100
101impl<T> ResourceState<T> {
102 pub fn as_ref(&self) -> ResourceState<&T> {
103 match self {
104 ResourceState::Valid(v) => ResourceState::Valid(v),
105 ResourceState::Invalid => ResourceState::Invalid,
106 }
107 }
108
109 pub fn valid(self) -> Option<T> {
110 match self {
111 ResourceState::Valid(v) => Some(v),
112 ResourceState::Invalid => None,
113 }
114 }
115}
116
117#[derive(thiserror::Error, Debug)]
118pub enum InvalidOrDestroyedResourceError {
119 #[error(transparent)]
120 InvalidResource(#[from] InvalidResourceError),
121 #[error(transparent)]
122 DestroyedResource(#[from] DestroyedResourceError),
123}
124
125pub trait ParentDevice: Labeled {
126 fn device(&self) -> &Arc<Device>;
127
128 fn is_equal(self: &Arc<Self>, other: &Arc<Self>) -> bool {
129 Arc::ptr_eq(self, other)
130 }
131
132 fn same_device_as<O: ParentDevice>(&self, other: &O) -> Result<(), DeviceError> {
133 if Arc::ptr_eq(self.device(), other.device()) {
134 Ok(())
135 } else {
136 Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
137 res: self.error_ident(),
138 res_device: self.device().error_ident(),
139 target: Some(other.error_ident()),
140 target_device: other.device().error_ident(),
141 })))
142 }
143 }
144
145 fn same_device(&self, device: &Device) -> Result<(), DeviceError> {
146 if core::ptr::eq(&**self.device(), device) {
147 Ok(())
148 } else {
149 Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
150 res: self.error_ident(),
151 res_device: self.device().error_ident(),
152 target: None,
153 target_device: device.error_ident(),
154 })))
155 }
156 }
157}
158
159#[macro_export]
160macro_rules! impl_parent_device {
161 ($ty:ident) => {
162 impl $crate::resource::ParentDevice for $ty {
163 fn device(&self) -> &Arc<Device> {
164 &self.device
165 }
166 }
167 };
168}
169
170pub trait RawResourceAccess: ParentDevice {
172 type DynResource: hal::DynResource + ?Sized;
173
174 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource>;
179
180 fn try_raw<'a>(
185 &'a self,
186 guard: &'a SnatchGuard,
187 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
188 self.raw(guard)
189 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
190 }
191}
192
193pub trait ResourceType {
194 const TYPE: &'static str;
195}
196
197#[macro_export]
198macro_rules! impl_resource_type {
199 ($ty:ident) => {
200 impl $crate::resource::ResourceType for $ty {
201 const TYPE: &'static str = stringify!($ty);
202 }
203 };
204}
205
206pub trait Labeled: ResourceType {
207 fn label(&self) -> &str;
213
214 fn error_ident(&self) -> ResourceErrorIdent {
215 ResourceErrorIdent {
216 r#type: Cow::Borrowed(Self::TYPE),
217 label: self.label().to_owned(),
218 }
219 }
220}
221
222#[macro_export]
223macro_rules! impl_labeled {
224 ($ty:ident) => {
225 impl $crate::resource::Labeled for $ty {
226 fn label(&self) -> &str {
227 &self.label
228 }
229 }
230 };
231}
232
233pub(crate) trait Trackable {
234 fn tracker_index(&self) -> TrackerIndex;
235}
236
237#[macro_export]
238macro_rules! impl_trackable {
239 ($ty:ident) => {
240 impl $crate::resource::Trackable for $ty {
241 fn tracker_index(&self) -> $crate::track::TrackerIndex {
242 self.tracking_data.tracker_index()
243 }
244 }
245 };
246}
247
248#[derive(Debug)]
249pub(crate) enum BufferMapState {
250 Init { staging_buffer: StagingBuffer },
252 Waiting(BufferPendingMapping),
254 Active {
256 mapping: hal::BufferMapping,
257 range: hal::MemoryRange,
258 host: HostMap,
259 },
260 Idle,
262}
263
264#[cfg(send_sync)]
265unsafe impl Send for BufferMapState {}
266#[cfg(send_sync)]
267unsafe impl Sync for BufferMapState {}
268
269#[cfg(send_sync)]
270pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + Send + 'static>;
271#[cfg(not(send_sync))]
272pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + 'static>;
273
274pub struct BufferMapOperation {
275 pub host: HostMap,
276 pub callback: Option<BufferMapCallback>,
277}
278
279impl fmt::Debug for BufferMapOperation {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 f.debug_struct("BufferMapOperation")
282 .field("host", &self.host)
283 .field("callback", &self.callback.as_ref().map(|_| "?"))
284 .finish()
285 }
286}
287
288#[derive(Clone, Debug, Error)]
289#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
290#[non_exhaustive]
291pub enum BufferAccessError {
292 #[error(transparent)]
293 Device(#[from] DeviceError),
294 #[error("Buffer map failed")]
295 Failed,
296 #[error(transparent)]
297 DestroyedResource(#[from] DestroyedResourceError),
298 #[error("Buffer is already mapped")]
299 AlreadyMapped,
300 #[error("Buffer map is pending")]
301 MapAlreadyPending,
302 #[error(transparent)]
303 MissingBufferUsage(#[from] MissingBufferUsageError),
304 #[error("Buffer is not mapped")]
305 NotMapped,
306 #[error(
307 "Buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`"
308 )]
309 UnalignedRange,
310 #[error("Buffer offset invalid: offset {offset} must be multiple of 8")]
311 UnalignedOffset { offset: wgt::BufferAddress },
312 #[error("Buffer range size invalid: range_size {range_size} must be multiple of 4")]
313 UnalignedRangeSize { range_size: wgt::BufferAddress },
314 #[error("Buffer access out of bounds: index {index} would underrun the buffer (limit: {min})")]
315 OutOfBoundsStartOffsetUnderrun {
316 index: wgt::BufferAddress,
317 min: wgt::BufferAddress,
318 },
319 #[error(
320 "Buffer access out of bounds: start offset {index} would overrun the buffer (limit: {max})"
321 )]
322 OutOfBoundsStartOffsetOverrun {
323 index: wgt::BufferAddress,
324 max: wgt::BufferAddress,
325 },
326 #[error(
327 "Buffer access out of bounds: start offset {index} + size {size} would overrun the buffer (limit: {max})"
328 )]
329 OutOfBoundsEndOffsetOverrun {
330 index: wgt::BufferAddress,
331 size: wgt::BufferAddress,
332 max: wgt::BufferAddress,
333 },
334 #[error("Buffer map aborted")]
335 MapAborted,
336 #[error(transparent)]
337 InvalidResource(#[from] InvalidResourceError),
338 #[error("Map start offset ({offset}) is out-of-bounds for buffer of size {buffer_size}")]
339 MapStartOffsetOverrun {
340 offset: wgt::BufferAddress,
341 buffer_size: wgt::BufferAddress,
342 },
343 #[error(
344 "Map end offset (start at {} + size of {}) is out-of-bounds for buffer of size {}",
345 offset,
346 size,
347 buffer_size
348 )]
349 MapEndOffsetOverrun {
350 offset: wgt::BufferAddress,
351 size: wgt::BufferAddress,
352 buffer_size: wgt::BufferAddress,
353 },
354}
355
356impl WebGpuError for BufferAccessError {
357 fn webgpu_error_type(&self) -> ErrorType {
358 match self {
359 Self::Device(e) => e.webgpu_error_type(),
360 Self::InvalidResource(e) => e.webgpu_error_type(),
361 Self::DestroyedResource(e) => e.webgpu_error_type(),
362
363 Self::Failed
364 | Self::AlreadyMapped
365 | Self::MapAlreadyPending
366 | Self::MissingBufferUsage(_)
367 | Self::NotMapped
368 | Self::UnalignedRange
369 | Self::UnalignedOffset { .. }
370 | Self::UnalignedRangeSize { .. }
371 | Self::OutOfBoundsStartOffsetUnderrun { .. }
372 | Self::OutOfBoundsStartOffsetOverrun { .. }
373 | Self::OutOfBoundsEndOffsetOverrun { .. }
374 | Self::MapAborted
375 | Self::MapStartOffsetOverrun { .. }
376 | Self::MapEndOffsetOverrun { .. } => ErrorType::Validation,
377 }
378 }
379}
380
381#[derive(Clone, Debug, Error)]
382#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
383#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
384pub struct MissingBufferUsageError {
385 pub(crate) res: ResourceErrorIdent,
386 pub(crate) actual: wgt::BufferUsages,
387 pub(crate) expected: wgt::BufferUsages,
388}
389
390impl WebGpuError for MissingBufferUsageError {
391 fn webgpu_error_type(&self) -> ErrorType {
392 ErrorType::Validation
393 }
394}
395
396#[derive(Clone, Debug, Error)]
397#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
398pub struct MissingTextureUsageError {
399 pub(crate) res: ResourceErrorIdent,
400 pub(crate) actual: wgt::TextureUsages,
401 pub(crate) expected: wgt::TextureUsages,
402}
403
404impl WebGpuError for MissingTextureUsageError {
405 fn webgpu_error_type(&self) -> ErrorType {
406 ErrorType::Validation
407 }
408}
409
410#[derive(Clone, Debug, Error)]
411#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
412#[error("{0} has been destroyed")]
413pub struct DestroyedResourceError(pub ResourceErrorIdent);
414
415impl WebGpuError for DestroyedResourceError {
416 fn webgpu_error_type(&self) -> ErrorType {
417 ErrorType::Validation
418 }
419}
420
421#[derive(Clone, Debug, Error)]
422#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
423#[error("{0} is invalid")]
424pub struct InvalidResourceError(pub ResourceErrorIdent);
425
426impl WebGpuError for InvalidResourceError {
427 fn webgpu_error_type(&self) -> ErrorType {
428 ErrorType::Validation
429 }
430}
431
432pub type BufferAccessResult = Result<(), BufferAccessError>;
433
434#[derive(Debug)]
435pub(crate) struct BufferPendingMapping {
436 pub(crate) range: Range<wgt::BufferAddress>,
437 pub(crate) op: BufferMapOperation,
438 pub(crate) _parent_buffer: Arc<Buffer>,
440}
441
442pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
443
444#[derive(Debug)]
445pub(crate) struct BufferState {
446 pub(crate) raw: Snatchable<Box<dyn hal::DynBuffer>>,
447}
448
449#[derive(Debug)]
450pub struct Buffer {
451 pub(crate) state: ResourceState<BufferState>,
452 pub(crate) device: Arc<Device>,
453 pub(crate) usage: wgt::BufferUsages,
454 pub(crate) size: wgt::BufferAddress,
455 pub(crate) initialization_status: RwLock<BufferInitTracker>,
456 pub(crate) label: String,
458 pub(crate) tracking_data: TrackingData,
459 pub(crate) map_state: Mutex<BufferMapState>,
460 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
462 pub(crate) timestamp_normalization_bind_group: Snatchable<TimestampNormalizationBindGroup>,
463 pub(crate) indirect_validation_bind_groups: Snatchable<crate::indirect_validation::BindGroups>,
464}
465
466impl Drop for Buffer {
467 #[allow(trivial_casts)]
468 fn drop(&mut self) {
469 profiling::scope!("Buffer::drop");
470 api_log!("Buffer::drop {:?}", self as *const _);
471 #[cfg(feature = "trace")]
472 if let Some(t) = self.device.trace.lock().as_mut() {
473 t.add(trace::Action::DropBuffer(unsafe { trace::to_trace(self) }));
474 }
475
476 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
477 raw.dispose(self.device.raw());
478 }
479
480 if let Some(raw) = self.indirect_validation_bind_groups.take() {
481 raw.dispose(self.device.raw());
482 }
483
484 let map_state = mem::replace(self.map_state.get_mut(), BufferMapState::Idle);
485 let active_map = match map_state {
486 BufferMapState::Init { staging_buffer } => {
487 staging_buffer.dispose();
488 false
489 }
490 BufferMapState::Waiting(buffer_pending_mapping) => {
491 if buffer_pending_mapping.op.callback.is_some() {
492 let result = Err(BufferAccessError::DestroyedResource(
493 DestroyedResourceError(self.error_ident()),
494 ));
495 self.device
496 .deferred_buffer_map_pending_closures
497 .push((buffer_pending_mapping.op, result));
498 }
499 false
500 }
501 BufferMapState::Active { .. } => true,
502 BufferMapState::Idle => false,
503 };
504
505 let ResourceState::Valid(state) = &mut self.state else {
506 return;
507 };
508
509 if let Some(raw) = state.raw.take() {
510 if active_map {
511 unsafe { self.device.raw().unmap_buffer(raw.as_ref()) }
512 }
513 resource_log!("Destroy raw {}", self.error_ident());
514 unsafe {
515 self.device.raw().destroy_buffer(raw);
516 }
517 }
518 }
519}
520
521impl RawResourceAccess for Buffer {
522 type DynResource = dyn hal::DynBuffer;
523
524 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
525 self.state()
526 .ok()
527 .and_then(|state| state.raw.get(guard).map(|b| b.as_ref()))
528 }
529}
530
531impl Buffer {
532 pub(crate) fn check_destroyed(
533 &self,
534 guard: &SnatchGuard,
535 ) -> Result<(), DestroyedResourceError> {
536 let ResourceState::Valid(state) = &self.state else {
537 return Ok(());
538 };
539 state
540 .raw
541 .get(guard)
542 .map(|_| ())
543 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
544 }
545
546 pub(crate) fn check_usage(
549 &self,
550 expected: wgt::BufferUsages,
551 ) -> Result<(), MissingBufferUsageError> {
552 if self.usage.contains(expected) {
553 Ok(())
554 } else {
555 Err(MissingBufferUsageError {
556 res: self.error_ident(),
557 actual: self.usage,
558 expected,
559 })
560 }
561 }
562
563 pub(crate) fn state(&self) -> Result<&BufferState, InvalidResourceError> {
564 match &self.state {
565 ResourceState::Valid(state) => Ok(state),
566 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
567 }
568 }
569
570 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
571 self.state().map(|_| ())
572 }
573
574 pub fn invalid(device: Arc<Device>, desc: &BufferDescriptor) -> Arc<Self> {
575 Arc::new(Buffer {
576 state: ResourceState::Invalid,
577 usage: desc.usage,
578 size: desc.size,
579 initialization_status: RwLock::new(
580 rank::BUFFER_INITIALIZATION_STATUS,
581 BufferInitTracker::new(0),
582 ),
583 map_state: Mutex::new(rank::BUFFER_MAP_STATE, BufferMapState::Idle),
584 label: desc.label.to_string(),
585 tracking_data: TrackingData::new(device.tracker_indices.buffers.clone()),
586 bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
587 timestamp_normalization_bind_group: Snatchable::empty(),
588 indirect_validation_bind_groups: Snatchable::empty(),
589 device,
590 })
591 }
592
593 pub fn resolve_binding_size(
605 &self,
606 offset: wgt::BufferAddress,
607 binding_size: Option<wgt::BufferSize>,
608 ) -> Result<u64, BindingError> {
609 let buffer_size = self.size;
610
611 match binding_size {
612 Some(binding_size) => match offset.checked_add(binding_size.get()) {
613 Some(end) if end <= buffer_size => Ok(binding_size.get()),
614 _ => Err(BindingError::BindingRangeTooLarge {
615 buffer: self.error_ident(),
616 offset,
617 binding_size: binding_size.get(),
618 buffer_size,
619 }),
620 },
621 None => {
622 buffer_size
623 .checked_sub(offset)
624 .ok_or_else(|| BindingError::BindingOffsetTooLarge {
625 buffer: self.error_ident(),
626 offset,
627 buffer_size,
628 })
629 }
630 }
631 }
632
633 pub fn binding<'a>(
654 &'a self,
655 offset: wgt::BufferAddress,
656 binding_size: Option<wgt::BufferSize>,
657 snatch_guard: &'a SnatchGuard,
658 ) -> Result<(hal::BufferBinding<'a, dyn hal::DynBuffer>, u64), BindingError> {
659 let buf_raw = self.try_raw(snatch_guard)?;
660 let resolved_size = self.resolve_binding_size(offset, binding_size)?;
661 Ok((
664 hal::BufferBinding::new_unchecked(buf_raw, offset, binding_size),
665 resolved_size,
666 ))
667 }
668
669 pub fn map_async(
673 self: &Arc<Self>,
674 offset: wgt::BufferAddress,
675 size: Option<wgt::BufferAddress>,
676 op: BufferMapOperation,
677 ) -> Result<SubmissionIndex, BufferAccessError> {
678 profiling::scope!("Buffer::map_async");
679 api_log!(
680 "Buffer::map_async {:?} offset {offset:?} size {size:?} op: {op:?}",
681 Arc::as_ptr(self)
682 );
683
684 self.try_map_async(offset, size, op)
685 .map_err(|(mut operation, err)| {
686 if let Some(callback) = operation.callback.take() {
687 callback(Err(err.clone()));
688 }
689 err
690 })
691 }
692
693 fn try_map_async(
716 self: &Arc<Self>,
717 offset: wgt::BufferAddress,
718 size: Option<wgt::BufferAddress>,
719 op: BufferMapOperation,
720 ) -> Result<SubmissionIndex, (BufferMapOperation, BufferAccessError)> {
721 let range_size = if let Some(size) = size {
722 size
723 } else {
724 self.size.saturating_sub(offset)
725 };
726
727 if let Err(e) = self.check_is_valid() {
728 return Err((op, e.into()));
729 }
730
731 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
732 return Err((op, BufferAccessError::UnalignedOffset { offset }));
733 }
734 if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
735 return Err((op, BufferAccessError::UnalignedRangeSize { range_size }));
736 }
737
738 if offset > self.size {
739 return Err((
740 op,
741 BufferAccessError::MapStartOffsetOverrun {
742 offset,
743 buffer_size: self.size,
744 },
745 ));
746 }
747 if range_size > self.size - offset {
749 return Err((
750 op,
751 BufferAccessError::MapEndOffsetOverrun {
752 offset,
753 size: range_size,
754 buffer_size: self.size,
755 },
756 ));
757 }
758 let end_offset = offset + range_size;
759
760 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT)
761 || !end_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT)
762 {
763 return Err((op, BufferAccessError::UnalignedRange));
764 }
765
766 let (pub_usage, internal_use) = match op.host {
767 HostMap::Read => (wgt::BufferUsages::MAP_READ, wgt::BufferUses::MAP_READ),
768 HostMap::Write => (wgt::BufferUsages::MAP_WRITE, wgt::BufferUses::MAP_WRITE),
769 };
770
771 if let Err(e) = self.check_usage(pub_usage) {
772 return Err((op, e.into()));
773 }
774
775 let device = &self.device;
776 if let Err(e) = device.check_is_valid() {
777 return Err((op, e.into()));
778 }
779
780 let submit_index = {
781 let snatch_guard = device.snatchable_lock.read();
782 if let Err(e) = self.check_destroyed(&snatch_guard) {
783 return Err((op, e.into()));
784 }
785
786 {
787 let map_state = &mut *self.map_state.lock();
788 *map_state = match *map_state {
789 BufferMapState::Init { .. } | BufferMapState::Active { .. } => {
790 return Err((op, BufferAccessError::AlreadyMapped));
791 }
792 BufferMapState::Waiting(_) => {
793 return Err((op, BufferAccessError::MapAlreadyPending));
794 }
795 BufferMapState::Idle => BufferMapState::Waiting(BufferPendingMapping {
796 range: offset..end_offset,
797 op,
798 _parent_buffer: self.clone(),
799 }),
800 };
801 }
802
803 if let Some(queue) = device.get_queue().as_ref() {
804 match queue.flush_writes_for_buffer(self, snatch_guard) {
805 Err(err) => {
806 let state = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
807 let BufferMapState::Waiting(BufferPendingMapping { op, .. }) = state else {
808 unreachable!();
809 };
810 return Err((op, err));
811 }
812 Ok(()) => {
813 Some(queue.lock_life().map(self).unwrap_or(0))
822 }
823 }
824 } else {
825 None
826 }
827 };
828
829 device
837 .trackers
838 .lock()
839 .buffers
840 .set_single(self, internal_use);
841
842 if let Some(index) = submit_index {
843 Ok(index)
844 } else {
845 let (mut operation, status) = self.map(&device.snatchable_lock.read()).unwrap();
848 if let Some(callback) = operation.callback.take() {
849 callback(status);
850 }
851 Ok(0)
852 }
853 }
854
855 pub fn get_mapped_range(
856 self: &Arc<Self>,
857 offset: wgt::BufferAddress,
858 size: Option<wgt::BufferAddress>,
859 ) -> Result<(NonNull<u8>, u64), BufferAccessError> {
860 profiling::scope!("Buffer::get_mapped_range");
861 api_log!(
862 "Buffer::get_mapped_range {:?} offset {offset:?} size {size:?}",
863 Arc::as_ptr(self)
864 );
865
866 self.check_is_valid()?;
867 {
868 let snatch_guard = self.device.snatchable_lock.read();
869 self.check_destroyed(&snatch_guard)?;
870 }
871
872 let range_size = if let Some(size) = size {
873 size
874 } else {
875 self.size.saturating_sub(offset)
876 };
877
878 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
879 return Err(BufferAccessError::UnalignedOffset { offset });
880 }
881 if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
882 return Err(BufferAccessError::UnalignedRangeSize { range_size });
883 }
884 let map_state = &*self.map_state.lock();
885 match *map_state {
886 BufferMapState::Init { ref staging_buffer } => {
887 if offset > self.size {
888 return Err(BufferAccessError::MapStartOffsetOverrun {
889 offset,
890 buffer_size: self.size,
891 });
892 }
893 if range_size > self.size - offset {
895 return Err(BufferAccessError::MapEndOffsetOverrun {
896 offset,
897 size: range_size,
898 buffer_size: self.size,
899 });
900 }
901 let ptr = unsafe { staging_buffer.ptr() };
902 let ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().offset(offset as isize)) };
903 Ok((ptr, range_size))
904 }
905 BufferMapState::Active {
906 ref mapping,
907 ref range,
908 ..
909 } => {
910 if offset > range.end {
911 return Err(BufferAccessError::OutOfBoundsStartOffsetOverrun {
912 index: offset,
913 max: range.end,
914 });
915 }
916 if offset < range.start {
917 return Err(BufferAccessError::OutOfBoundsStartOffsetUnderrun {
918 index: offset,
919 min: range.start,
920 });
921 }
922 if range_size > range.end - offset {
923 return Err(BufferAccessError::OutOfBoundsEndOffsetOverrun {
924 index: offset,
925 size: range_size,
926 max: range.end,
927 });
928 }
929 let relative_offset = (offset - range.start) as isize;
932 unsafe {
933 Ok((
934 NonNull::new_unchecked(mapping.ptr.as_ptr().offset(relative_offset)),
935 range_size,
936 ))
937 }
938 }
939 BufferMapState::Idle | BufferMapState::Waiting(_) => Err(BufferAccessError::NotMapped),
940 }
941 }
942 #[must_use]
945 pub(crate) fn map(&self, snatch_guard: &SnatchGuard) -> Option<BufferMapPendingClosure> {
946 let mapping = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
950 let pending_mapping = match mapping {
951 BufferMapState::Waiting(pending_mapping) => pending_mapping,
952 BufferMapState::Idle => return None,
954 BufferMapState::Active { .. } => {
957 *self.map_state.lock() = mapping;
958 return None;
959 }
960 _ => panic!("No pending mapping."),
961 };
962 let status = if pending_mapping.range.start != pending_mapping.range.end {
963 let host = pending_mapping.op.host;
964 let size = pending_mapping.range.end - pending_mapping.range.start;
965 match crate::device::map_buffer(
966 self,
967 pending_mapping.range.start,
968 size,
969 host,
970 snatch_guard,
971 ) {
972 Ok(mapping) => {
973 *self.map_state.lock() = BufferMapState::Active {
974 mapping,
975 range: pending_mapping.range.clone(),
976 host,
977 };
978 Ok(())
979 }
980 Err(e) => Err(e),
981 }
982 } else {
983 *self.map_state.lock() = BufferMapState::Active {
984 mapping: hal::BufferMapping {
985 ptr: NonNull::dangling(),
986 is_coherent: true,
987 },
988 range: pending_mapping.range,
989 host: pending_mapping.op.host,
990 };
991 Ok(())
992 };
993 Some((pending_mapping.op, status))
994 }
995
996 pub fn unmap(self: &Arc<Self>) -> Result<(), BufferAccessError> {
998 profiling::scope!("unmap", "Buffer");
999 api_log!("Buffer::unmap {:?}", Arc::as_ptr(self));
1000 if let Some((mut operation, status)) = self.unmap_inner()? {
1001 if let Some(callback) = operation.callback.take() {
1002 callback(status);
1003 }
1004 }
1005
1006 Ok(())
1007 }
1008
1009 fn unmap_inner(self: &Arc<Self>) -> Result<Option<BufferMapPendingClosure>, BufferAccessError> {
1010 let device = &self.device;
1011 self.check_is_valid()?;
1012 self.device.check_is_valid()?;
1013 let snatch_guard = device.snatchable_lock.read();
1014 self.check_destroyed(&snatch_guard)?;
1015 let raw_buf = self.try_raw(&snatch_guard)?;
1016 let map_state = mem::replace(&mut *self.map_state.lock(), BufferMapState::Idle);
1017 match map_state {
1018 BufferMapState::Init { staging_buffer } => {
1019 #[cfg(feature = "trace")]
1020 if let Some(ref mut trace) = *device.trace.lock() {
1021 use crate::device::trace::{DataKind, IntoTrace};
1022
1023 let data = trace.make_binary(DataKind::Bin, staging_buffer.get_data());
1024 trace.add(trace::Action::WriteBuffer {
1025 id: self.to_trace(),
1026 data,
1027 offset: 0,
1029 size: self.size,
1030 queued: true,
1031 });
1032 }
1033
1034 let staging_buffer = staging_buffer.flush();
1035
1036 if let Some(queue) = device.get_queue() {
1037 let region = Some(hal::BufferCopy {
1040 src_offset: 0,
1041 dst_offset: 0,
1042 size: staging_buffer.size,
1043 });
1044 let transition_src = hal::BufferBarrier {
1045 buffer: staging_buffer.raw(),
1046 usage: hal::StateTransition {
1047 from: wgt::BufferUses::MAP_WRITE,
1048 to: wgt::BufferUses::COPY_SRC,
1049 },
1050 };
1051 let transition_dst = hal::BufferBarrier::<dyn hal::DynBuffer> {
1052 buffer: raw_buf,
1053 usage: hal::StateTransition {
1054 from: wgt::BufferUses::empty(),
1055 to: wgt::BufferUses::COPY_DST,
1056 },
1057 };
1058 let mut pending_writes = queue.pending_writes.lock();
1059 let encoder = pending_writes.activate();
1060 unsafe {
1061 encoder.transition_buffers(&[transition_src, transition_dst]);
1062 encoder.copy_buffer_to_buffer(
1065 staging_buffer.raw(),
1066 raw_buf,
1067 region.as_slice(),
1068 );
1069 }
1070 pending_writes.consume(staging_buffer);
1071 pending_writes.insert_buffer(self);
1072 }
1073 }
1074 BufferMapState::Idle => {
1075 return Err(BufferAccessError::NotMapped);
1076 }
1077 BufferMapState::Waiting(pending) => {
1078 return Ok(Some((pending.op, Err(BufferAccessError::MapAborted))));
1079 }
1080 BufferMapState::Active {
1081 mapping,
1082 range,
1083 host,
1084 } => {
1085 if host == HostMap::Write {
1086 #[cfg(feature = "trace")]
1087 if let Some(ref mut trace) = *device.trace.lock() {
1088 use crate::device::trace::{DataKind, IntoTrace};
1089
1090 let size = range.end - range.start;
1091 let data = trace.make_binary(DataKind::Bin, unsafe {
1092 core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize)
1093 });
1094 trace.add(trace::Action::WriteBuffer {
1095 id: self.to_trace(),
1096 data,
1097 offset: range.start,
1098 size,
1099 queued: false,
1100 });
1101 }
1102 if !mapping.is_coherent {
1103 unsafe { device.raw().flush_mapped_ranges(raw_buf, &[range]) };
1104 }
1105 }
1106 unsafe { device.raw().unmap_buffer(raw_buf) };
1107 }
1108 }
1109 Ok(None)
1110 }
1111
1112 pub fn destroy(self: &Arc<Self>) {
1113 profiling::scope!("Buffer::destroy");
1114 api_log!("Buffer::destroy {:?}", Arc::as_ptr(self));
1115
1116 let device = &self.device;
1117
1118 #[cfg(feature = "trace")]
1119 if let Some(trace) = device.trace.lock().as_mut() {
1120 use crate::device::trace::IntoTrace;
1121 trace.add(trace::Action::DestroyBuffer(self.to_trace()));
1122 }
1123
1124 let ResourceState::Valid(state) = &self.state else {
1125 return;
1126 };
1127
1128 let _ = self.unmap();
1129
1130 let temp = {
1131 let mut snatch_guard = device.snatchable_lock.write();
1132
1133 let raw = match state.raw.snatch(&mut snatch_guard) {
1134 Some(raw) => raw,
1135 None => {
1136 return;
1138 }
1139 };
1140
1141 let timestamp_normalization_bind_group = self
1142 .timestamp_normalization_bind_group
1143 .snatch(&mut snatch_guard);
1144
1145 let indirect_validation_bind_groups = self
1146 .indirect_validation_bind_groups
1147 .snatch(&mut snatch_guard);
1148
1149 drop(snatch_guard);
1150
1151 let bind_groups = {
1152 let mut guard = self.bind_groups.lock();
1153 mem::take(&mut *guard)
1154 };
1155
1156 queue::TempResource::DestroyedBuffer(DestroyedBuffer {
1157 raw: ManuallyDrop::new(raw),
1158 device: Arc::clone(&self.device),
1159 label: self.label().to_owned(),
1160 bind_groups,
1161 timestamp_normalization_bind_group,
1162 indirect_validation_bind_groups,
1163 })
1164 };
1165
1166 let Some(queue) = device.get_queue() else {
1167 return;
1168 };
1169
1170 {
1171 let mut pending_writes = queue.pending_writes.lock();
1172 if pending_writes.contains_buffer(self) {
1173 pending_writes.consume_temp(temp);
1174 return;
1175 }
1176 }
1177
1178 let mut life_lock = queue.lock_life();
1179 let last_submit_index = life_lock.get_buffer_latest_submission_index(self);
1180 if let Some(last_submit_index) = last_submit_index {
1181 life_lock.schedule_resource_destruction(temp, last_submit_index);
1182 }
1183 }
1184}
1185
1186#[derive(Clone, Debug, Error)]
1187#[non_exhaustive]
1188pub enum CreateBufferError {
1189 #[error(transparent)]
1190 Device(#[from] DeviceError),
1191 #[error("Failed to map buffer while creating: {0}")]
1192 AccessError(#[from] BufferAccessError),
1193 #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
1194 UnalignedSize,
1195 #[error("Invalid usage flags {0:?}")]
1196 InvalidUsage(wgt::BufferUsages),
1197 #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
1198 UsageMismatch(wgt::BufferUsages),
1199 #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
1200 MaxBufferSize { requested: u64, maximum: u64 },
1201 #[error(transparent)]
1202 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1203 #[error(transparent)]
1204 MissingFeatures(#[from] MissingFeatures),
1205 #[error("Failed to create bind group for indirect buffer validation: {0}")]
1206 IndirectValidationBindGroup(DeviceError),
1207 #[error("Error initializing buffer: {0}")]
1208 QueueWrite(#[from] queue::QueueWriteError),
1209}
1210
1211crate::impl_resource_type!(Buffer);
1212crate::impl_labeled!(Buffer);
1213crate::impl_parent_device!(Buffer);
1214crate::impl_storage_item!(Buffer);
1215crate::impl_trackable!(Buffer);
1216
1217impl WebGpuError for CreateBufferError {
1218 fn webgpu_error_type(&self) -> ErrorType {
1219 match self {
1220 Self::Device(e) => e.webgpu_error_type(),
1221 Self::AccessError(e) => e.webgpu_error_type(),
1222 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1223 Self::IndirectValidationBindGroup(e) => e.webgpu_error_type(),
1224 Self::MissingFeatures(e) => e.webgpu_error_type(),
1225 Self::QueueWrite(e) => e.webgpu_error_type(),
1226
1227 Self::UnalignedSize
1228 | Self::InvalidUsage(_)
1229 | Self::UsageMismatch(_)
1230 | Self::MaxBufferSize { .. } => ErrorType::Validation,
1231 }
1232 }
1233}
1234
1235#[derive(Debug)]
1237pub struct DestroyedBuffer {
1238 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1239 device: Arc<Device>,
1240 label: String,
1241 bind_groups: WeakVec<BindGroup>,
1242 timestamp_normalization_bind_group: Option<TimestampNormalizationBindGroup>,
1243 indirect_validation_bind_groups: Option<crate::indirect_validation::BindGroups>,
1244}
1245
1246impl DestroyedBuffer {
1247 pub fn label(&self) -> &dyn fmt::Debug {
1248 &self.label
1249 }
1250}
1251
1252impl Drop for DestroyedBuffer {
1253 fn drop(&mut self) {
1254 let mut deferred = self.device.deferred_destroy.lock();
1255 deferred.push(DeferredDestroy::BindGroups(mem::take(
1256 &mut self.bind_groups,
1257 )));
1258 drop(deferred);
1259
1260 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
1261 raw.dispose(self.device.raw());
1262 }
1263
1264 if let Some(raw) = self.indirect_validation_bind_groups.take() {
1265 raw.dispose(self.device.raw());
1266 }
1267
1268 resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
1269 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1271 unsafe {
1272 hal::DynDevice::destroy_buffer(self.device.raw(), raw);
1273 }
1274 }
1275}
1276
1277#[cfg(send_sync)]
1278unsafe impl Send for StagingBuffer {}
1279#[cfg(send_sync)]
1280unsafe impl Sync for StagingBuffer {}
1281
1282#[derive(Debug)]
1302pub struct StagingBuffer {
1303 raw: Box<dyn hal::DynBuffer>,
1304 device: Arc<Device>,
1305 pub(crate) size: wgt::BufferSize,
1306 is_coherent: bool,
1307 ptr: NonNull<u8>,
1308}
1309
1310impl StagingBuffer {
1311 pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {
1312 profiling::scope!("StagingBuffer::new");
1313 let stage_desc = hal::BufferDescriptor {
1314 label: hal_label(Some("(wgpu internal) Staging"), device.instance_flags),
1315 size: size.get(),
1316 usage: wgt::BufferUses::MAP_WRITE | wgt::BufferUses::COPY_SRC,
1317 memory_flags: hal::MemoryFlags::TRANSIENT,
1318 };
1319
1320 let raw = unsafe { device.raw().create_buffer(&stage_desc) }
1321 .map_err(|e| device.handle_hal_error(e))?;
1322 let mapping = unsafe { device.raw().map_buffer(raw.as_ref(), 0..size.get()) }
1323 .map_err(|e| device.handle_hal_error(e))?;
1324
1325 let staging_buffer = StagingBuffer {
1326 raw,
1327 device: device.clone(),
1328 size,
1329 is_coherent: mapping.is_coherent,
1330 ptr: mapping.ptr,
1331 };
1332
1333 Ok(staging_buffer)
1334 }
1335
1336 pub(crate) unsafe fn ptr(&self) -> NonNull<u8> {
1339 self.ptr
1340 }
1341
1342 #[cfg(feature = "trace")]
1343 pub(crate) fn get_data(&self) -> &[u8] {
1344 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.size.get() as usize) }
1345 }
1346
1347 pub(crate) fn write_zeros(&mut self) {
1348 unsafe { core::ptr::write_bytes(self.ptr.as_ptr(), 0, self.size.get() as usize) };
1349 }
1350
1351 pub(crate) fn write(&mut self, data: &[u8]) {
1352 assert!(data.len() >= self.size.get() as usize);
1353 unsafe {
1356 core::ptr::copy_nonoverlapping(
1357 data.as_ptr(),
1358 self.ptr.as_ptr(),
1359 self.size.get() as usize,
1360 );
1361 }
1362 }
1363
1364 pub(crate) unsafe fn write_with_offset(
1366 &mut self,
1367 data: &[u8],
1368 src_offset: isize,
1369 dst_offset: isize,
1370 size: usize,
1371 ) {
1372 unsafe {
1373 debug_assert!(
1374 (src_offset + size as isize) as usize <= data.len(),
1375 "src_offset + size must be in-bounds: src_offset = {}, size = {}, data.len() = {}",
1376 src_offset,
1377 size,
1378 data.len()
1379 );
1380 core::ptr::copy_nonoverlapping(
1381 data.as_ptr().offset(src_offset),
1382 self.ptr.as_ptr().offset(dst_offset),
1383 size,
1384 );
1385 }
1386 }
1387
1388 pub(crate) fn flush(self) -> FlushedStagingBuffer {
1389 let device = self.device.raw();
1390 if !self.is_coherent {
1391 #[allow(clippy::single_range_in_vec_init)]
1392 unsafe {
1393 device.flush_mapped_ranges(self.raw.as_ref(), &[0..self.size.get()])
1394 };
1395 }
1396 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1397
1398 let StagingBuffer {
1399 raw, device, size, ..
1400 } = self;
1401
1402 FlushedStagingBuffer {
1403 raw: ManuallyDrop::new(raw),
1404 device,
1405 size,
1406 }
1407 }
1408
1409 pub(crate) fn dispose(self) {
1410 let device = self.device.raw();
1411 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1412 unsafe { device.destroy_buffer(self.raw) };
1413 }
1414}
1415
1416crate::impl_resource_type!(StagingBuffer);
1417crate::impl_storage_item!(StagingBuffer);
1418
1419#[derive(Debug)]
1420pub struct FlushedStagingBuffer {
1421 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1422 device: Arc<Device>,
1423 pub(crate) size: wgt::BufferSize,
1424}
1425
1426impl FlushedStagingBuffer {
1427 pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {
1428 self.raw.as_ref()
1429 }
1430}
1431
1432impl Drop for FlushedStagingBuffer {
1433 fn drop(&mut self) {
1434 resource_log!("Destroy raw StagingBuffer");
1435 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1437 unsafe { self.device.raw().destroy_buffer(raw) };
1438 }
1439}
1440
1441pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
1442
1443#[derive(Debug)]
1444pub(crate) enum TextureInner {
1445 Native {
1446 raw: Box<dyn hal::DynTexture>,
1447 },
1448 Surface {
1449 raw: Box<dyn hal::DynSurfaceTexture>,
1450 },
1451}
1452
1453impl TextureInner {
1454 pub(crate) fn raw(&self) -> &dyn hal::DynTexture {
1455 match self {
1456 Self::Native { raw } => raw.as_ref(),
1457 Self::Surface { raw, .. } => raw.as_ref().borrow(),
1458 }
1459 }
1460}
1461
1462#[derive(Debug)]
1463pub enum TextureClearMode {
1464 BufferCopy,
1465 RenderPass {
1467 clear_views: SmallVec<[ManuallyDrop<Box<dyn hal::DynTextureView>>; 1]>,
1468 is_color: bool,
1469 },
1470 Surface {
1471 clear_view: ManuallyDrop<Box<dyn hal::DynTextureView>>,
1472 },
1473 None,
1476}
1477
1478#[derive(Debug)]
1479pub struct TextureState {
1480 pub(crate) inner: Snatchable<TextureInner>,
1481}
1482
1483#[derive(Debug)]
1484pub struct Texture {
1485 pub(crate) state: ResourceState<TextureState>,
1486 pub(crate) device: Arc<Device>,
1487 pub(crate) desc: wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
1488 pub(crate) _hal_usage: wgt::TextureUses,
1489 pub(crate) format_features: wgt::TextureFormatFeatures,
1490 pub(crate) initialization_status: RwLock<TextureInitTracker>,
1491 pub(crate) full_range: TextureSelector,
1492 pub(crate) tracking_data: TrackingData,
1493 pub(crate) clear_mode: RwLock<TextureClearMode>,
1494 pub(crate) views: Mutex<WeakVec<TextureView>>,
1495 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
1497}
1498
1499impl Texture {
1500 pub(crate) fn new(
1501 device: &Arc<Device>,
1502 inner: TextureInner,
1503 hal_usage: wgt::TextureUses,
1504 desc: &TextureDescriptor,
1505 format_features: wgt::TextureFormatFeatures,
1506 clear_mode: TextureClearMode,
1507 init: bool,
1508 ) -> Self {
1509 Texture {
1510 state: ResourceState::Valid(TextureState {
1511 inner: Snatchable::new(inner),
1512 }),
1513 device: device.clone(),
1514 desc: desc.map_label(|label| label.to_string()),
1515 _hal_usage: hal_usage,
1516 format_features,
1517 initialization_status: RwLock::new(
1518 rank::TEXTURE_INITIALIZATION_STATUS,
1519 if init {
1520 TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count())
1521 } else {
1522 TextureInitTracker::new(desc.mip_level_count, 0)
1523 },
1524 ),
1525 full_range: TextureSelector {
1526 mips: 0..desc.mip_level_count,
1527 layers: 0..desc.array_layer_count(),
1528 },
1529 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1530 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
1531 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1532 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1533 }
1534 }
1535
1536 pub fn invalid(device: &Arc<Device>, desc: &TextureDescriptor) -> Arc<Self> {
1537 Arc::new(Texture {
1538 state: ResourceState::Invalid,
1539 device: device.clone(),
1540 desc: desc.map_label(|label| label.to_string()),
1541 _hal_usage: wgt::TextureUses::empty(),
1542 format_features: wgt::TextureFormatFeatures {
1543 allowed_usages: wgt::TextureUsages::empty(),
1544 flags: wgt::TextureFormatFeatureFlags::empty(),
1545 },
1546 initialization_status: RwLock::new(
1547 rank::TEXTURE_INITIALIZATION_STATUS,
1548 TextureInitTracker::new(0, 0),
1549 ),
1550 full_range: TextureSelector {
1551 mips: 0..desc.mip_level_count,
1552 layers: 0..desc.array_layer_count(),
1553 },
1554 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1555 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, TextureClearMode::None),
1556 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1557 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1558 })
1559 }
1560
1561 pub(crate) fn check_usage(
1564 &self,
1565 expected: wgt::TextureUsages,
1566 ) -> Result<(), MissingTextureUsageError> {
1567 if self.desc.usage.contains(expected) {
1568 Ok(())
1569 } else {
1570 Err(MissingTextureUsageError {
1571 res: self.error_ident(),
1572 actual: self.desc.usage,
1573 expected,
1574 })
1575 }
1576 }
1577}
1578
1579impl Drop for Texture {
1580 #[allow(trivial_casts)]
1581 fn drop(&mut self) {
1582 profiling::scope!("Texture::drop");
1583 api_log!("Texture::drop {:?}", self as *const _);
1584
1585 #[cfg(feature = "trace")]
1586 {
1587 let mut t = self.device.trace.lock();
1588 if let Some(t) = t.as_mut() {
1589 use crate::device::trace::to_trace;
1590
1591 t.add(trace::Action::DropTexture(unsafe { to_trace(self) }));
1593 }
1594 }
1595 match *self.clear_mode.write() {
1596 TextureClearMode::Surface {
1597 ref mut clear_view, ..
1598 } => {
1599 let raw = unsafe { ManuallyDrop::take(clear_view) };
1601 unsafe {
1602 self.device.raw().destroy_texture_view(raw);
1603 }
1604 }
1605 TextureClearMode::RenderPass {
1606 ref mut clear_views,
1607 ..
1608 } => {
1609 clear_views.iter_mut().for_each(|clear_view| {
1610 let raw = unsafe { ManuallyDrop::take(clear_view) };
1612 unsafe {
1613 self.device.raw().destroy_texture_view(raw);
1614 }
1615 });
1616 }
1617 _ => {}
1618 };
1619
1620 let ResourceState::Valid(state) = &mut self.state else {
1621 return;
1622 };
1623 if let Some(TextureInner::Native { raw }) = state.inner.take() {
1624 resource_log!("Destroy raw {}", self.error_ident());
1625 unsafe {
1626 self.device.raw().destroy_texture(raw);
1627 }
1628 }
1629 }
1630}
1631
1632impl RawResourceAccess for Texture {
1633 type DynResource = dyn hal::DynTexture;
1634
1635 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1636 self.state
1637 .as_ref()
1638 .valid()
1639 .and_then(|t| t.inner.get(guard).map(|t| t.raw()))
1640 }
1641}
1642
1643impl Texture {
1644 pub(crate) fn state(&self) -> Result<&TextureState, InvalidResourceError> {
1645 match &self.state {
1646 ResourceState::Valid(state) => Ok(state),
1647 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
1648 }
1649 }
1650
1651 pub(crate) fn check_destroyed(
1652 &self,
1653 guard: &SnatchGuard,
1654 ) -> Result<(), DestroyedResourceError> {
1655 let Ok(state) = self.state() else {
1656 return Ok(());
1657 };
1658 state
1659 .inner
1660 .get(guard)
1661 .map(|_| ())
1662 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1663 }
1664
1665 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
1666 self.state().map(|_| ())
1667 }
1668
1669 pub(crate) fn try_inner<'a>(
1670 &'a self,
1671 guard: &'a SnatchGuard,
1672 ) -> Result<&'a TextureInner, InvalidOrDestroyedResourceError> {
1673 self.state()?
1674 .inner
1675 .get(guard)
1676 .ok_or_else(|| DestroyedResourceError(self.error_ident()).into())
1677 }
1678
1679 pub(crate) fn get_clear_view<'a>(
1680 clear_mode: &'a TextureClearMode,
1681 desc: &'a wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
1682 mip_level: u32,
1683 depth_or_layer: u32,
1684 ) -> &'a dyn hal::DynTextureView {
1685 match *clear_mode {
1686 TextureClearMode::BufferCopy => {
1687 panic!("Given texture is cleared with buffer copies, not render passes")
1688 }
1689 TextureClearMode::None => {
1690 panic!("Given texture can't be cleared")
1691 }
1692 TextureClearMode::Surface { ref clear_view, .. } => clear_view.as_ref(),
1693 TextureClearMode::RenderPass {
1694 ref clear_views, ..
1695 } => {
1696 let index = if desc.dimension == wgt::TextureDimension::D3 {
1697 (0..mip_level).fold(0, |acc, mip| {
1698 acc + (desc.size.depth_or_array_layers >> mip).max(1)
1699 })
1700 } else {
1701 mip_level * desc.size.depth_or_array_layers
1702 } + depth_or_layer;
1703 clear_views[index as usize].as_ref()
1704 }
1705 }
1706 }
1707
1708 pub fn destroy(self: &Arc<Self>) {
1709 profiling::scope!("Texture::destroy");
1710 api_log!("Texture::destroy {:?}", Arc::as_ptr(self));
1711
1712 #[cfg(feature = "trace")]
1713 if let Some(trace) = self.device.trace.lock().as_mut() {
1714 use crate::device::trace::IntoTrace as _;
1715
1716 trace.add(trace::Action::DestroyTexture(self.to_trace()));
1717 }
1718
1719 let device = &self.device;
1720
1721 let ResourceState::Valid(state) = &self.state else {
1722 return;
1723 };
1724
1725 let temp = {
1726 let raw = match state.inner.snatch(&mut device.snatchable_lock.write()) {
1727 Some(TextureInner::Native { raw }) => raw,
1728 Some(TextureInner::Surface { .. }) => {
1729 return;
1730 }
1731 None => {
1732 return;
1734 }
1735 };
1736
1737 let views = {
1738 let mut guard = self.views.lock();
1739 mem::take(&mut *guard)
1740 };
1741
1742 let bind_groups = {
1743 let mut guard = self.bind_groups.lock();
1744 mem::take(&mut *guard)
1745 };
1746
1747 queue::TempResource::DestroyedTexture(DestroyedTexture {
1748 raw: ManuallyDrop::new(raw),
1749 views,
1750 clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None),
1751 bind_groups,
1752 device: Arc::clone(&self.device),
1753 label: self.label().to_owned(),
1754 })
1755 };
1756
1757 let Some(queue) = device.get_queue() else {
1758 return;
1759 };
1760
1761 {
1762 let mut pending_writes = queue.pending_writes.lock();
1763 if pending_writes.contains_texture(self) {
1764 pending_writes.consume_temp(temp);
1765 return;
1766 }
1767 }
1768
1769 let mut life_lock = queue.lock_life();
1770 let last_submit_index = life_lock.get_texture_latest_submission_index(self);
1771 if let Some(last_submit_index) = last_submit_index {
1772 life_lock.schedule_resource_destruction(temp, last_submit_index);
1773 }
1774 }
1775}
1776
1777#[derive(Debug)]
1779pub struct DestroyedTexture {
1780 raw: ManuallyDrop<Box<dyn hal::DynTexture>>,
1781 views: WeakVec<TextureView>,
1782 clear_mode: TextureClearMode,
1783 bind_groups: WeakVec<BindGroup>,
1784 device: Arc<Device>,
1785 label: String,
1786}
1787
1788impl DestroyedTexture {
1789 pub fn label(&self) -> &dyn fmt::Debug {
1790 &self.label
1791 }
1792}
1793
1794impl Drop for DestroyedTexture {
1795 fn drop(&mut self) {
1796 let device = &self.device;
1797
1798 let mut deferred = device.deferred_destroy.lock();
1799 deferred.push(DeferredDestroy::TextureViews(mem::take(&mut self.views)));
1800 deferred.push(DeferredDestroy::BindGroups(mem::take(
1801 &mut self.bind_groups,
1802 )));
1803 drop(deferred);
1804
1805 match mem::replace(&mut self.clear_mode, TextureClearMode::None) {
1806 TextureClearMode::RenderPass { clear_views, .. } => {
1807 for clear_view in clear_views {
1808 let raw = ManuallyDrop::into_inner(clear_view);
1809 unsafe { self.device.raw().destroy_texture_view(raw) };
1810 }
1811 }
1812 TextureClearMode::Surface { clear_view } => {
1813 let raw = ManuallyDrop::into_inner(clear_view);
1814 unsafe { self.device.raw().destroy_texture_view(raw) };
1815 }
1816 _ => (),
1817 }
1818
1819 resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
1820 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1822 unsafe {
1823 self.device.raw().destroy_texture(raw);
1824 }
1825 }
1826}
1827
1828#[derive(Clone, Copy, Debug)]
1829pub enum TextureErrorDimension {
1830 X,
1831 Y,
1832 Z,
1833}
1834
1835#[derive(Clone, Debug, Error)]
1836#[non_exhaustive]
1837pub enum TextureDimensionError {
1838 #[error("Dimension {0:?} is zero")]
1839 Zero(TextureErrorDimension),
1840 #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
1841 LimitExceeded {
1842 dim: TextureErrorDimension,
1843 given: u32,
1844 limit: u32,
1845 },
1846 #[error("Sample count {0} is invalid")]
1847 InvalidSampleCount(u32),
1848 #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
1849 NotMultipleOfBlockWidth {
1850 width: u32,
1851 block_width: u32,
1852 format: wgt::TextureFormat,
1853 },
1854 #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
1855 NotMultipleOfBlockHeight {
1856 height: u32,
1857 block_height: u32,
1858 format: wgt::TextureFormat,
1859 },
1860 #[error(
1861 "Width {width} is not a multiple of {format:?}'s width multiple requirement ({multiple})"
1862 )]
1863 WidthNotMultipleOf {
1864 width: u32,
1865 multiple: u32,
1866 format: wgt::TextureFormat,
1867 },
1868 #[error("Height {height} is not a multiple of {format:?}'s height multiple requirement ({multiple})")]
1869 HeightNotMultipleOf {
1870 height: u32,
1871 multiple: u32,
1872 format: wgt::TextureFormat,
1873 },
1874 #[error("Multisampled texture depth or array layers must be 1, got {0}")]
1875 MultisampledDepthOrArrayLayer(u32),
1876}
1877
1878impl WebGpuError for TextureDimensionError {
1879 fn webgpu_error_type(&self) -> ErrorType {
1880 ErrorType::Validation
1881 }
1882}
1883
1884#[derive(Clone, Debug, Error)]
1885#[non_exhaustive]
1886pub enum CreateTextureError {
1887 #[error(transparent)]
1888 Device(#[from] DeviceError),
1889 #[error(transparent)]
1890 CreateTextureView(#[from] CreateTextureViewError),
1891 #[error("Invalid usage flags {0:?}")]
1892 InvalidUsage(wgt::TextureUsages),
1893 #[error(transparent)]
1894 InvalidDimension(#[from] TextureDimensionError),
1895 #[error("Depth texture ({1:?}) can't be created as {0:?}")]
1896 InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
1897 #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
1898 InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
1899 #[error(
1900 "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
1901 )]
1902 InvalidMipLevelCount { requested: u32, maximum: u32 },
1903 #[error(
1904 "Texture usages {0:?} are not allowed on a texture of type {1:?}{downlevel_suffix}",
1905 downlevel_suffix = if *.2 { " due to downlevel restrictions" } else { "" }
1906 )]
1907 InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
1908 #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
1909 InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
1910 #[error("Transient texture usage must be equal to `TRANSIENT_ATTACHMENT | RENDER_ATTACHMENT`, but got `{0:?}`")]
1911 InvalidTransientTextureUsage(wgt::TextureUsages),
1912 #[error("Transient texture view formats must be empty")]
1913 InvalidTransientTextureViewFormats,
1914 #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
1915 InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
1916 #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
1917 InvalidMultisampledStorageBinding,
1918 #[error("Format {0:?} does not support multisampling")]
1919 InvalidMultisampledFormat(wgt::TextureFormat),
1920 #[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:?}.")]
1921 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
1922 #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
1923 MultisampledNotRenderAttachment,
1924 #[error("Transient texture mip level count ({0}) must be 1")]
1925 InvalidTransientTextureMipLevelCount(u32),
1926 #[error("Transient texture layer count ({0}) must be 1")]
1927 InvalidTransientTextureLayerCount(u32),
1928 #[error("Texture format {0:?} can't be used due to missing features")]
1929 MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
1930 #[error(transparent)]
1931 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1932}
1933
1934crate::impl_resource_type!(Texture);
1935impl Labeled for Texture {
1936 fn label(&self) -> &str {
1937 &self.desc.label
1938 }
1939}
1940crate::impl_parent_device!(Texture);
1941crate::impl_storage_item!(Texture);
1942crate::impl_trackable!(Texture);
1943
1944impl Borrow<TextureSelector> for Texture {
1945 fn borrow(&self) -> &TextureSelector {
1946 &self.full_range
1947 }
1948}
1949
1950impl WebGpuError for CreateTextureError {
1951 fn webgpu_error_type(&self) -> ErrorType {
1952 match self {
1953 Self::Device(e) => e.webgpu_error_type(),
1954 Self::CreateTextureView(e) => e.webgpu_error_type(),
1955 Self::InvalidDimension(e) => e.webgpu_error_type(),
1956 Self::MissingFeatures(_, e) => e.webgpu_error_type(),
1957 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1958
1959 Self::InvalidUsage(_)
1960 | Self::InvalidDepthDimension(_, _)
1961 | Self::InvalidCompressedDimension(_, _)
1962 | Self::InvalidMipLevelCount { .. }
1963 | Self::InvalidFormatUsages(_, _, _)
1964 | Self::InvalidViewFormat(_, _)
1965 | Self::InvalidDimensionUsages(_, _)
1966 | Self::InvalidMultisampledStorageBinding
1967 | Self::InvalidMultisampledFormat(_)
1968 | Self::InvalidSampleCount(..)
1969 | Self::InvalidTransientTextureUsage(_)
1970 | Self::InvalidTransientTextureMipLevelCount(_)
1971 | Self::InvalidTransientTextureLayerCount(_)
1972 | Self::InvalidTransientTextureViewFormats
1973 | Self::MultisampledNotRenderAttachment => ErrorType::Validation,
1974 }
1975 }
1976}
1977
1978#[derive(Clone, Debug, Default, Eq, PartialEq)]
1980#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1981#[cfg_attr(feature = "serde", serde(default))]
1982pub struct TextureViewDescriptor<'a> {
1983 pub label: Label<'a>,
1987 pub format: Option<wgt::TextureFormat>,
1992 pub dimension: Option<wgt::TextureViewDimension>,
1998 pub usage: Option<wgt::TextureUsages>,
2001 pub range: wgt::ImageSubresourceRange,
2003}
2004
2005#[derive(Debug)]
2006pub(crate) struct HalTextureViewDescriptor {
2007 pub texture_format: wgt::TextureFormat,
2008 pub format: wgt::TextureFormat,
2009 pub usage: wgt::TextureUsages,
2010 pub dimension: wgt::TextureViewDimension,
2011 pub range: wgt::ImageSubresourceRange,
2012}
2013
2014impl HalTextureViewDescriptor {
2015 pub fn aspects(&self) -> hal::FormatAspects {
2016 hal::FormatAspects::new(self.texture_format, self.range.aspect)
2017 }
2018}
2019
2020#[derive(Debug, Copy, Clone, Error)]
2021pub enum TextureViewNotRenderableReason {
2022 #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
2023 Usage(wgt::TextureUsages),
2024 #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
2025 Dimension(wgt::TextureViewDimension),
2026 #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
2027 MipLevelCount(u32),
2028 #[error("This texture view has more than one array layer. View array layers: {0:?}")]
2029 ArrayLayerCount(u32),
2030 #[error(
2031 "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
2032 )]
2033 Aspects(hal::FormatAspects),
2034}
2035
2036#[derive(Debug)]
2037pub struct TextureViewState {
2038 pub(crate) raw: Snatchable<Box<dyn hal::DynTextureView>>,
2039 pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
2041}
2042
2043#[derive(Debug)]
2044pub struct TextureView {
2045 pub(crate) state: ResourceState<TextureViewState>,
2046 pub(crate) parent: Arc<Texture>,
2048 pub(crate) device: Arc<Device>,
2049 pub(crate) desc: HalTextureViewDescriptor,
2050 pub(crate) format_features: wgt::TextureFormatFeatures,
2051 pub(crate) samples: u32,
2052 pub(crate) selector: TextureSelector,
2053 pub(crate) label: String,
2055}
2056
2057impl Drop for TextureView {
2058 #[expect(trivial_casts)]
2059 fn drop(&mut self) {
2060 profiling::scope!("TextureView::drop");
2061 api_log!("TextureView::drop {:?}", self as *const _);
2062 #[cfg(feature = "trace")]
2063 if let Some(t) = self.device.trace.lock().as_mut() {
2064 t.add(trace::Action::DropTextureView(unsafe {
2065 trace::to_trace(self)
2066 }));
2067 }
2068 let ResourceState::Valid(state) = &mut self.state else {
2069 return;
2070 };
2071
2072 if let Some(raw) = state.raw.take() {
2073 resource_log!("Destroy raw {}", self.error_ident());
2074 unsafe {
2075 self.device.raw().destroy_texture_view(raw);
2076 }
2077 }
2078 }
2079}
2080
2081impl RawResourceAccess for TextureView {
2082 type DynResource = dyn hal::DynTextureView;
2083
2084 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2085 self.state()
2086 .ok()
2087 .and_then(|state| state.raw.get(guard).map(|it| it.as_ref()))
2088 }
2089
2090 fn try_raw<'a>(
2091 &'a self,
2092 guard: &'a SnatchGuard,
2093 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
2094 self.parent.check_destroyed(guard)?;
2095
2096 self.raw(guard)
2097 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
2098 }
2099}
2100
2101impl TextureView {
2102 pub(crate) fn check_usage(
2105 &self,
2106 expected: wgt::TextureUsages,
2107 ) -> Result<(), MissingTextureUsageError> {
2108 if self.desc.usage.contains(expected) {
2109 Ok(())
2110 } else {
2111 Err(MissingTextureUsageError {
2112 res: self.error_ident(),
2113 actual: self.desc.usage,
2114 expected,
2115 })
2116 }
2117 }
2118
2119 pub(crate) fn state(&self) -> Result<&TextureViewState, InvalidResourceError> {
2120 match &self.state {
2121 ResourceState::Valid(state) => Ok(state),
2122 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2123 }
2124 }
2125
2126 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
2127 self.state().map(|_| ())
2128 }
2129
2130 pub(crate) fn invalid(
2131 device: &Arc<Device>,
2132 texture: &Arc<Texture>,
2133 desc: &TextureViewDescriptor,
2134 ) -> Arc<Self> {
2135 Arc::new(TextureView {
2137 state: ResourceState::Invalid,
2138 parent: texture.clone(),
2139 device: device.clone(),
2140 desc: HalTextureViewDescriptor {
2141 texture_format: texture.desc.format,
2142 format: desc.format.unwrap_or(texture.desc.format),
2143 usage: desc.usage.unwrap_or(texture.desc.usage),
2144 dimension: desc.dimension.unwrap_or(match texture.desc.dimension {
2145 wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1,
2146 wgt::TextureDimension::D2 => wgt::TextureViewDimension::D2,
2147 wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3,
2148 }),
2149 range: desc.range,
2150 },
2151 format_features: texture.format_features,
2152 samples: texture.desc.sample_count,
2153 selector: TextureSelector {
2154 mips: desc.range.mip_range(texture.desc.mip_level_count),
2155 layers: desc.range.layer_range(texture.desc.array_layer_count()),
2156 },
2157 label: desc.label.to_string(),
2158 })
2159 }
2160}
2161
2162#[derive(Clone, Debug, Error)]
2163#[non_exhaustive]
2164pub enum CreateTextureViewError {
2165 #[error(transparent)]
2166 Device(#[from] DeviceError),
2167 #[error(transparent)]
2168 DestroyedResource(#[from] DestroyedResourceError),
2169 #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
2170 InvalidTextureViewDimension {
2171 view: wgt::TextureViewDimension,
2172 texture: wgt::TextureDimension,
2173 },
2174 #[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.")]
2175 TextureViewFormatNotRenderable(wgt::TextureFormat),
2176 #[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.")]
2177 TextureViewFormatNotStorage(wgt::TextureFormat),
2178 #[error("Texture view usages (`{view:?}`) must be a subset of the texture's original usages (`{texture:?}`)")]
2179 InvalidTextureViewUsage {
2180 view: wgt::TextureUsages,
2181 texture: wgt::TextureUsages,
2182 },
2183 #[error("Texture view dimension `{0:?}` cannot be used with a multisampled texture")]
2184 InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
2185 #[error(
2186 "TextureView has an arrayLayerCount of {depth}. Views of type `Cube` must have arrayLayerCount of 6."
2187 )]
2188 InvalidCubemapTextureDepth { depth: u32 },
2189 #[error("TextureView has an arrayLayerCount of {depth}. Views of type `CubeArray` must have an arrayLayerCount that is a multiple of 6.")]
2190 InvalidCubemapArrayTextureDepth { depth: u32 },
2191 #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
2192 InvalidCubeTextureViewSize,
2193 #[error("Mip level count is 0")]
2194 ZeroMipLevelCount,
2195 #[error("Array layer count is 0")]
2196 ZeroArrayLayerCount,
2197 #[error(
2198 "`TextureView` starts at mip level {base_mip_level} and spans {mip_level_count} mip \
2199 levels, but the texture view only has {total} total mip level(s)"
2200 )]
2201 TooManyMipLevels {
2202 base_mip_level: u32,
2203 mip_level_count: u32,
2204 total: u32,
2205 },
2206 #[error(
2207 "`TextureView` starts at array layer {base_array_layer} and spans {array_layer_count}) \
2208 array layers, but the texture view only has {total} total layer(s)"
2209 )]
2210 TooManyArrayLayers {
2211 base_array_layer: u32,
2212 array_layer_count: u32,
2213 total: u32,
2214 },
2215 #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
2216 InvalidArrayLayerCount {
2217 requested: u32,
2218 dim: wgt::TextureViewDimension,
2219 },
2220 #[error(
2221 "Aspect {requested_aspect:?} is not a valid aspect of the source texture format {texture_format:?}"
2222 )]
2223 InvalidAspect {
2224 texture_format: wgt::TextureFormat,
2225 requested_aspect: wgt::TextureAspect,
2226 },
2227 #[error(
2228 "Trying to create a view of format {view:?} of a texture with format {texture:?}, \
2229 but this view format is not present in the texture's viewFormat array"
2230 )]
2231 FormatReinterpretation {
2232 texture: wgt::TextureFormat,
2233 view: wgt::TextureFormat,
2234 },
2235 #[error(
2236 "The texture view (`{view:?}`) from transient texture (`{texture:?}`) must have the same usage"
2237 )]
2238 InvalidTransientTextureViewUsage {
2239 texture: wgt::TextureUsages,
2240 view: wgt::TextureUsages,
2241 },
2242 #[error(transparent)]
2243 InvalidResource(#[from] InvalidResourceError),
2244 #[error(transparent)]
2245 MissingFeatures(#[from] MissingFeatures),
2246 #[error("TextureAspect::All cannot be used in texture views on multi-planar formats")]
2247 MultiplanarFullTexture(wgt::TextureFormat),
2248}
2249
2250impl From<InvalidOrDestroyedResourceError> for CreateTextureViewError {
2251 fn from(value: InvalidOrDestroyedResourceError) -> Self {
2252 match value {
2253 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
2254 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
2255 }
2256 }
2257}
2258
2259impl WebGpuError for CreateTextureViewError {
2260 fn webgpu_error_type(&self) -> ErrorType {
2261 match self {
2262 Self::Device(e) => e.webgpu_error_type(),
2263
2264 Self::InvalidTextureViewDimension { .. }
2265 | Self::InvalidResource(_)
2266 | Self::InvalidMultisampledTextureViewDimension(_)
2267 | Self::InvalidCubemapTextureDepth { .. }
2268 | Self::InvalidCubemapArrayTextureDepth { .. }
2269 | Self::InvalidCubeTextureViewSize
2270 | Self::ZeroMipLevelCount
2271 | Self::ZeroArrayLayerCount
2272 | Self::TooManyMipLevels { .. }
2273 | Self::TooManyArrayLayers { .. }
2274 | Self::InvalidArrayLayerCount { .. }
2275 | Self::InvalidAspect { .. }
2276 | Self::FormatReinterpretation { .. }
2277 | Self::DestroyedResource(_)
2278 | Self::TextureViewFormatNotRenderable(_)
2279 | Self::TextureViewFormatNotStorage(_)
2280 | Self::InvalidTextureViewUsage { .. }
2281 | Self::InvalidTransientTextureViewUsage { .. }
2282 | Self::MissingFeatures(_)
2283 | Self::MultiplanarFullTexture(_) => ErrorType::Validation,
2284 }
2285 }
2286}
2287
2288crate::impl_resource_type!(TextureView);
2289crate::impl_labeled!(TextureView);
2290crate::impl_parent_device!(TextureView);
2291crate::impl_storage_item!(TextureView);
2292
2293pub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;
2294
2295#[derive(Debug)]
2296pub(crate) struct ExternalTextureState {
2297 pub(crate) params: Arc<Buffer>,
2300}
2301
2302#[derive(Debug)]
2303pub struct ExternalTexture {
2304 pub(crate) state: ResourceState<ExternalTextureState>,
2305 pub(crate) device: Arc<Device>,
2306 pub(crate) planes: arrayvec::ArrayVec<Arc<TextureView>, 3>,
2308 pub(crate) label: String,
2310 pub(crate) tracking_data: TrackingData,
2311}
2312
2313impl Drop for ExternalTexture {
2314 #[allow(trivial_casts)]
2315 fn drop(&mut self) {
2316 profiling::scope!("ExternalTexture::drop");
2317 api_log!("ExternalTexture::drop {:?}", self as *const _);
2318
2319 resource_log!("Destroy raw {}", self.error_ident());
2320 #[cfg(feature = "trace")]
2321 if let Some(t) = self.device.trace.lock().as_mut() {
2322 t.add(trace::Action::DropExternalTexture(unsafe {
2323 trace::to_trace(self)
2324 }));
2325 }
2326 }
2327}
2328
2329impl ExternalTexture {
2330 pub(crate) fn state(&self) -> Result<&ExternalTextureState, InvalidResourceError> {
2331 match &self.state {
2332 ResourceState::Valid(state) => Ok(state),
2333 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2334 }
2335 }
2336
2337 pub fn destroy(self: &Arc<Self>) {
2338 profiling::scope!("ExternalTexture::destroy");
2339 api_log!("ExternalTexture::destroy {:?}", Arc::as_ptr(self));
2340
2341 #[cfg(feature = "trace")]
2342 if let Some(trace) = self.device.trace.lock().as_mut() {
2343 use crate::device::trace::IntoTrace as _;
2344
2345 trace.add(trace::Action::DestroyExternalTexture(self.to_trace()));
2346 }
2347 if let Ok(state) = self.state() {
2348 state.params.destroy();
2349 }
2350 }
2351
2352 pub fn invalid(device: Arc<Device>, desc: &ExternalTextureDescriptor) -> Arc<Self> {
2353 Arc::new(ExternalTexture {
2354 state: ResourceState::Invalid,
2355 planes: arrayvec::ArrayVec::new(),
2356 label: desc.label.to_string(),
2357 tracking_data: TrackingData::new(device.tracker_indices.external_textures.clone()),
2358 device,
2359 })
2360 }
2361}
2362
2363#[derive(Clone, Debug, Error)]
2364#[non_exhaustive]
2365pub enum CreateExternalTextureError {
2366 #[error(transparent)]
2367 Device(#[from] DeviceError),
2368 #[error(transparent)]
2369 MissingFeatures(#[from] MissingFeatures),
2370 #[error(transparent)]
2371 InvalidResource(#[from] InvalidResourceError),
2372 #[error(transparent)]
2373 CreateBuffer(#[from] CreateBufferError),
2374 #[error(transparent)]
2375 QueueWrite(#[from] queue::QueueWriteError),
2376 #[error("External texture format {format:?} expects {expected} planes, but given {provided}")]
2377 IncorrectPlaneCount {
2378 format: wgt::ExternalTextureFormat,
2379 expected: usize,
2380 provided: usize,
2381 },
2382 #[error("External texture planes cannot be multisampled, but given view with samples = {0}")]
2383 InvalidPlaneMultisample(u32),
2384 #[error("External texture planes expect a filterable float sample type, but given view with format {format:?} (sample type {sample_type:?})")]
2385 InvalidPlaneSampleType {
2386 format: wgt::TextureFormat,
2387 sample_type: wgt::TextureSampleType,
2388 },
2389 #[error("External texture planes expect 2D dimension, but given view with dimension = {0:?}")]
2390 InvalidPlaneDimension(wgt::TextureViewDimension),
2391 #[error(transparent)]
2392 MissingTextureUsage(#[from] MissingTextureUsageError),
2393 #[error("External texture format {format:?} plane {plane} expects format with {expected} components but given view with format {provided:?} ({} components)",
2394 provided.components())]
2395 InvalidPlaneFormat {
2396 format: wgt::ExternalTextureFormat,
2397 plane: usize,
2398 expected: u8,
2399 provided: wgt::TextureFormat,
2400 },
2401}
2402
2403impl WebGpuError for CreateExternalTextureError {
2404 fn webgpu_error_type(&self) -> ErrorType {
2405 match self {
2406 CreateExternalTextureError::Device(e) => e.webgpu_error_type(),
2407 CreateExternalTextureError::MissingFeatures(e) => e.webgpu_error_type(),
2408 CreateExternalTextureError::InvalidResource(e) => e.webgpu_error_type(),
2409 CreateExternalTextureError::CreateBuffer(e) => e.webgpu_error_type(),
2410 CreateExternalTextureError::QueueWrite(e) => e.webgpu_error_type(),
2411 CreateExternalTextureError::MissingTextureUsage(e) => e.webgpu_error_type(),
2412 CreateExternalTextureError::IncorrectPlaneCount { .. }
2413 | CreateExternalTextureError::InvalidPlaneMultisample(_)
2414 | CreateExternalTextureError::InvalidPlaneSampleType { .. }
2415 | CreateExternalTextureError::InvalidPlaneDimension(_)
2416 | CreateExternalTextureError::InvalidPlaneFormat { .. } => ErrorType::Validation,
2417 }
2418 }
2419}
2420
2421crate::impl_resource_type!(ExternalTexture);
2422crate::impl_labeled!(ExternalTexture);
2423crate::impl_parent_device!(ExternalTexture);
2424crate::impl_storage_item!(ExternalTexture);
2425crate::impl_trackable!(ExternalTexture);
2426
2427#[derive(Clone, Debug, PartialEq)]
2429#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2430pub struct SamplerDescriptor<'a> {
2431 pub label: Label<'a>,
2435 pub address_modes: [wgt::AddressMode; 3],
2437 pub mag_filter: wgt::FilterMode,
2439 pub min_filter: wgt::FilterMode,
2441 pub mipmap_filter: wgt::MipmapFilterMode,
2443 pub lod_min_clamp: f32,
2445 pub lod_max_clamp: f32,
2447 pub compare: Option<wgt::CompareFunction>,
2449 pub anisotropy_clamp: u16,
2451 pub border_color: Option<wgt::SamplerBorderColor>,
2454}
2455
2456#[derive(Debug)]
2457pub struct Sampler {
2458 pub(crate) raw: ResourceState<Box<dyn hal::DynSampler>>,
2459 pub(crate) device: Arc<Device>,
2460 pub(crate) label: String,
2462 pub(crate) tracking_data: TrackingData,
2463 pub(crate) comparison: bool,
2465 pub(crate) filtering: bool,
2467}
2468
2469impl Drop for Sampler {
2470 #[allow(trivial_casts)]
2471 fn drop(&mut self) {
2472 profiling::scope!("Sampler::drop");
2473 api_log!("Sampler::drop {:?}", self as *const _);
2474 #[cfg(feature = "trace")]
2475 if let Some(t) = self.device.trace.lock().as_mut() {
2476 t.add(trace::Action::DropSampler(unsafe { trace::to_trace(self) }));
2477 }
2478 resource_log!("Destroy raw {}", self.error_ident());
2479 if let ResourceState::Valid(raw) = mem::replace(&mut self.raw, ResourceState::Invalid) {
2480 unsafe {
2481 self.device.raw().destroy_sampler(raw);
2482 }
2483 }
2484 }
2485}
2486
2487impl Sampler {
2488 pub(crate) fn raw(&self) -> Result<&dyn hal::DynSampler, InvalidResourceError> {
2489 self.raw
2490 .as_ref()
2491 .valid()
2492 .map(|raw| raw.as_ref())
2493 .ok_or_else(|| InvalidResourceError(self.error_ident()))
2494 }
2495
2496 pub(crate) fn invalid(device: Arc<Device>, desc: &SamplerDescriptor) -> Arc<Self> {
2497 Arc::new(Sampler {
2498 raw: ResourceState::Invalid,
2499 label: desc.label.to_string(),
2500 tracking_data: TrackingData::new(device.tracker_indices.samplers.clone()),
2501 device,
2502 comparison: desc.compare.is_some(),
2503 filtering: desc.mag_filter == wgt::FilterMode::Linear
2504 || desc.min_filter == wgt::FilterMode::Linear
2505 || desc.mipmap_filter == wgt::MipmapFilterMode::Linear,
2506 })
2507 }
2508}
2509
2510#[derive(Copy, Clone)]
2511pub enum SamplerFilterErrorType {
2512 MagFilter,
2513 MinFilter,
2514 MipmapFilter,
2515}
2516
2517impl fmt::Debug for SamplerFilterErrorType {
2518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2519 match *self {
2520 SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
2521 SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
2522 SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
2523 }
2524 }
2525}
2526
2527#[derive(Clone, Debug, Error)]
2528#[non_exhaustive]
2529pub enum CreateSamplerError {
2530 #[error(transparent)]
2531 Device(#[from] DeviceError),
2532 #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
2533 InvalidLodMinClamp(f32),
2534 #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
2535 InvalidLodMaxClamp {
2536 lod_min_clamp: f32,
2537 lod_max_clamp: f32,
2538 },
2539 #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
2540 InvalidAnisotropy(u16),
2541 #[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.")]
2542 InvalidFilterModeWithAnisotropy {
2543 filter_type: SamplerFilterErrorType,
2544 filter_mode: wgt::FilterMode,
2545 anisotropic_clamp: u16,
2546 },
2547 #[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.")]
2548 InvalidMipmapFilterModeWithAnisotropy {
2549 filter_type: SamplerFilterErrorType,
2550 filter_mode: wgt::MipmapFilterMode,
2551 anisotropic_clamp: u16,
2552 },
2553 #[error(transparent)]
2554 MissingFeatures(#[from] MissingFeatures),
2555}
2556
2557crate::impl_resource_type!(Sampler);
2558crate::impl_labeled!(Sampler);
2559crate::impl_parent_device!(Sampler);
2560crate::impl_storage_item!(Sampler);
2561crate::impl_trackable!(Sampler);
2562
2563impl WebGpuError for CreateSamplerError {
2564 fn webgpu_error_type(&self) -> ErrorType {
2565 match self {
2566 Self::Device(e) => e.webgpu_error_type(),
2567 Self::MissingFeatures(e) => e.webgpu_error_type(),
2568
2569 Self::InvalidLodMinClamp(_)
2570 | Self::InvalidLodMaxClamp { .. }
2571 | Self::InvalidAnisotropy(_)
2572 | Self::InvalidFilterModeWithAnisotropy { .. }
2573 | Self::InvalidMipmapFilterModeWithAnisotropy { .. } => ErrorType::Validation,
2574 }
2575 }
2576}
2577
2578#[derive(Clone, Debug, Error)]
2579#[non_exhaustive]
2580pub enum CreateQuerySetError {
2581 #[error(transparent)]
2582 Device(#[from] DeviceError),
2583 #[error("QuerySets cannot be made with zero queries")]
2584 ZeroCount,
2585 #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
2586 TooManyQueries { count: u32, maximum: u32 },
2587 #[error(transparent)]
2588 MissingFeatures(#[from] MissingFeatures),
2589}
2590
2591impl WebGpuError for CreateQuerySetError {
2592 fn webgpu_error_type(&self) -> ErrorType {
2593 match self {
2594 Self::Device(e) => e.webgpu_error_type(),
2595 Self::MissingFeatures(e) => e.webgpu_error_type(),
2596
2597 Self::TooManyQueries { .. } | Self::ZeroCount => ErrorType::Validation,
2598 }
2599 }
2600}
2601
2602pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
2603
2604#[derive(Debug)]
2605pub(crate) struct QuerySetState {
2606 pub(crate) raw: Snatchable<Box<dyn hal::DynQuerySet>>,
2607}
2608
2609#[derive(Debug)]
2610pub struct QuerySet {
2611 pub(crate) state: ResourceState<QuerySetState>,
2612 pub(crate) device: Arc<Device>,
2613 pub(crate) label: String,
2615 pub(crate) tracking_data: TrackingData,
2616 pub(crate) desc: wgt::QuerySetDescriptor<()>,
2617 pub(crate) initialized_slots: Mutex<bit_vec::BitVec>,
2618}
2619
2620impl RawResourceAccess for QuerySet {
2621 type DynResource = dyn hal::DynQuerySet;
2622
2623 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2624 self.state().ok()?.raw.get(guard).map(|b| b.as_ref())
2625 }
2626}
2627
2628impl QuerySet {
2629 pub(crate) fn state(&self) -> Result<&QuerySetState, InvalidResourceError> {
2630 match &self.state {
2631 ResourceState::Valid(state) => Ok(state),
2632 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2633 }
2634 }
2635
2636 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
2637 self.state().map(|_| ())
2638 }
2639
2640 pub fn invalid(device: Arc<Device>, desc: &QuerySetDescriptor) -> Arc<Self> {
2641 Arc::new(QuerySet {
2642 state: ResourceState::Invalid,
2643 label: desc.label.to_string(),
2644 tracking_data: TrackingData::new(device.tracker_indices.query_sets.clone()),
2645 desc: desc.clone().map_label(|_| ()),
2646 initialized_slots: Mutex::new(
2647 rank::QUERY_SET_INITIALIZED_SLOTS,
2648 bit_vec::BitVec::new(),
2649 ),
2650 device,
2651 })
2652 }
2653
2654 pub fn destroy(self: &Arc<Self>) {
2655 let device = &self.device;
2656
2657 profiling::scope!("QuerySet::destroy");
2658 api_log!("QuerySet::destroy {:?}", Arc::as_ptr(self));
2659
2660 #[cfg(feature = "trace")]
2661 if let Some(trace) = device.trace.lock().as_mut() {
2662 use crate::device::trace::IntoTrace as _;
2663
2664 trace.add(trace::Action::DestroyQuerySet(self.to_trace()));
2665 };
2666
2667 let ResourceState::Valid(state) = &self.state else {
2668 return;
2669 };
2670
2671 let temp = {
2672 let mut snatch_guard = self.device.snatchable_lock.write();
2673
2674 let raw = match state.raw.snatch(&mut snatch_guard) {
2675 Some(raw) => raw,
2676 None => {
2677 return;
2679 }
2680 };
2681
2682 drop(snatch_guard);
2683
2684 queue::TempResource::DestroyedQuerySet(DestroyedQuerySet {
2685 raw: ManuallyDrop::new(raw),
2686 device: Arc::clone(&self.device),
2687 label: self.label().to_owned(),
2688 })
2689 };
2690
2691 let Some(queue) = device.get_queue() else {
2692 return;
2693 };
2694
2695 let mut life_lock = queue.lock_life();
2696 let last_submit_index = life_lock.get_query_set_latest_submission_index(self);
2697 if let Some(last_submit_index) = last_submit_index {
2698 life_lock.schedule_resource_destruction(temp, last_submit_index);
2699 }
2700 }
2701}
2702
2703impl Drop for QuerySet {
2704 #[allow(trivial_casts)]
2705 fn drop(&mut self) {
2706 profiling::scope!("QuerySet::drop");
2707 api_log!("QuerySet::drop {:?}", self as *const _);
2708 resource_log!("Destroy raw {}", self.error_ident());
2709 #[cfg(feature = "trace")]
2710 if let Some(trace) = self.device.trace.lock().as_mut() {
2711 use crate::device::trace::to_trace;
2712
2713 trace.add(trace::Action::DropQuerySet(unsafe { to_trace(self) }));
2714 }
2715 let ResourceState::Valid(state) = &mut self.state else {
2716 return;
2717 };
2718 if let Some(raw) = state.raw.take() {
2719 unsafe {
2721 self.device.raw().destroy_query_set(raw);
2722 }
2723 }
2724 }
2725}
2726
2727crate::impl_resource_type!(QuerySet);
2728crate::impl_labeled!(QuerySet);
2729crate::impl_parent_device!(QuerySet);
2730crate::impl_storage_item!(QuerySet);
2731crate::impl_trackable!(QuerySet);
2732
2733#[derive(Debug)]
2735pub struct DestroyedQuerySet {
2736 raw: ManuallyDrop<Box<dyn hal::DynQuerySet>>,
2737 device: Arc<Device>,
2738 label: String,
2739}
2740
2741impl DestroyedQuerySet {
2742 pub fn label(&self) -> &dyn fmt::Debug {
2743 &self.label
2744 }
2745}
2746
2747impl Drop for DestroyedQuerySet {
2748 fn drop(&mut self) {
2749 resource_log!("Destroy raw QuerySet (destroyed) {:?}", self.label());
2750 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
2752 unsafe {
2753 hal::DynDevice::destroy_query_set(self.device.raw(), raw);
2754 }
2755 }
2756}
2757
2758pub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;
2759pub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;
2760
2761pub type BlasPrepareCompactResult = Result<(), BlasPrepareCompactError>;
2762
2763#[cfg(send_sync)]
2764pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + Send + 'static>;
2765#[cfg(not(send_sync))]
2766pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + 'static>;
2767
2768pub(crate) struct BlasPendingCompact {
2769 pub(crate) op: Option<BlasCompactCallback>,
2770 pub(crate) _parent_blas: Arc<Blas>,
2772}
2773
2774impl fmt::Debug for BlasPendingCompact {
2775 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2776 f.debug_struct("BlasPendingCompact")
2777 .field("op", &())
2778 .field("_parent_blas", &self._parent_blas)
2779 .finish()
2780 }
2781}
2782
2783#[derive(Debug)]
2784pub(crate) enum BlasCompactState {
2785 Compacted,
2787 Waiting(BlasPendingCompact),
2789 Ready { size: wgt::BufferAddress },
2791 Idle,
2793}
2794
2795#[cfg(send_sync)]
2796unsafe impl Send for BlasCompactState {}
2797#[cfg(send_sync)]
2798unsafe impl Sync for BlasCompactState {}
2799
2800#[derive(Debug)]
2801pub(crate) struct BlasState {
2802 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
2803}
2804
2805#[derive(Debug)]
2806pub struct Blas {
2807 pub(crate) state: ResourceState<BlasState>,
2808 pub(crate) device: Arc<Device>,
2809 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
2810 pub(crate) sizes: wgt::BlasGeometrySizeDescriptors,
2811 pub(crate) flags: wgt::AccelerationStructureFlags,
2812 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
2813 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
2814 pub(crate) handle: u64,
2815 pub(crate) label: String,
2817 pub(crate) tracking_data: TrackingData,
2818 pub(crate) compaction_buffer: Option<ManuallyDrop<Box<dyn hal::DynBuffer>>>,
2819 pub(crate) compacted_state: Mutex<BlasCompactState>,
2820}
2821
2822impl Drop for Blas {
2823 #[allow(trivial_casts)]
2824 fn drop(&mut self) {
2825 profiling::scope!("Blas::drop");
2826 api_log!("Blas::drop {:?}", self as *const _);
2827 #[cfg(feature = "trace")]
2828 if let Some(t) = self.device.trace.lock().as_mut() {
2829 use crate::device::trace::{to_trace, Action};
2830 t.add(Action::DropBlas(unsafe { to_trace(self) }));
2831 }
2832 resource_log!("Destroy raw {}", self.error_ident());
2833 if let ResourceState::Valid(state) = &mut self.state {
2835 if let Some(raw) = state.raw.take() {
2836 unsafe {
2837 self.device.raw().destroy_acceleration_structure(raw);
2838 }
2839 }
2840 }
2841 if let Some(mut raw) = self.compaction_buffer.take() {
2842 unsafe {
2843 self.device
2844 .raw()
2845 .destroy_buffer(ManuallyDrop::take(&mut raw))
2846 }
2847 }
2848 }
2849}
2850
2851impl RawResourceAccess for Blas {
2852 type DynResource = dyn hal::DynAccelerationStructure;
2853
2854 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2855 self.state().ok()?.raw.get(guard).map(|it| it.as_ref())
2856 }
2857}
2858
2859impl Blas {
2860 pub(crate) fn state(&self) -> Result<&BlasState, InvalidResourceError> {
2861 match &self.state {
2862 ResourceState::Valid(state) => Ok(state),
2863 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2864 }
2865 }
2866
2867 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
2868 self.state().map(|_| ())
2869 }
2870
2871 pub(crate) fn invalid(device: Arc<Device>, desc: &BlasDescriptor) -> Arc<Self> {
2872 Arc::new(Blas {
2873 state: ResourceState::Invalid,
2874 size_info: hal::AccelerationStructureBuildSizes {
2875 acceleration_structure_size: 0,
2876 update_scratch_size: 0,
2877 build_scratch_size: 0,
2878 },
2879 sizes: wgt::BlasGeometrySizeDescriptors::Triangles {
2880 descriptors: Vec::new(),
2881 },
2882 flags: desc.flags,
2883 update_mode: desc.update_mode,
2884 built_index: RwLock::new(rank::BLAS_BUILT_INDEX, None),
2885 handle: 0,
2886 label: desc.label.to_string(),
2887 tracking_data: TrackingData::new(device.tracker_indices.blas_s.clone()),
2888 device,
2889 compaction_buffer: None,
2890 compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Idle),
2891 })
2892 }
2893
2894 pub fn handle(&self) -> Option<u64> {
2895 Some(self.handle)
2896 }
2897
2898 pub fn ready_for_compaction(self: &Arc<Self>) -> Result<bool, InvalidResourceError> {
2899 profiling::scope!("Blas::prepare_compact_async");
2900 api_log!("Blas::prepare_compact_async {:?}", Arc::as_ptr(self));
2901
2902 self.check_is_valid()?;
2903 let state = self.compacted_state.lock();
2904 Ok(matches!(*state, BlasCompactState::Ready { .. }))
2905 }
2906
2907 pub fn prepare_compact_async(
2908 self: &Arc<Self>,
2909 callback: Option<BlasCompactCallback>,
2910 ) -> Result<SubmissionIndex, BlasPrepareCompactError> {
2911 profiling::scope!("Blas::prepare_compact_async");
2912 api_log!("Blas::prepare_compact_async {:?}", Arc::as_ptr(self));
2913
2914 let compact_result = self.prepare_compact_async_inner(callback);
2915
2916 match compact_result {
2917 Ok(submission_index) => Ok(submission_index),
2918 Err((mut callback, err)) => {
2919 if let Some(callback) = callback.take() {
2920 callback(Err(err.clone()));
2921 }
2922 Err(err)
2923 }
2924 }
2925 }
2926
2927 fn prepare_compact_async_inner(
2928 self: &Arc<Self>,
2929 op: Option<BlasCompactCallback>,
2930 ) -> Result<SubmissionIndex, (Option<BlasCompactCallback>, BlasPrepareCompactError)> {
2931 let device = &self.device;
2932 if let Err(e) = device.check_is_valid() {
2933 return Err((op, e.into()));
2934 }
2935
2936 if let Err(e) = self.check_is_valid() {
2937 return Err((op, e.into()));
2938 }
2939
2940 if self.built_index.read().is_none() {
2941 return Err((op, BlasPrepareCompactError::NotBuilt));
2942 }
2943
2944 if !self
2945 .flags
2946 .contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION)
2947 {
2948 return Err((op, BlasPrepareCompactError::CompactionUnsupported));
2949 }
2950
2951 let mut state = self.compacted_state.lock();
2952 *state = match *state {
2953 BlasCompactState::Compacted => {
2954 return Err((op, BlasPrepareCompactError::DoubleCompaction))
2955 }
2956 BlasCompactState::Waiting(_) => {
2957 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2958 }
2959 BlasCompactState::Ready { .. } => {
2960 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
2961 }
2962 BlasCompactState::Idle => BlasCompactState::Waiting(BlasPendingCompact {
2963 op,
2964 _parent_blas: self.clone(),
2965 }),
2966 };
2967
2968 let submit_index = if let Some(queue) = device.get_queue() {
2969 queue.lock_life().prepare_compact(self).unwrap_or(0) } else {
2971 let (mut callback, status) = self.read_back_compact_size().unwrap();
2973 if let Some(callback) = callback.take() {
2974 callback(status);
2975 }
2976 0
2977 };
2978
2979 Ok(submit_index)
2980 }
2981
2982 #[must_use]
2984 pub(crate) fn read_back_compact_size(&self) -> Option<BlasCompactReadyPendingClosure> {
2985 let mut state = self.compacted_state.lock();
2986 let pending_compact = match mem::replace(&mut *state, BlasCompactState::Idle) {
2987 BlasCompactState::Waiting(pending_mapping) => pending_mapping,
2988 BlasCompactState::Idle => return None,
2990 BlasCompactState::Ready { .. } => {
2991 unreachable!("This should be validated out by `prepare_for_compaction`")
2992 }
2993 _ => panic!("No pending mapping."),
2994 };
2995 let status = {
2996 let compaction_buffer = self.compaction_buffer.as_ref().unwrap().as_ref();
2997 unsafe {
2998 let map_res = self.device.raw().map_buffer(
2999 compaction_buffer,
3000 0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress,
3001 );
3002 match map_res {
3003 Ok(mapping) => {
3004 if !mapping.is_coherent {
3005 #[expect(clippy::single_range_in_vec_init, reason = "intentional")]
3006 self.device.raw().invalidate_mapped_ranges(
3007 compaction_buffer,
3008 &[0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress],
3009 );
3010 }
3011 let size = core::ptr::read_unaligned(
3012 mapping.ptr.as_ptr().cast::<wgt::BufferAddress>(),
3013 );
3014 self.device.raw().unmap_buffer(compaction_buffer);
3015 if self.size_info.acceleration_structure_size != 0 {
3016 debug_assert_ne!(size, 0);
3017 }
3018 *state = BlasCompactState::Ready { size };
3019 Ok(())
3020 }
3021 Err(err) => Err(BlasPrepareCompactError::from(DeviceError::from_hal(err))),
3022 }
3023 }
3024 };
3025 Some((pending_compact.op, status))
3026 }
3027}
3028
3029crate::impl_resource_type!(Blas);
3030crate::impl_labeled!(Blas);
3031crate::impl_parent_device!(Blas);
3032crate::impl_storage_item!(Blas);
3033crate::impl_trackable!(Blas);
3034
3035#[derive(Debug)]
3036pub(crate) struct TlasState {
3037 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
3038 pub(crate) instance_buffer: Box<dyn hal::DynBuffer>,
3039}
3040
3041#[derive(Debug)]
3042pub struct Tlas {
3043 pub(crate) state: ResourceState<TlasState>,
3044 pub(crate) device: Arc<Device>,
3045 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
3046 pub(crate) max_instance_count: u32,
3047 pub(crate) flags: wgt::AccelerationStructureFlags,
3048 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
3049 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
3050 pub(crate) dependencies: RwLock<Vec<Arc<Blas>>>,
3051 pub(crate) label: String,
3053 pub(crate) tracking_data: TrackingData,
3054}
3055
3056impl Drop for Tlas {
3057 #[allow(trivial_casts)]
3058 fn drop(&mut self) {
3059 profiling::scope!("Tlas::drop");
3060 api_log!("Tlas::drop {:?}", self as *const _);
3061
3062 #[cfg(feature = "trace")]
3063 if let Some(t) = self.device.trace.lock().as_mut() {
3064 use crate::device::trace::{to_trace, Action};
3065 t.add(Action::DropTlas(unsafe { to_trace(self) }));
3066 }
3067
3068 resource_log!("Destroy raw {}", self.error_ident());
3069 let ResourceState::Valid(mut state) = mem::replace(&mut self.state, ResourceState::Invalid)
3070 else {
3071 return;
3072 };
3073 if let Some(structure) = state.raw.take() {
3074 unsafe { self.device.raw().destroy_acceleration_structure(structure) };
3075 }
3076 unsafe { self.device.raw().destroy_buffer(state.instance_buffer) };
3077 }
3078}
3079
3080impl Tlas {
3081 pub(crate) fn state(&self) -> Result<&TlasState, InvalidResourceError> {
3082 match &self.state {
3083 ResourceState::Valid(state) => Ok(state),
3084 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3085 }
3086 }
3087
3088 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
3089 self.state().map(|_| ())
3090 }
3091
3092 pub(crate) fn invalid(device: Arc<Device>, desc: &TlasDescriptor) -> Arc<Self> {
3093 Arc::new(Self {
3094 state: ResourceState::Invalid,
3095 label: desc.label.to_string(),
3096 tracking_data: TrackingData::new(device.tracker_indices.tlas_s.clone()),
3097 size_info: hal::AccelerationStructureBuildSizes {
3098 acceleration_structure_size: 0,
3099 update_scratch_size: 0,
3100 build_scratch_size: 0,
3101 },
3102 max_instance_count: desc.max_instances,
3103 flags: desc.flags,
3104 update_mode: desc.update_mode,
3105 built_index: RwLock::new(rank::TLAS_BUILT_INDEX, None),
3106 dependencies: RwLock::new(rank::TLAS_DEPENDENCIES, Vec::new()),
3107 device,
3108 })
3109 }
3110}
3111
3112impl RawResourceAccess for Tlas {
3113 type DynResource = dyn hal::DynAccelerationStructure;
3114
3115 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
3116 self.state().ok()?.raw.get(guard).map(|raw| raw.as_ref())
3117 }
3118}
3119
3120crate::impl_resource_type!(Tlas);
3121crate::impl_labeled!(Tlas);
3122crate::impl_parent_device!(Tlas);
3123crate::impl_storage_item!(Tlas);
3124crate::impl_trackable!(Tlas);