1 // Copyright (c) 2016 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 //! How to retrieve data from a sampled image within a shader.
11 //!
12 //! When you retrieve data from a sampled image, you have to pass the coordinates of the pixel you
13 //! want to retrieve. The implementation then performs various calculations, and these operations
14 //! are what the `Sampler` object describes.
15 //!
16 //! # Level of detail
17 //!
18 //! The level-of-detail (LOD) is a floating-point value that expresses a sense of how much texture
19 //! detail is visible to the viewer. It is used in texture filtering and mipmapping calculations.
20 //!
21 //! LOD is calculated through one or more steps to produce a final value. The base LOD is
22 //! determined by one of two ways:
23 //! - Implicitly, by letting Vulkan calculate it automatically, based on factors such as number of
24 //!   pixels, distance and viewing angle. This is done using an `ImplicitLod` SPIR-V sampling
25 //!   operation, which corresponds to the `texture*` functions not suffixed with `Lod` in GLSL.
26 //! - Explicitly, specified in the shader. This is done using an `ExplicitLod` SPIR-V sampling
27 //!   operation, which corresponds to the `texture*Lod` functions in GLSL.
28 //!
29 //! It is possible to provide a *bias* to the base LOD value, which is simply added to it.
30 //! An LOD bias can be provided both in the sampler object and as part of the sampling operation in
31 //! the shader, and are combined by addition to produce the final bias value, which is then added to
32 //! the base LOD.
33 //!
34 //! Once LOD bias has been applied, the resulting value may be *clamped* to a minimum and maximum
35 //! value to provide the final LOD. A maximum may be specified by the sampler, while a minimum
36 //! can be specified by the sampler or the shader sampling operation.
37 //!
38 //! # Texel filtering
39 //!
40 //! Texel filtering operations determine how the color value to be sampled from each mipmap is
41 //! calculated. The filtering mode can be set independently for different signs of the LOD value:
42 //! - Negative or zero: **magnification**. The rendered object is closer to the viewer, and each
43 //!   pixel in the texture corresponds to exactly one or more than one framebuffer pixel.
44 //! - Positive: **minification**. The rendered object is further from the viewer, and each pixel in
45 //!   the texture corresponds to less than one framebuffer pixel.
46 
47 pub mod ycbcr;
48 
49 use self::ycbcr::SamplerYcbcrConversion;
50 use crate::{
51     device::{Device, DeviceOwned},
52     format::FormatFeatures,
53     image::{view::ImageViewType, ImageAspects, ImageViewAbstract},
54     macros::{impl_id_counter, vulkan_enum},
55     pipeline::graphics::depth_stencil::CompareOp,
56     shader::ShaderScalarType,
57     OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject,
58 };
59 use std::{
60     error::Error,
61     fmt::{Display, Error as FmtError, Formatter},
62     mem::MaybeUninit,
63     num::NonZeroU64,
64     ops::RangeInclusive,
65     ptr,
66     sync::Arc,
67 };
68 
69 /// Describes how to retrieve data from a sampled image within a shader.
70 ///
71 /// # Examples
72 ///
73 /// A simple sampler for most usages:
74 ///
75 /// ```
76 /// use vulkano::sampler::{Sampler, SamplerCreateInfo};
77 ///
78 /// # let device: std::sync::Arc<vulkano::device::Device> = return;
79 /// let _sampler = Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear_no_mipmap());
80 /// ```
81 ///
82 /// More detailed sampler creation:
83 ///
84 /// ```
85 /// use vulkano::sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo};
86 ///
87 /// # let device: std::sync::Arc<vulkano::device::Device> = return;
88 /// let _sampler = Sampler::new(device.clone(), SamplerCreateInfo {
89 ///     mag_filter: Filter::Linear,
90 ///     min_filter: Filter::Linear,
91 ///     address_mode: [SamplerAddressMode::Repeat; 3],
92 ///     mip_lod_bias: 1.0,
93 ///     lod: 0.0..=100.0,
94 ///     ..Default::default()
95 /// })
96 /// .unwrap();
97 /// ```
98 #[derive(Debug)]
99 pub struct Sampler {
100     handle: ash::vk::Sampler,
101     device: Arc<Device>,
102     id: NonZeroU64,
103 
104     address_mode: [SamplerAddressMode; 3],
105     anisotropy: Option<f32>,
106     border_color: Option<BorderColor>,
107     compare: Option<CompareOp>,
108     lod: RangeInclusive<f32>,
109     mag_filter: Filter,
110     min_filter: Filter,
111     mip_lod_bias: f32,
112     mipmap_mode: SamplerMipmapMode,
113     reduction_mode: SamplerReductionMode,
114     sampler_ycbcr_conversion: Option<Arc<SamplerYcbcrConversion>>,
115     unnormalized_coordinates: bool,
116 }
117 
118 impl Sampler {
119     /// Creates a new `Sampler`.
120     ///
121     /// # Panics
122     ///
123     /// - Panics if `create_info.anisotropy` is `Some` and contains a value less than 1.0.
124     /// - Panics if `create_info.lod` is empty.
new( device: Arc<Device>, create_info: SamplerCreateInfo, ) -> Result<Arc<Sampler>, SamplerCreationError>125     pub fn new(
126         device: Arc<Device>,
127         create_info: SamplerCreateInfo,
128     ) -> Result<Arc<Sampler>, SamplerCreationError> {
129         let SamplerCreateInfo {
130             mag_filter,
131             min_filter,
132             mipmap_mode,
133             address_mode,
134             mip_lod_bias,
135             anisotropy,
136             compare,
137             lod,
138             border_color,
139             unnormalized_coordinates,
140             reduction_mode,
141             sampler_ycbcr_conversion,
142             _ne: _,
143         } = create_info;
144 
145         for filter in [mag_filter, min_filter] {
146             // VUID-VkSamplerCreateInfo-magFilter-parameter
147             // VUID-VkSamplerCreateInfo-minFilter-parameter
148             filter.validate_device(&device)?;
149         }
150 
151         // VUID-VkSamplerCreateInfo-mipmapMode-parameter
152         mipmap_mode.validate_device(&device)?;
153 
154         for mode in address_mode {
155             // VUID-VkSamplerCreateInfo-addressModeU-parameter
156             // VUID-VkSamplerCreateInfo-addressModeV-parameter
157             // VUID-VkSamplerCreateInfo-addressModeW-parameter
158             mode.validate_device(&device)?;
159 
160             if mode == SamplerAddressMode::ClampToBorder {
161                 // VUID-VkSamplerCreateInfo-addressModeU-01078
162                 border_color.validate_device(&device)?;
163             }
164         }
165 
166         if address_mode.contains(&SamplerAddressMode::MirrorClampToEdge) {
167             if !device.enabled_features().sampler_mirror_clamp_to_edge
168                 && !device.enabled_extensions().khr_sampler_mirror_clamp_to_edge
169             {
170                 return Err(SamplerCreationError::RequirementNotMet {
171                     required_for: "`create_info.address_mode` contains \
172                         `SamplerAddressMode::MirrorClampToEdge`",
173                     requires_one_of: RequiresOneOf {
174                         features: &["sampler_mirror_clamp_to_edge"],
175                         device_extensions: &["khr_sampler_mirror_clamp_to_edge"],
176                         ..Default::default()
177                     },
178                 });
179             }
180         }
181 
182         {
183             assert!(!lod.is_empty());
184             let limit = device.physical_device().properties().max_sampler_lod_bias;
185             if mip_lod_bias.abs() > limit {
186                 return Err(SamplerCreationError::MaxSamplerLodBiasExceeded {
187                     requested: mip_lod_bias,
188                     maximum: limit,
189                 });
190             }
191         }
192 
193         // VUID-VkSamplerCreateInfo-samplerMipLodBias-04467
194         if device.enabled_extensions().khr_portability_subset
195             && !device.enabled_features().sampler_mip_lod_bias
196             && mip_lod_bias != 0.0
197         {
198             return Err(SamplerCreationError::RequirementNotMet {
199                 required_for: "this device is a portability subset device, and \
200                     `create_info.mip_lod_bias` is not zero",
201                 requires_one_of: RequiresOneOf {
202                     features: &["sampler_mip_lod_bias"],
203                     ..Default::default()
204                 },
205             });
206         }
207 
208         let (anisotropy_enable, max_anisotropy) = if let Some(max_anisotropy) = anisotropy {
209             assert!(max_anisotropy >= 1.0);
210 
211             if !device.enabled_features().sampler_anisotropy {
212                 return Err(SamplerCreationError::RequirementNotMet {
213                     required_for: "`create_info.anisotropy` is `Some`",
214                     requires_one_of: RequiresOneOf {
215                         features: &["sampler_anisotropy"],
216                         ..Default::default()
217                     },
218                 });
219             }
220 
221             let limit = device.physical_device().properties().max_sampler_anisotropy;
222             if max_anisotropy > limit {
223                 return Err(SamplerCreationError::MaxSamplerAnisotropyExceeded {
224                     requested: max_anisotropy,
225                     maximum: limit,
226                 });
227             }
228 
229             if [mag_filter, min_filter]
230                 .into_iter()
231                 .any(|filter| filter == Filter::Cubic)
232             {
233                 return Err(SamplerCreationError::AnisotropyInvalidFilter {
234                     mag_filter,
235                     min_filter,
236                 });
237             }
238 
239             (ash::vk::TRUE, max_anisotropy)
240         } else {
241             (ash::vk::FALSE, 1.0)
242         };
243 
244         let (compare_enable, compare_op) = if let Some(compare_op) = compare {
245             // VUID-VkSamplerCreateInfo-compareEnable-01080
246             compare_op.validate_device(&device)?;
247 
248             if reduction_mode != SamplerReductionMode::WeightedAverage {
249                 return Err(SamplerCreationError::CompareInvalidReductionMode { reduction_mode });
250             }
251 
252             (ash::vk::TRUE, compare_op)
253         } else {
254             (ash::vk::FALSE, CompareOp::Never)
255         };
256 
257         if unnormalized_coordinates {
258             if min_filter != mag_filter {
259                 return Err(
260                     SamplerCreationError::UnnormalizedCoordinatesFiltersNotEqual {
261                         mag_filter,
262                         min_filter,
263                     },
264                 );
265             }
266 
267             if mipmap_mode != SamplerMipmapMode::Nearest {
268                 return Err(
269                     SamplerCreationError::UnnormalizedCoordinatesInvalidMipmapMode { mipmap_mode },
270                 );
271             }
272 
273             if lod != (0.0..=0.0) {
274                 return Err(SamplerCreationError::UnnormalizedCoordinatesNonzeroLod { lod });
275             }
276 
277             if address_mode[0..2].iter().any(|mode| {
278                 !matches!(
279                     mode,
280                     SamplerAddressMode::ClampToEdge | SamplerAddressMode::ClampToBorder
281                 )
282             }) {
283                 return Err(
284                     SamplerCreationError::UnnormalizedCoordinatesInvalidAddressMode {
285                         address_mode: [address_mode[0], address_mode[1]],
286                     },
287                 );
288             }
289 
290             if anisotropy.is_some() {
291                 return Err(SamplerCreationError::UnnormalizedCoordinatesAnisotropyEnabled);
292             }
293 
294             if compare.is_some() {
295                 return Err(SamplerCreationError::UnnormalizedCoordinatesCompareEnabled);
296             }
297         }
298 
299         let mut sampler_reduction_mode_create_info =
300             if reduction_mode != SamplerReductionMode::WeightedAverage {
301                 if !(device.enabled_features().sampler_filter_minmax
302                     || device.enabled_extensions().ext_sampler_filter_minmax)
303                 {
304                     return Err(SamplerCreationError::RequirementNotMet {
305                         required_for: "`create_info.reduction_mode` is not \
306                             `SamplerReductionMode::WeightedAverage`",
307                         requires_one_of: RequiresOneOf {
308                             features: &["sampler_filter_minmax"],
309                             device_extensions: &["ext_sampler_filter_minmax"],
310                             ..Default::default()
311                         },
312                     });
313                 }
314 
315                 // VUID-VkSamplerReductionModeCreateInfo-reductionMode-parameter
316                 reduction_mode.validate_device(&device)?;
317 
318                 Some(ash::vk::SamplerReductionModeCreateInfo {
319                     reduction_mode: reduction_mode.into(),
320                     ..Default::default()
321                 })
322             } else {
323                 None
324             };
325 
326         // Don't need to check features because you can't create a conversion object without the
327         // feature anyway.
328         let mut sampler_ycbcr_conversion_info = if let Some(sampler_ycbcr_conversion) =
329             &sampler_ycbcr_conversion
330         {
331             assert_eq!(&device, sampler_ycbcr_conversion.device());
332 
333             // Use unchecked, because all validation has been done by the SamplerYcbcrConversion.
334             let potential_format_features = unsafe {
335                 device
336                     .physical_device()
337                     .format_properties_unchecked(sampler_ycbcr_conversion.format().unwrap())
338                     .potential_format_features()
339             };
340 
341             // VUID-VkSamplerCreateInfo-minFilter-01645
342             if !potential_format_features.intersects(
343                 FormatFeatures::SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER,
344             ) && !(mag_filter == sampler_ycbcr_conversion.chroma_filter()
345                 && min_filter == sampler_ycbcr_conversion.chroma_filter())
346             {
347                 return Err(
348                     SamplerCreationError::SamplerYcbcrConversionChromaFilterMismatch {
349                         chroma_filter: sampler_ycbcr_conversion.chroma_filter(),
350                         mag_filter,
351                         min_filter,
352                     },
353                 );
354             }
355 
356             // VUID-VkSamplerCreateInfo-addressModeU-01646
357             if address_mode
358                 .into_iter()
359                 .any(|mode| !matches!(mode, SamplerAddressMode::ClampToEdge))
360             {
361                 return Err(
362                     SamplerCreationError::SamplerYcbcrConversionInvalidAddressMode { address_mode },
363                 );
364             }
365 
366             // VUID-VkSamplerCreateInfo-addressModeU-01646
367             if anisotropy.is_some() {
368                 return Err(SamplerCreationError::SamplerYcbcrConversionAnisotropyEnabled);
369             }
370 
371             // VUID-VkSamplerCreateInfo-addressModeU-01646
372             if unnormalized_coordinates {
373                 return Err(
374                     SamplerCreationError::SamplerYcbcrConversionUnnormalizedCoordinatesEnabled,
375                 );
376             }
377 
378             // VUID-VkSamplerCreateInfo-None-01647
379             if reduction_mode != SamplerReductionMode::WeightedAverage {
380                 return Err(
381                     SamplerCreationError::SamplerYcbcrConversionInvalidReductionMode {
382                         reduction_mode,
383                     },
384                 );
385             }
386 
387             Some(ash::vk::SamplerYcbcrConversionInfo {
388                 conversion: sampler_ycbcr_conversion.handle(),
389                 ..Default::default()
390             })
391         } else {
392             None
393         };
394 
395         let mut create_info = ash::vk::SamplerCreateInfo {
396             flags: ash::vk::SamplerCreateFlags::empty(),
397             mag_filter: mag_filter.into(),
398             min_filter: min_filter.into(),
399             mipmap_mode: mipmap_mode.into(),
400             address_mode_u: address_mode[0].into(),
401             address_mode_v: address_mode[1].into(),
402             address_mode_w: address_mode[2].into(),
403             mip_lod_bias,
404             anisotropy_enable,
405             max_anisotropy,
406             compare_enable,
407             compare_op: compare_op.into(),
408             min_lod: *lod.start(),
409             max_lod: *lod.end(),
410             border_color: border_color.into(),
411             unnormalized_coordinates: unnormalized_coordinates as ash::vk::Bool32,
412             ..Default::default()
413         };
414 
415         if let Some(sampler_reduction_mode_create_info) =
416             sampler_reduction_mode_create_info.as_mut()
417         {
418             sampler_reduction_mode_create_info.p_next = create_info.p_next;
419             create_info.p_next = sampler_reduction_mode_create_info as *const _ as *const _;
420         }
421 
422         if let Some(sampler_ycbcr_conversion_info) = sampler_ycbcr_conversion_info.as_mut() {
423             sampler_ycbcr_conversion_info.p_next = create_info.p_next;
424             create_info.p_next = sampler_ycbcr_conversion_info as *const _ as *const _;
425         }
426 
427         let handle = unsafe {
428             let fns = device.fns();
429             let mut output = MaybeUninit::uninit();
430             (fns.v1_0.create_sampler)(
431                 device.handle(),
432                 &create_info,
433                 ptr::null(),
434                 output.as_mut_ptr(),
435             )
436             .result()
437             .map_err(VulkanError::from)?;
438             output.assume_init()
439         };
440 
441         Ok(Arc::new(Sampler {
442             handle,
443             device,
444             id: Self::next_id(),
445             address_mode,
446             anisotropy,
447             border_color: address_mode
448                 .into_iter()
449                 .any(|mode| mode == SamplerAddressMode::ClampToBorder)
450                 .then_some(border_color),
451             compare,
452             lod,
453             mag_filter,
454             min_filter,
455             mip_lod_bias,
456             mipmap_mode,
457             reduction_mode,
458             sampler_ycbcr_conversion,
459             unnormalized_coordinates,
460         }))
461     }
462 
463     /// Creates a new `Sampler` from a raw object handle.
464     ///
465     /// # Safety
466     ///
467     /// - `handle` must be a valid Vulkan object handle created from `device`.
468     /// - `create_info` must match the info used to create the object.
469     #[inline]
from_handle( device: Arc<Device>, handle: ash::vk::Sampler, create_info: SamplerCreateInfo, ) -> Arc<Sampler>470     pub unsafe fn from_handle(
471         device: Arc<Device>,
472         handle: ash::vk::Sampler,
473         create_info: SamplerCreateInfo,
474     ) -> Arc<Sampler> {
475         let SamplerCreateInfo {
476             mag_filter,
477             min_filter,
478             mipmap_mode,
479             address_mode,
480             mip_lod_bias,
481             anisotropy,
482             compare,
483             lod,
484             border_color,
485             unnormalized_coordinates,
486             reduction_mode,
487             sampler_ycbcr_conversion,
488             _ne: _,
489         } = create_info;
490 
491         Arc::new(Sampler {
492             handle,
493             device,
494             id: Self::next_id(),
495             address_mode,
496             anisotropy,
497             border_color: address_mode
498                 .into_iter()
499                 .any(|mode| mode == SamplerAddressMode::ClampToBorder)
500                 .then_some(border_color),
501             compare,
502             lod,
503             mag_filter,
504             min_filter,
505             mip_lod_bias,
506             mipmap_mode,
507             reduction_mode,
508             sampler_ycbcr_conversion,
509             unnormalized_coordinates,
510         })
511     }
512 
513     /// Checks whether this sampler is compatible with `image_view`.
check_can_sample( &self, image_view: &(impl ImageViewAbstract + ?Sized), ) -> Result<(), SamplerImageViewIncompatibleError>514     pub fn check_can_sample(
515         &self,
516         image_view: &(impl ImageViewAbstract + ?Sized),
517     ) -> Result<(), SamplerImageViewIncompatibleError> {
518         /*
519             Note: Most of these checks come from the Instruction/Sampler/Image View Validation
520             section, and are not strictly VUIDs.
521             https://registry.khronos.org/vulkan/specs/1.2-extensions/html/chap16.html#textures-input-validation
522         */
523 
524         if self.compare.is_some() {
525             // VUID-vkCmdDispatch-None-06479
526             if !image_view
527                 .format_features()
528                 .intersects(FormatFeatures::SAMPLED_IMAGE_DEPTH_COMPARISON)
529             {
530                 return Err(SamplerImageViewIncompatibleError::DepthComparisonNotSupported);
531             }
532 
533             // The SPIR-V instruction is one of the OpImage*Dref* instructions, the image
534             // view format is one of the depth/stencil formats, and the image view aspect
535             // is not VK_IMAGE_ASPECT_DEPTH_BIT.
536             if !image_view
537                 .subresource_range()
538                 .aspects
539                 .intersects(ImageAspects::DEPTH)
540             {
541                 return Err(SamplerImageViewIncompatibleError::DepthComparisonWrongAspect);
542             }
543         } else {
544             if !image_view
545                 .format_features()
546                 .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR)
547             {
548                 // VUID-vkCmdDispatch-magFilter-04553
549                 if self.mag_filter == Filter::Linear || self.min_filter == Filter::Linear {
550                     return Err(SamplerImageViewIncompatibleError::FilterLinearNotSupported);
551                 }
552 
553                 // VUID-vkCmdDispatch-mipmapMode-04770
554                 if self.mipmap_mode == SamplerMipmapMode::Linear {
555                     return Err(SamplerImageViewIncompatibleError::MipmapModeLinearNotSupported);
556                 }
557             }
558         }
559 
560         if self.mag_filter == Filter::Cubic || self.min_filter == Filter::Cubic {
561             // VUID-vkCmdDispatch-None-02692
562             if !image_view
563                 .format_features()
564                 .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC)
565             {
566                 return Err(SamplerImageViewIncompatibleError::FilterCubicNotSupported);
567             }
568 
569             // VUID-vkCmdDispatch-filterCubic-02694
570             if !image_view.filter_cubic() {
571                 return Err(SamplerImageViewIncompatibleError::FilterCubicNotSupported);
572             }
573 
574             // VUID-vkCmdDispatch-filterCubicMinmax-02695
575             if matches!(
576                 self.reduction_mode,
577                 SamplerReductionMode::Min | SamplerReductionMode::Max
578             ) && !image_view.filter_cubic_minmax()
579             {
580                 return Err(SamplerImageViewIncompatibleError::FilterCubicMinmaxNotSupported);
581             }
582         }
583 
584         if let Some(border_color) = self.border_color {
585             let aspects = image_view.subresource_range().aspects;
586             let view_scalar_type = ShaderScalarType::from(
587                 if aspects.intersects(
588                     ImageAspects::COLOR
589                         | ImageAspects::PLANE_0
590                         | ImageAspects::PLANE_1
591                         | ImageAspects::PLANE_2,
592                 ) {
593                     image_view.format().unwrap().type_color().unwrap()
594                 } else if aspects.intersects(ImageAspects::DEPTH) {
595                     image_view.format().unwrap().type_depth().unwrap()
596                 } else if aspects.intersects(ImageAspects::STENCIL) {
597                     image_view.format().unwrap().type_stencil().unwrap()
598                 } else {
599                     // Per `ImageViewBuilder::aspects` and
600                     // VUID-VkDescriptorImageInfo-imageView-01976
601                     unreachable!()
602                 },
603             );
604 
605             match border_color {
606                 BorderColor::IntTransparentBlack
607                 | BorderColor::IntOpaqueBlack
608                 | BorderColor::IntOpaqueWhite => {
609                     // The sampler borderColor is an integer type and the image view
610                     // format is not one of the VkFormat integer types or a stencil
611                     // component of a depth/stencil format.
612                     if !matches!(
613                         view_scalar_type,
614                         ShaderScalarType::Sint | ShaderScalarType::Uint
615                     ) {
616                         return Err(
617                             SamplerImageViewIncompatibleError::BorderColorFormatNotCompatible,
618                         );
619                     }
620                 }
621                 BorderColor::FloatTransparentBlack
622                 | BorderColor::FloatOpaqueBlack
623                 | BorderColor::FloatOpaqueWhite => {
624                     // The sampler borderColor is a float type and the image view
625                     // format is not one of the VkFormat float types or a depth
626                     // component of a depth/stencil format.
627                     if !matches!(view_scalar_type, ShaderScalarType::Float) {
628                         return Err(
629                             SamplerImageViewIncompatibleError::BorderColorFormatNotCompatible,
630                         );
631                     }
632                 }
633             }
634 
635             // The sampler borderColor is one of the opaque black colors
636             // (VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK or VK_BORDER_COLOR_INT_OPAQUE_BLACK)
637             // and the image view VkComponentSwizzle for any of the VkComponentMapping
638             // components is not the identity swizzle, and
639             // VkPhysicalDeviceBorderColorSwizzleFeaturesEXT::borderColorSwizzleFromImage
640             // feature is not enabled, and
641             // VkSamplerBorderColorComponentMappingCreateInfoEXT is not specified.
642             if matches!(
643                 border_color,
644                 BorderColor::FloatOpaqueBlack | BorderColor::IntOpaqueBlack
645             ) && !image_view.component_mapping().is_identity()
646             {
647                 return Err(
648                     SamplerImageViewIncompatibleError::BorderColorOpaqueBlackNotIdentitySwizzled,
649                 );
650             }
651         }
652 
653         // The sampler unnormalizedCoordinates is VK_TRUE and any of the limitations of
654         // unnormalized coordinates are violated.
655         // https://registry.khronos.org/vulkan/specs/1.2-extensions/html/chap13.html#samplers-unnormalizedCoordinates
656         if self.unnormalized_coordinates {
657             // The viewType must be either VK_IMAGE_VIEW_TYPE_1D or
658             // VK_IMAGE_VIEW_TYPE_2D.
659             // VUID-vkCmdDispatch-None-02702
660             if !matches!(
661                 image_view.view_type(),
662                 ImageViewType::Dim1d | ImageViewType::Dim2d
663             ) {
664                 return Err(
665                     SamplerImageViewIncompatibleError::UnnormalizedCoordinatesViewTypeNotCompatible,
666                 );
667             }
668 
669             // The image view must have a single layer and a single mip level.
670             if image_view.subresource_range().mip_levels.end
671                 - image_view.subresource_range().mip_levels.start
672                 != 1
673             {
674                 return Err(
675                     SamplerImageViewIncompatibleError::UnnormalizedCoordinatesMultipleMipLevels,
676                 );
677             }
678         }
679 
680         Ok(())
681     }
682 
683     /// Returns the address modes for the u, v and w coordinates.
684     #[inline]
address_mode(&self) -> [SamplerAddressMode; 3]685     pub fn address_mode(&self) -> [SamplerAddressMode; 3] {
686         self.address_mode
687     }
688 
689     /// Returns the anisotropy mode.
690     #[inline]
anisotropy(&self) -> Option<f32>691     pub fn anisotropy(&self) -> Option<f32> {
692         self.anisotropy
693     }
694 
695     /// Returns the border color if one is used by this sampler.
696     #[inline]
border_color(&self) -> Option<BorderColor>697     pub fn border_color(&self) -> Option<BorderColor> {
698         self.border_color
699     }
700 
701     /// Returns the compare operation if the sampler is a compare-mode sampler.
702     #[inline]
compare(&self) -> Option<CompareOp>703     pub fn compare(&self) -> Option<CompareOp> {
704         self.compare
705     }
706 
707     /// Returns the LOD range.
708     #[inline]
lod(&self) -> RangeInclusive<f32>709     pub fn lod(&self) -> RangeInclusive<f32> {
710         self.lod.clone()
711     }
712 
713     /// Returns the magnification filter.
714     #[inline]
mag_filter(&self) -> Filter715     pub fn mag_filter(&self) -> Filter {
716         self.mag_filter
717     }
718 
719     /// Returns the minification filter.
720     #[inline]
min_filter(&self) -> Filter721     pub fn min_filter(&self) -> Filter {
722         self.min_filter
723     }
724 
725     /// Returns the mip LOD bias.
726     #[inline]
mip_lod_bias(&self) -> f32727     pub fn mip_lod_bias(&self) -> f32 {
728         self.mip_lod_bias
729     }
730 
731     /// Returns the mipmap mode.
732     #[inline]
mipmap_mode(&self) -> SamplerMipmapMode733     pub fn mipmap_mode(&self) -> SamplerMipmapMode {
734         self.mipmap_mode
735     }
736 
737     /// Returns the reduction mode.
738     #[inline]
reduction_mode(&self) -> SamplerReductionMode739     pub fn reduction_mode(&self) -> SamplerReductionMode {
740         self.reduction_mode
741     }
742 
743     /// Returns a reference to the sampler YCbCr conversion of this sampler, if any.
744     #[inline]
sampler_ycbcr_conversion(&self) -> Option<&Arc<SamplerYcbcrConversion>>745     pub fn sampler_ycbcr_conversion(&self) -> Option<&Arc<SamplerYcbcrConversion>> {
746         self.sampler_ycbcr_conversion.as_ref()
747     }
748 
749     /// Returns true if the sampler uses unnormalized coordinates.
750     #[inline]
unnormalized_coordinates(&self) -> bool751     pub fn unnormalized_coordinates(&self) -> bool {
752         self.unnormalized_coordinates
753     }
754 }
755 
756 impl Drop for Sampler {
757     #[inline]
drop(&mut self)758     fn drop(&mut self) {
759         unsafe {
760             let fns = self.device.fns();
761             (fns.v1_0.destroy_sampler)(self.device.handle(), self.handle, ptr::null());
762         }
763     }
764 }
765 
766 unsafe impl VulkanObject for Sampler {
767     type Handle = ash::vk::Sampler;
768 
769     #[inline]
handle(&self) -> Self::Handle770     fn handle(&self) -> Self::Handle {
771         self.handle
772     }
773 }
774 
775 unsafe impl DeviceOwned for Sampler {
776     #[inline]
device(&self) -> &Arc<Device>777     fn device(&self) -> &Arc<Device> {
778         &self.device
779     }
780 }
781 
782 impl_id_counter!(Sampler);
783 
784 /// Error that can happen when creating an instance.
785 #[derive(Clone, Debug, PartialEq)]
786 pub enum SamplerCreationError {
787     /// Not enough memory.
788     OomError(OomError),
789 
790     /// Too many sampler objects have been created. You must destroy some before creating new ones.
791     /// Note the specs guarantee that at least 4000 samplers can exist simultaneously.
792     TooManyObjects,
793 
794     RequirementNotMet {
795         required_for: &'static str,
796         requires_one_of: RequiresOneOf,
797     },
798 
799     /// Anisotropy was enabled with an invalid filter.
800     AnisotropyInvalidFilter {
801         mag_filter: Filter,
802         min_filter: Filter,
803     },
804 
805     /// Depth comparison was enabled with an invalid reduction mode.
806     CompareInvalidReductionMode {
807         reduction_mode: SamplerReductionMode,
808     },
809 
810     /// The requested anisotropy level exceeds the device's limits.
811     MaxSamplerAnisotropyExceeded {
812         /// The value that was requested.
813         requested: f32,
814         /// The maximum supported value.
815         maximum: f32,
816     },
817 
818     /// The requested mip lod bias exceeds the device's limits.
819     MaxSamplerLodBiasExceeded {
820         /// The value that was requested.
821         requested: f32,
822         /// The maximum supported value.
823         maximum: f32,
824     },
825 
826     /// Sampler YCbCr conversion was enabled together with anisotropy.
827     SamplerYcbcrConversionAnisotropyEnabled,
828 
829     /// Sampler YCbCr conversion was enabled, and its format does not support
830     /// `sampled_image_ycbcr_conversion_separate_reconstruction_filter`, but `mag_filter` or
831     /// `min_filter` did not match the conversion's `chroma_filter`.
832     SamplerYcbcrConversionChromaFilterMismatch {
833         chroma_filter: Filter,
834         mag_filter: Filter,
835         min_filter: Filter,
836     },
837 
838     /// Sampler YCbCr conversion was enabled, but the address mode for `u`, `v` or `w` was
839     /// something other than `ClampToEdge`.
840     SamplerYcbcrConversionInvalidAddressMode {
841         address_mode: [SamplerAddressMode; 3],
842     },
843 
844     /// Sampler YCbCr conversion was enabled, but the reduction mode was something other than
845     /// `WeightedAverage`.
846     SamplerYcbcrConversionInvalidReductionMode {
847         reduction_mode: SamplerReductionMode,
848     },
849 
850     /// Sampler YCbCr conversion was enabled together with unnormalized coordinates.
851     SamplerYcbcrConversionUnnormalizedCoordinatesEnabled,
852 
853     /// Unnormalized coordinates were enabled together with anisotropy.
854     UnnormalizedCoordinatesAnisotropyEnabled,
855 
856     /// Unnormalized coordinates were enabled together with depth comparison.
857     UnnormalizedCoordinatesCompareEnabled,
858 
859     /// Unnormalized coordinates were enabled, but the min and mag filters were not equal.
860     UnnormalizedCoordinatesFiltersNotEqual {
861         mag_filter: Filter,
862         min_filter: Filter,
863     },
864 
865     /// Unnormalized coordinates were enabled, but the address mode for `u` or `v` was something
866     /// other than `ClampToEdge` or `ClampToBorder`.
867     UnnormalizedCoordinatesInvalidAddressMode {
868         address_mode: [SamplerAddressMode; 2],
869     },
870 
871     /// Unnormalized coordinates were enabled, but the mipmap mode was not `Nearest`.
872     UnnormalizedCoordinatesInvalidMipmapMode { mipmap_mode: SamplerMipmapMode },
873 
874     /// Unnormalized coordinates were enabled, but the LOD range was not zero.
875     UnnormalizedCoordinatesNonzeroLod { lod: RangeInclusive<f32> },
876 }
877 
878 impl Error for SamplerCreationError {
source(&self) -> Option<&(dyn Error + 'static)>879     fn source(&self) -> Option<&(dyn Error + 'static)> {
880         match self {
881             SamplerCreationError::OomError(err) => Some(err),
882             _ => None,
883         }
884     }
885 }
886 
887 impl Display for SamplerCreationError {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>888     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
889         match self {
890             Self::OomError(_) => write!(f, "not enough memory available"),
891             Self::TooManyObjects => write!(f, "too many simultaneous sampler objects"),
892             Self::RequirementNotMet {
893                 required_for,
894                 requires_one_of,
895             } => write!(
896                 f,
897                 "a requirement was not met for: {}; requires one of: {}",
898                 required_for, requires_one_of,
899             ),
900             Self::AnisotropyInvalidFilter { .. } => {
901                 write!(f, "anisotropy was enabled with an invalid filter")
902             }
903             Self::CompareInvalidReductionMode { .. } => write!(
904                 f,
905                 "depth comparison was enabled with an invalid reduction mode",
906             ),
907             Self::MaxSamplerAnisotropyExceeded { .. } => {
908                 write!(f, "max_sampler_anisotropy limit exceeded")
909             }
910             Self::MaxSamplerLodBiasExceeded { .. } => write!(f, "mip lod bias limit exceeded"),
911             Self::SamplerYcbcrConversionAnisotropyEnabled => write!(
912                 f,
913                 "sampler YCbCr conversion was enabled together with anisotropy",
914             ),
915             Self::SamplerYcbcrConversionChromaFilterMismatch { .. } => write!(
916                 f,
917                 "sampler YCbCr conversion was enabled, and its format does not support
918                 `sampled_image_ycbcr_conversion_separate_reconstruction_filter`, but `mag_filter`
919                 or `min_filter` did not match the conversion's `chroma_filter`",
920             ),
921             Self::SamplerYcbcrConversionInvalidAddressMode { .. } => write!(
922                 f,
923                 "sampler YCbCr conversion was enabled, but the address mode for u, v or w was
924                 something other than `ClampToEdge`",
925             ),
926             Self::SamplerYcbcrConversionInvalidReductionMode { .. } => write!(
927                 f,
928                 "sampler YCbCr conversion was enabled, but the reduction mode was something other \
929                 than `WeightedAverage`",
930             ),
931             Self::SamplerYcbcrConversionUnnormalizedCoordinatesEnabled => write!(
932                 f,
933                 "sampler YCbCr conversion was enabled together with unnormalized coordinates",
934             ),
935             Self::UnnormalizedCoordinatesAnisotropyEnabled => write!(
936                 f,
937                 "unnormalized coordinates were enabled together with anisotropy",
938             ),
939             Self::UnnormalizedCoordinatesCompareEnabled => write!(
940                 f,
941                 "unnormalized coordinates were enabled together with depth comparison",
942             ),
943             Self::UnnormalizedCoordinatesFiltersNotEqual { .. } => write!(
944                 f,
945                 "unnormalized coordinates were enabled, but the min and mag filters were not equal",
946             ),
947             Self::UnnormalizedCoordinatesInvalidAddressMode { .. } => write!(
948                 f,
949                 "unnormalized coordinates were enabled, but the address mode for u or v was \
950                 something other than `ClampToEdge` or `ClampToBorder`",
951             ),
952             Self::UnnormalizedCoordinatesInvalidMipmapMode { .. } => write!(
953                 f,
954                 "unnormalized coordinates were enabled, but the mipmap mode was not `Nearest`",
955             ),
956             Self::UnnormalizedCoordinatesNonzeroLod { .. } => write!(
957                 f,
958                 "unnormalized coordinates were enabled, but the LOD range was not zero",
959             ),
960         }
961     }
962 }
963 
964 impl From<OomError> for SamplerCreationError {
from(err: OomError) -> Self965     fn from(err: OomError) -> Self {
966         Self::OomError(err)
967     }
968 }
969 
970 impl From<VulkanError> for SamplerCreationError {
from(err: VulkanError) -> Self971     fn from(err: VulkanError) -> Self {
972         match err {
973             err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)),
974             err @ VulkanError::OutOfDeviceMemory => Self::OomError(OomError::from(err)),
975             VulkanError::TooManyObjects => Self::TooManyObjects,
976             _ => panic!("unexpected error: {:?}", err),
977         }
978     }
979 }
980 
981 impl From<RequirementNotMet> for SamplerCreationError {
from(err: RequirementNotMet) -> Self982     fn from(err: RequirementNotMet) -> Self {
983         Self::RequirementNotMet {
984             required_for: err.required_for,
985             requires_one_of: err.requires_one_of,
986         }
987     }
988 }
989 
990 /// Parameters to create a new `Sampler`.
991 #[derive(Clone, Debug)]
992 pub struct SamplerCreateInfo {
993     /// How the sampled value of a single mipmap should be calculated,
994     /// when magnification is applied (LOD <= 0.0).
995     ///
996     /// The default value is [`Nearest`](Filter::Nearest).
997     pub mag_filter: Filter,
998 
999     /// How the sampled value of a single mipmap should be calculated,
1000     /// when minification is applied (LOD > 0.0).
1001     ///
1002     /// The default value is [`Nearest`](Filter::Nearest).
1003     pub min_filter: Filter,
1004 
1005     /// How the final sampled value should be calculated from the samples of individual
1006     /// mipmaps.
1007     ///
1008     /// The default value is [`Nearest`](SamplerMipmapMode::Nearest).
1009     pub mipmap_mode: SamplerMipmapMode,
1010 
1011     /// How out-of-range texture coordinates should be treated, for the `u`, `v` and `w` texture
1012     /// coordinate indices respectively.
1013     ///
1014     /// The default value is [`ClampToEdge`](SamplerAddressMode::ClampToEdge).
1015     pub address_mode: [SamplerAddressMode; 3],
1016 
1017     /// The bias value to be added to the base LOD before clamping.
1018     ///
1019     /// The absolute value of the provided value must not exceed the
1020     /// [`max_sampler_lod_bias`](crate::device::Properties::max_sampler_lod_bias) limit of the
1021     /// device.
1022     ///
1023     /// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
1024     /// devices, if `mip_lod_bias` is not `0.0`, the
1025     /// [`sampler_mip_lod_bias`](crate::device::Features::sampler_mip_lod_bias)
1026     /// feature must be enabled on the device.
1027     ///
1028     /// The default value is `0.0`.
1029     pub mip_lod_bias: f32,
1030 
1031     /// Whether anisotropic texel filtering is enabled (`Some`), and the maximum anisotropy value
1032     /// to use if it is enabled.
1033     ///
1034     /// Anisotropic filtering is a special filtering mode that takes into account the differences in
1035     /// scaling between the horizontal and vertical framebuffer axes.
1036     ///
1037     /// If set to `Some`, the [`sampler_anisotropy`](crate::device::Features::sampler_anisotropy)
1038     /// feature must be enabled on the device, the provided maximum value must not exceed the
1039     /// [`max_sampler_anisotropy`](crate::device::Properties::max_sampler_anisotropy) limit, and
1040     /// the [`Cubic`](Filter::Cubic) filter must not be used.
1041     ///
1042     /// The default value is `None`.
1043     pub anisotropy: Option<f32>,
1044 
1045     /// Whether depth comparison is enabled (`Some`), and the comparison operator to use if it is
1046     /// enabled.
1047     ///
1048     /// Depth comparison is an alternative mode for samplers that can be used in combination with
1049     /// image views specifying the depth aspect. Instead of returning a value that is sampled from
1050     /// the image directly, a comparison operation is applied between the sampled value and a
1051     /// reference value that is specified as part of the operation. The result is binary: 1.0 if the
1052     /// operation returns `true`, 0.0 if it returns `false`.
1053     ///
1054     /// If set to `Some`, the `reduction_mode` must be set to
1055     /// [`WeightedAverage`](SamplerReductionMode::WeightedAverage).
1056     ///
1057     /// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
1058     /// devices, if the sampler is going to be used as a mutable sampler (written to descriptor sets
1059     /// rather than being an immutable part of a descriptor set layout), the
1060     /// [`mutable_comparison_samplers`](crate::device::Features::mutable_comparison_samplers)
1061     /// feature must be enabled on the device.
1062     ///
1063     /// The default value is `None`.
1064     pub compare: Option<CompareOp>,
1065 
1066     /// The range that LOD values must be clamped to.
1067     ///
1068     /// If the end of the range is set to [`LOD_CLAMP_NONE`], it is unbounded.
1069     ///
1070     /// The default value is `0.0..=0.0`.
1071     pub lod: RangeInclusive<f32>,
1072 
1073     /// The border color to use if `address_mode` is set to
1074     /// [`ClampToBorder`](SamplerAddressMode::ClampToBorder).
1075     ///
1076     /// The default value is [`FloatTransparentBlack`](BorderColor::FloatTransparentBlack).
1077     pub border_color: BorderColor,
1078 
1079     /// Whether unnormalized texture coordinates are enabled.
1080     ///
1081     /// When a sampler is set to use unnormalized coordinates as input, the texture coordinates are
1082     /// not scaled by the size of the image, and therefore range up to the size of the image rather
1083     /// than 1.0. Enabling this comes with several restrictions:
1084     /// - `min_filter` and `mag_filter` must be equal.
1085     /// - `mipmap_mode` must be [`Nearest`](SamplerMipmapMode::Nearest).
1086     /// - The `lod` range must be `0.0..=0.0`.
1087     /// - `address_mode` for u and v must be either
1088     ///   [`ClampToEdge`](`SamplerAddressMode::ClampToEdge`) or
1089     ///   [`ClampToBorder`](`SamplerAddressMode::ClampToBorder`).
1090     /// - Anisotropy and depth comparison must be disabled.
1091     ///
1092     /// Some restrictions also apply to the image view being sampled:
1093     /// - The view type must be [`Dim1d`](crate::image::view::ImageViewType::Dim1d) or
1094     ///   [`Dim2d`](crate::image::view::ImageViewType::Dim2d). Arrayed types are not allowed.
1095     /// - It must have a single mipmap level.
1096     ///
1097     /// Finally, restrictions apply to the sampling operations that can be used in a shader:
1098     /// - Only explicit LOD operations are allowed, implicit LOD operations are not.
1099     /// - Sampling with projection is not allowed.
1100     /// - Sampling with an LOD bias is not allowed.
1101     /// - Sampling with an offset is not allowed.
1102     ///
1103     /// The default value is `false`.
1104     pub unnormalized_coordinates: bool,
1105 
1106     /// How the value sampled from a mipmap should be calculated from the selected
1107     /// pixels, for the `Linear` and `Cubic` filters.
1108     ///
1109     /// The default value is [`WeightedAverage`](SamplerReductionMode::WeightedAverage).
1110     pub reduction_mode: SamplerReductionMode,
1111 
1112     /// Adds a sampler YCbCr conversion to the sampler.
1113     ///
1114     /// If set to `Some`, several restrictions apply:
1115     /// - If the `format` of `conversion` does not support
1116     ///   `sampled_image_ycbcr_conversion_separate_reconstruction_filter`, then `mag_filter` and
1117     ///   `min_filter` must be equal to the `chroma_filter` of `conversion`.
1118     /// - `address_mode` for u, v and w must be [`ClampToEdge`](`SamplerAddressMode::ClampToEdge`).
1119     /// - Anisotropy and unnormalized coordinates must be disabled.
1120     /// - The `reduction_mode` must be [`WeightedAverage`](SamplerReductionMode::WeightedAverage).
1121     ///
1122     /// In addition, the sampler must only be used as an immutable sampler within a descriptor set
1123     /// layout, and only in a combined image sampler descriptor.
1124     ///
1125     /// The default value is `None`.
1126     pub sampler_ycbcr_conversion: Option<Arc<SamplerYcbcrConversion>>,
1127 
1128     pub _ne: crate::NonExhaustive,
1129 }
1130 
1131 impl Default for SamplerCreateInfo {
1132     #[inline]
default() -> Self1133     fn default() -> Self {
1134         Self {
1135             mag_filter: Filter::Nearest,
1136             min_filter: Filter::Nearest,
1137             mipmap_mode: SamplerMipmapMode::Nearest,
1138             address_mode: [SamplerAddressMode::ClampToEdge; 3],
1139             mip_lod_bias: 0.0,
1140             anisotropy: None,
1141             compare: None,
1142             lod: 0.0..=0.0,
1143             border_color: BorderColor::FloatTransparentBlack,
1144             unnormalized_coordinates: false,
1145             reduction_mode: SamplerReductionMode::WeightedAverage,
1146             sampler_ycbcr_conversion: None,
1147             _ne: crate::NonExhaustive(()),
1148         }
1149     }
1150 }
1151 
1152 impl SamplerCreateInfo {
1153     /// Shortcut for creating a sampler with linear sampling, linear mipmaps, and with the repeat
1154     /// mode for borders.
1155     #[inline]
simple_repeat_linear() -> Self1156     pub fn simple_repeat_linear() -> Self {
1157         Self {
1158             mag_filter: Filter::Linear,
1159             min_filter: Filter::Linear,
1160             mipmap_mode: SamplerMipmapMode::Linear,
1161             address_mode: [SamplerAddressMode::Repeat; 3],
1162             lod: 0.0..=LOD_CLAMP_NONE,
1163             ..Default::default()
1164         }
1165     }
1166 
1167     /// Shortcut for creating a sampler with linear sampling, that only uses the main level of
1168     /// images, and with the repeat mode for borders.
1169     #[inline]
simple_repeat_linear_no_mipmap() -> Self1170     pub fn simple_repeat_linear_no_mipmap() -> Self {
1171         Self {
1172             mag_filter: Filter::Linear,
1173             min_filter: Filter::Linear,
1174             address_mode: [SamplerAddressMode::Repeat; 3],
1175             lod: 0.0..=1.0,
1176             ..Default::default()
1177         }
1178     }
1179 }
1180 
1181 /// A special value to indicate that the maximum LOD should not be clamped.
1182 pub const LOD_CLAMP_NONE: f32 = ash::vk::LOD_CLAMP_NONE;
1183 
1184 /// A mapping between components of a source format and components read by a shader.
1185 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
1186 pub struct ComponentMapping {
1187     /// First component.
1188     pub r: ComponentSwizzle,
1189     /// Second component.
1190     pub g: ComponentSwizzle,
1191     /// Third component.
1192     pub b: ComponentSwizzle,
1193     /// Fourth component.
1194     pub a: ComponentSwizzle,
1195 }
1196 
1197 impl ComponentMapping {
1198     /// Creates a `ComponentMapping` with all components identity swizzled.
1199     #[inline]
identity() -> Self1200     pub fn identity() -> Self {
1201         Self::default()
1202     }
1203 
1204     /// Returns `true` if all components are identity swizzled,
1205     /// meaning that all the members are `Identity` or the name of that member.
1206     ///
1207     /// Certain operations require views that are identity swizzled, and will return an error
1208     /// otherwise. For example, attaching a view to a framebuffer is only possible if the view is
1209     /// identity swizzled.
1210     #[inline]
is_identity(&self) -> bool1211     pub fn is_identity(&self) -> bool {
1212         self.r_is_identity() && self.g_is_identity() && self.b_is_identity() && self.a_is_identity()
1213     }
1214 
1215     /// Returns `true` if the red component mapping is identity swizzled.
1216     #[inline]
r_is_identity(&self) -> bool1217     pub fn r_is_identity(&self) -> bool {
1218         matches!(self.r, ComponentSwizzle::Identity | ComponentSwizzle::Red)
1219     }
1220 
1221     /// Returns `true` if the green component mapping is identity swizzled.
1222     #[inline]
g_is_identity(&self) -> bool1223     pub fn g_is_identity(&self) -> bool {
1224         matches!(self.g, ComponentSwizzle::Identity | ComponentSwizzle::Green)
1225     }
1226 
1227     /// Returns `true` if the blue component mapping is identity swizzled.
1228     #[inline]
b_is_identity(&self) -> bool1229     pub fn b_is_identity(&self) -> bool {
1230         matches!(self.b, ComponentSwizzle::Identity | ComponentSwizzle::Blue)
1231     }
1232 
1233     /// Returns `true` if the alpha component mapping is identity swizzled.
1234     #[inline]
a_is_identity(&self) -> bool1235     pub fn a_is_identity(&self) -> bool {
1236         matches!(self.a, ComponentSwizzle::Identity | ComponentSwizzle::Alpha)
1237     }
1238 
1239     /// Returns the component indices that each component reads from. The index is `None` if the
1240     /// component has a fixed value and is not read from anywhere (`Zero` or `One`).
1241     #[inline]
component_map(&self) -> [Option<usize>; 4]1242     pub fn component_map(&self) -> [Option<usize>; 4] {
1243         [
1244             match self.r {
1245                 ComponentSwizzle::Identity => Some(0),
1246                 ComponentSwizzle::Zero => None,
1247                 ComponentSwizzle::One => None,
1248                 ComponentSwizzle::Red => Some(0),
1249                 ComponentSwizzle::Green => Some(1),
1250                 ComponentSwizzle::Blue => Some(2),
1251                 ComponentSwizzle::Alpha => Some(3),
1252             },
1253             match self.g {
1254                 ComponentSwizzle::Identity => Some(1),
1255                 ComponentSwizzle::Zero => None,
1256                 ComponentSwizzle::One => None,
1257                 ComponentSwizzle::Red => Some(0),
1258                 ComponentSwizzle::Green => Some(1),
1259                 ComponentSwizzle::Blue => Some(2),
1260                 ComponentSwizzle::Alpha => Some(3),
1261             },
1262             match self.b {
1263                 ComponentSwizzle::Identity => Some(2),
1264                 ComponentSwizzle::Zero => None,
1265                 ComponentSwizzle::One => None,
1266                 ComponentSwizzle::Red => Some(0),
1267                 ComponentSwizzle::Green => Some(1),
1268                 ComponentSwizzle::Blue => Some(2),
1269                 ComponentSwizzle::Alpha => Some(3),
1270             },
1271             match self.a {
1272                 ComponentSwizzle::Identity => Some(3),
1273                 ComponentSwizzle::Zero => None,
1274                 ComponentSwizzle::One => None,
1275                 ComponentSwizzle::Red => Some(0),
1276                 ComponentSwizzle::Green => Some(1),
1277                 ComponentSwizzle::Blue => Some(2),
1278                 ComponentSwizzle::Alpha => Some(3),
1279             },
1280         ]
1281     }
1282 }
1283 
1284 impl From<ComponentMapping> for ash::vk::ComponentMapping {
1285     #[inline]
from(value: ComponentMapping) -> Self1286     fn from(value: ComponentMapping) -> Self {
1287         Self {
1288             r: value.r.into(),
1289             g: value.g.into(),
1290             b: value.b.into(),
1291             a: value.a.into(),
1292         }
1293     }
1294 }
1295 
1296 vulkan_enum! {
1297     #[non_exhaustive]
1298 
1299     /// Describes the value that an individual component must return when being accessed.
1300     ComponentSwizzle = ComponentSwizzle(i32);
1301 
1302     /// Returns the value that this component should normally have.
1303     ///
1304     /// This is the `Default` value.
1305     Identity = IDENTITY,
1306 
1307     /// Always return zero.
1308     Zero = ZERO,
1309 
1310     /// Always return one.
1311     One = ONE,
1312 
1313     /// Returns the value of the first component.
1314     Red = R,
1315 
1316     /// Returns the value of the second component.
1317     Green = G,
1318 
1319     /// Returns the value of the third component.
1320     Blue = B,
1321 
1322     /// Returns the value of the fourth component.
1323     Alpha = A,
1324 }
1325 
1326 impl Default for ComponentSwizzle {
1327     #[inline]
default() -> ComponentSwizzle1328     fn default() -> ComponentSwizzle {
1329         ComponentSwizzle::Identity
1330     }
1331 }
1332 
1333 vulkan_enum! {
1334     #[non_exhaustive]
1335 
1336     /// Describes how the color of each pixel should be determined.
1337     Filter = Filter(i32);
1338 
1339     /// The pixel whose center is nearest to the requested coordinates is taken from the source
1340     /// and its value is returned as-is.
1341     Nearest = NEAREST,
1342 
1343     /// The 8/4/2 pixels (depending on view dimensionality) whose center surround the requested
1344     /// coordinates are taken, then their values are combined according to the chosen
1345     /// `reduction_mode`.
1346     Linear = LINEAR,
1347 
1348     /// The 64/16/4 pixels (depending on the view dimensionality) whose center surround the
1349     /// requested coordinates are taken, then their values are combined according to the chosen
1350     /// `reduction_mode`.
1351     ///
1352     /// The [`ext_filter_cubic`](crate::device::DeviceExtensions::ext_filter_cubic) extension must
1353     /// be enabled on the device, and anisotropy must be disabled. Sampled image views must have
1354     /// a type of [`Dim2d`](crate::image::view::ImageViewType::Dim2d).
1355     Cubic = CUBIC_EXT {
1356         device_extensions: [ext_filter_cubic, img_filter_cubic],
1357     },
1358 }
1359 
1360 vulkan_enum! {
1361     #[non_exhaustive]
1362 
1363     /// Describes which mipmap from the source to use.
1364     SamplerMipmapMode = SamplerMipmapMode(i32);
1365 
1366     /// Use the mipmap whose dimensions are the nearest to the dimensions of the destination.
1367     Nearest = NEAREST,
1368 
1369     /// Take the mipmap whose dimensions are no greater than that of the destination together
1370     /// with the next higher level mipmap, calculate the value for both, and interpolate them.
1371     Linear = LINEAR,
1372 }
1373 
1374 vulkan_enum! {
1375     #[non_exhaustive]
1376 
1377     /// How the sampler should behave when it needs to access a pixel that is out of range of the
1378     /// texture.
1379     SamplerAddressMode = SamplerAddressMode(i32);
1380 
1381     /// Repeat the texture. In other words, the pixel at coordinate `x + 1.0` is the same as the
1382     /// one at coordinate `x`.
1383     Repeat = REPEAT,
1384 
1385     /// Repeat the texture but mirror it at every repetition. In other words, the pixel at
1386     /// coordinate `x + 1.0` is the same as the one at coordinate `1.0 - x`.
1387     MirroredRepeat = MIRRORED_REPEAT,
1388 
1389     /// The coordinates are clamped to the valid range. Coordinates below 0.0 have the same value
1390     /// as coordinate 0.0. Coordinates over 1.0 have the same value as coordinate 1.0.
1391     ClampToEdge = CLAMP_TO_EDGE,
1392 
1393     /// Any pixel out of range is colored using the colour selected with the `border_color` on the
1394     /// `SamplerBuilder`.
1395     ///
1396     /// When this mode is chosen, the numeric type of the image view's format must match the border
1397     /// color. When using a floating-point border color, the sampler can only be used with
1398     /// floating-point or depth image views. When using an integer border color, the sampler can
1399     /// only be used with integer or stencil image views. In addition to this, you can't use an
1400     /// opaque black border color with an image view that uses component swizzling.
1401     ClampToBorder = CLAMP_TO_BORDER,
1402 
1403     /// Similar to `MirroredRepeat`, except that coordinates are clamped to the range
1404     /// `[-1.0, 1.0]`.
1405     ///
1406     /// The [`sampler_mirror_clamp_to_edge`](crate::device::Features::sampler_mirror_clamp_to_edge)
1407     /// feature or the
1408     /// [`khr_sampler_mirror_clamp_to_edge`](crate::device::DeviceExtensions::khr_sampler_mirror_clamp_to_edge)
1409     /// extension must be enabled on the device.
1410     MirrorClampToEdge = MIRROR_CLAMP_TO_EDGE {
1411         api_version: V1_2,
1412         device_extensions: [khr_sampler_mirror_clamp_to_edge],
1413     },
1414 }
1415 
1416 vulkan_enum! {
1417     #[non_exhaustive]
1418 
1419     /// The color to use for the border of an image.
1420     ///
1421     /// Only relevant if you use `ClampToBorder`.
1422     ///
1423     /// Using a border color restricts the sampler to either floating-point images or integer images.
1424     BorderColor = BorderColor(i32);
1425 
1426     /// The value `(0.0, 0.0, 0.0, 0.0)`. Can only be used with floating-point images.
1427     FloatTransparentBlack = FLOAT_TRANSPARENT_BLACK,
1428 
1429     /// The value `(0, 0, 0, 0)`. Can only be used with integer images.
1430     IntTransparentBlack = INT_TRANSPARENT_BLACK,
1431 
1432     /// The value `(0.0, 0.0, 0.0, 1.0)`. Can only be used with floating-point identity-swizzled
1433     /// images.
1434     FloatOpaqueBlack = FLOAT_OPAQUE_BLACK,
1435 
1436     /// The value `(0, 0, 0, 1)`. Can only be used with integer identity-swizzled images.
1437     IntOpaqueBlack = INT_OPAQUE_BLACK,
1438 
1439     /// The value `(1.0, 1.0, 1.0, 1.0)`. Can only be used with floating-point images.
1440     FloatOpaqueWhite = FLOAT_OPAQUE_WHITE,
1441 
1442     /// The value `(1, 1, 1, 1)`. Can only be used with integer images.
1443     IntOpaqueWhite = INT_OPAQUE_WHITE,
1444 
1445     /* TODO: enable
1446     // TODO: document
1447     FloatCustom = VK_BORDER_COLOR_FLOAT_CUSTOM_EXT {
1448         device_extensions: [ext_custom_border_color],
1449     },*/
1450 
1451     /* TODO: enable
1452     // TODO: document
1453     IntCustom = INT_CUSTOM_EXT {
1454         device_extensions: [ext_custom_border_color],
1455     },*/
1456 }
1457 
1458 vulkan_enum! {
1459     #[non_exhaustive]
1460 
1461     /// Describes how the value sampled from a mipmap should be calculated from the selected
1462     /// pixels, for the `Linear` and `Cubic` filters.
1463     SamplerReductionMode = SamplerReductionMode(i32);
1464 
1465     /// Calculates a weighted average of the selected pixels. For `Linear` filtering the pixels
1466     /// are evenly weighted, for `Cubic` filtering they use Catmull-Rom weights.
1467     WeightedAverage = WEIGHTED_AVERAGE,
1468 
1469     /// Calculates the minimum of the selected pixels.
1470     ///
1471     /// The [`sampler_filter_minmax`](crate::device::Features::sampler_filter_minmax)
1472     /// feature or the
1473     /// [`ext_sampler_filter_minmax`](crate::device::DeviceExtensions::ext_sampler_filter_minmax)
1474     /// extension must be enabled on the device.
1475     Min = MIN,
1476 
1477     /// Calculates the maximum of the selected pixels.
1478     ///
1479     /// The [`sampler_filter_minmax`](crate::device::Features::sampler_filter_minmax)
1480     /// feature or the
1481     /// [`ext_sampler_filter_minmax`](crate::device::DeviceExtensions::ext_sampler_filter_minmax)
1482     /// extension must be enabled on the device.
1483     Max = MAX,
1484 }
1485 
1486 #[derive(Clone, Copy, Debug)]
1487 pub enum SamplerImageViewIncompatibleError {
1488     /// The sampler has a border color with a numeric type different from the image view.
1489     BorderColorFormatNotCompatible,
1490 
1491     /// The sampler has an opaque black border color, but the image view is not identity swizzled.
1492     BorderColorOpaqueBlackNotIdentitySwizzled,
1493 
1494     /// The sampler has depth comparison enabled, but this is not supported by the image view.
1495     DepthComparisonNotSupported,
1496 
1497     /// The sampler has depth comparison enabled, but the image view does not select the `depth`
1498     /// aspect.
1499     DepthComparisonWrongAspect,
1500 
1501     /// The sampler uses a linear filter, but this is not supported by the image view's format
1502     /// features.
1503     FilterLinearNotSupported,
1504 
1505     /// The sampler uses a cubic filter, but this is not supported by the image view's format
1506     /// features.
1507     FilterCubicNotSupported,
1508 
1509     /// The sampler uses a cubic filter with a `Min` or `Max` reduction mode, but this is not
1510     /// supported by the image view's format features.
1511     FilterCubicMinmaxNotSupported,
1512 
1513     /// The sampler uses a linear mipmap mode, but this is not supported by the image view's format
1514     /// features.
1515     MipmapModeLinearNotSupported,
1516 
1517     /// The sampler uses unnormalized coordinates, but the image view has multiple mip levels.
1518     UnnormalizedCoordinatesMultipleMipLevels,
1519 
1520     /// The sampler uses unnormalized coordinates, but the image view has a type other than `Dim1d`
1521     /// or `Dim2d`.
1522     UnnormalizedCoordinatesViewTypeNotCompatible,
1523 }
1524 
1525 impl Error for SamplerImageViewIncompatibleError {}
1526 
1527 impl Display for SamplerImageViewIncompatibleError {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>1528     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
1529         match self {
1530             Self::BorderColorFormatNotCompatible => write!(
1531                 f,
1532                 "the sampler has a border color with a numeric type different from the image view",
1533             ),
1534             Self::BorderColorOpaqueBlackNotIdentitySwizzled => write!(
1535                 f,
1536                 "the sampler has an opaque black border color, but the image view is not identity \
1537                 swizzled",
1538             ),
1539             Self::DepthComparisonNotSupported => write!(
1540                 f,
1541                 "the sampler has depth comparison enabled, but this is not supported by the image \
1542                 view",
1543             ),
1544             Self::DepthComparisonWrongAspect => write!(
1545                 f,
1546                 "the sampler has depth comparison enabled, but the image view does not select the \
1547                 `depth` aspect",
1548             ),
1549             Self::FilterLinearNotSupported => write!(
1550                 f,
1551                 "the sampler uses a linear filter, but this is not supported by the image view's \
1552                 format features",
1553             ),
1554             Self::FilterCubicNotSupported => write!(
1555                 f,
1556                 "the sampler uses a cubic filter, but this is not supported by the image view's \
1557                 format features",
1558             ),
1559             Self::FilterCubicMinmaxNotSupported => write!(
1560                 f,
1561                 "the sampler uses a cubic filter with a `Min` or `Max` reduction mode, but this is \
1562                 not supported by the image view's format features",
1563             ),
1564             Self::MipmapModeLinearNotSupported => write!(
1565                 f,
1566                 "the sampler uses a linear mipmap mode, but this is not supported by the image \
1567                 view's format features",
1568             ),
1569             Self::UnnormalizedCoordinatesMultipleMipLevels => write!(
1570                 f,
1571                 "the sampler uses unnormalized coordinates, but the image view has multiple mip \
1572                 levels",
1573             ),
1574             Self::UnnormalizedCoordinatesViewTypeNotCompatible => write!(
1575                 f,
1576                 "the sampler uses unnormalized coordinates, but the image view has a type other \
1577                 than `Dim1d` or `Dim2d`",
1578             ),
1579         }
1580     }
1581 }
1582 
1583 #[cfg(test)]
1584 mod tests {
1585     use crate::{
1586         pipeline::graphics::depth_stencil::CompareOp,
1587         sampler::{
1588             Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerCreationError,
1589             SamplerReductionMode,
1590         },
1591         RequiresOneOf,
1592     };
1593 
1594     #[test]
create_regular()1595     fn create_regular() {
1596         let (device, _queue) = gfx_dev_and_queue!();
1597 
1598         let s = Sampler::new(
1599             device,
1600             SamplerCreateInfo {
1601                 mag_filter: Filter::Linear,
1602                 min_filter: Filter::Linear,
1603                 address_mode: [SamplerAddressMode::Repeat; 3],
1604                 mip_lod_bias: 1.0,
1605                 lod: 0.0..=2.0,
1606                 ..Default::default()
1607             },
1608         )
1609         .unwrap();
1610         assert!(s.compare().is_none());
1611         assert!(!s.unnormalized_coordinates());
1612     }
1613 
1614     #[test]
create_compare()1615     fn create_compare() {
1616         let (device, _queue) = gfx_dev_and_queue!();
1617 
1618         let s = Sampler::new(
1619             device,
1620             SamplerCreateInfo {
1621                 mag_filter: Filter::Linear,
1622                 min_filter: Filter::Linear,
1623                 address_mode: [SamplerAddressMode::Repeat; 3],
1624                 mip_lod_bias: 1.0,
1625                 compare: Some(CompareOp::Less),
1626                 lod: 0.0..=2.0,
1627                 ..Default::default()
1628             },
1629         )
1630         .unwrap();
1631         assert!(s.compare().is_some());
1632         assert!(!s.unnormalized_coordinates());
1633     }
1634 
1635     #[test]
create_unnormalized()1636     fn create_unnormalized() {
1637         let (device, _queue) = gfx_dev_and_queue!();
1638 
1639         let s = Sampler::new(
1640             device,
1641             SamplerCreateInfo {
1642                 mag_filter: Filter::Linear,
1643                 min_filter: Filter::Linear,
1644                 unnormalized_coordinates: true,
1645                 ..Default::default()
1646             },
1647         )
1648         .unwrap();
1649         assert!(s.compare().is_none());
1650         assert!(s.unnormalized_coordinates());
1651     }
1652 
1653     #[test]
simple_repeat_linear()1654     fn simple_repeat_linear() {
1655         let (device, _queue) = gfx_dev_and_queue!();
1656         let _ = Sampler::new(device, SamplerCreateInfo::simple_repeat_linear());
1657     }
1658 
1659     #[test]
simple_repeat_linear_no_mipmap()1660     fn simple_repeat_linear_no_mipmap() {
1661         let (device, _queue) = gfx_dev_and_queue!();
1662         let _ = Sampler::new(device, SamplerCreateInfo::simple_repeat_linear_no_mipmap());
1663     }
1664 
1665     #[test]
min_lod_inferior()1666     fn min_lod_inferior() {
1667         let (device, _queue) = gfx_dev_and_queue!();
1668 
1669         assert_should_panic!({
1670             let _ = Sampler::new(
1671                 device,
1672                 SamplerCreateInfo {
1673                     mag_filter: Filter::Linear,
1674                     min_filter: Filter::Linear,
1675                     address_mode: [SamplerAddressMode::Repeat; 3],
1676                     mip_lod_bias: 1.0,
1677                     lod: 5.0..=2.0,
1678                     ..Default::default()
1679                 },
1680             );
1681         });
1682     }
1683 
1684     #[test]
max_anisotropy()1685     fn max_anisotropy() {
1686         let (device, _queue) = gfx_dev_and_queue!();
1687 
1688         assert_should_panic!({
1689             let _ = Sampler::new(
1690                 device,
1691                 SamplerCreateInfo {
1692                     mag_filter: Filter::Linear,
1693                     min_filter: Filter::Linear,
1694                     address_mode: [SamplerAddressMode::Repeat; 3],
1695                     mip_lod_bias: 1.0,
1696                     anisotropy: Some(0.5),
1697                     lod: 0.0..=2.0,
1698                     ..Default::default()
1699                 },
1700             );
1701         });
1702     }
1703 
1704     #[test]
anisotropy_feature()1705     fn anisotropy_feature() {
1706         let (device, _queue) = gfx_dev_and_queue!();
1707 
1708         let r = Sampler::new(
1709             device,
1710             SamplerCreateInfo {
1711                 mag_filter: Filter::Linear,
1712                 min_filter: Filter::Linear,
1713                 address_mode: [SamplerAddressMode::Repeat; 3],
1714                 mip_lod_bias: 1.0,
1715                 anisotropy: Some(2.0),
1716                 lod: 0.0..=2.0,
1717                 ..Default::default()
1718             },
1719         );
1720 
1721         match r {
1722             Err(SamplerCreationError::RequirementNotMet {
1723                 requires_one_of: RequiresOneOf { features, .. },
1724                 ..
1725             }) if features.contains(&"sampler_anisotropy") => (),
1726             _ => panic!(),
1727         }
1728     }
1729 
1730     #[test]
anisotropy_limit()1731     fn anisotropy_limit() {
1732         let (device, _queue) = gfx_dev_and_queue!(sampler_anisotropy);
1733 
1734         let r = Sampler::new(
1735             device,
1736             SamplerCreateInfo {
1737                 mag_filter: Filter::Linear,
1738                 min_filter: Filter::Linear,
1739                 address_mode: [SamplerAddressMode::Repeat; 3],
1740                 mip_lod_bias: 1.0,
1741                 anisotropy: Some(100000000.0),
1742                 lod: 0.0..=2.0,
1743                 ..Default::default()
1744             },
1745         );
1746 
1747         match r {
1748             Err(SamplerCreationError::MaxSamplerAnisotropyExceeded { .. }) => (),
1749             _ => panic!(),
1750         }
1751     }
1752 
1753     #[test]
mip_lod_bias_limit()1754     fn mip_lod_bias_limit() {
1755         let (device, _queue) = gfx_dev_and_queue!();
1756 
1757         let r = Sampler::new(
1758             device,
1759             SamplerCreateInfo {
1760                 mag_filter: Filter::Linear,
1761                 min_filter: Filter::Linear,
1762                 address_mode: [SamplerAddressMode::Repeat; 3],
1763                 mip_lod_bias: 100000000.0,
1764                 lod: 0.0..=2.0,
1765                 ..Default::default()
1766             },
1767         );
1768 
1769         match r {
1770             Err(SamplerCreationError::MaxSamplerLodBiasExceeded { .. }) => (),
1771             _ => panic!(),
1772         }
1773     }
1774 
1775     #[test]
sampler_mirror_clamp_to_edge_extension()1776     fn sampler_mirror_clamp_to_edge_extension() {
1777         let (device, _queue) = gfx_dev_and_queue!();
1778 
1779         let r = Sampler::new(
1780             device,
1781             SamplerCreateInfo {
1782                 mag_filter: Filter::Linear,
1783                 min_filter: Filter::Linear,
1784                 address_mode: [SamplerAddressMode::MirrorClampToEdge; 3],
1785                 mip_lod_bias: 1.0,
1786                 lod: 0.0..=2.0,
1787                 ..Default::default()
1788             },
1789         );
1790 
1791         match r {
1792             Err(SamplerCreationError::RequirementNotMet {
1793                 requires_one_of:
1794                     RequiresOneOf {
1795                         features,
1796                         device_extensions,
1797                         ..
1798                     },
1799                 ..
1800             }) if features.contains(&"sampler_mirror_clamp_to_edge")
1801                 && device_extensions.contains(&"khr_sampler_mirror_clamp_to_edge") => {}
1802             _ => panic!(),
1803         }
1804     }
1805 
1806     #[test]
sampler_filter_minmax_extension()1807     fn sampler_filter_minmax_extension() {
1808         let (device, _queue) = gfx_dev_and_queue!();
1809 
1810         let r = Sampler::new(
1811             device,
1812             SamplerCreateInfo {
1813                 mag_filter: Filter::Linear,
1814                 min_filter: Filter::Linear,
1815                 reduction_mode: SamplerReductionMode::Min,
1816                 ..Default::default()
1817             },
1818         );
1819 
1820         match r {
1821             Err(SamplerCreationError::RequirementNotMet {
1822                 requires_one_of:
1823                     RequiresOneOf {
1824                         features,
1825                         device_extensions,
1826                         ..
1827                     },
1828                 ..
1829             }) if features.contains(&"sampler_filter_minmax")
1830                 && device_extensions.contains(&"ext_sampler_filter_minmax") => {}
1831             _ => panic!(),
1832         }
1833     }
1834 }
1835