1use crate::{
2 device::{DeviceError, HostMap, MissingDownlevelFlags, MissingFeatures},
3 global::Global,
4 hal_api::HalApi,
5 hub::Token,
6 id::{AdapterId, DeviceId, SurfaceId, TextureId, Valid},
7 identity::GlobalIdentityHandlerFactory,
8 init_tracker::{BufferInitTracker, TextureInitTracker},
9 track::TextureSelector,
10 validation::MissingBufferUsageError,
11 Label, LifeGuard, RefCount, Stored,
12};
13
14use smallvec::SmallVec;
15use thiserror::Error;
16
17use std::{borrow::Borrow, ops::Range, ptr::NonNull};
18
19pub trait Resource {
20 const TYPE: &'static str;
21 fn life_guard(&self) -> &LifeGuard;
22 fn label(&self) -> &str {
23 #[cfg(debug_assertions)]
24 return &self.life_guard().label;
25 #[cfg(not(debug_assertions))]
26 return "";
27 }
28}
29
30#[repr(C)]
34#[derive(Debug)]
35pub enum BufferMapAsyncStatus {
36 Success,
40 AlreadyMapped,
44 MapAlreadyPending,
46 Error,
48 Aborted,
51 ContextLost,
53 Invalid,
55 InvalidRange,
57 InvalidAlignment,
59 InvalidUsageFlags,
61}
62
63pub(crate) enum BufferMapState<A: hal::Api> {
64 Init {
66 ptr: NonNull<u8>,
67 stage_buffer: A::Buffer,
68 needs_flush: bool,
69 },
70 Waiting(BufferPendingMapping),
72 Active {
74 ptr: NonNull<u8>,
75 range: hal::MemoryRange,
76 host: HostMap,
77 },
78 Idle,
80}
81
82#[cfg(any(
83 not(target_arch = "wasm32"),
84 all(
85 feature = "fragile-send-sync-non-atomic-wasm",
86 not(target_feature = "atomics")
87 )
88))]
89unsafe impl<A: hal::Api> Send for BufferMapState<A> {}
90#[cfg(any(
91 not(target_arch = "wasm32"),
92 all(
93 feature = "fragile-send-sync-non-atomic-wasm",
94 not(target_feature = "atomics")
95 )
96))]
97unsafe impl<A: hal::Api> Sync for BufferMapState<A> {}
98
99#[repr(C)]
100pub struct BufferMapCallbackC {
101 pub callback: unsafe extern "C" fn(status: BufferMapAsyncStatus, user_data: *mut u8),
102 pub user_data: *mut u8,
103}
104
105#[cfg(any(
106 not(target_arch = "wasm32"),
107 all(
108 feature = "fragile-send-sync-non-atomic-wasm",
109 not(target_feature = "atomics")
110 )
111))]
112unsafe impl Send for BufferMapCallbackC {}
113
114pub struct BufferMapCallback {
115 inner: Option<BufferMapCallbackInner>,
118}
119
120#[cfg(any(
121 not(target_arch = "wasm32"),
122 all(
123 feature = "fragile-send-sync-non-atomic-wasm",
124 not(target_feature = "atomics")
125 )
126))]
127type BufferMapCallbackCallback = Box<dyn FnOnce(BufferAccessResult) + Send + 'static>;
128#[cfg(not(any(
129 not(target_arch = "wasm32"),
130 all(
131 feature = "fragile-send-sync-non-atomic-wasm",
132 not(target_feature = "atomics")
133 )
134)))]
135type BufferMapCallbackCallback = Box<dyn FnOnce(BufferAccessResult) + 'static>;
136
137enum BufferMapCallbackInner {
138 Rust { callback: BufferMapCallbackCallback },
139 C { inner: BufferMapCallbackC },
140}
141
142impl BufferMapCallback {
143 pub fn from_rust(callback: BufferMapCallbackCallback) -> Self {
144 Self {
145 inner: Some(BufferMapCallbackInner::Rust { callback }),
146 }
147 }
148
149 pub unsafe fn from_c(inner: BufferMapCallbackC) -> Self {
157 Self {
158 inner: Some(BufferMapCallbackInner::C { inner }),
159 }
160 }
161
162 pub(crate) fn call(mut self, result: BufferAccessResult) {
163 match self.inner.take() {
164 Some(BufferMapCallbackInner::Rust { callback }) => {
165 callback(result);
166 }
167 Some(BufferMapCallbackInner::C { inner }) => unsafe {
169 let status = match result {
170 Ok(()) => BufferMapAsyncStatus::Success,
171 Err(BufferAccessError::Device(_)) => BufferMapAsyncStatus::ContextLost,
172 Err(BufferAccessError::Invalid) | Err(BufferAccessError::Destroyed) => {
173 BufferMapAsyncStatus::Invalid
174 }
175 Err(BufferAccessError::AlreadyMapped) => BufferMapAsyncStatus::AlreadyMapped,
176 Err(BufferAccessError::MapAlreadyPending) => {
177 BufferMapAsyncStatus::MapAlreadyPending
178 }
179 Err(BufferAccessError::MissingBufferUsage(_)) => {
180 BufferMapAsyncStatus::InvalidUsageFlags
181 }
182 Err(BufferAccessError::UnalignedRange)
183 | Err(BufferAccessError::UnalignedRangeSize { .. })
184 | Err(BufferAccessError::UnalignedOffset { .. }) => {
185 BufferMapAsyncStatus::InvalidAlignment
186 }
187 Err(BufferAccessError::OutOfBoundsUnderrun { .. })
188 | Err(BufferAccessError::OutOfBoundsOverrun { .. })
189 | Err(BufferAccessError::NegativeRange { .. }) => {
190 BufferMapAsyncStatus::InvalidRange
191 }
192 Err(_) => BufferMapAsyncStatus::Error,
193 };
194
195 (inner.callback)(status, inner.user_data);
196 },
197 None => {
198 panic!("Map callback invoked twice");
199 }
200 }
201 }
202}
203
204impl Drop for BufferMapCallback {
205 fn drop(&mut self) {
206 if self.inner.is_some() {
207 panic!("Map callback was leaked");
208 }
209 }
210}
211
212pub struct BufferMapOperation {
213 pub host: HostMap,
214 pub callback: BufferMapCallback,
215}
216
217#[derive(Clone, Debug, Error)]
218#[non_exhaustive]
219pub enum BufferAccessError {
220 #[error(transparent)]
221 Device(#[from] DeviceError),
222 #[error("Buffer map failed")]
223 Failed,
224 #[error("Buffer is invalid")]
225 Invalid,
226 #[error("Buffer is destroyed")]
227 Destroyed,
228 #[error("Buffer is already mapped")]
229 AlreadyMapped,
230 #[error("Buffer map is pending")]
231 MapAlreadyPending,
232 #[error(transparent)]
233 MissingBufferUsage(#[from] MissingBufferUsageError),
234 #[error("Buffer is not mapped")]
235 NotMapped,
236 #[error(
237 "Buffer map range must start aligned to `MAP_ALIGNMENT` and end to `COPY_BUFFER_ALIGNMENT`"
238 )]
239 UnalignedRange,
240 #[error("Buffer offset invalid: offset {offset} must be multiple of 8")]
241 UnalignedOffset { offset: wgt::BufferAddress },
242 #[error("Buffer range size invalid: range_size {range_size} must be multiple of 4")]
243 UnalignedRangeSize { range_size: wgt::BufferAddress },
244 #[error("Buffer access out of bounds: index {index} would underrun the buffer (limit: {min})")]
245 OutOfBoundsUnderrun {
246 index: wgt::BufferAddress,
247 min: wgt::BufferAddress,
248 },
249 #[error(
250 "Buffer access out of bounds: last index {index} would overrun the buffer (limit: {max})"
251 )]
252 OutOfBoundsOverrun {
253 index: wgt::BufferAddress,
254 max: wgt::BufferAddress,
255 },
256 #[error("Buffer map range start {start} is greater than end {end}")]
257 NegativeRange {
258 start: wgt::BufferAddress,
259 end: wgt::BufferAddress,
260 },
261 #[error("Buffer map aborted")]
262 MapAborted,
263}
264
265pub type BufferAccessResult = Result<(), BufferAccessError>;
266pub(crate) struct BufferPendingMapping {
267 pub range: Range<wgt::BufferAddress>,
268 pub op: BufferMapOperation,
269 pub _parent_ref_count: RefCount,
271}
272
273pub type BufferDescriptor<'a> = wgt::BufferDescriptor<Label<'a>>;
274
275pub struct Buffer<A: hal::Api> {
276 pub(crate) raw: Option<A::Buffer>,
277 pub(crate) device_id: Stored<DeviceId>,
278 pub(crate) usage: wgt::BufferUsages,
279 pub(crate) size: wgt::BufferAddress,
280 pub(crate) initialization_status: BufferInitTracker,
281 pub(crate) sync_mapped_writes: Option<hal::MemoryRange>,
282 pub(crate) life_guard: LifeGuard,
283 pub(crate) map_state: BufferMapState<A>,
284}
285
286#[derive(Clone, Debug, Error)]
287#[non_exhaustive]
288pub enum CreateBufferError {
289 #[error(transparent)]
290 Device(#[from] DeviceError),
291 #[error("Failed to map buffer while creating: {0}")]
292 AccessError(#[from] BufferAccessError),
293 #[error("Buffers that are mapped at creation have to be aligned to `COPY_BUFFER_ALIGNMENT`")]
294 UnalignedSize,
295 #[error("Invalid usage flags {0:?}")]
296 InvalidUsage(wgt::BufferUsages),
297 #[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
298 UsageMismatch(wgt::BufferUsages),
299 #[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
300 MaxBufferSize { requested: u64, maximum: u64 },
301 #[error(transparent)]
302 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
303}
304
305impl<A: hal::Api> Resource for Buffer<A> {
306 const TYPE: &'static str = "Buffer";
307
308 fn life_guard(&self) -> &LifeGuard {
309 &self.life_guard
310 }
311}
312
313pub struct StagingBuffer<A: hal::Api> {
333 pub(crate) raw: A::Buffer,
334 pub(crate) size: wgt::BufferAddress,
335 pub(crate) is_coherent: bool,
336}
337
338impl<A: hal::Api> Resource for StagingBuffer<A> {
339 const TYPE: &'static str = "StagingBuffer";
340
341 fn life_guard(&self) -> &LifeGuard {
342 unreachable!()
343 }
344
345 fn label(&self) -> &str {
346 "<StagingBuffer>"
347 }
348}
349
350pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;
351
352#[derive(Debug)]
353pub(crate) enum TextureInner<A: hal::Api> {
354 Native {
355 raw: Option<A::Texture>,
356 },
357 Surface {
358 raw: A::SurfaceTexture,
359 parent_id: Valid<SurfaceId>,
360 has_work: bool,
361 },
362}
363
364impl<A: hal::Api> TextureInner<A> {
365 pub fn as_raw(&self) -> Option<&A::Texture> {
366 match *self {
367 Self::Native { raw: Some(ref tex) } => Some(tex),
368 Self::Native { raw: None } => None,
369 Self::Surface { ref raw, .. } => Some(raw.borrow()),
370 }
371 }
372}
373
374#[derive(Debug)]
375pub enum TextureClearMode<A: hal::Api> {
376 BufferCopy,
377 RenderPass {
379 clear_views: SmallVec<[A::TextureView; 1]>,
380 is_color: bool,
381 },
382 None,
385}
386
387#[derive(Debug)]
388pub struct Texture<A: hal::Api> {
389 pub(crate) inner: TextureInner<A>,
390 pub(crate) device_id: Stored<DeviceId>,
391 pub(crate) desc: wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
392 pub(crate) hal_usage: hal::TextureUses,
393 pub(crate) format_features: wgt::TextureFormatFeatures,
394 pub(crate) initialization_status: TextureInitTracker,
395 pub(crate) full_range: TextureSelector,
396 pub(crate) life_guard: LifeGuard,
397 pub(crate) clear_mode: TextureClearMode<A>,
398}
399
400impl<A: hal::Api> Texture<A> {
401 pub(crate) fn get_clear_view(&self, mip_level: u32, depth_or_layer: u32) -> &A::TextureView {
402 match self.clear_mode {
403 TextureClearMode::BufferCopy => {
404 panic!("Given texture is cleared with buffer copies, not render passes")
405 }
406 TextureClearMode::None => {
407 panic!("Given texture can't be cleared")
408 }
409 TextureClearMode::RenderPass {
410 ref clear_views, ..
411 } => {
412 let index = if self.desc.dimension == wgt::TextureDimension::D3 {
413 (0..mip_level).fold(0, |acc, mip| {
414 acc + (self.desc.size.depth_or_array_layers >> mip).max(1)
415 })
416 } else {
417 mip_level * self.desc.size.depth_or_array_layers
418 } + depth_or_layer;
419 &clear_views[index as usize]
420 }
421 }
422 }
423}
424
425impl<G: GlobalIdentityHandlerFactory> Global<G> {
426 pub unsafe fn texture_as_hal<A: HalApi, F: FnOnce(Option<&A::Texture>)>(
430 &self,
431 id: TextureId,
432 hal_texture_callback: F,
433 ) {
434 profiling::scope!("Texture::as_hal");
435
436 let hub = A::hub(self);
437 let mut token = Token::root();
438 let (guard, _) = hub.textures.read(&mut token);
439 let texture = guard.try_get(id).ok().flatten();
440 let hal_texture = texture.and_then(|tex| tex.inner.as_raw());
441
442 hal_texture_callback(hal_texture);
443 }
444
445 pub unsafe fn adapter_as_hal<A: HalApi, F: FnOnce(Option<&A::Adapter>) -> R, R>(
449 &self,
450 id: AdapterId,
451 hal_adapter_callback: F,
452 ) -> R {
453 profiling::scope!("Adapter::as_hal");
454
455 let hub = A::hub(self);
456 let mut token = Token::root();
457
458 let (guard, _) = hub.adapters.read(&mut token);
459 let adapter = guard.try_get(id).ok().flatten();
460 let hal_adapter = adapter.map(|adapter| &adapter.raw.adapter);
461
462 hal_adapter_callback(hal_adapter)
463 }
464
465 pub unsafe fn device_as_hal<A: HalApi, F: FnOnce(Option<&A::Device>) -> R, R>(
469 &self,
470 id: DeviceId,
471 hal_device_callback: F,
472 ) -> R {
473 profiling::scope!("Device::as_hal");
474
475 let hub = A::hub(self);
476 let mut token = Token::root();
477 let (guard, _) = hub.devices.read(&mut token);
478 let device = guard.try_get(id).ok().flatten();
479 let hal_device = device.map(|device| &device.raw);
480
481 hal_device_callback(hal_device)
482 }
483
484 pub unsafe fn surface_as_hal_mut<A: HalApi, F: FnOnce(Option<&mut A::Surface>) -> R, R>(
487 &self,
488 id: SurfaceId,
489 hal_surface_callback: F,
490 ) -> R {
491 profiling::scope!("Surface::as_hal_mut");
492
493 let mut token = Token::root();
494 let (mut guard, _) = self.surfaces.write(&mut token);
495 let surface = guard.get_mut(id).ok();
496 let hal_surface = surface
497 .and_then(|surface| A::get_surface_mut(surface))
498 .map(|surface| &mut surface.raw);
499
500 hal_surface_callback(hal_surface)
501 }
502}
503
504#[derive(Clone, Copy, Debug)]
505pub enum TextureErrorDimension {
506 X,
507 Y,
508 Z,
509}
510
511#[derive(Clone, Debug, Error)]
512#[non_exhaustive]
513pub enum TextureDimensionError {
514 #[error("Dimension {0:?} is zero")]
515 Zero(TextureErrorDimension),
516 #[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
517 LimitExceeded {
518 dim: TextureErrorDimension,
519 given: u32,
520 limit: u32,
521 },
522 #[error("Sample count {0} is invalid")]
523 InvalidSampleCount(u32),
524 #[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
525 NotMultipleOfBlockWidth {
526 width: u32,
527 block_width: u32,
528 format: wgt::TextureFormat,
529 },
530 #[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
531 NotMultipleOfBlockHeight {
532 height: u32,
533 block_height: u32,
534 format: wgt::TextureFormat,
535 },
536 #[error("Multisampled texture depth or array layers must be 1, got {0}")]
537 MultisampledDepthOrArrayLayer(u32),
538}
539
540#[derive(Clone, Debug, Error)]
541#[non_exhaustive]
542pub enum CreateTextureError {
543 #[error(transparent)]
544 Device(#[from] DeviceError),
545 #[error("Invalid usage flags {0:?}")]
546 InvalidUsage(wgt::TextureUsages),
547 #[error(transparent)]
548 InvalidDimension(#[from] TextureDimensionError),
549 #[error("Depth texture ({1:?}) can't be created as {0:?}")]
550 InvalidDepthDimension(wgt::TextureDimension, wgt::TextureFormat),
551 #[error("Compressed texture ({1:?}) can't be created as {0:?}")]
552 InvalidCompressedDimension(wgt::TextureDimension, wgt::TextureFormat),
553 #[error(
554 "Texture descriptor mip level count {requested} is invalid, maximum allowed is {maximum}"
555 )]
556 InvalidMipLevelCount { requested: u32, maximum: u32 },
557 #[error(
558 "Texture usages {0:?} are not allowed on a texture of type {1:?}{}",
559 if *.2 { " due to downlevel restrictions" } else { "" }
560 )]
561 InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool),
562 #[error("The view format {0:?} is not compatible with texture format {1:?}, only changing srgb-ness is allowed.")]
563 InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat),
564 #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
565 InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
566 #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
567 InvalidMultisampledStorageBinding,
568 #[error("Format {0:?} does not support multisampling")]
569 InvalidMultisampledFormat(wgt::TextureFormat),
570 #[error("Sample count {0} is not supported by format {1:?} on this device. It may be supported by your adapter through the TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES feature.")]
571 InvalidSampleCount(u32, wgt::TextureFormat),
572 #[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
573 MultisampledNotRenderAttachment,
574 #[error("Texture format {0:?} can't be used due to missing features")]
575 MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
576 #[error(transparent)]
577 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
578}
579
580impl<A: hal::Api> Resource for Texture<A> {
581 const TYPE: &'static str = "Texture";
582
583 fn life_guard(&self) -> &LifeGuard {
584 &self.life_guard
585 }
586}
587
588impl<A: hal::Api> Borrow<TextureSelector> for Texture<A> {
589 fn borrow(&self) -> &TextureSelector {
590 &self.full_range
591 }
592}
593
594#[derive(Clone, Debug, Default, Eq, PartialEq)]
596#[cfg_attr(feature = "trace", derive(serde::Serialize))]
597#[cfg_attr(feature = "replay", derive(serde::Deserialize), serde(default))]
598pub struct TextureViewDescriptor<'a> {
599 pub label: Label<'a>,
603 pub format: Option<wgt::TextureFormat>,
608 pub dimension: Option<wgt::TextureViewDimension>,
614 pub range: wgt::ImageSubresourceRange,
616}
617
618#[derive(Debug)]
619pub(crate) struct HalTextureViewDescriptor {
620 pub format: wgt::TextureFormat,
621 pub dimension: wgt::TextureViewDimension,
622 pub range: wgt::ImageSubresourceRange,
623}
624
625impl HalTextureViewDescriptor {
626 pub fn aspects(&self) -> hal::FormatAspects {
627 hal::FormatAspects::new(self.format, self.range.aspect)
628 }
629}
630
631#[derive(Debug, Copy, Clone, Error)]
632pub enum TextureViewNotRenderableReason {
633 #[error("The texture this view references doesn't include the RENDER_ATTACHMENT usage. Provided usages: {0:?}")]
634 Usage(wgt::TextureUsages),
635 #[error("The dimension of this texture view is not 2D. View dimension: {0:?}")]
636 Dimension(wgt::TextureViewDimension),
637 #[error("This texture view has more than one mipmap level. View mipmap levels: {0:?}")]
638 MipLevelCount(u32),
639 #[error("This texture view has more than one array layer. View array layers: {0:?}")]
640 ArrayLayerCount(u32),
641 #[error(
642 "The aspects of this texture view are a subset of the aspects in the original texture. Aspects: {0:?}"
643 )]
644 Aspects(hal::FormatAspects),
645}
646
647#[derive(Debug)]
648pub struct TextureView<A: hal::Api> {
649 pub(crate) raw: A::TextureView,
650 pub(crate) parent_id: Stored<TextureId>,
653 pub(crate) device_id: Stored<DeviceId>,
654 pub(crate) desc: HalTextureViewDescriptor,
656 pub(crate) format_features: wgt::TextureFormatFeatures,
657 pub(crate) render_extent: Result<wgt::Extent3d, TextureViewNotRenderableReason>,
659 pub(crate) samples: u32,
660 pub(crate) selector: TextureSelector,
661 pub(crate) life_guard: LifeGuard,
662}
663
664#[derive(Clone, Debug, Error)]
665#[non_exhaustive]
666pub enum CreateTextureViewError {
667 #[error("Parent texture is invalid or destroyed")]
668 InvalidTexture,
669 #[error("Not enough memory left")]
670 OutOfMemory,
671 #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")]
672 InvalidTextureViewDimension {
673 view: wgt::TextureViewDimension,
674 texture: wgt::TextureDimension,
675 },
676 #[error("Invalid texture view dimension `{0:?}` of a multisampled texture")]
677 InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension),
678 #[error("Invalid texture depth `{depth}` for texture view of dimension `Cubemap`. Cubemap views must use images of size 6.")]
679 InvalidCubemapTextureDepth { depth: u32 },
680 #[error("Invalid texture depth `{depth}` for texture view of dimension `CubemapArray`. Cubemap views must use images with sizes which are a multiple of 6.")]
681 InvalidCubemapArrayTextureDepth { depth: u32 },
682 #[error("Source texture width and height must be equal for a texture view of dimension `Cube`/`CubeArray`")]
683 InvalidCubeTextureViewSize,
684 #[error("Mip level count is 0")]
685 ZeroMipLevelCount,
686 #[error("Array layer count is 0")]
687 ZeroArrayLayerCount,
688 #[error(
689 "TextureView mip level count + base mip level {requested} must be <= Texture mip level count {total}"
690 )]
691 TooManyMipLevels { requested: u32, total: u32 },
692 #[error("TextureView array layer count + base array layer {requested} must be <= Texture depth/array layer count {total}")]
693 TooManyArrayLayers { requested: u32, total: u32 },
694 #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")]
695 InvalidArrayLayerCount {
696 requested: u32,
697 dim: wgt::TextureViewDimension,
698 },
699 #[error("Aspect {requested_aspect:?} is not in the source texture format {texture_format:?}")]
700 InvalidAspect {
701 texture_format: wgt::TextureFormat,
702 requested_aspect: wgt::TextureAspect,
703 },
704 #[error("Unable to view texture {texture:?} as {view:?}")]
705 FormatReinterpretation {
706 texture: wgt::TextureFormat,
707 view: wgt::TextureFormat,
708 },
709}
710
711#[derive(Clone, Debug, Error)]
712#[non_exhaustive]
713pub enum TextureViewDestroyError {}
714
715impl<A: hal::Api> Resource for TextureView<A> {
716 const TYPE: &'static str = "TextureView";
717
718 fn life_guard(&self) -> &LifeGuard {
719 &self.life_guard
720 }
721}
722
723#[derive(Clone, Debug, PartialEq)]
725#[cfg_attr(feature = "trace", derive(serde::Serialize))]
726#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
727pub struct SamplerDescriptor<'a> {
728 pub label: Label<'a>,
732 pub address_modes: [wgt::AddressMode; 3],
734 pub mag_filter: wgt::FilterMode,
736 pub min_filter: wgt::FilterMode,
738 pub mipmap_filter: wgt::FilterMode,
740 pub lod_min_clamp: f32,
742 pub lod_max_clamp: f32,
744 pub compare: Option<wgt::CompareFunction>,
746 pub anisotropy_clamp: u16,
748 pub border_color: Option<wgt::SamplerBorderColor>,
751}
752
753#[derive(Debug)]
754pub struct Sampler<A: hal::Api> {
755 pub(crate) raw: A::Sampler,
756 pub(crate) device_id: Stored<DeviceId>,
757 pub(crate) life_guard: LifeGuard,
758 pub(crate) comparison: bool,
760 pub(crate) filtering: bool,
762}
763
764#[derive(Copy, Clone)]
765pub enum SamplerFilterErrorType {
766 MagFilter,
767 MinFilter,
768 MipmapFilter,
769}
770
771impl std::fmt::Debug for SamplerFilterErrorType {
772 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
773 match *self {
774 SamplerFilterErrorType::MagFilter => write!(f, "magFilter"),
775 SamplerFilterErrorType::MinFilter => write!(f, "minFilter"),
776 SamplerFilterErrorType::MipmapFilter => write!(f, "mipmapFilter"),
777 }
778 }
779}
780
781#[derive(Clone, Debug, Error)]
782#[non_exhaustive]
783pub enum CreateSamplerError {
784 #[error(transparent)]
785 Device(#[from] DeviceError),
786 #[error("Invalid lodMinClamp: {0}. Must be greater or equal to 0.0")]
787 InvalidLodMinClamp(f32),
788 #[error("Invalid lodMaxClamp: {lod_max_clamp}. Must be greater or equal to lodMinClamp (which is {lod_min_clamp}).")]
789 InvalidLodMaxClamp {
790 lod_min_clamp: f32,
791 lod_max_clamp: f32,
792 },
793 #[error("Invalid anisotropic clamp: {0}. Must be at least 1.")]
794 InvalidAnisotropy(u16),
795 #[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.")]
796 InvalidFilterModeWithAnisotropy {
797 filter_type: SamplerFilterErrorType,
798 filter_mode: wgt::FilterMode,
799 anisotropic_clamp: u16,
800 },
801 #[error("Cannot create any more samplers")]
802 TooManyObjects,
803 #[error(transparent)]
805 MissingFeatures(#[from] MissingFeatures),
806}
807
808impl<A: hal::Api> Resource for Sampler<A> {
809 const TYPE: &'static str = "Sampler";
810
811 fn life_guard(&self) -> &LifeGuard {
812 &self.life_guard
813 }
814}
815
816#[derive(Clone, Debug, Error)]
817#[non_exhaustive]
818pub enum CreateQuerySetError {
819 #[error(transparent)]
820 Device(#[from] DeviceError),
821 #[error("QuerySets cannot be made with zero queries")]
822 ZeroCount,
823 #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")]
824 TooManyQueries { count: u32, maximum: u32 },
825 #[error(transparent)]
826 MissingFeatures(#[from] MissingFeatures),
827}
828
829pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor<Label<'a>>;
830
831#[derive(Debug)]
832pub struct QuerySet<A: hal::Api> {
833 pub(crate) raw: A::QuerySet,
834 pub(crate) device_id: Stored<DeviceId>,
835 pub(crate) life_guard: LifeGuard,
836 pub(crate) desc: wgt::QuerySetDescriptor<()>,
837}
838
839impl<A: hal::Api> Resource for QuerySet<A> {
840 const TYPE: &'static str = "QuerySet";
841
842 fn life_guard(&self) -> &LifeGuard {
843 &self.life_guard
844 }
845}
846
847#[derive(Clone, Debug, Error)]
848#[non_exhaustive]
849pub enum DestroyError {
850 #[error("Resource is invalid")]
851 Invalid,
852 #[error("Resource is already destroyed")]
853 AlreadyDestroyed,
854}