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