1use alloc::{
2 borrow::{Cow, ToOwned as _},
3 boxed::Box,
4 string::String,
5 sync::Arc,
6 vec::Vec,
7};
8use core::{
9 borrow::Borrow,
10 fmt,
11 mem::{self, size_of, ManuallyDrop},
12 num::NonZeroU64,
13 ops::Range,
14 ptr::NonNull,
15};
16use smallvec::SmallVec;
17use thiserror::Error;
18use wgt::{
19 error::{ErrorType, WebGpuError},
20 math::align_to,
21 MapMode, TextureSelector, WriteOnly,
22};
23
24#[cfg(feature = "trace")]
25use crate::device::trace;
26use crate::{
27 api_log,
28 binding_model::{BindGroup, BindingError},
29 device::{
30 queue, resource::DeferredDestroy, BufferMapPendingClosure, Device, DeviceError,
31 DeviceMismatch, MissingDownlevelFlags, MissingFeatures,
32 },
33 hal_label,
34 init_tracker::{BufferInitTracker, TextureInitTracker},
35 lock::{rank, Mutex, MutexGuard, RwLock},
36 ray_tracing::{BlasCompactReadyPendingClosure, BlasPrepareCompactError},
37 resource_log,
38 snatch::{SnatchGuard, Snatchable},
39 timestamp_normalization::TimestampNormalizationBindGroup,
40 track::{SharedTrackerIndexAllocator, TrackerIndex},
41 weak_vec::WeakVec,
42 Label, LabelHelpers, SubmissionIndex,
43};
44
45#[derive(Debug)]
65pub(crate) struct TrackingData {
66 tracker_index: TrackerIndex,
67 tracker_indices: Arc<SharedTrackerIndexAllocator>,
68}
69
70impl Drop for TrackingData {
71 fn drop(&mut self) {
72 self.tracker_indices.free(self.tracker_index);
73 }
74}
75
76impl TrackingData {
77 pub(crate) fn new(tracker_indices: Arc<SharedTrackerIndexAllocator>) -> Self {
78 Self {
79 tracker_index: tracker_indices.alloc(),
80 tracker_indices,
81 }
82 }
83
84 pub(crate) fn tracker_index(&self) -> TrackerIndex {
85 self.tracker_index
86 }
87}
88
89#[derive(Clone, Debug)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91pub struct ResourceErrorIdent {
92 r#type: Cow<'static, str>,
93 label: String,
94}
95
96impl fmt::Display for ResourceErrorIdent {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
98 write!(f, "{} with '{}' label", self.r#type, self.label)
99 }
100}
101
102#[derive(Debug)]
103pub enum ResourceState<T> {
104 Valid(T),
105 Invalid,
106}
107
108impl<T> ResourceState<T> {
109 pub fn as_ref(&self) -> ResourceState<&T> {
110 match self {
111 ResourceState::Valid(v) => ResourceState::Valid(v),
112 ResourceState::Invalid => ResourceState::Invalid,
113 }
114 }
115
116 pub fn valid(self) -> Option<T> {
117 match self {
118 ResourceState::Valid(v) => Some(v),
119 ResourceState::Invalid => None,
120 }
121 }
122}
123
124#[derive(thiserror::Error, Debug)]
125pub enum InvalidOrDestroyedResourceError {
126 #[error(transparent)]
127 InvalidResource(#[from] InvalidResourceError),
128 #[error(transparent)]
129 DestroyedResource(#[from] DestroyedResourceError),
130}
131
132pub trait ParentDevice: Labeled {
133 fn device(&self) -> &Arc<Device>;
134
135 fn is_equal(self: &Arc<Self>, other: &Arc<Self>) -> bool {
136 Arc::ptr_eq(self, other)
137 }
138
139 fn same_device_as<O: ParentDevice>(&self, other: &O) -> Result<(), DeviceError> {
140 if Arc::ptr_eq(self.device(), other.device()) {
141 Ok(())
142 } else {
143 Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
144 res: self.error_ident(),
145 res_device: self.device().error_ident(),
146 target: Some(other.error_ident()),
147 target_device: other.device().error_ident(),
148 })))
149 }
150 }
151
152 fn same_device(&self, device: &Device) -> Result<(), DeviceError> {
153 if core::ptr::eq(&**self.device(), device) {
154 Ok(())
155 } else {
156 Err(DeviceError::DeviceMismatch(Box::new(DeviceMismatch {
157 res: self.error_ident(),
158 res_device: self.device().error_ident(),
159 target: None,
160 target_device: device.error_ident(),
161 })))
162 }
163 }
164}
165
166#[macro_export]
167macro_rules! impl_parent_device {
168 ($ty:ident) => {
169 impl $crate::resource::ParentDevice for $ty {
170 fn device(&self) -> &Arc<Device> {
171 &self.device
172 }
173 }
174 };
175}
176
177pub trait RawResourceAccess: ParentDevice {
179 type DynResource: hal::DynResource + ?Sized;
180
181 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource>;
186
187 fn try_raw<'a>(
192 &'a self,
193 guard: &'a SnatchGuard,
194 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
195 self.raw(guard)
196 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
197 }
198}
199
200pub trait ResourceType {
201 const TYPE: &'static str;
202}
203
204#[macro_export]
205macro_rules! impl_resource_type {
206 ($ty:ident) => {
207 impl $crate::resource::ResourceType for $ty {
208 const TYPE: &'static str = stringify!($ty);
209 }
210 };
211}
212
213pub trait Labeled: ResourceType {
214 fn label(&self) -> &str;
220
221 fn error_ident(&self) -> ResourceErrorIdent {
222 ResourceErrorIdent {
223 r#type: Cow::Borrowed(Self::TYPE),
224 label: self.label().to_owned(),
225 }
226 }
227}
228
229#[macro_export]
230macro_rules! impl_labeled {
231 ($ty:ident) => {
232 impl $crate::resource::Labeled for $ty {
233 fn label(&self) -> &str {
234 &self.label
235 }
236 }
237 };
238}
239
240pub(crate) trait Trackable {
241 fn tracker_index(&self) -> TrackerIndex;
242}
243
244#[macro_export]
245macro_rules! impl_trackable {
246 ($ty:ident) => {
247 impl $crate::resource::Trackable for $ty {
248 fn tracker_index(&self) -> $crate::track::TrackerIndex {
249 self.tracking_data.tracker_index()
250 }
251 }
252 };
253}
254
255#[derive(Debug)]
256pub(crate) enum BufferMapState {
257 Init { staging_buffer: StagingBuffer },
259 Waiting(BufferPendingMapping),
261 Active {
263 mapping: hal::BufferMapping,
264 range: hal::MemoryRange,
265 host: MapMode,
266 },
267 Idle,
269}
270
271#[cfg(send_sync)]
272unsafe impl Send for BufferMapState {}
273#[cfg(send_sync)]
274unsafe impl Sync for BufferMapState {}
275
276#[derive(Debug)]
280pub struct BufferMapping {
281 ptr: NonNull<u8>,
282 len: u64,
283 mode: MapMode,
284 _guard: wgpu_sync::RwLockReadGuard<'static, BufferMapState>,
286 _lock: Arc<RwLock<BufferMapState>>,
287}
288
289impl BufferMapping {
290 fn new(ptr: NonNull<u8>, len: u64, mode: MapMode, lock: Arc<RwLock<BufferMapState>>) -> Self {
291 let guard = unsafe { lock.underlying() }.read();
294 Self {
295 ptr,
296 len,
297 mode,
298 _guard: unsafe {
301 mem::transmute::<
302 wgpu_sync::RwLockReadGuard<'_, BufferMapState>,
303 wgpu_sync::RwLockReadGuard<'static, BufferMapState>,
304 >(guard)
305 },
306 _lock: lock,
307 }
308 }
309
310 pub fn ptr(&self) -> NonNull<u8> {
319 self.ptr
320 }
321
322 pub fn mode(&self) -> MapMode {
323 self.mode
324 }
325
326 #[expect(clippy::len_without_is_empty)]
327 pub fn len(&self) -> u64 {
328 self.len
329 }
330
331 pub fn read_slice(&self) -> &[u8] {
334 assert!(self.mode == MapMode::Read);
335 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len as usize) }
336 }
337
338 pub fn write_slice(&'_ self) -> WriteOnly<'_, [u8]> {
341 assert!(self.mode == MapMode::Write);
342 unsafe { WriteOnly::new(NonNull::slice_from_raw_parts(self.ptr, self.len as usize)) }
343 }
344}
345
346#[cfg(send_sync)]
347pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + Send + 'static>;
348#[cfg(not(send_sync))]
349pub type BufferMapCallback = Box<dyn FnOnce(BufferAccessResult) + 'static>;
350
351#[derive(Clone, Debug, Error)]
352#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
353#[non_exhaustive]
354pub enum BufferAccessError {
355 #[error(transparent)]
356 Device(#[from] DeviceError),
357 #[error("Buffer map failed")]
358 Failed,
359 #[error(transparent)]
360 DestroyedResource(#[from] DestroyedResourceError),
361 #[error("Buffer is already mapped")]
362 AlreadyMapped,
363 #[error("Buffer map is pending")]
364 MapAlreadyPending,
365 #[error(transparent)]
366 MissingBufferUsage(#[from] MissingBufferUsageError),
367 #[error("Buffer is not mapped")]
368 NotMapped,
369 #[error(
370 "Buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`"
371 )]
372 UnalignedRange,
373 #[error("Buffer offset invalid: offset {offset} must be multiple of 8")]
374 UnalignedOffset { offset: wgt::BufferAddress },
375 #[error("Buffer range size invalid: range_size {range_size} must be multiple of 4")]
376 UnalignedRangeSize { range_size: wgt::BufferAddress },
377 #[error("Buffer access out of bounds: index {index} would underrun the buffer (limit: {min})")]
378 OutOfBoundsStartOffsetUnderrun {
379 index: wgt::BufferAddress,
380 min: wgt::BufferAddress,
381 },
382 #[error(
383 "Buffer access out of bounds: start offset {index} would overrun the buffer (limit: {max})"
384 )]
385 OutOfBoundsStartOffsetOverrun {
386 index: wgt::BufferAddress,
387 max: wgt::BufferAddress,
388 },
389 #[error(
390 "Buffer access out of bounds: start offset {index} + size {size} would overrun the buffer (limit: {max})"
391 )]
392 OutOfBoundsEndOffsetOverrun {
393 index: wgt::BufferAddress,
394 size: wgt::BufferAddress,
395 max: wgt::BufferAddress,
396 },
397 #[error("Buffer map aborted")]
398 MapAborted,
399 #[error(transparent)]
400 InvalidResource(#[from] InvalidResourceError),
401 #[error("Map start offset ({offset}) is out-of-bounds for buffer of size {buffer_size}")]
402 MapStartOffsetOverrun {
403 offset: wgt::BufferAddress,
404 buffer_size: wgt::BufferAddress,
405 },
406 #[error(
407 "Map end offset (start at {} + size of {}) is out-of-bounds for buffer of size {}",
408 offset,
409 size,
410 buffer_size
411 )]
412 MapEndOffsetOverrun {
413 offset: wgt::BufferAddress,
414 size: wgt::BufferAddress,
415 buffer_size: wgt::BufferAddress,
416 },
417 #[error(transparent)]
418 InvalidMapMode(#[from] BadMapMode),
419}
420
421#[derive(Clone, Debug, Error)]
422#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
423#[error("Invalid map mode: {0:?}, exactly one of READ or WRITE map mode must be set")]
424pub struct BadMapMode(MapModeFlags);
425
426bitflags::bitflags! {
427 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
429 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
430 pub struct MapModeFlags: u32 {
431 const READ = 0x0001;
432 const WRITE = 0x0002;
433 }
434}
435
436impl From<core::convert::Infallible> for BadMapMode {
437 fn from(_: core::convert::Infallible) -> Self {
438 unreachable!()
439 }
440}
441
442impl TryFrom<MapModeFlags> for MapMode {
443 type Error = BadMapMode;
444
445 fn try_from(mode: MapModeFlags) -> Result<Self, Self::Error> {
446 match mode {
447 m if m == MapModeFlags::READ => Ok(MapMode::Read),
448 m if m == MapModeFlags::WRITE => Ok(MapMode::Write),
449 m => Err(BadMapMode(m)),
450 }
451 }
452}
453
454impl WebGpuError for BufferAccessError {
455 fn webgpu_error_type(&self) -> ErrorType {
456 match self {
457 Self::Device(e) => e.webgpu_error_type(),
458 Self::InvalidResource(e) => e.webgpu_error_type(),
459 Self::DestroyedResource(e) => e.webgpu_error_type(),
460
461 Self::Failed
462 | Self::AlreadyMapped
463 | Self::MapAlreadyPending
464 | Self::MissingBufferUsage(_)
465 | Self::NotMapped
466 | Self::UnalignedRange
467 | Self::UnalignedOffset { .. }
468 | Self::UnalignedRangeSize { .. }
469 | Self::OutOfBoundsStartOffsetUnderrun { .. }
470 | Self::OutOfBoundsStartOffsetOverrun { .. }
471 | Self::OutOfBoundsEndOffsetOverrun { .. }
472 | Self::MapAborted
473 | Self::MapStartOffsetOverrun { .. }
474 | Self::MapEndOffsetOverrun { .. }
475 | Self::InvalidMapMode(_) => ErrorType::Validation,
476 }
477 }
478}
479
480#[derive(Clone, Debug, Error)]
481#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
482#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
483pub struct MissingBufferUsageError {
484 pub(crate) res: ResourceErrorIdent,
485 pub(crate) actual: wgt::BufferUsages,
486 pub(crate) expected: wgt::BufferUsages,
487}
488
489impl WebGpuError for MissingBufferUsageError {
490 fn webgpu_error_type(&self) -> ErrorType {
491 ErrorType::Validation
492 }
493}
494
495#[derive(Clone, Debug, Error)]
496#[error("Usage flags {actual:?} of {res} do not contain required usage flags {expected:?}")]
497pub struct MissingTextureUsageError {
498 pub(crate) res: ResourceErrorIdent,
499 pub(crate) actual: wgt::TextureUsages,
500 pub(crate) expected: wgt::TextureUsages,
501}
502
503impl WebGpuError for MissingTextureUsageError {
504 fn webgpu_error_type(&self) -> ErrorType {
505 ErrorType::Validation
506 }
507}
508
509#[derive(Clone, Debug, Error)]
510#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
511#[error("{0} has been destroyed")]
512pub struct DestroyedResourceError(pub ResourceErrorIdent);
513
514impl WebGpuError for DestroyedResourceError {
515 fn webgpu_error_type(&self) -> ErrorType {
516 ErrorType::Validation
517 }
518}
519
520#[derive(Clone, Debug, Error)]
521#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
522#[error("{0} is invalid")]
523pub struct InvalidResourceError(pub ResourceErrorIdent);
524
525impl WebGpuError for InvalidResourceError {
526 fn webgpu_error_type(&self) -> ErrorType {
527 ErrorType::Validation
528 }
529}
530
531pub type BufferAccessResult = Result<MapMode, BufferAccessError>;
532
533pub(crate) struct BufferPendingMapping {
534 pub(crate) range: Range<wgt::BufferAddress>,
535 pub(crate) callback: Option<BufferMapCallback>,
536 pub(crate) mode: MapMode,
537 pub(crate) _parent_buffer: Arc<Buffer>,
539}
540
541impl fmt::Debug for BufferPendingMapping {
542 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543 f.debug_struct("BufferPendingMapping")
544 .field("range", &self.range)
545 .field("callback", &self.callback.as_ref().map(|_| "?"))
546 .field("mode", &self.mode)
547 .field("_parent_buffer", &self._parent_buffer)
548 .finish()
549 }
550}
551
552pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
553
554#[derive(Debug)]
555pub(crate) struct BufferState {
556 pub(crate) raw: Snatchable<Box<dyn hal::DynBuffer>>,
557}
558
559#[derive(Debug)]
560pub struct Buffer {
561 pub(crate) state: ResourceState<BufferState>,
562 pub(crate) device: Arc<Device>,
563 pub(crate) usage: wgt::BufferUsages,
564 pub(crate) size: wgt::BufferAddress,
565 pub(crate) initialization_status: RwLock<BufferInitTracker>,
566 pub(crate) label: String,
568 pub(crate) tracking_data: TrackingData,
569 pub(crate) map_state: Arc<RwLock<BufferMapState>>,
570 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
572 pub(crate) timestamp_normalization_bind_group: Snatchable<TimestampNormalizationBindGroup>,
573 pub(crate) indirect_validation_bind_groups: Snatchable<crate::indirect_validation::BindGroups>,
574}
575
576impl Drop for Buffer {
577 #[allow(trivial_casts)]
578 fn drop(&mut self) {
579 profiling::scope!("Buffer::drop");
580 api_log!("Buffer::drop {:?}", self as *const _);
581 #[cfg(feature = "trace")]
582 if let Some(t) = self.device.trace.lock().as_mut() {
583 t.add(trace::Action::DropBuffer(unsafe { trace::to_trace(self) }));
584 }
585
586 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
587 raw.dispose(self.device.raw());
588 }
589
590 if let Some(raw) = self.indirect_validation_bind_groups.take() {
591 raw.dispose(self.device.raw());
592 }
593
594 let map_state = mem::replace(&mut *self.map_state.write(), BufferMapState::Idle);
595 let active_map = match map_state {
596 BufferMapState::Init { staging_buffer } => {
597 staging_buffer.dispose();
598 false
599 }
600 BufferMapState::Waiting(buffer_pending_mapping) => {
601 if buffer_pending_mapping.callback.is_some() {
602 let result = Err(BufferAccessError::DestroyedResource(
603 DestroyedResourceError(self.error_ident()),
604 ));
605 self.device
606 .deferred_buffer_map_pending_closures
607 .push((buffer_pending_mapping.callback, result));
608 }
609 false
610 }
611 BufferMapState::Active { .. } => true,
612 BufferMapState::Idle => false,
613 };
614
615 let ResourceState::Valid(state) = &mut self.state else {
616 return;
617 };
618
619 if let Some(raw) = state.raw.take() {
620 if active_map {
621 unsafe { self.device.raw().unmap_buffer(raw.as_ref()) }
622 }
623 resource_log!("Destroy raw {}", self.error_ident());
624 unsafe {
625 self.device.raw().destroy_buffer(raw);
626 }
627 }
628 }
629}
630
631impl RawResourceAccess for Buffer {
632 type DynResource = dyn hal::DynBuffer;
633
634 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
635 self.state()
636 .ok()
637 .and_then(|state| state.raw.get(guard).map(|b| b.as_ref()))
638 }
639}
640
641impl Buffer {
642 pub(crate) fn check_destroyed(
643 &self,
644 guard: &SnatchGuard,
645 ) -> Result<(), DestroyedResourceError> {
646 let ResourceState::Valid(state) = &self.state else {
647 return Ok(());
648 };
649 state
650 .raw
651 .get(guard)
652 .map(|_| ())
653 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
654 }
655
656 pub(crate) fn check_usage(
659 &self,
660 expected: wgt::BufferUsages,
661 ) -> Result<(), MissingBufferUsageError> {
662 if self.usage.contains(expected) {
663 Ok(())
664 } else {
665 Err(MissingBufferUsageError {
666 res: self.error_ident(),
667 actual: self.usage,
668 expected,
669 })
670 }
671 }
672
673 pub(crate) fn state(&self) -> Result<&BufferState, InvalidResourceError> {
674 match &self.state {
675 ResourceState::Valid(state) => Ok(state),
676 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
677 }
678 }
679
680 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
681 self.state().map(|_| ())
682 }
683
684 pub fn invalid(device: Arc<Device>, desc: &BufferDescriptor) -> Arc<Self> {
685 Arc::new(Buffer {
686 state: ResourceState::Invalid,
687 usage: desc.usage,
688 size: desc.size,
689 initialization_status: RwLock::new(
690 rank::BUFFER_INITIALIZATION_STATUS,
691 BufferInitTracker::new(0),
692 ),
693 map_state: Arc::new(RwLock::new(rank::BUFFER_MAP_STATE, BufferMapState::Idle)),
694 label: desc.label.to_string(),
695 tracking_data: TrackingData::new(device.tracker_indices.buffers.clone()),
696 bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()),
697 timestamp_normalization_bind_group: Snatchable::empty(),
698 indirect_validation_bind_groups: Snatchable::empty(),
699 device,
700 })
701 }
702
703 fn resolve_binding_size<S: Copy + Into<wgt::BufferAddress> + TryFrom<wgt::BufferAddress>>(
715 &self,
716 offset: wgt::BufferAddress,
717 binding_size: Option<S>,
718 ) -> Result<S, BindingError> {
719 let buffer_size = self.size;
720
721 match binding_size {
722 Some(binding_size) => match offset.checked_add(binding_size.into()) {
723 Some(end) if end <= buffer_size => Ok(binding_size),
724 _ => Err(BindingError::BindingRangeTooLarge {
725 buffer: self.error_ident(),
726 offset,
727 binding_size: binding_size.into(),
728 buffer_size,
729 }),
730 },
731 None => buffer_size
732 .checked_sub(offset)
733 .and_then(|remaining| S::try_from(remaining).ok())
734 .ok_or_else(|| {
735 if offset <= buffer_size {
736 debug_assert_eq!(offset, buffer_size);
737 BindingError::BindingOffsetEqualsSize {
738 buffer: self.error_ident(),
739 offset,
740 buffer_size,
741 }
742 } else {
743 BindingError::BindingOffsetTooLarge {
744 buffer: self.error_ident(),
745 offset,
746 buffer_size,
747 }
748 }
749 }),
750 }
751 }
752
753 pub fn resolve_vertex_or_index_binding_range(
761 &self,
762 offset: wgt::BufferAddress,
763 size: Option<wgt::BufferAddress>,
764 ) -> Result<Range<wgt::BufferAddress>, BindingError> {
765 let resolved_size = self.resolve_binding_size(offset, size)?;
766 if resolved_size != 0 {
767 Ok(offset..offset + resolved_size)
768 } else {
769 const _: () = {
773 assert!(wgt::VERTEX_ALIGNMENT == wgt::COPY_BUFFER_ALIGNMENT);
774 };
775 let target = align_to(self.size, wgt::VERTEX_ALIGNMENT);
776 Ok(target..target)
777 }
778 }
779
780 pub fn binding<'a>(
792 &'a self,
793 offset: wgt::BufferAddress,
794 binding_size: Option<wgt::BufferSize>,
795 snatch_guard: &'a SnatchGuard,
796 ) -> Result<hal::BufferBinding<'a, dyn hal::DynBuffer, wgt::BufferSize>, BindingError> {
797 let buf_raw = self.try_raw(snatch_guard)?;
798 let resolved_size = self.resolve_binding_size(offset, binding_size)?;
799 Ok(hal::BufferBinding::new_unchecked(
802 buf_raw,
803 offset,
804 resolved_size,
805 ))
806 }
807
808 pub fn map_async<M>(
812 self: &Arc<Self>,
813 mode: M,
814 offset: wgt::BufferAddress,
815 size: Option<wgt::BufferAddress>,
816 callback: Option<BufferMapCallback>,
817 ) -> Option<SubmissionIndex>
818 where
819 M: fmt::Debug + TryInto<MapMode> + Copy,
820 M::Error: Into<BadMapMode>,
821 {
822 profiling::scope!("Buffer::map_async");
823 api_log!(
824 "Buffer::map_async {:?} mode {mode:?} offset {offset:?} size {size:?}",
825 Arc::as_ptr(self)
826 );
827
828 self.try_map_async(mode, offset, size, callback)
829 .map_err(|(mut callback, err)| {
830 self.device
831 .handle_error(err.clone(), Some(&self.label), "Buffer::map_async");
832 if let Some(callback) = callback.take() {
833 callback(Err(err));
834 }
835 })
836 .ok()
837 }
838
839 fn try_map_async<M>(
862 self: &Arc<Self>,
863 mode: M,
864 offset: wgt::BufferAddress,
865 size: Option<wgt::BufferAddress>,
866 callback: Option<BufferMapCallback>,
867 ) -> Result<SubmissionIndex, (Option<BufferMapCallback>, BufferAccessError)>
868 where
869 M: fmt::Debug + TryInto<MapMode> + Copy,
870 M::Error: Into<BadMapMode>,
871 {
872 let range_size = if let Some(size) = size {
873 size
874 } else {
875 self.size.saturating_sub(offset)
876 };
877
878 let device = &self.device;
879 if let Err(e) = device.check_is_valid() {
880 return Err((callback, e.into()));
881 }
882
883 if let Err(e) = self.check_is_valid() {
884 return Err((callback, e.into()));
885 }
886
887 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
888 return Err((callback, BufferAccessError::UnalignedOffset { offset }));
889 }
890 if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
891 return Err((
892 callback,
893 BufferAccessError::UnalignedRangeSize { range_size },
894 ));
895 }
896
897 if offset > self.size {
898 return Err((
899 callback,
900 BufferAccessError::MapStartOffsetOverrun {
901 offset,
902 buffer_size: self.size,
903 },
904 ));
905 }
906 if range_size > self.size - offset {
908 return Err((
909 callback,
910 BufferAccessError::MapEndOffsetOverrun {
911 offset,
912 size: range_size,
913 buffer_size: self.size,
914 },
915 ));
916 }
917 let end_offset = offset + range_size;
918
919 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT)
920 || !end_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT)
921 {
922 return Err((callback, BufferAccessError::UnalignedRange));
923 }
924
925 let mode = match mode.try_into() {
926 Ok(mode) => mode,
927 Err(e) => return Err((callback, e.into().into())),
928 };
929
930 let (pub_usage, internal_use) = match mode {
931 MapMode::Read => (wgt::BufferUsages::MAP_READ, wgt::BufferUses::MAP_READ),
932 MapMode::Write => (wgt::BufferUsages::MAP_WRITE, wgt::BufferUses::MAP_WRITE),
933 };
934
935 if let Err(e) = self.check_usage(pub_usage) {
936 return Err((callback, e.into()));
937 }
938
939 let submit_index = {
940 let snatch_guard = device.snatchable_lock.read();
941 if let Err(e) = self.check_destroyed(&snatch_guard) {
942 return Err((callback, e.into()));
943 }
944
945 {
946 let map_state = &mut *self.map_state.write();
947 *map_state = match *map_state {
948 BufferMapState::Init { .. } | BufferMapState::Active { .. } => {
949 return Err((callback, BufferAccessError::AlreadyMapped));
950 }
951 BufferMapState::Waiting(_) => {
952 return Err((callback, BufferAccessError::MapAlreadyPending));
953 }
954 BufferMapState::Idle => BufferMapState::Waiting(BufferPendingMapping {
955 range: offset..end_offset,
956 mode,
957 callback,
958 _parent_buffer: self.clone(),
959 }),
960 };
961 }
962
963 if let Some(queue) = device.get_queue().as_ref() {
964 match queue.flush_writes_for_buffer(self, snatch_guard) {
965 Err(err) => {
966 let state =
967 mem::replace(&mut *self.map_state.write(), BufferMapState::Idle);
968 let BufferMapState::Waiting(BufferPendingMapping { callback, .. }) = state
969 else {
970 unreachable!();
971 };
972 return Err((callback, err));
973 }
974 Ok(()) => {
975 Some(queue.lock_life().map(self).unwrap_or(0))
984 }
985 }
986 } else {
987 None
988 }
989 };
990
991 device
999 .trackers
1000 .lock()
1001 .buffers
1002 .set_single(self, internal_use);
1003
1004 if let Some(index) = submit_index {
1005 Ok(index)
1006 } else {
1007 let (mut callback, status) = self.map(&device.snatchable_lock.read()).unwrap();
1010 if let Some(callback) = callback.take() {
1011 callback(status);
1012 }
1013 Ok(0)
1014 }
1015 }
1016
1017 pub fn get_mapped_range(
1022 self: &Arc<Self>,
1023 offset: wgt::BufferAddress,
1024 size: Option<wgt::BufferAddress>,
1025 ) -> Result<BufferMapping, BufferAccessError> {
1026 profiling::scope!("Buffer::get_mapped_range");
1027 api_log!(
1028 "Buffer::get_mapped_range {:?} offset {offset:?} size {size:?}",
1029 Arc::as_ptr(self)
1030 );
1031
1032 self.check_is_valid()?;
1033 {
1034 let snatch_guard = self.device.snatchable_lock.read();
1035 self.check_destroyed(&snatch_guard)?;
1036 }
1037
1038 let range_size = if let Some(size) = size {
1039 size
1040 } else {
1041 self.size.saturating_sub(offset)
1042 };
1043
1044 if !offset.is_multiple_of(wgt::MAP_ALIGNMENT) {
1045 return Err(BufferAccessError::UnalignedOffset { offset });
1046 }
1047 if !range_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1048 return Err(BufferAccessError::UnalignedRangeSize { range_size });
1049 }
1050 let map_state = &*self.map_state.read();
1051 match *map_state {
1052 BufferMapState::Init { ref staging_buffer } => {
1053 if offset > self.size {
1054 return Err(BufferAccessError::MapStartOffsetOverrun {
1055 offset,
1056 buffer_size: self.size,
1057 });
1058 }
1059 if range_size > self.size - offset {
1061 return Err(BufferAccessError::MapEndOffsetOverrun {
1062 offset,
1063 size: range_size,
1064 buffer_size: self.size,
1065 });
1066 }
1067 let ptr = unsafe { staging_buffer.ptr() };
1068 let ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().offset(offset as isize)) };
1069 Ok(BufferMapping::new(
1070 ptr,
1071 range_size,
1072 MapMode::Write,
1073 self.map_state.clone(),
1074 ))
1075 }
1076 BufferMapState::Active {
1077 ref mapping,
1078 ref range,
1079 ref host,
1080 } => {
1081 if offset > range.end {
1082 return Err(BufferAccessError::OutOfBoundsStartOffsetOverrun {
1083 index: offset,
1084 max: range.end,
1085 });
1086 }
1087 if offset < range.start {
1088 return Err(BufferAccessError::OutOfBoundsStartOffsetUnderrun {
1089 index: offset,
1090 min: range.start,
1091 });
1092 }
1093 if range_size > range.end - offset {
1094 return Err(BufferAccessError::OutOfBoundsEndOffsetOverrun {
1095 index: offset,
1096 size: range_size,
1097 max: range.end,
1098 });
1099 }
1100 let relative_offset = (offset - range.start) as isize;
1103 unsafe {
1104 Ok(BufferMapping::new(
1105 NonNull::new_unchecked(mapping.ptr.as_ptr().offset(relative_offset)),
1106 range_size,
1107 *host,
1108 self.map_state.clone(),
1109 ))
1110 }
1111 }
1112 BufferMapState::Idle | BufferMapState::Waiting(_) => Err(BufferAccessError::NotMapped),
1113 }
1114 }
1115 #[must_use]
1118 pub(crate) fn map(&self, snatch_guard: &SnatchGuard) -> Option<BufferMapPendingClosure> {
1119 let mut map_state = self.map_state.write();
1128 let pending_mapping = match mem::replace(&mut *map_state, BufferMapState::Idle) {
1129 BufferMapState::Waiting(pending_mapping) => pending_mapping,
1130 BufferMapState::Idle => return None,
1132 mapping @ BufferMapState::Active { .. } => {
1135 *map_state = mapping;
1136 return None;
1137 }
1138 _ => panic!("No pending mapping."),
1139 };
1140 let status = if let Err(error) = self.device.check_is_valid() {
1141 Err(error.into())
1142 } else if pending_mapping.range.start != pending_mapping.range.end {
1143 let host = pending_mapping.mode;
1144 let size = pending_mapping.range.end - pending_mapping.range.start;
1145 match crate::device::map_buffer(
1146 self,
1147 pending_mapping.range.start,
1148 size,
1149 host,
1150 snatch_guard,
1151 ) {
1152 Ok(mapping) => {
1153 *map_state = BufferMapState::Active {
1154 mapping,
1155 range: pending_mapping.range.clone(),
1156 host,
1157 };
1158 Ok(host)
1159 }
1160 Err(e) => Err(e),
1161 }
1162 } else {
1163 *map_state = BufferMapState::Active {
1164 mapping: hal::BufferMapping {
1165 ptr: NonNull::dangling(),
1166 is_coherent: true,
1167 },
1168 range: pending_mapping.range,
1169 host: pending_mapping.mode,
1170 };
1171 Ok(pending_mapping.mode)
1172 };
1173 Some((pending_mapping.callback, status))
1174 }
1175
1176 pub fn unmap(self: &Arc<Self>) {
1178 profiling::scope!("unmap", "Buffer");
1179 api_log!("Buffer::unmap {:?}", Arc::as_ptr(self));
1180 if let Some((mut callback, status)) = self.unmap_inner() {
1181 if let Some(callback) = callback.take() {
1182 callback(status);
1183 }
1184 }
1185 }
1186
1187 fn unmap_inner(self: &Arc<Self>) -> Option<BufferMapPendingClosure> {
1196 let device = &self.device;
1197 let snatch_guard = device.snatchable_lock.read();
1198 let raw_buf = self.try_raw(&snatch_guard).ok()?;
1202 let map_state = mem::replace(&mut *self.map_state.write(), BufferMapState::Idle);
1203 match map_state {
1204 BufferMapState::Init { staging_buffer } => {
1205 if !self.device.is_valid() {
1206 staging_buffer.dispose();
1207 return None;
1208 }
1209 #[cfg(feature = "trace")]
1210 if let Some(ref mut trace) = *device.trace.lock() {
1211 use crate::device::trace::{DataKind, IntoTrace};
1212
1213 let data = trace.make_binary(DataKind::Bin, staging_buffer.get_data());
1214 trace.add(trace::Action::WriteBuffer {
1215 id: self.to_trace(),
1216 data,
1217 offset: 0,
1219 size: self.size,
1220 queued: true,
1221 });
1222 }
1223
1224 let staging_buffer = staging_buffer.flush();
1225
1226 if let Some(queue) = device.get_queue() {
1227 let region = Some(hal::BufferCopy {
1230 src_offset: 0,
1231 dst_offset: 0,
1232 size: staging_buffer.size,
1233 });
1234 let transition_dst = hal::BufferBarrier::<dyn hal::DynBuffer> {
1238 buffer: raw_buf,
1239 usage: hal::StateTransition {
1240 from: wgt::BufferUses::empty(),
1241 to: wgt::BufferUses::COPY_DST,
1242 },
1243 };
1244 let mut pending_writes = queue.pending_writes.lock();
1245 let encoder = pending_writes.activate();
1246 unsafe {
1247 encoder.transition_buffers(&[transition_dst]);
1248 encoder.copy_buffer_to_buffer(
1251 staging_buffer.raw(),
1252 raw_buf,
1253 region.as_slice(),
1254 );
1255 }
1256 pending_writes.consume(staging_buffer);
1257 pending_writes.insert_buffer(self);
1258 }
1259 None
1260 }
1261 BufferMapState::Idle => None,
1262 BufferMapState::Waiting(pending) => {
1263 Some((pending.callback, Err(BufferAccessError::MapAborted)))
1264 }
1265 BufferMapState::Active {
1266 mapping,
1267 range,
1268 host,
1269 } => {
1270 self.device.check_is_valid().ok()?;
1271 if host == MapMode::Write {
1272 #[cfg(feature = "trace")]
1273 if let Some(ref mut trace) = *device.trace.lock() {
1274 use crate::device::trace::{DataKind, IntoTrace};
1275
1276 let size = range.end - range.start;
1277 let data = trace.make_binary(DataKind::Bin, unsafe {
1278 core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize)
1279 });
1280 trace.add(trace::Action::WriteBuffer {
1281 id: self.to_trace(),
1282 data,
1283 offset: range.start,
1284 size,
1285 queued: false,
1286 });
1287 }
1288 if !mapping.is_coherent {
1289 unsafe { device.raw().flush_mapped_ranges(raw_buf, &[range]) };
1290 }
1291 }
1292 unsafe { device.raw().unmap_buffer(raw_buf) };
1293 None
1294 }
1295 }
1296 }
1297
1298 pub fn destroy(self: &Arc<Self>) {
1299 profiling::scope!("Buffer::destroy");
1300 api_log!("Buffer::destroy {:?}", Arc::as_ptr(self));
1301
1302 let device = &self.device;
1303
1304 #[cfg(feature = "trace")]
1305 if let Some(trace) = device.trace.lock().as_mut() {
1306 use crate::device::trace::IntoTrace;
1307 trace.add(trace::Action::DestroyBuffer(self.to_trace()));
1308 }
1309
1310 let ResourceState::Valid(state) = &self.state else {
1311 return;
1312 };
1313
1314 self.unmap();
1315
1316 let temp = {
1317 let mut snatch_guard = device.snatchable_lock.write();
1318
1319 let raw = match state.raw.snatch(&mut snatch_guard) {
1320 Some(raw) => raw,
1321 None => {
1322 return;
1324 }
1325 };
1326
1327 let timestamp_normalization_bind_group = self
1328 .timestamp_normalization_bind_group
1329 .snatch(&mut snatch_guard);
1330
1331 let indirect_validation_bind_groups = self
1332 .indirect_validation_bind_groups
1333 .snatch(&mut snatch_guard);
1334
1335 drop(snatch_guard);
1336
1337 let bind_groups = {
1338 let mut guard = self.bind_groups.lock();
1339 mem::take(&mut *guard)
1340 };
1341
1342 queue::TempResource::DestroyedBuffer(DestroyedBuffer {
1343 raw: ManuallyDrop::new(raw),
1344 device: Arc::clone(&self.device),
1345 label: self.label().to_owned(),
1346 bind_groups,
1347 timestamp_normalization_bind_group,
1348 indirect_validation_bind_groups,
1349 })
1350 };
1351
1352 let Some(queue) = device.get_queue() else {
1353 return;
1354 };
1355
1356 {
1357 let mut pending_writes = queue.pending_writes.lock();
1358 if pending_writes.contains_buffer(self) {
1359 pending_writes.consume_temp(temp);
1360 return;
1361 }
1362 }
1363
1364 let mut life_lock = queue.lock_life();
1365 let last_submit_index = life_lock.get_buffer_latest_submission_index(self);
1366 if let Some(last_submit_index) = last_submit_index {
1367 life_lock.schedule_resource_destruction(temp, last_submit_index);
1368 }
1369 }
1370
1371 pub fn size(&self) -> wgt::BufferAddress {
1372 self.size
1373 }
1374
1375 pub fn usage(&self) -> wgt::BufferUsages {
1376 self.usage
1377 }
1378}
1379
1380#[derive(Clone, Debug, Error)]
1381#[non_exhaustive]
1382pub enum CreateBufferError {
1383 #[error(transparent)]
1384 Device(#[from] DeviceError),
1385 #[error("Failed to map buffer while creating: {0}")]
1386 AccessError(#[from] BufferAccessError),
1387 #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
1388 UnalignedSize,
1389 #[error("Invalid usage flags {0:?}")]
1390 InvalidUsage(wgt::BufferUsages),
1391 #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
1392 UsageMismatch(wgt::BufferUsages),
1393 #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
1394 MaxBufferSize { requested: u64, maximum: u64 },
1395 #[error(transparent)]
1396 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1397 #[error(transparent)]
1398 MissingFeatures(#[from] MissingFeatures),
1399 #[error("Failed to create bind group for indirect buffer validation: {0}")]
1400 IndirectValidationBindGroup(DeviceError),
1401 #[error("Error initializing buffer: {0}")]
1402 QueueWrite(#[from] queue::QueueWriteError),
1403}
1404
1405crate::impl_resource_type!(Buffer);
1406crate::impl_labeled!(Buffer);
1407crate::impl_parent_device!(Buffer);
1408crate::impl_storage_item!(Buffer);
1409crate::impl_trackable!(Buffer);
1410
1411impl WebGpuError for CreateBufferError {
1412 fn webgpu_error_type(&self) -> ErrorType {
1413 match self {
1414 Self::Device(e) => e.webgpu_error_type(),
1415 Self::AccessError(e) => e.webgpu_error_type(),
1416 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1417 Self::IndirectValidationBindGroup(e) => e.webgpu_error_type(),
1418 Self::MissingFeatures(e) => e.webgpu_error_type(),
1419 Self::QueueWrite(e) => e.webgpu_error_type(),
1420
1421 Self::UnalignedSize
1422 | Self::InvalidUsage(_)
1423 | Self::UsageMismatch(_)
1424 | Self::MaxBufferSize { .. } => ErrorType::Validation,
1425 }
1426 }
1427}
1428
1429#[derive(Debug)]
1431pub struct DestroyedBuffer {
1432 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1433 device: Arc<Device>,
1434 label: String,
1435 bind_groups: WeakVec<BindGroup>,
1436 timestamp_normalization_bind_group: Option<TimestampNormalizationBindGroup>,
1437 indirect_validation_bind_groups: Option<crate::indirect_validation::BindGroups>,
1438}
1439
1440impl DestroyedBuffer {
1441 pub fn label(&self) -> &dyn fmt::Debug {
1442 &self.label
1443 }
1444}
1445
1446impl Drop for DestroyedBuffer {
1447 fn drop(&mut self) {
1448 let mut deferred = self.device.deferred_destroy.lock();
1449 deferred.push(DeferredDestroy::BindGroups(mem::take(
1450 &mut self.bind_groups,
1451 )));
1452 drop(deferred);
1453
1454 if let Some(raw) = self.timestamp_normalization_bind_group.take() {
1455 raw.dispose(self.device.raw());
1456 }
1457
1458 if let Some(raw) = self.indirect_validation_bind_groups.take() {
1459 raw.dispose(self.device.raw());
1460 }
1461
1462 resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label());
1463 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1465 unsafe {
1466 hal::DynDevice::destroy_buffer(self.device.raw(), raw);
1467 }
1468 }
1469}
1470
1471#[cfg(send_sync)]
1472unsafe impl Send for StagingBuffer {}
1473#[cfg(send_sync)]
1474unsafe impl Sync for StagingBuffer {}
1475
1476#[derive(Debug)]
1496pub struct StagingBuffer {
1497 raw: Box<dyn hal::DynBuffer>,
1498 device: Arc<Device>,
1499 pub(crate) size: wgt::BufferSize,
1500 is_coherent: bool,
1501 ptr: NonNull<u8>,
1502}
1503
1504impl StagingBuffer {
1505 pub(crate) fn new(device: &Arc<Device>, size: wgt::BufferSize) -> Result<Self, DeviceError> {
1506 profiling::scope!("StagingBuffer::new");
1507 let stage_desc = hal::BufferDescriptor {
1508 label: hal_label(Some("(wgpu internal) Staging"), device.instance_flags),
1509 size: size.get(),
1510 usage: wgt::BufferUses::MAP_WRITE | wgt::BufferUses::COPY_SRC,
1511 memory_flags: hal::MemoryFlags::TRANSIENT,
1512 };
1513
1514 let (raw, _) = unsafe { device.raw().create_buffer(&stage_desc) }
1515 .map_err(|e| device.handle_hal_error(e))?;
1516 let mapping = unsafe { device.raw().map_buffer(raw.as_ref(), 0..size.get()) }
1517 .map_err(|e| device.handle_hal_error(e))?;
1518
1519 let staging_buffer = StagingBuffer {
1520 raw,
1521 device: device.clone(),
1522 size,
1523 is_coherent: mapping.is_coherent,
1524 ptr: mapping.ptr,
1525 };
1526
1527 Ok(staging_buffer)
1528 }
1529
1530 pub unsafe fn ptr(&self) -> NonNull<u8> {
1534 self.ptr
1535 }
1536
1537 pub fn size(&self) -> wgt::BufferSize {
1538 self.size
1539 }
1540
1541 #[cfg(feature = "trace")]
1542 pub(crate) fn get_data(&self) -> &[u8] {
1543 unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.size.get() as usize) }
1544 }
1545
1546 pub(crate) fn write_zeros(&mut self) {
1547 unsafe { core::ptr::write_bytes(self.ptr.as_ptr(), 0, self.size.get() as usize) };
1548 }
1549
1550 pub(crate) fn write_exact(&mut self, data: &[u8]) {
1556 assert_eq!(data.len(), self.size.get() as usize);
1557 unsafe {
1560 core::ptr::copy_nonoverlapping(
1561 data.as_ptr(),
1562 self.ptr.as_ptr(),
1563 self.size.get() as usize,
1564 );
1565 }
1566 }
1567
1568 pub(crate) fn write_with_zero_padding(&mut self, data: &[u8]) {
1574 assert!(data.len() <= self.size.get() as usize);
1575 unsafe {
1578 core::ptr::copy_nonoverlapping(data.as_ptr(), self.ptr.as_ptr(), data.len());
1579 core::ptr::write_bytes(
1580 self.ptr.as_ptr().add(data.len()),
1581 0,
1582 self.size.get() as usize - data.len(),
1583 );
1584 }
1585 }
1586
1587 pub(crate) unsafe fn write_with_offset(
1589 &mut self,
1590 data: &[u8],
1591 src_offset: isize,
1592 dst_offset: isize,
1593 size: usize,
1594 ) {
1595 unsafe {
1596 debug_assert!(
1597 (src_offset + size as isize) as usize <= data.len(),
1598 "src_offset + size must be in-bounds: src_offset = {}, size = {}, data.len() = {}",
1599 src_offset,
1600 size,
1601 data.len()
1602 );
1603 core::ptr::copy_nonoverlapping(
1604 data.as_ptr().offset(src_offset),
1605 self.ptr.as_ptr().offset(dst_offset),
1606 size,
1607 );
1608 }
1609 }
1610
1611 pub(crate) fn flush(self) -> FlushedStagingBuffer {
1612 let device = self.device.raw();
1613 if !self.is_coherent {
1614 #[allow(clippy::single_range_in_vec_init)]
1615 unsafe {
1616 device.flush_mapped_ranges(self.raw.as_ref(), &[0..self.size.get()])
1617 };
1618 }
1619 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1620
1621 let StagingBuffer {
1622 raw, device, size, ..
1623 } = self;
1624
1625 FlushedStagingBuffer {
1626 raw: ManuallyDrop::new(raw),
1627 device,
1628 size,
1629 }
1630 }
1631
1632 pub(crate) fn dispose(self) {
1633 let device = self.device.raw();
1634 unsafe { device.unmap_buffer(self.raw.as_ref()) };
1635 unsafe { device.destroy_buffer(self.raw) };
1636 }
1637}
1638
1639crate::impl_resource_type!(StagingBuffer);
1640crate::impl_storage_item!(StagingBuffer);
1641
1642#[derive(Debug)]
1643pub struct FlushedStagingBuffer {
1644 raw: ManuallyDrop<Box<dyn hal::DynBuffer>>,
1645 device: Arc<Device>,
1646 pub(crate) size: wgt::BufferSize,
1647}
1648
1649impl FlushedStagingBuffer {
1650 pub(crate) fn raw(&self) -> &dyn hal::DynBuffer {
1651 self.raw.as_ref()
1652 }
1653}
1654
1655impl Drop for FlushedStagingBuffer {
1656 fn drop(&mut self) {
1657 resource_log!("Destroy raw StagingBuffer");
1658 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
1660 unsafe { self.device.raw().destroy_buffer(raw) };
1661 }
1662}
1663
1664pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
1665
1666#[derive(Debug)]
1667pub(crate) enum TextureInner {
1668 Native {
1669 raw: Box<dyn hal::DynTexture>,
1670 },
1671 Surface {
1672 raw: Box<dyn hal::DynSurfaceTexture>,
1673 },
1674}
1675
1676impl TextureInner {
1677 pub(crate) fn raw(&self) -> &dyn hal::DynTexture {
1678 match self {
1679 Self::Native { raw } => raw.as_ref(),
1680 Self::Surface { raw, .. } => raw.as_ref().borrow(),
1681 }
1682 }
1683}
1684
1685#[derive(Debug)]
1686pub enum TextureClearMode {
1687 BufferCopy,
1688 RenderPass {
1690 clear_views: SmallVec<[ManuallyDrop<Box<dyn hal::DynTextureView>>; 1]>,
1691 is_color: bool,
1692 },
1693 Surface {
1694 clear_view: ManuallyDrop<Box<dyn hal::DynTextureView>>,
1695 },
1696 None,
1699}
1700
1701#[derive(Debug)]
1702pub struct TextureState {
1703 pub(crate) inner: Snatchable<TextureInner>,
1704}
1705
1706#[derive(Debug)]
1707pub struct Texture {
1708 pub(crate) state: ResourceState<TextureState>,
1709 pub(crate) device: Arc<Device>,
1710 pub(crate) desc: wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
1711 pub(crate) _hal_usage: wgt::TextureUses,
1712 pub(crate) format_features: wgt::TextureFormatFeatures,
1713 pub(crate) initialization_status: RwLock<TextureInitTracker>,
1714 pub(crate) full_range: TextureSelector,
1715 pub(crate) tracking_data: TrackingData,
1716 pub(crate) clear_mode: RwLock<TextureClearMode>,
1717 pub(crate) views: Mutex<WeakVec<TextureView>>,
1718 pub(crate) bind_groups: Mutex<WeakVec<BindGroup>>,
1720}
1721
1722impl Texture {
1723 pub(crate) fn new(
1724 device: &Arc<Device>,
1725 inner: TextureInner,
1726 hal_usage: wgt::TextureUses,
1727 desc: &TextureDescriptor,
1728 format_features: wgt::TextureFormatFeatures,
1729 clear_mode: TextureClearMode,
1730 init: bool,
1731 ) -> Self {
1732 Texture {
1733 state: ResourceState::Valid(TextureState {
1734 inner: Snatchable::new(inner),
1735 }),
1736 device: device.clone(),
1737 desc: desc.map_label(|label| label.to_string()),
1738 _hal_usage: hal_usage,
1739 format_features,
1740 initialization_status: RwLock::new(
1741 rank::TEXTURE_INITIALIZATION_STATUS,
1742 if init {
1743 TextureInitTracker::new(desc.mip_level_count, desc.array_layer_count())
1744 } else {
1745 TextureInitTracker::new(desc.mip_level_count, 0)
1746 },
1747 ),
1748 full_range: TextureSelector {
1749 mips: 0..desc.mip_level_count,
1750 layers: 0..desc.array_layer_count(),
1751 },
1752 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1753 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, clear_mode),
1754 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1755 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1756 }
1757 }
1758
1759 pub fn invalid(device: &Arc<Device>, desc: &TextureDescriptor) -> Arc<Self> {
1760 Arc::new(Texture {
1761 state: ResourceState::Invalid,
1762 device: device.clone(),
1763 desc: desc.map_label(|label| label.to_string()),
1764 _hal_usage: wgt::TextureUses::empty(),
1765 format_features: wgt::TextureFormatFeatures {
1766 allowed_usages: wgt::TextureUsages::empty(),
1767 flags: wgt::TextureFormatFeatureFlags::empty(),
1768 },
1769 initialization_status: RwLock::new(
1770 rank::TEXTURE_INITIALIZATION_STATUS,
1771 TextureInitTracker::new(0, 0),
1772 ),
1773 full_range: TextureSelector {
1774 mips: 0..desc.mip_level_count,
1775 layers: 0..desc.array_layer_count(),
1776 },
1777 tracking_data: TrackingData::new(device.tracker_indices.textures.clone()),
1778 clear_mode: RwLock::new(rank::TEXTURE_CLEAR_MODE, TextureClearMode::None),
1779 views: Mutex::new(rank::TEXTURE_VIEWS, WeakVec::new()),
1780 bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()),
1781 })
1782 }
1783
1784 pub(crate) fn check_usage(
1787 &self,
1788 expected: wgt::TextureUsages,
1789 ) -> Result<(), MissingTextureUsageError> {
1790 if self.desc.usage.contains(expected) {
1791 Ok(())
1792 } else {
1793 Err(MissingTextureUsageError {
1794 res: self.error_ident(),
1795 actual: self.desc.usage,
1796 expected,
1797 })
1798 }
1799 }
1800}
1801
1802impl Drop for Texture {
1803 #[allow(trivial_casts)]
1804 fn drop(&mut self) {
1805 profiling::scope!("Texture::drop");
1806 api_log!("Texture::drop {:?}", self as *const _);
1807
1808 #[cfg(feature = "trace")]
1809 {
1810 let mut t = self.device.trace.lock();
1811 if let Some(t) = t.as_mut() {
1812 use crate::device::trace::to_trace;
1813
1814 t.add(trace::Action::DropTexture(unsafe { to_trace(self) }));
1816 }
1817 }
1818 match *self.clear_mode.write() {
1819 TextureClearMode::Surface {
1820 ref mut clear_view, ..
1821 } => {
1822 let raw = unsafe { ManuallyDrop::take(clear_view) };
1824 unsafe {
1825 self.device.raw().destroy_texture_view(raw);
1826 }
1827 }
1828 TextureClearMode::RenderPass {
1829 ref mut clear_views,
1830 ..
1831 } => {
1832 clear_views.iter_mut().for_each(|clear_view| {
1833 let raw = unsafe { ManuallyDrop::take(clear_view) };
1835 unsafe {
1836 self.device.raw().destroy_texture_view(raw);
1837 }
1838 });
1839 }
1840 _ => {}
1841 };
1842
1843 let ResourceState::Valid(state) = &mut self.state else {
1844 return;
1845 };
1846 if let Some(TextureInner::Native { raw }) = state.inner.take() {
1847 resource_log!("Destroy raw {}", self.error_ident());
1848 unsafe {
1849 self.device.raw().destroy_texture(raw);
1850 }
1851 }
1852 }
1853}
1854
1855impl RawResourceAccess for Texture {
1856 type DynResource = dyn hal::DynTexture;
1857
1858 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
1859 self.state
1860 .as_ref()
1861 .valid()
1862 .and_then(|t| t.inner.get(guard).map(|t| t.raw()))
1863 }
1864}
1865
1866impl Texture {
1867 pub(crate) fn state(&self) -> Result<&TextureState, InvalidResourceError> {
1868 match &self.state {
1869 ResourceState::Valid(state) => Ok(state),
1870 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
1871 }
1872 }
1873
1874 pub(crate) fn check_destroyed(
1875 &self,
1876 guard: &SnatchGuard,
1877 ) -> Result<(), DestroyedResourceError> {
1878 let Ok(state) = self.state() else {
1879 return Ok(());
1880 };
1881 state
1882 .inner
1883 .get(guard)
1884 .map(|_| ())
1885 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
1886 }
1887
1888 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
1889 self.state().map(|_| ())
1890 }
1891
1892 pub(crate) fn try_inner<'a>(
1893 &'a self,
1894 guard: &'a SnatchGuard,
1895 ) -> Result<&'a TextureInner, InvalidOrDestroyedResourceError> {
1896 self.state()?
1897 .inner
1898 .get(guard)
1899 .ok_or_else(|| DestroyedResourceError(self.error_ident()).into())
1900 }
1901
1902 pub(crate) fn get_clear_view<'a>(
1903 clear_mode: &'a TextureClearMode,
1904 desc: &'a wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
1905 mip_level: u32,
1906 depth_or_layer: u32,
1907 ) -> &'a dyn hal::DynTextureView {
1908 match *clear_mode {
1909 TextureClearMode::BufferCopy => {
1910 panic!("Given texture is cleared with buffer copies, not render passes")
1911 }
1912 TextureClearMode::None => {
1913 panic!("Given texture can't be cleared")
1914 }
1915 TextureClearMode::Surface { ref clear_view, .. } => clear_view.as_ref(),
1916 TextureClearMode::RenderPass {
1917 ref clear_views, ..
1918 } => {
1919 let index = if desc.dimension == wgt::TextureDimension::D3 {
1920 (0..mip_level).fold(0, |acc, mip| {
1921 acc + (desc.size.depth_or_array_layers >> mip).max(1)
1922 })
1923 } else {
1924 mip_level * desc.size.depth_or_array_layers
1925 } + depth_or_layer;
1926 clear_views[index as usize].as_ref()
1927 }
1928 }
1929 }
1930
1931 pub fn destroy(self: &Arc<Self>) {
1932 profiling::scope!("Texture::destroy");
1933 api_log!("Texture::destroy {:?}", Arc::as_ptr(self));
1934
1935 #[cfg(feature = "trace")]
1936 if let Some(trace) = self.device.trace.lock().as_mut() {
1937 use crate::device::trace::IntoTrace as _;
1938
1939 trace.add(trace::Action::DestroyTexture(self.to_trace()));
1940 }
1941
1942 let device = &self.device;
1943
1944 let ResourceState::Valid(state) = &self.state else {
1945 return;
1946 };
1947
1948 let temp = {
1949 let raw = match state.inner.snatch(&mut device.snatchable_lock.write()) {
1950 Some(TextureInner::Native { raw }) => raw,
1951 Some(TextureInner::Surface { .. }) => {
1952 return;
1953 }
1954 None => {
1955 return;
1957 }
1958 };
1959
1960 let views = {
1961 let mut guard = self.views.lock();
1962 mem::take(&mut *guard)
1963 };
1964
1965 let bind_groups = {
1966 let mut guard = self.bind_groups.lock();
1967 mem::take(&mut *guard)
1968 };
1969
1970 queue::TempResource::DestroyedTexture(DestroyedTexture {
1971 raw: ManuallyDrop::new(raw),
1972 views,
1973 clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None),
1974 bind_groups,
1975 device: Arc::clone(&self.device),
1976 label: self.label().to_owned(),
1977 })
1978 };
1979
1980 let Some(queue) = device.get_queue() else {
1981 return;
1982 };
1983
1984 {
1985 let mut pending_writes = queue.pending_writes.lock();
1986 if pending_writes.contains_texture(self) {
1987 pending_writes.consume_temp(temp);
1988 return;
1989 }
1990 }
1991
1992 let mut life_lock = queue.lock_life();
1993 let last_submit_index = life_lock.get_texture_latest_submission_index(self);
1994 if let Some(last_submit_index) = last_submit_index {
1995 life_lock.schedule_resource_destruction(temp, last_submit_index);
1996 }
1997 }
1998
1999 fn create_view_inner(
2000 self: &Arc<Self>,
2001 desc: &TextureViewDescriptor,
2002 ) -> Result<Arc<TextureView>, CreateTextureViewError> {
2003 let device = &self.device;
2004 device.check_is_valid()?;
2005
2006 if desc.swizzle != wgt::TextureComponentSwizzle::default() {
2007 self.device
2008 .require_features(wgt::Features::TEXTURE_COMPONENT_SWIZZLE)?;
2009 }
2010
2011 let snatch_guard = device.snatchable_lock.read();
2012
2013 let texture_raw = self.try_inner(&snatch_guard)?.raw();
2014
2015 let resolved_format = desc.format.unwrap_or_else(|| {
2018 self.desc
2019 .format
2020 .aspect_specific_format(desc.range.aspect)
2021 .unwrap_or(self.desc.format)
2022 });
2023
2024 let resolved_dimension = desc.dimension.unwrap_or_else(|| match self.desc.dimension {
2025 wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1,
2026 wgt::TextureDimension::D2 => {
2027 if self.desc.array_layer_count() == 1 {
2028 wgt::TextureViewDimension::D2
2029 } else {
2030 wgt::TextureViewDimension::D2Array
2031 }
2032 }
2033 wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3,
2034 });
2035
2036 let resolved_mip_level_count = desc.range.mip_level_count.unwrap_or_else(|| {
2037 self.desc
2038 .mip_level_count
2039 .saturating_sub(desc.range.base_mip_level)
2040 });
2041
2042 let resolved_array_layer_count =
2043 desc.range
2044 .array_layer_count
2045 .unwrap_or_else(|| match resolved_dimension {
2046 wgt::TextureViewDimension::D1
2047 | wgt::TextureViewDimension::D2
2048 | wgt::TextureViewDimension::D3 => 1,
2049 wgt::TextureViewDimension::Cube => 6,
2050 wgt::TextureViewDimension::D2Array | wgt::TextureViewDimension::CubeArray => {
2051 self.desc
2052 .array_layer_count()
2053 .saturating_sub(desc.range.base_array_layer)
2054 }
2055 });
2056
2057 let resolved_usage = {
2058 let usage = desc.usage.unwrap_or(wgt::TextureUsages::empty());
2059 if usage.is_empty() {
2060 self.desc.usage
2061 } else if self.desc.usage.contains(usage) {
2062 if self
2064 .desc
2065 .usage
2066 .contains(wgt::TextureUsages::TRANSIENT_ATTACHMENT)
2067 && self.desc.usage != usage
2068 {
2069 return Err(CreateTextureViewError::InvalidTransientTextureViewUsage {
2070 texture: self.desc.usage,
2071 view: usage,
2072 });
2073 }
2074
2075 usage
2076 } else {
2077 return Err(CreateTextureViewError::InvalidTextureViewUsage {
2078 view: usage,
2079 texture: self.desc.usage,
2080 });
2081 }
2082 };
2083
2084 let format_features = device.describe_format_features(resolved_format)?;
2085 let allowed_format_usages = format_features.allowed_usages;
2086 if resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
2087 && !allowed_format_usages.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
2088 {
2089 return Err(CreateTextureViewError::TextureViewFormatNotRenderable(
2090 resolved_format,
2091 ));
2092 }
2093
2094 if resolved_usage.contains(wgt::TextureUsages::STORAGE_BINDING)
2095 && !allowed_format_usages.contains(wgt::TextureUsages::STORAGE_BINDING)
2096 {
2097 return Err(CreateTextureViewError::TextureViewFormatNotStorage(
2098 resolved_format,
2099 ));
2100 }
2101
2102 let aspects = hal::FormatAspects::new(self.desc.format, desc.range.aspect);
2105 if aspects.is_empty() {
2106 return Err(CreateTextureViewError::InvalidAspect {
2107 texture_format: self.desc.format,
2108 requested_aspect: desc.range.aspect,
2109 });
2110 }
2111
2112 if desc.range.aspect == wgt::TextureAspect::All && resolved_format.is_multi_planar_format()
2113 {
2114 return Err(CreateTextureViewError::MultiplanarFullTexture(
2115 resolved_format,
2116 ));
2117 }
2118
2119 if desc.range.aspect == wgt::TextureAspect::All {
2120 if resolved_format != self.desc.format
2121 && !self.desc.view_formats.contains(&resolved_format)
2122 {
2123 return Err(CreateTextureViewError::FormatReinterpretation {
2124 texture: self.desc.format,
2125 view: resolved_format,
2126 });
2127 }
2128 } else {
2129 let aspect_format = self.desc.format.aspect_specific_format(desc.range.aspect);
2130 match aspect_format {
2131 Some(aspect_format) if aspect_format == resolved_format => (),
2132 Some(aspect_format) => {
2133 return Err(CreateTextureViewError::WrongAspectReinterpretation {
2134 texture: self.desc.format,
2135 aspect: desc.range.aspect,
2136 aspect_format,
2137 requested_format: resolved_format,
2138 })
2139 }
2140 None => {
2141 unreachable!()
2144 }
2145 }
2146 }
2147
2148 if self.desc.sample_count > 1 && resolved_dimension != wgt::TextureViewDimension::D2 {
2150 let multisample_array_exception = resolved_dimension
2152 == wgt::TextureViewDimension::D2Array
2153 && device.features.contains(wgt::Features::MULTISAMPLE_ARRAY);
2154
2155 if !multisample_array_exception {
2156 return Err(
2157 CreateTextureViewError::InvalidMultisampledTextureViewDimension(
2158 resolved_dimension,
2159 ),
2160 );
2161 }
2162 }
2163
2164 if self.desc.dimension != resolved_dimension.compatible_texture_dimension() {
2166 return Err(CreateTextureViewError::InvalidTextureViewDimension {
2167 view: resolved_dimension,
2168 texture: self.desc.dimension,
2169 });
2170 }
2171
2172 match resolved_dimension {
2173 wgt::TextureViewDimension::D1
2174 | wgt::TextureViewDimension::D2
2175 | wgt::TextureViewDimension::D3
2176 if resolved_array_layer_count != 1 =>
2177 {
2178 return Err(CreateTextureViewError::InvalidArrayLayerCount {
2179 requested: resolved_array_layer_count,
2180 dim: resolved_dimension,
2181 });
2182 }
2183 wgt::TextureViewDimension::Cube if resolved_array_layer_count != 6 => {
2184 return Err(CreateTextureViewError::InvalidCubemapTextureDepth {
2185 depth: resolved_array_layer_count,
2186 });
2187 }
2188 wgt::TextureViewDimension::CubeArray
2189 if !resolved_array_layer_count.is_multiple_of(6) =>
2190 {
2191 return Err(CreateTextureViewError::InvalidCubemapArrayTextureDepth {
2192 depth: resolved_array_layer_count,
2193 });
2194 }
2195 _ => {}
2196 }
2197
2198 match resolved_dimension {
2199 wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray
2200 if self.desc.size.width != self.desc.size.height =>
2201 {
2202 return Err(CreateTextureViewError::InvalidCubeTextureViewSize);
2203 }
2204 _ => {}
2205 }
2206
2207 if resolved_mip_level_count == 0 {
2208 return Err(CreateTextureViewError::ZeroMipLevelCount);
2209 }
2210
2211 let mip_level_end = desc
2212 .range
2213 .base_mip_level
2214 .saturating_add(resolved_mip_level_count);
2215
2216 let level_end = self.desc.mip_level_count;
2217 if mip_level_end > level_end {
2218 return Err(CreateTextureViewError::TooManyMipLevels {
2219 base_mip_level: desc.range.base_mip_level,
2220 mip_level_count: resolved_mip_level_count,
2221 total: level_end,
2222 });
2223 }
2224
2225 if resolved_array_layer_count == 0 {
2226 return Err(CreateTextureViewError::ZeroArrayLayerCount);
2227 }
2228
2229 let array_layer_end = desc
2230 .range
2231 .base_array_layer
2232 .saturating_add(resolved_array_layer_count);
2233
2234 let layer_end = self.desc.array_layer_count();
2235 if array_layer_end > layer_end {
2236 return Err(CreateTextureViewError::TooManyArrayLayers {
2237 base_array_layer: desc.range.base_array_layer,
2238 array_layer_count: resolved_array_layer_count,
2239 total: layer_end,
2240 });
2241 };
2242
2243 let render_extent = 'error: {
2245 if !resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
2246 break 'error Err(TextureViewNotRenderableReason::Usage(resolved_usage));
2247 }
2248
2249 let allowed_view_dimensions = [
2250 wgt::TextureViewDimension::D2,
2251 wgt::TextureViewDimension::D2Array,
2252 wgt::TextureViewDimension::D3,
2253 ];
2254 if !allowed_view_dimensions.contains(&resolved_dimension) {
2255 break 'error Err(TextureViewNotRenderableReason::Dimension(
2256 resolved_dimension,
2257 ));
2258 }
2259
2260 if resolved_mip_level_count != 1 {
2261 break 'error Err(TextureViewNotRenderableReason::MipLevelCount(
2262 resolved_mip_level_count,
2263 ));
2264 }
2265
2266 if resolved_array_layer_count != 1
2267 && !(device.features.contains(wgt::Features::MULTIVIEW))
2268 {
2269 break 'error Err(TextureViewNotRenderableReason::ArrayLayerCount(
2270 resolved_array_layer_count,
2271 ));
2272 }
2273
2274 if !self.desc.format.is_multi_planar_format()
2275 && aspects != hal::FormatAspects::from(self.desc.format)
2276 {
2277 break 'error Err(TextureViewNotRenderableReason::Aspects(aspects));
2278 }
2279
2280 if desc.swizzle != wgt::TextureComponentSwizzle::default() {
2281 break 'error Err(TextureViewNotRenderableReason::Swizzle(desc.swizzle));
2282 }
2283
2284 Ok(self
2285 .desc
2286 .compute_render_extent(desc.range.base_mip_level, desc.range.aspect.to_plane()))
2287 };
2288
2289 let usage = {
2291 let resolved_hal_usage = crate::conv::map_texture_usage(
2292 resolved_usage,
2293 resolved_format.into(),
2294 format_features.flags,
2295 );
2296 let mask_copy = !(wgt::TextureUses::COPY_SRC | wgt::TextureUses::COPY_DST);
2297 let mask_dimension = match resolved_dimension {
2298 wgt::TextureViewDimension::Cube | wgt::TextureViewDimension::CubeArray => {
2299 wgt::TextureUses::RESOURCE
2300 }
2301 wgt::TextureViewDimension::D3 => {
2302 wgt::TextureUses::RESOURCE
2303 | wgt::TextureUses::STORAGE_READ_ONLY
2304 | wgt::TextureUses::STORAGE_WRITE_ONLY
2305 | wgt::TextureUses::STORAGE_READ_WRITE
2306 }
2307 _ => wgt::TextureUses::all(),
2308 };
2309 let mask_mip_level = if resolved_mip_level_count == 1 {
2310 wgt::TextureUses::all()
2311 } else {
2312 wgt::TextureUses::RESOURCE
2313 };
2314 resolved_hal_usage & mask_copy & mask_dimension & mask_mip_level
2315 };
2316
2317 let format = if resolved_format.is_depth_stencil_component(self.desc.format) {
2319 self.desc.format
2320 } else {
2321 resolved_format
2322 };
2323
2324 let resolved_range = wgt::ImageSubresourceRange {
2325 aspect: desc.range.aspect,
2326 base_mip_level: desc.range.base_mip_level,
2327 mip_level_count: Some(resolved_mip_level_count),
2328 base_array_layer: desc.range.base_array_layer,
2329 array_layer_count: Some(resolved_array_layer_count),
2330 };
2331
2332 let hal_desc = hal::TextureViewDescriptor {
2333 label: desc.label.to_hal(device.instance_flags),
2334 format,
2335 dimension: resolved_dimension,
2336 usage,
2337 range: resolved_range,
2338 swizzle: desc.swizzle,
2339 };
2340
2341 let raw = unsafe { device.raw().create_texture_view(texture_raw, &hal_desc) }
2342 .map_err(|e| device.handle_hal_error(e))?;
2343
2344 let selector = TextureSelector {
2345 mips: desc.range.base_mip_level..mip_level_end,
2346 layers: desc.range.base_array_layer..array_layer_end,
2347 };
2348
2349 let view = TextureView {
2350 state: ResourceState::Valid(TextureViewState {
2351 raw: Snatchable::new(raw),
2352 render_extent,
2353 }),
2354 parent: self.clone(),
2355 device: device.clone(),
2356 desc: HalTextureViewDescriptor {
2357 texture_format: self.desc.format,
2358 format: resolved_format,
2359 dimension: resolved_dimension,
2360 usage: resolved_usage,
2361 range: resolved_range,
2362 swizzle: desc.swizzle,
2363 },
2364 format_features: self.format_features,
2365 samples: self.desc.sample_count,
2366 selector,
2367 label: desc.label.to_string(),
2368 };
2369
2370 let view = Arc::new(view);
2371
2372 {
2373 let mut views = self.views.lock();
2374 views.push(Arc::downgrade(&view));
2375 }
2376
2377 Ok(view)
2378 }
2379
2380 pub fn create_view(self: &Arc<Self>, desc: &TextureViewDescriptor) -> Arc<TextureView> {
2381 profiling::scope!("Texture::create_view");
2382
2383 let view = self.create_view_inner(desc).unwrap_or_else(|err| {
2384 self.device
2385 .handle_error(err, desc.label.as_deref(), "Texture::create_view failed");
2386 TextureView::invalid(&self.device, self, desc)
2387 });
2388
2389 api_log!(
2390 "Texture::create_view({:?}) -> {:?}",
2391 Arc::as_ptr(self),
2392 Arc::as_ptr(&view)
2393 );
2394
2395 #[cfg(feature = "trace")]
2396 if let Some(ref mut trace) = *self.device.trace.lock() {
2397 use crate::device::trace;
2398 use trace::IntoTrace as _;
2399 trace.add(trace::Action::CreateTextureView {
2400 id: view.to_trace(),
2401 parent: self.to_trace(),
2402 desc: desc.clone(),
2403 });
2404 }
2405
2406 view
2407 }
2408
2409 pub fn descriptor(&self) -> &wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>> {
2410 &self.desc
2411 }
2412
2413 pub unsafe fn mark_externally_initialized(&self) {
2420 let mut initialization_status = self.initialization_status.write();
2421 for mip_tracker in initialization_status.mips.iter_mut() {
2422 mip_tracker.drain(0..self.desc.array_layer_count());
2423 }
2424 }
2425}
2426
2427#[derive(Debug)]
2429pub struct DestroyedTexture {
2430 raw: ManuallyDrop<Box<dyn hal::DynTexture>>,
2431 views: WeakVec<TextureView>,
2432 clear_mode: TextureClearMode,
2433 bind_groups: WeakVec<BindGroup>,
2434 device: Arc<Device>,
2435 label: String,
2436}
2437
2438impl DestroyedTexture {
2439 pub fn label(&self) -> &dyn fmt::Debug {
2440 &self.label
2441 }
2442}
2443
2444impl Drop for DestroyedTexture {
2445 fn drop(&mut self) {
2446 let device = &self.device;
2447
2448 let mut deferred = device.deferred_destroy.lock();
2449 deferred.push(DeferredDestroy::TextureViews(mem::take(&mut self.views)));
2450 deferred.push(DeferredDestroy::BindGroups(mem::take(
2451 &mut self.bind_groups,
2452 )));
2453 drop(deferred);
2454
2455 match mem::replace(&mut self.clear_mode, TextureClearMode::None) {
2456 TextureClearMode::RenderPass { clear_views, .. } => {
2457 for clear_view in clear_views {
2458 let raw = ManuallyDrop::into_inner(clear_view);
2459 unsafe { self.device.raw().destroy_texture_view(raw) };
2460 }
2461 }
2462 TextureClearMode::Surface { clear_view } => {
2463 let raw = ManuallyDrop::into_inner(clear_view);
2464 unsafe { self.device.raw().destroy_texture_view(raw) };
2465 }
2466 _ => (),
2467 }
2468
2469 resource_log!("Destroy raw Texture (destroyed) {:?}", self.label());
2470 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
2472 unsafe {
2473 self.device.raw().destroy_texture(raw);
2474 }
2475 }
2476}
2477
2478#[derive(Clone, Copy, Debug)]
2479pub enum TextureErrorDimension {
2480 X,
2481 Y,
2482 Z,
2483}
2484
2485#[derive(Clone, Debug, Error)]
2486#[non_exhaustive]
2487pub enum TextureDimensionError {
2488 #[error("Dimension {0:?} is zero")]
2489 Zero(TextureErrorDimension),
2490 #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
2491 LimitExceeded {
2492 dim: TextureErrorDimension,
2493 given: u32,
2494 limit: u32,
2495 },
2496 #[error("Sample count {0} is invalid")]
2497 InvalidSampleCount(u32),
2498 #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
2499 NotMultipleOfBlockWidth {
2500 width: u32,
2501 block_width: u32,
2502 format: wgt::TextureFormat,
2503 },
2504 #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
2505 NotMultipleOfBlockHeight {
2506 height: u32,
2507 block_height: u32,
2508 format: wgt::TextureFormat,
2509 },
2510 #[error(
2511 "Width {width} is not a multiple of {format:?}'s width multiple requirement ({multiple})"
2512 )]
2513 WidthNotMultipleOf {
2514 width: u32,
2515 multiple: u32,
2516 format: wgt::TextureFormat,
2517 },
2518 #[error("Height {height} is not a multiple of {format:?}'s height multiple requirement ({multiple})")]
2519 HeightNotMultipleOf {
2520 height: u32,
2521 multiple: u32,
2522 format: wgt::TextureFormat,
2523 },
2524 #[error("Multisampled texture depth or array layers must be 1, got {0}")]
2525 MultisampledDepthOrArrayLayer(u32),
2526}
2527
2528impl WebGpuError for TextureDimensionError {
2529 fn webgpu_error_type(&self) -> ErrorType {
2530 ErrorType::Validation
2531 }
2532}
2533
2534#[derive(Clone, Debug, Error)]
2535#[non_exhaustive]
2536pub enum CreateTextureError {
2537 #[error(transparent)]
2538 Device(#[from] DeviceError),
2539 #[error(transparent)]
2540 CreateTextureView(#[from] CreateTextureViewError),
2541 #[error("Invalid usage flags {0:?}")]
2542 InvalidUsage(wgt::TextureUsages),
2543 #[error(transparent)]
2544 InvalidDimension(#[from] TextureDimensionError),
2545 #[error("Depth texture ({1:?}) can't be created as {0:?}")]
2546 InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
2547 #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
2548 InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
2549 #[error(
2550 "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
2551 )]
2552 InvalidMipLevelCount { requested: u32, maximum: u32 },
2553 #[error(
2554 "Texture usages {0:?} are not allowed on a texture of type {1:?}{downlevel_suffix}",
2555 downlevel_suffix = if *.2 { " due to downlevel restrictions" } else { "" }
2556 )]
2557 InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
2558 #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
2559 InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
2560 #[error("Transient texture usage must be equal to `TRANSIENT_ATTACHMENT | RENDER_ATTACHMENT`, but got `{0:?}`")]
2561 InvalidTransientTextureUsage(wgt::TextureUsages),
2562 #[error("Transient texture view formats must be empty")]
2563 InvalidTransientTextureViewFormats,
2564 #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
2565 InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
2566 #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
2567 InvalidMultisampledStorageBinding,
2568 #[error("Format {0:?} does not support multisampling")]
2569 InvalidMultisampledFormat(wgt::TextureFormat),
2570 #[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:?}.")]
2571 InvalidSampleCount(u32, wgt::TextureFormat, Vec<u32>, Vec<u32>),
2572 #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
2573 MultisampledNotRenderAttachment,
2574 #[error("Transient texture mip level count ({0}) must be 1")]
2575 InvalidTransientTextureMipLevelCount(u32),
2576 #[error("Transient texture layer count ({0}) must be 1")]
2577 InvalidTransientTextureLayerCount(u32),
2578 #[error("Texture format {0:?} can't be used due to missing features")]
2579 MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
2580 #[error(transparent)]
2581 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
2582}
2583
2584crate::impl_resource_type!(Texture);
2585impl Labeled for Texture {
2586 fn label(&self) -> &str {
2587 &self.desc.label
2588 }
2589}
2590crate::impl_parent_device!(Texture);
2591crate::impl_storage_item!(Texture);
2592crate::impl_trackable!(Texture);
2593
2594impl Borrow<TextureSelector> for Texture {
2595 fn borrow(&self) -> &TextureSelector {
2596 &self.full_range
2597 }
2598}
2599
2600impl WebGpuError for CreateTextureError {
2601 fn webgpu_error_type(&self) -> ErrorType {
2602 match self {
2603 Self::Device(e) => e.webgpu_error_type(),
2604 Self::CreateTextureView(e) => e.webgpu_error_type(),
2605 Self::InvalidDimension(e) => e.webgpu_error_type(),
2606 Self::MissingFeatures(_, e) => e.webgpu_error_type(),
2607 Self::MissingDownlevelFlags(e) => e.webgpu_error_type(),
2608
2609 Self::InvalidUsage(_)
2610 | Self::InvalidDepthDimension(_, _)
2611 | Self::InvalidCompressedDimension(_, _)
2612 | Self::InvalidMipLevelCount { .. }
2613 | Self::InvalidFormatUsages(_, _, _)
2614 | Self::InvalidViewFormat(_, _)
2615 | Self::InvalidDimensionUsages(_, _)
2616 | Self::InvalidMultisampledStorageBinding
2617 | Self::InvalidMultisampledFormat(_)
2618 | Self::InvalidSampleCount(..)
2619 | Self::InvalidTransientTextureUsage(_)
2620 | Self::InvalidTransientTextureMipLevelCount(_)
2621 | Self::InvalidTransientTextureLayerCount(_)
2622 | Self::InvalidTransientTextureViewFormats
2623 | Self::MultisampledNotRenderAttachment => ErrorType::Validation,
2624 }
2625 }
2626}
2627
2628#[derive(Clone, Debug, Default, Eq, PartialEq)]
2630#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2631#[cfg_attr(feature = "serde", serde(default))]
2632pub struct TextureViewDescriptor<'a> {
2633 pub label: Label<'a>,
2637 pub format: Option<wgt::TextureFormat>,
2642 pub dimension: Option<wgt::TextureViewDimension>,
2648 pub usage: Option<wgt::TextureUsages>,
2651 pub range: wgt::ImageSubresourceRange,
2653 pub swizzle: wgt::TextureComponentSwizzle,
2657}
2658
2659#[derive(Debug)]
2660pub(crate) struct HalTextureViewDescriptor {
2661 pub texture_format: wgt::TextureFormat,
2662 pub format: wgt::TextureFormat,
2663 pub usage: wgt::TextureUsages,
2664 pub dimension: wgt::TextureViewDimension,
2665 pub range: wgt::ImageSubresourceRange,
2666 pub swizzle: wgt::TextureComponentSwizzle,
2667}
2668
2669impl HalTextureViewDescriptor {
2670 pub fn aspects(&self) -> hal::FormatAspects {
2671 hal::FormatAspects::new(self.texture_format, self.range.aspect)
2672 }
2673}
2674
2675#[derive(Debug, Copy, Clone, Error)]
2676pub enum TextureViewNotRenderableReason {
2677 #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
2678 Usage(wgt::TextureUsages),
2679 #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
2680 Dimension(wgt::TextureViewDimension),
2681 #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
2682 MipLevelCount(u32),
2683 #[error("This texture view has more than one array layer. View array layers: {0:?}")]
2684 ArrayLayerCount(u32),
2685 #[error(
2686 "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
2687 )]
2688 Aspects(hal::FormatAspects),
2689 #[error("The texture view swizzle must be identity. View swizzle: {0:?}")]
2690 Swizzle(wgt::TextureComponentSwizzle),
2691}
2692
2693#[derive(Debug)]
2694pub struct TextureViewState {
2695 pub(crate) raw: Snatchable<Box<dyn hal::DynTextureView>>,
2696 pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
2698}
2699
2700#[derive(Debug)]
2701pub struct TextureView {
2702 pub(crate) state: ResourceState<TextureViewState>,
2703 pub(crate) parent: Arc<Texture>,
2705 pub(crate) device: Arc<Device>,
2706 pub(crate) desc: HalTextureViewDescriptor,
2707 pub(crate) format_features: wgt::TextureFormatFeatures,
2708 pub(crate) samples: u32,
2709 pub(crate) selector: TextureSelector,
2710 pub(crate) label: String,
2712}
2713
2714impl Drop for TextureView {
2715 #[expect(trivial_casts)]
2716 fn drop(&mut self) {
2717 profiling::scope!("TextureView::drop");
2718 api_log!("TextureView::drop {:?}", self as *const _);
2719 #[cfg(feature = "trace")]
2720 if let Some(t) = self.device.trace.lock().as_mut() {
2721 t.add(trace::Action::DropTextureView(unsafe {
2722 trace::to_trace(self)
2723 }));
2724 }
2725 let ResourceState::Valid(state) = &mut self.state else {
2726 return;
2727 };
2728
2729 if let Some(raw) = state.raw.take() {
2730 resource_log!("Destroy raw {}", self.error_ident());
2731 unsafe {
2732 self.device.raw().destroy_texture_view(raw);
2733 }
2734 }
2735 }
2736}
2737
2738impl RawResourceAccess for TextureView {
2739 type DynResource = dyn hal::DynTextureView;
2740
2741 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
2742 self.state()
2743 .ok()
2744 .and_then(|state| state.raw.get(guard).map(|it| it.as_ref()))
2745 }
2746
2747 fn try_raw<'a>(
2748 &'a self,
2749 guard: &'a SnatchGuard,
2750 ) -> Result<&'a Self::DynResource, DestroyedResourceError> {
2751 self.parent.check_destroyed(guard)?;
2752
2753 self.raw(guard)
2754 .ok_or_else(|| DestroyedResourceError(self.error_ident()))
2755 }
2756}
2757
2758impl TextureView {
2759 pub(crate) fn check_usage(
2762 &self,
2763 expected: wgt::TextureUsages,
2764 ) -> Result<(), MissingTextureUsageError> {
2765 if self.desc.usage.contains(expected) {
2766 Ok(())
2767 } else {
2768 Err(MissingTextureUsageError {
2769 res: self.error_ident(),
2770 actual: self.desc.usage,
2771 expected,
2772 })
2773 }
2774 }
2775
2776 pub(crate) fn state(&self) -> Result<&TextureViewState, InvalidResourceError> {
2777 match &self.state {
2778 ResourceState::Valid(state) => Ok(state),
2779 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
2780 }
2781 }
2782
2783 pub(crate) fn check_valid(&self) -> Result<(), InvalidResourceError> {
2784 self.state().map(|_| ())
2785 }
2786
2787 pub(crate) fn invalid(
2788 device: &Arc<Device>,
2789 texture: &Arc<Texture>,
2790 desc: &TextureViewDescriptor,
2791 ) -> Arc<Self> {
2792 Arc::new(TextureView {
2794 state: ResourceState::Invalid,
2795 parent: texture.clone(),
2796 device: device.clone(),
2797 desc: HalTextureViewDescriptor {
2798 texture_format: texture.desc.format,
2799 format: desc.format.unwrap_or(texture.desc.format),
2800 usage: desc.usage.unwrap_or(texture.desc.usage),
2801 dimension: desc.dimension.unwrap_or(match texture.desc.dimension {
2802 wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1,
2803 wgt::TextureDimension::D2 => wgt::TextureViewDimension::D2,
2804 wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3,
2805 }),
2806 range: desc.range,
2807 swizzle: desc.swizzle,
2808 },
2809 format_features: texture.format_features,
2810 samples: texture.desc.sample_count,
2811 selector: TextureSelector {
2812 mips: desc.range.mip_range(texture.desc.mip_level_count),
2813 layers: desc.range.layer_range(texture.desc.array_layer_count()),
2814 },
2815 label: desc.label.to_string(),
2816 })
2817 }
2818
2819 pub fn texture(&self) -> &Arc<Texture> {
2820 &self.parent
2821 }
2822}
2823
2824#[derive(Clone, Debug, Error)]
2825#[non_exhaustive]
2826pub enum CreateTextureViewError {
2827 #[error(transparent)]
2828 Device(#[from] DeviceError),
2829 #[error(transparent)]
2830 DestroyedResource(#[from] DestroyedResourceError),
2831 #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
2832 InvalidTextureViewDimension {
2833 view: wgt::TextureViewDimension,
2834 texture: wgt::TextureDimension,
2835 },
2836 #[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.")]
2837 TextureViewFormatNotRenderable(wgt::TextureFormat),
2838 #[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.")]
2839 TextureViewFormatNotStorage(wgt::TextureFormat),
2840 #[error("Texture view usages (`{view:?}`) must be a subset of the texture's original usages (`{texture:?}`)")]
2841 InvalidTextureViewUsage {
2842 view: wgt::TextureUsages,
2843 texture: wgt::TextureUsages,
2844 },
2845 #[error("Texture view dimension `{0:?}` cannot be used with a multisampled texture")]
2846 InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
2847 #[error(
2848 "TextureView has an arrayLayerCount of {depth}. Views of type `Cube` must have arrayLayerCount of 6."
2849 )]
2850 InvalidCubemapTextureDepth { depth: u32 },
2851 #[error("TextureView has an arrayLayerCount of {depth}. Views of type `CubeArray` must have an arrayLayerCount that is a multiple of 6.")]
2852 InvalidCubemapArrayTextureDepth { depth: u32 },
2853 #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
2854 InvalidCubeTextureViewSize,
2855 #[error("Mip level count is 0")]
2856 ZeroMipLevelCount,
2857 #[error("Array layer count is 0")]
2858 ZeroArrayLayerCount,
2859 #[error(
2860 "`TextureView` starts at mip level {base_mip_level} and spans {mip_level_count} mip \
2861 levels, but the texture view only has {total} total mip level(s)"
2862 )]
2863 TooManyMipLevels {
2864 base_mip_level: u32,
2865 mip_level_count: u32,
2866 total: u32,
2867 },
2868 #[error(
2869 "`TextureView` starts at array layer {base_array_layer} and spans {array_layer_count}) \
2870 array layers, but the texture view only has {total} total layer(s)"
2871 )]
2872 TooManyArrayLayers {
2873 base_array_layer: u32,
2874 array_layer_count: u32,
2875 total: u32,
2876 },
2877 #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
2878 InvalidArrayLayerCount {
2879 requested: u32,
2880 dim: wgt::TextureViewDimension,
2881 },
2882 #[error(
2883 "Aspect {requested_aspect:?} is not a valid aspect of the source texture format {texture_format:?}"
2884 )]
2885 InvalidAspect {
2886 texture_format: wgt::TextureFormat,
2887 requested_aspect: wgt::TextureAspect,
2888 },
2889 #[error(
2890 "Trying to create a view of format {view:?} of a texture with format {texture:?}, \
2891 but this view format is not present in the texture's viewFormat array"
2892 )]
2893 FormatReinterpretation {
2894 texture: wgt::TextureFormat,
2895 view: wgt::TextureFormat,
2896 },
2897 #[error(
2898 "The texture view (`{view:?}`) from transient texture (`{texture:?}`) must have the same usage"
2899 )]
2900 InvalidTransientTextureViewUsage {
2901 texture: wgt::TextureUsages,
2902 view: wgt::TextureUsages,
2903 },
2904 #[error(transparent)]
2905 InvalidResource(#[from] InvalidResourceError),
2906 #[error(transparent)]
2907 MissingFeatures(#[from] MissingFeatures),
2908
2909 #[error(
2910 "Trying to create a view of format {requested_format:?} on aspect {aspect:?} of format {texture:?}, \
2911 but the actual format of this aspect is {aspect_format:?}"
2912 )]
2913 WrongAspectReinterpretation {
2914 texture: wgt::TextureFormat,
2915 aspect: wgt::TextureAspect,
2916 aspect_format: wgt::TextureFormat,
2917 requested_format: wgt::TextureFormat,
2918 },
2919 #[error("TextureAspect::All cannot be used in texture views on multi-planar formats")]
2920 MultiplanarFullTexture(wgt::TextureFormat),
2921}
2922
2923impl From<InvalidOrDestroyedResourceError> for CreateTextureViewError {
2924 fn from(value: InvalidOrDestroyedResourceError) -> Self {
2925 match value {
2926 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
2927 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
2928 }
2929 }
2930}
2931
2932impl WebGpuError for CreateTextureViewError {
2933 fn webgpu_error_type(&self) -> ErrorType {
2934 match self {
2935 Self::Device(e) => e.webgpu_error_type(),
2936
2937 Self::InvalidTextureViewDimension { .. }
2938 | Self::InvalidResource(_)
2939 | Self::InvalidMultisampledTextureViewDimension(_)
2940 | Self::InvalidCubemapTextureDepth { .. }
2941 | Self::InvalidCubemapArrayTextureDepth { .. }
2942 | Self::InvalidCubeTextureViewSize
2943 | Self::ZeroMipLevelCount
2944 | Self::ZeroArrayLayerCount
2945 | Self::TooManyMipLevels { .. }
2946 | Self::TooManyArrayLayers { .. }
2947 | Self::InvalidArrayLayerCount { .. }
2948 | Self::InvalidAspect { .. }
2949 | Self::FormatReinterpretation { .. }
2950 | Self::DestroyedResource(_)
2951 | Self::TextureViewFormatNotRenderable(_)
2952 | Self::TextureViewFormatNotStorage(_)
2953 | Self::InvalidTextureViewUsage { .. }
2954 | Self::InvalidTransientTextureViewUsage { .. }
2955 | Self::MissingFeatures(_)
2956 | Self::WrongAspectReinterpretation { .. }
2957 | Self::MultiplanarFullTexture(_) => ErrorType::Validation,
2958 }
2959 }
2960}
2961
2962crate::impl_resource_type!(TextureView);
2963crate::impl_labeled!(TextureView);
2964crate::impl_parent_device!(TextureView);
2965crate::impl_storage_item!(TextureView);
2966
2967pub type ExternalTextureDescriptor<'a> = wgt::ExternalTextureDescriptor<Label<'a>>;
2968
2969#[derive(Debug)]
2970pub(crate) struct ExternalTextureState {
2971 pub(crate) params: Arc<Buffer>,
2974}
2975
2976#[derive(Debug)]
2977pub struct ExternalTexture {
2978 pub(crate) state: ResourceState<ExternalTextureState>,
2979 pub(crate) device: Arc<Device>,
2980 pub(crate) planes: arrayvec::ArrayVec<Arc<TextureView>, 3>,
2982 pub(crate) label: String,
2984 pub(crate) tracking_data: TrackingData,
2985}
2986
2987impl Drop for ExternalTexture {
2988 #[allow(trivial_casts)]
2989 fn drop(&mut self) {
2990 profiling::scope!("ExternalTexture::drop");
2991 api_log!("ExternalTexture::drop {:?}", self as *const _);
2992
2993 resource_log!("Destroy raw {}", self.error_ident());
2994 #[cfg(feature = "trace")]
2995 if let Some(t) = self.device.trace.lock().as_mut() {
2996 t.add(trace::Action::DropExternalTexture(unsafe {
2997 trace::to_trace(self)
2998 }));
2999 }
3000 }
3001}
3002
3003impl ExternalTexture {
3004 pub(crate) fn state(&self) -> Result<&ExternalTextureState, InvalidResourceError> {
3005 match &self.state {
3006 ResourceState::Valid(state) => Ok(state),
3007 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3008 }
3009 }
3010
3011 pub fn destroy(self: &Arc<Self>) {
3012 profiling::scope!("ExternalTexture::destroy");
3013 api_log!("ExternalTexture::destroy {:?}", Arc::as_ptr(self));
3014
3015 #[cfg(feature = "trace")]
3016 if let Some(trace) = self.device.trace.lock().as_mut() {
3017 use crate::device::trace::IntoTrace as _;
3018
3019 trace.add(trace::Action::DestroyExternalTexture(self.to_trace()));
3020 }
3021 if let Ok(state) = self.state() {
3022 state.params.destroy();
3023 }
3024 }
3025
3026 pub fn invalid(device: Arc<Device>, desc: &ExternalTextureDescriptor) -> Arc<Self> {
3027 Arc::new(ExternalTexture {
3028 state: ResourceState::Invalid,
3029 planes: arrayvec::ArrayVec::new(),
3030 label: desc.label.to_string(),
3031 tracking_data: TrackingData::new(device.tracker_indices.external_textures.clone()),
3032 device,
3033 })
3034 }
3035}
3036
3037#[derive(Clone, Debug, Error)]
3038#[non_exhaustive]
3039pub enum CreateExternalTextureError {
3040 #[error(transparent)]
3041 Device(#[from] DeviceError),
3042 #[error(transparent)]
3043 MissingFeatures(#[from] MissingFeatures),
3044 #[error(transparent)]
3045 InvalidResource(#[from] InvalidResourceError),
3046 #[error(transparent)]
3047 CreateBuffer(#[from] CreateBufferError),
3048 #[error(transparent)]
3049 QueueWrite(#[from] queue::QueueWriteError),
3050 #[error("External texture format {format:?} expects {expected} planes, but given {provided}")]
3051 IncorrectPlaneCount {
3052 format: wgt::ExternalTextureFormat,
3053 expected: usize,
3054 provided: usize,
3055 },
3056 #[error("External texture planes cannot be multisampled, but given view with samples = {0}")]
3057 InvalidPlaneMultisample(u32),
3058 #[error("External texture planes expect a filterable float sample type, but given view with format {format:?} (sample type {sample_type:?})")]
3059 InvalidPlaneSampleType {
3060 format: wgt::TextureFormat,
3061 sample_type: wgt::TextureSampleType,
3062 },
3063 #[error("External texture planes expect 2D dimension, but given view with dimension = {0:?}")]
3064 InvalidPlaneDimension(wgt::TextureViewDimension),
3065 #[error(transparent)]
3066 MissingTextureUsage(#[from] MissingTextureUsageError),
3067 #[error("External texture format {format:?} plane {plane} expects format with {expected} components but given view with format {provided:?} ({} components)",
3068 provided.components())]
3069 InvalidPlaneFormat {
3070 format: wgt::ExternalTextureFormat,
3071 plane: usize,
3072 expected: u8,
3073 provided: wgt::TextureFormat,
3074 },
3075}
3076
3077impl WebGpuError for CreateExternalTextureError {
3078 fn webgpu_error_type(&self) -> ErrorType {
3079 match self {
3080 CreateExternalTextureError::Device(e) => e.webgpu_error_type(),
3081 CreateExternalTextureError::MissingFeatures(e) => e.webgpu_error_type(),
3082 CreateExternalTextureError::InvalidResource(e) => e.webgpu_error_type(),
3083 CreateExternalTextureError::CreateBuffer(e) => e.webgpu_error_type(),
3084 CreateExternalTextureError::QueueWrite(e) => e.webgpu_error_type(),
3085 CreateExternalTextureError::MissingTextureUsage(e) => e.webgpu_error_type(),
3086 CreateExternalTextureError::IncorrectPlaneCount { .. }
3087 | CreateExternalTextureError::InvalidPlaneMultisample(_)
3088 | CreateExternalTextureError::InvalidPlaneSampleType { .. }
3089 | CreateExternalTextureError::InvalidPlaneDimension(_)
3090 | CreateExternalTextureError::InvalidPlaneFormat { .. } => ErrorType::Validation,
3091 }
3092 }
3093}
3094
3095crate::impl_resource_type!(ExternalTexture);
3096crate::impl_labeled!(ExternalTexture);
3097crate::impl_parent_device!(ExternalTexture);
3098crate::impl_storage_item!(ExternalTexture);
3099crate::impl_trackable!(ExternalTexture);
3100
3101#[derive(Clone, Debug, PartialEq)]
3103#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
3104pub struct SamplerDescriptor<'a> {
3105 pub label: Label<'a>,
3109 pub address_modes: [wgt::AddressMode; 3],
3111 pub mag_filter: wgt::FilterMode,
3113 pub min_filter: wgt::FilterMode,
3115 pub mipmap_filter: wgt::MipmapFilterMode,
3117 pub lod_min_clamp: f32,
3119 pub lod_max_clamp: f32,
3121 pub compare: Option<wgt::CompareFunction>,
3123 pub anisotropy_clamp: u16,
3125 pub border_color: Option<wgt::SamplerBorderColor>,
3128}
3129
3130#[derive(Debug)]
3131pub struct Sampler {
3132 pub(crate) raw: ResourceState<Box<dyn hal::DynSampler>>,
3133 pub(crate) device: Arc<Device>,
3134 pub(crate) label: String,
3136 pub(crate) tracking_data: TrackingData,
3137 pub(crate) comparison: bool,
3139 pub(crate) filtering: bool,
3141}
3142
3143impl Drop for Sampler {
3144 #[allow(trivial_casts)]
3145 fn drop(&mut self) {
3146 profiling::scope!("Sampler::drop");
3147 api_log!("Sampler::drop {:?}", self as *const _);
3148 #[cfg(feature = "trace")]
3149 if let Some(t) = self.device.trace.lock().as_mut() {
3150 t.add(trace::Action::DropSampler(unsafe { trace::to_trace(self) }));
3151 }
3152 resource_log!("Destroy raw {}", self.error_ident());
3153 if let ResourceState::Valid(raw) = mem::replace(&mut self.raw, ResourceState::Invalid) {
3154 unsafe {
3155 self.device.raw().destroy_sampler(raw);
3156 }
3157 }
3158 }
3159}
3160
3161impl Sampler {
3162 pub(crate) fn raw(&self) -> Result<&dyn hal::DynSampler, InvalidResourceError> {
3163 self.raw
3164 .as_ref()
3165 .valid()
3166 .map(|raw| raw.as_ref())
3167 .ok_or_else(|| InvalidResourceError(self.error_ident()))
3168 }
3169
3170 pub(crate) fn invalid(device: Arc<Device>, desc: &SamplerDescriptor) -> Arc<Self> {
3171 Arc::new(Sampler {
3172 raw: ResourceState::Invalid,
3173 label: desc.label.to_string(),
3174 tracking_data: TrackingData::new(device.tracker_indices.samplers.clone()),
3175 device,
3176 comparison: desc.compare.is_some(),
3177 filtering: desc.mag_filter == wgt::FilterMode::Linear
3178 || desc.min_filter == wgt::FilterMode::Linear
3179 || desc.mipmap_filter == wgt::MipmapFilterMode::Linear,
3180 })
3181 }
3182}
3183
3184#[derive(Copy, Clone)]
3185pub enum SamplerFilterErrorType {
3186 MagFilter,
3187 MinFilter,
3188 MipmapFilter,
3189}
3190
3191impl fmt::Debug for SamplerFilterErrorType {
3192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3193 match *self {
3194 SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
3195 SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
3196 SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
3197 }
3198 }
3199}
3200
3201#[derive(Clone, Debug, Error)]
3202#[non_exhaustive]
3203pub enum CreateSamplerError {
3204 #[error(transparent)]
3205 Device(#[from] DeviceError),
3206 #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
3207 InvalidLodMinClamp(f32),
3208 #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
3209 InvalidLodMaxClamp {
3210 lod_min_clamp: f32,
3211 lod_max_clamp: f32,
3212 },
3213 #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
3214 InvalidAnisotropy(u16),
3215 #[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.")]
3216 InvalidFilterModeWithAnisotropy {
3217 filter_type: SamplerFilterErrorType,
3218 filter_mode: wgt::FilterMode,
3219 anisotropic_clamp: u16,
3220 },
3221 #[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.")]
3222 InvalidMipmapFilterModeWithAnisotropy {
3223 filter_type: SamplerFilterErrorType,
3224 filter_mode: wgt::MipmapFilterMode,
3225 anisotropic_clamp: u16,
3226 },
3227 #[error(transparent)]
3228 MissingFeatures(#[from] MissingFeatures),
3229}
3230
3231crate::impl_resource_type!(Sampler);
3232crate::impl_labeled!(Sampler);
3233crate::impl_parent_device!(Sampler);
3234crate::impl_storage_item!(Sampler);
3235crate::impl_trackable!(Sampler);
3236
3237impl WebGpuError for CreateSamplerError {
3238 fn webgpu_error_type(&self) -> ErrorType {
3239 match self {
3240 Self::Device(e) => e.webgpu_error_type(),
3241 Self::MissingFeatures(e) => e.webgpu_error_type(),
3242
3243 Self::InvalidLodMinClamp(_)
3244 | Self::InvalidLodMaxClamp { .. }
3245 | Self::InvalidAnisotropy(_)
3246 | Self::InvalidFilterModeWithAnisotropy { .. }
3247 | Self::InvalidMipmapFilterModeWithAnisotropy { .. } => ErrorType::Validation,
3248 }
3249 }
3250}
3251
3252#[derive(Clone, Debug, Error)]
3253#[non_exhaustive]
3254pub enum CreateQuerySetError {
3255 #[error(transparent)]
3256 Device(#[from] DeviceError),
3257 #[error("QuerySets cannot be made with zero queries")]
3258 ZeroCount,
3259 #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
3260 TooManyQueries { count: u32, maximum: u32 },
3261 #[error(transparent)]
3262 MissingFeatures(#[from] MissingFeatures),
3263}
3264
3265impl WebGpuError for CreateQuerySetError {
3266 fn webgpu_error_type(&self) -> ErrorType {
3267 match self {
3268 Self::Device(e) => e.webgpu_error_type(),
3269 Self::MissingFeatures(e) => e.webgpu_error_type(),
3270
3271 Self::TooManyQueries { .. } | Self::ZeroCount => ErrorType::Validation,
3272 }
3273 }
3274}
3275
3276pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
3277
3278#[derive(Debug)]
3279pub(crate) struct QuerySetState {
3280 pub(crate) raw: Snatchable<Box<dyn hal::DynQuerySet>>,
3281}
3282
3283#[derive(Debug)]
3284pub struct QuerySet {
3285 pub(crate) state: ResourceState<QuerySetState>,
3286 pub(crate) device: Arc<Device>,
3287 pub(crate) tracking_data: TrackingData,
3288 pub(crate) desc: wgt::QuerySetDescriptor<String>,
3289 pub(crate) initialized_slots: Mutex<bit_vec::BitVec>,
3290}
3291
3292impl RawResourceAccess for QuerySet {
3293 type DynResource = dyn hal::DynQuerySet;
3294
3295 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
3296 self.state().ok()?.raw.get(guard).map(|b| b.as_ref())
3297 }
3298}
3299
3300impl QuerySet {
3301 pub(crate) fn state(&self) -> Result<&QuerySetState, InvalidResourceError> {
3302 match &self.state {
3303 ResourceState::Valid(state) => Ok(state),
3304 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3305 }
3306 }
3307
3308 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
3309 self.state().map(|_| ())
3310 }
3311
3312 pub fn invalid(device: Arc<Device>, desc: &QuerySetDescriptor) -> Arc<Self> {
3313 Arc::new(QuerySet {
3314 state: ResourceState::Invalid,
3315 tracking_data: TrackingData::new(device.tracker_indices.query_sets.clone()),
3316 desc: desc.map_label(|l| l.to_string()),
3317 initialized_slots: Mutex::new(
3318 rank::QUERY_SET_INITIALIZED_SLOTS,
3319 bit_vec::BitVec::new(),
3320 ),
3321 device,
3322 })
3323 }
3324
3325 pub fn destroy(self: &Arc<Self>) {
3326 let device = &self.device;
3327
3328 profiling::scope!("QuerySet::destroy");
3329 api_log!("QuerySet::destroy {:?}", Arc::as_ptr(self));
3330
3331 #[cfg(feature = "trace")]
3332 if let Some(trace) = device.trace.lock().as_mut() {
3333 use crate::device::trace::IntoTrace as _;
3334
3335 trace.add(trace::Action::DestroyQuerySet(self.to_trace()));
3336 };
3337
3338 let ResourceState::Valid(state) = &self.state else {
3339 return;
3340 };
3341
3342 let temp = {
3343 let mut snatch_guard = self.device.snatchable_lock.write();
3344
3345 let raw = match state.raw.snatch(&mut snatch_guard) {
3346 Some(raw) => raw,
3347 None => {
3348 return;
3350 }
3351 };
3352
3353 drop(snatch_guard);
3354
3355 queue::TempResource::DestroyedQuerySet(DestroyedQuerySet {
3356 raw: ManuallyDrop::new(raw),
3357 device: Arc::clone(&self.device),
3358 label: self.label().to_owned(),
3359 })
3360 };
3361
3362 let Some(queue) = device.get_queue() else {
3363 return;
3364 };
3365
3366 let mut life_lock = queue.lock_life();
3367 let last_submit_index = life_lock.get_query_set_latest_submission_index(self);
3368 if let Some(last_submit_index) = last_submit_index {
3369 life_lock.schedule_resource_destruction(temp, last_submit_index);
3370 }
3371 }
3372
3373 pub fn descriptor(&self) -> &wgt::QuerySetDescriptor<String> {
3374 &self.desc
3375 }
3376}
3377
3378impl Drop for QuerySet {
3379 #[allow(trivial_casts)]
3380 fn drop(&mut self) {
3381 profiling::scope!("QuerySet::drop");
3382 api_log!("QuerySet::drop {:?}", self as *const _);
3383 resource_log!("Destroy raw {}", self.error_ident());
3384 #[cfg(feature = "trace")]
3385 if let Some(trace) = self.device.trace.lock().as_mut() {
3386 use crate::device::trace::to_trace;
3387
3388 trace.add(trace::Action::DropQuerySet(unsafe { to_trace(self) }));
3389 }
3390 let ResourceState::Valid(state) = &mut self.state else {
3391 return;
3392 };
3393 if let Some(raw) = state.raw.take() {
3394 unsafe {
3396 self.device.raw().destroy_query_set(raw);
3397 }
3398 }
3399 }
3400}
3401
3402crate::impl_resource_type!(QuerySet);
3403impl Labeled for QuerySet {
3404 fn label(&self) -> &str {
3405 &self.desc.label
3406 }
3407}
3408crate::impl_parent_device!(QuerySet);
3409crate::impl_storage_item!(QuerySet);
3410crate::impl_trackable!(QuerySet);
3411
3412#[derive(Debug)]
3414pub struct DestroyedQuerySet {
3415 raw: ManuallyDrop<Box<dyn hal::DynQuerySet>>,
3416 device: Arc<Device>,
3417 label: String,
3418}
3419
3420impl DestroyedQuerySet {
3421 pub fn label(&self) -> &dyn fmt::Debug {
3422 &self.label
3423 }
3424}
3425
3426impl Drop for DestroyedQuerySet {
3427 fn drop(&mut self) {
3428 resource_log!("Destroy raw QuerySet (destroyed) {:?}", self.label());
3429 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
3431 unsafe {
3432 hal::DynDevice::destroy_query_set(self.device.raw(), raw);
3433 }
3434 }
3435}
3436
3437pub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor<Label<'a>>;
3438pub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor<Label<'a>>;
3439
3440pub type BlasPrepareCompactResult = Result<(), BlasPrepareCompactError>;
3441
3442#[cfg(send_sync)]
3443pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + Send + 'static>;
3444#[cfg(not(send_sync))]
3445pub type BlasCompactCallback = Box<dyn FnOnce(BlasPrepareCompactResult) + 'static>;
3446
3447pub(crate) struct BlasPendingCompact {
3448 pub(crate) op: Option<BlasCompactCallback>,
3449 pub(crate) _parent_blas: Arc<Blas>,
3451}
3452
3453impl fmt::Debug for BlasPendingCompact {
3454 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3455 f.debug_struct("BlasPendingCompact")
3456 .field("op", &())
3457 .field("_parent_blas", &self._parent_blas)
3458 .finish()
3459 }
3460}
3461
3462#[derive(Debug)]
3463pub(crate) enum BlasCompactState {
3464 Compacted,
3466 Waiting(BlasPendingCompact),
3468 Ready { size: wgt::BufferAddress },
3470 Idle,
3472}
3473
3474#[cfg(send_sync)]
3475unsafe impl Send for BlasCompactState {}
3476#[cfg(send_sync)]
3477unsafe impl Sync for BlasCompactState {}
3478
3479#[derive(Debug)]
3480pub(crate) struct BlasState {
3481 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
3482}
3483
3484#[derive(Debug)]
3485pub struct Blas {
3486 pub(crate) state: ResourceState<BlasState>,
3487 pub(crate) device: Arc<Device>,
3488 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
3489 pub(crate) sizes: wgt::BlasGeometrySizeDescriptors,
3490 pub(crate) flags: wgt::AccelerationStructureFlags,
3491 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
3492 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
3493 pub(crate) handle: u64,
3494 pub(crate) label: String,
3496 pub(crate) tracking_data: TrackingData,
3497 pub(crate) compaction_buffer: Option<ManuallyDrop<Box<dyn hal::DynBuffer>>>,
3498 pub(crate) compacted_state: Mutex<BlasCompactState>,
3499}
3500
3501impl Drop for Blas {
3502 #[allow(trivial_casts)]
3503 fn drop(&mut self) {
3504 profiling::scope!("Blas::drop");
3505 api_log!("Blas::drop {:?}", self as *const _);
3506 #[cfg(feature = "trace")]
3507 if let Some(t) = self.device.trace.lock().as_mut() {
3508 use crate::device::trace::{to_trace, Action};
3509 t.add(Action::DropBlas(unsafe { to_trace(self) }));
3510 }
3511 resource_log!("Destroy raw {}", self.error_ident());
3512 if let ResourceState::Valid(state) = &mut self.state {
3514 if let Some(raw) = state.raw.take() {
3515 unsafe {
3516 self.device.raw().destroy_acceleration_structure(raw);
3517 }
3518 }
3519 }
3520 if let Some(mut raw) = self.compaction_buffer.take() {
3521 unsafe {
3522 self.device
3523 .raw()
3524 .destroy_buffer(ManuallyDrop::take(&mut raw))
3525 }
3526 }
3527 }
3528}
3529
3530impl RawResourceAccess for Blas {
3531 type DynResource = dyn hal::DynAccelerationStructure;
3532
3533 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
3534 self.state().ok()?.raw.get(guard).map(|it| it.as_ref())
3535 }
3536}
3537
3538impl Blas {
3539 pub(crate) fn state(&self) -> Result<&BlasState, InvalidResourceError> {
3540 match &self.state {
3541 ResourceState::Valid(state) => Ok(state),
3542 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3543 }
3544 }
3545
3546 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
3547 self.state().map(|_| ())
3548 }
3549
3550 pub(crate) fn invalid(device: Arc<Device>, desc: &BlasDescriptor) -> Arc<Self> {
3551 Arc::new(Blas {
3552 state: ResourceState::Invalid,
3553 size_info: hal::AccelerationStructureBuildSizes {
3554 acceleration_structure_size: 0,
3555 update_scratch_size: 0,
3556 build_scratch_size: 0,
3557 },
3558 sizes: wgt::BlasGeometrySizeDescriptors::Triangles {
3559 descriptors: Vec::new(),
3560 },
3561 flags: desc.flags,
3562 update_mode: desc.update_mode,
3563 built_index: RwLock::new(rank::BLAS_BUILT_INDEX, None),
3564 handle: 0,
3565 label: desc.label.to_string(),
3566 tracking_data: TrackingData::new(device.tracker_indices.blas_s.clone()),
3567 device,
3568 compaction_buffer: None,
3569 compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Idle),
3570 })
3571 }
3572
3573 pub fn handle(&self) -> Option<u64> {
3574 Some(self.handle)
3575 }
3576
3577 pub fn ready_for_compaction(self: &Arc<Self>) -> Result<bool, InvalidResourceError> {
3578 profiling::scope!("Blas::prepare_compact_async");
3579 api_log!("Blas::prepare_compact_async {:?}", Arc::as_ptr(self));
3580
3581 self.check_is_valid()?;
3582 let state = self.compacted_state.lock();
3583 Ok(matches!(*state, BlasCompactState::Ready { .. }))
3584 }
3585
3586 pub fn prepare_compact_async(
3587 self: &Arc<Self>,
3588 callback: Option<BlasCompactCallback>,
3589 ) -> Result<SubmissionIndex, BlasPrepareCompactError> {
3590 profiling::scope!("Blas::prepare_compact_async");
3591 api_log!("Blas::prepare_compact_async {:?}", Arc::as_ptr(self));
3592
3593 let compact_result = self.prepare_compact_async_inner(callback);
3594
3595 match compact_result {
3596 Ok(submission_index) => Ok(submission_index),
3597 Err((mut callback, err)) => {
3598 if let Some(callback) = callback.take() {
3599 callback(Err(err.clone()));
3600 }
3601 Err(err)
3602 }
3603 }
3604 }
3605
3606 fn prepare_compact_async_inner(
3607 self: &Arc<Self>,
3608 op: Option<BlasCompactCallback>,
3609 ) -> Result<SubmissionIndex, (Option<BlasCompactCallback>, BlasPrepareCompactError)> {
3610 let device = &self.device;
3611 if let Err(e) = device.check_is_valid() {
3612 return Err((op, e.into()));
3613 }
3614
3615 if let Err(e) = self.check_is_valid() {
3616 return Err((op, e.into()));
3617 }
3618
3619 if self.built_index.read().is_none() {
3620 return Err((op, BlasPrepareCompactError::NotBuilt));
3621 }
3622
3623 if !self
3624 .flags
3625 .contains(wgt::AccelerationStructureFlags::ALLOW_COMPACTION)
3626 {
3627 return Err((op, BlasPrepareCompactError::CompactionUnsupported));
3628 }
3629
3630 let mut state = self.compacted_state.lock();
3631 *state = match *state {
3632 BlasCompactState::Compacted => {
3633 return Err((op, BlasPrepareCompactError::DoubleCompaction))
3634 }
3635 BlasCompactState::Waiting(_) => {
3636 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
3637 }
3638 BlasCompactState::Ready { .. } => {
3639 return Err((op, BlasPrepareCompactError::CompactionPreparingAlready))
3640 }
3641 BlasCompactState::Idle => BlasCompactState::Waiting(BlasPendingCompact {
3642 op,
3643 _parent_blas: self.clone(),
3644 }),
3645 };
3646
3647 let submit_index = if let Some(queue) = device.get_queue() {
3648 drop(state);
3649 queue.lock_life().prepare_compact(self).unwrap_or(0) } else {
3651 let (mut callback, status) = self.read_back_compact_size(state).unwrap();
3653 if let Some(callback) = callback.take() {
3654 callback(status);
3655 }
3656 0
3657 };
3658
3659 Ok(submit_index)
3660 }
3661
3662 #[must_use]
3664 pub(crate) fn read_back_compact_size(
3665 &self,
3666 mut state: MutexGuard<'_, BlasCompactState>,
3667 ) -> Option<BlasCompactReadyPendingClosure> {
3668 let pending_compact = match mem::replace(&mut *state, BlasCompactState::Idle) {
3669 BlasCompactState::Waiting(pending_mapping) => pending_mapping,
3670 BlasCompactState::Idle => return None,
3672 BlasCompactState::Ready { .. } => {
3673 unreachable!("This should be validated out by `prepare_for_compaction`")
3674 }
3675 _ => panic!("No pending mapping."),
3676 };
3677 let status = {
3678 let compaction_buffer = self.compaction_buffer.as_ref().unwrap().as_ref();
3679 unsafe {
3680 let map_res = self.device.raw().map_buffer(
3681 compaction_buffer,
3682 0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress,
3683 );
3684 match map_res {
3685 Ok(mapping) => {
3686 if !mapping.is_coherent {
3687 #[expect(clippy::single_range_in_vec_init, reason = "intentional")]
3688 self.device.raw().invalidate_mapped_ranges(
3689 compaction_buffer,
3690 &[0..size_of::<wgpu_types::BufferAddress>() as wgt::BufferAddress],
3691 );
3692 }
3693 let size = core::ptr::read_unaligned(
3694 mapping.ptr.as_ptr().cast::<wgt::BufferAddress>(),
3695 );
3696 self.device.raw().unmap_buffer(compaction_buffer);
3697 if self.size_info.acceleration_structure_size != 0 {
3698 debug_assert_ne!(size, 0);
3699 }
3700 *state = BlasCompactState::Ready { size };
3701 Ok(())
3702 }
3703 Err(err) => Err(BlasPrepareCompactError::from(DeviceError::from_hal(err))),
3704 }
3705 }
3706 };
3707 Some((pending_compact.op, status))
3708 }
3709}
3710
3711crate::impl_resource_type!(Blas);
3712crate::impl_labeled!(Blas);
3713crate::impl_parent_device!(Blas);
3714crate::impl_storage_item!(Blas);
3715crate::impl_trackable!(Blas);
3716
3717#[derive(Debug)]
3718pub(crate) struct TlasState {
3719 pub(crate) raw: Snatchable<Box<dyn hal::DynAccelerationStructure>>,
3720 pub(crate) instance_buffer: Box<dyn hal::DynBuffer>,
3721}
3722
3723#[derive(Debug)]
3724pub struct Tlas {
3725 pub(crate) state: ResourceState<TlasState>,
3726 pub(crate) device: Arc<Device>,
3727 pub(crate) size_info: hal::AccelerationStructureBuildSizes,
3728 pub(crate) max_instance_count: u32,
3729 pub(crate) flags: wgt::AccelerationStructureFlags,
3730 pub(crate) update_mode: wgt::AccelerationStructureUpdateMode,
3731 pub(crate) built_index: RwLock<Option<NonZeroU64>>,
3732 pub(crate) dependencies: RwLock<Vec<Arc<Blas>>>,
3733 pub(crate) label: String,
3735 pub(crate) tracking_data: TrackingData,
3736}
3737
3738impl Drop for Tlas {
3739 #[allow(trivial_casts)]
3740 fn drop(&mut self) {
3741 profiling::scope!("Tlas::drop");
3742 api_log!("Tlas::drop {:?}", self as *const _);
3743
3744 #[cfg(feature = "trace")]
3745 if let Some(t) = self.device.trace.lock().as_mut() {
3746 use crate::device::trace::{to_trace, Action};
3747 t.add(Action::DropTlas(unsafe { to_trace(self) }));
3748 }
3749
3750 resource_log!("Destroy raw {}", self.error_ident());
3751 let ResourceState::Valid(mut state) = mem::replace(&mut self.state, ResourceState::Invalid)
3752 else {
3753 return;
3754 };
3755 if let Some(structure) = state.raw.take() {
3756 unsafe { self.device.raw().destroy_acceleration_structure(structure) };
3757 }
3758 unsafe { self.device.raw().destroy_buffer(state.instance_buffer) };
3759 }
3760}
3761
3762impl Tlas {
3763 pub(crate) fn state(&self) -> Result<&TlasState, InvalidResourceError> {
3764 match &self.state {
3765 ResourceState::Valid(state) => Ok(state),
3766 ResourceState::Invalid => Err(InvalidResourceError(self.error_ident())),
3767 }
3768 }
3769
3770 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
3771 self.state().map(|_| ())
3772 }
3773
3774 pub(crate) fn invalid(device: Arc<Device>, desc: &TlasDescriptor) -> Arc<Self> {
3775 Arc::new(Self {
3776 state: ResourceState::Invalid,
3777 label: desc.label.to_string(),
3778 tracking_data: TrackingData::new(device.tracker_indices.tlas_s.clone()),
3779 size_info: hal::AccelerationStructureBuildSizes {
3780 acceleration_structure_size: 0,
3781 update_scratch_size: 0,
3782 build_scratch_size: 0,
3783 },
3784 max_instance_count: desc.max_instances,
3785 flags: desc.flags,
3786 update_mode: desc.update_mode,
3787 built_index: RwLock::new(rank::TLAS_BUILT_INDEX, None),
3788 dependencies: RwLock::new(rank::TLAS_DEPENDENCIES, Vec::new()),
3789 device,
3790 })
3791 }
3792}
3793
3794impl RawResourceAccess for Tlas {
3795 type DynResource = dyn hal::DynAccelerationStructure;
3796
3797 fn raw<'a>(&'a self, guard: &'a SnatchGuard) -> Option<&'a Self::DynResource> {
3798 self.state().ok()?.raw.get(guard).map(|raw| raw.as_ref())
3799 }
3800}
3801
3802crate::impl_resource_type!(Tlas);
3803crate::impl_labeled!(Tlas);
3804crate::impl_parent_device!(Tlas);
3805crate::impl_storage_item!(Tlas);
3806crate::impl_trackable!(Tlas);