• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 an image within a shader.
11 //!
12 //! When you retrieve data from an image, you have to pass the coordinates of the pixel you want
13 //! to retrieve. The implementation then performs various calculations, and these operations are
14 //! what the `Sampler` struct describes.
15 //!
16 //! Sampling is a very complex topic but that hasn't changed much since the beginnings of 3D
17 //! rendering. Documentation here is missing, but any tutorial about OpenGL or DirectX can teach
18 //! you how it works.
19 //!
20 //! # Examples
21 //!
22 //! A simple sampler for most usages:
23 //!
24 //! ```
25 //! use vulkano::sampler::Sampler;
26 //!
27 //! # let device: std::sync::Arc<vulkano::device::Device> = return;
28 //! let _sampler = Sampler::simple_repeat_linear_no_mipmap(device.clone());
29 //! ```
30 //!
31 //! More detailed sampler creation:
32 //!
33 //! ```
34 //! use vulkano::sampler;
35 //!
36 //! # let device: std::sync::Arc<vulkano::device::Device> = return;
37 //! let _sampler = sampler::Sampler::new(device.clone(), sampler::Filter::Linear,
38 //!                                      sampler::Filter::Linear,
39 //!                                      sampler::MipmapMode::Nearest,
40 //!                                      sampler::SamplerAddressMode::Repeat,
41 //!                                      sampler::SamplerAddressMode::Repeat,
42 //!                                      sampler::SamplerAddressMode::Repeat, 1.0, 1.0,
43 //!                                      0.0, 100.0).unwrap();;
44 //! ```
45 //!
46 //! # About border colors
47 //!
48 //! One of the possible values of `SamplerAddressMode` and `UnnormalizedSamplerAddressMode` is
49 //! `ClampToBorder`. This value indicates that accessing an image outside of its range must return
50 //! the specified color.
51 //!
52 //! However this comes with restrictions. When using a floating-point border color, the sampler can
53 //! only be used with floating-point or depth image views. When using an integer border color, the
54 //! sampler can only be used with integer or stencil image views. In addition to this, you can't
55 //! use an opaque black border color with an image view that uses components swizzling.
56 //!
57 //! > **Note**: The reason for this restriction about opaque black borders is that the value of the
58 //! > alpha is 1.0 while the value of the color components is 0.0. In the other border colors, the
59 //! > value of all the components is the same.
60 //!
61 //! Samplers that don't use `ClampToBorder` are not concerned by these restrictions.
62 //!
63 // FIXME: restrictions aren't checked yet
64 
65 use crate::check_errors;
66 use crate::device::Device;
67 use crate::device::DeviceOwned;
68 pub use crate::pipeline::depth_stencil::Compare;
69 use crate::Error;
70 use crate::OomError;
71 use crate::VulkanObject;
72 use std::error;
73 use std::fmt;
74 use std::mem::MaybeUninit;
75 use std::ptr;
76 use std::sync::Arc;
77 
78 /// Describes how to retrieve data from an image within a shader.
79 pub struct Sampler {
80     sampler: ash::vk::Sampler,
81     device: Arc<Device>,
82     compare_mode: bool,
83     unnormalized: bool,
84     usable_with_float_formats: bool,
85     usable_with_int_formats: bool,
86     usable_with_swizzling: bool,
87 }
88 
89 impl Sampler {
90     /// Shortcut for creating a sampler with linear sampling, linear mipmaps, and with the repeat
91     /// mode for borders.
92     ///
93     /// Useful for prototyping, but can also be used in real projects.
94     ///
95     /// # Panic
96     ///
97     /// - Panics if out of memory or the maximum number of samplers has exceeded.
98     ///
99     #[inline]
simple_repeat_linear(device: Arc<Device>) -> Arc<Sampler>100     pub fn simple_repeat_linear(device: Arc<Device>) -> Arc<Sampler> {
101         Sampler::new(
102             device,
103             Filter::Linear,
104             Filter::Linear,
105             MipmapMode::Linear,
106             SamplerAddressMode::Repeat,
107             SamplerAddressMode::Repeat,
108             SamplerAddressMode::Repeat,
109             0.0,
110             1.0,
111             0.0,
112             1_000.0,
113         )
114         .unwrap()
115     }
116 
117     /// Shortcut for creating a sampler with linear sampling, that only uses the main level of
118     /// images, and with the repeat mode for borders.
119     ///
120     /// Useful for prototyping, but can also be used in real projects.
121     ///
122     /// # Panic
123     ///
124     /// - Panics if out of memory or the maximum number of samplers has exceeded.
125     ///
126     #[inline]
simple_repeat_linear_no_mipmap(device: Arc<Device>) -> Arc<Sampler>127     pub fn simple_repeat_linear_no_mipmap(device: Arc<Device>) -> Arc<Sampler> {
128         Sampler::new(
129             device,
130             Filter::Linear,
131             Filter::Linear,
132             MipmapMode::Nearest,
133             SamplerAddressMode::Repeat,
134             SamplerAddressMode::Repeat,
135             SamplerAddressMode::Repeat,
136             0.0,
137             1.0,
138             0.0,
139             1.0,
140         )
141         .unwrap()
142     }
143 
144     /// Creates a new `Sampler` with the given behavior.
145     ///
146     /// `mag_filter` and `min_filter` define how the implementation should sample from the image
147     /// when it is respectively larger and smaller than the original.
148     ///
149     /// `mipmap_mode` defines how the implementation should choose which mipmap to use.
150     ///
151     /// `address_u`, `address_v` and `address_w` define how the implementation should behave when
152     /// sampling outside of the texture coordinates range `[0.0, 1.0]`.
153     ///
154     /// `mip_lod_bias` is a value to add to .
155     ///
156     /// `max_anisotropy` must be greater than or equal to 1.0. If greater than 1.0, the
157     /// implementation will use anisotropic filtering. Using a value greater than 1.0 requires
158     /// the `sampler_anisotropy` feature to be enabled when creating the device.
159     ///
160     /// `min_lod` and `max_lod` are respectively the minimum and maximum mipmap level to use.
161     /// `max_lod` must always be greater than or equal to `min_lod`.
162     ///
163     /// # Panic
164     ///
165     /// - Panics if multiple `ClampToBorder` values are passed and the border color is different.
166     /// - Panics if `max_anisotropy < 1.0`.
167     /// - Panics if `min_lod > max_lod`.
168     ///
169     #[inline(always)]
new( device: Arc<Device>, mag_filter: Filter, min_filter: Filter, mipmap_mode: MipmapMode, address_u: SamplerAddressMode, address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32, max_anisotropy: f32, min_lod: f32, max_lod: f32, ) -> Result<Arc<Sampler>, SamplerCreationError>170     pub fn new(
171         device: Arc<Device>,
172         mag_filter: Filter,
173         min_filter: Filter,
174         mipmap_mode: MipmapMode,
175         address_u: SamplerAddressMode,
176         address_v: SamplerAddressMode,
177         address_w: SamplerAddressMode,
178         mip_lod_bias: f32,
179         max_anisotropy: f32,
180         min_lod: f32,
181         max_lod: f32,
182     ) -> Result<Arc<Sampler>, SamplerCreationError> {
183         Sampler::new_impl(
184             device,
185             mag_filter,
186             min_filter,
187             mipmap_mode,
188             address_u,
189             address_v,
190             address_w,
191             mip_lod_bias,
192             max_anisotropy,
193             min_lod,
194             max_lod,
195             None,
196         )
197     }
198 
199     /// Creates a new `Sampler` with the given behavior.
200     ///
201     /// Contrary to `new`, this creates a sampler that is used to compare depth values.
202     ///
203     /// A sampler like this can only operate on depth or depth-stencil textures. Instead of
204     /// returning the value of the texture, this sampler will return a value between 0.0 and 1.0
205     /// indicating how much the reference value (passed by the shader) compares to the value in the
206     /// texture.
207     ///
208     /// Note that it doesn't make sense to create a compare-mode sampler with an integer border
209     /// color, as such a sampler would be unusable.
210     ///
211     /// # Panic
212     ///
213     /// Same panic reasons as `new`.
214     ///
215     #[inline(always)]
compare( device: Arc<Device>, mag_filter: Filter, min_filter: Filter, mipmap_mode: MipmapMode, address_u: SamplerAddressMode, address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32, max_anisotropy: f32, min_lod: f32, max_lod: f32, compare: Compare, ) -> Result<Arc<Sampler>, SamplerCreationError>216     pub fn compare(
217         device: Arc<Device>,
218         mag_filter: Filter,
219         min_filter: Filter,
220         mipmap_mode: MipmapMode,
221         address_u: SamplerAddressMode,
222         address_v: SamplerAddressMode,
223         address_w: SamplerAddressMode,
224         mip_lod_bias: f32,
225         max_anisotropy: f32,
226         min_lod: f32,
227         max_lod: f32,
228         compare: Compare,
229     ) -> Result<Arc<Sampler>, SamplerCreationError> {
230         Sampler::new_impl(
231             device,
232             mag_filter,
233             min_filter,
234             mipmap_mode,
235             address_u,
236             address_v,
237             address_w,
238             mip_lod_bias,
239             max_anisotropy,
240             min_lod,
241             max_lod,
242             Some(compare),
243         )
244     }
245 
new_impl( device: Arc<Device>, mag_filter: Filter, min_filter: Filter, mipmap_mode: MipmapMode, address_u: SamplerAddressMode, address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32, max_anisotropy: f32, min_lod: f32, max_lod: f32, compare: Option<Compare>, ) -> Result<Arc<Sampler>, SamplerCreationError>246     fn new_impl(
247         device: Arc<Device>,
248         mag_filter: Filter,
249         min_filter: Filter,
250         mipmap_mode: MipmapMode,
251         address_u: SamplerAddressMode,
252         address_v: SamplerAddressMode,
253         address_w: SamplerAddressMode,
254         mip_lod_bias: f32,
255         max_anisotropy: f32,
256         min_lod: f32,
257         max_lod: f32,
258         compare: Option<Compare>,
259     ) -> Result<Arc<Sampler>, SamplerCreationError> {
260         assert!(max_anisotropy >= 1.0);
261         assert!(min_lod <= max_lod);
262 
263         // Check max anisotropy.
264         if max_anisotropy > 1.0 {
265             if !device.enabled_features().sampler_anisotropy {
266                 return Err(SamplerCreationError::SamplerAnisotropyFeatureNotEnabled);
267             }
268 
269             let limit = device
270                 .physical_device()
271                 .properties()
272                 .max_sampler_anisotropy;
273             if max_anisotropy > limit {
274                 return Err(SamplerCreationError::AnisotropyLimitExceeded {
275                     requested: max_anisotropy,
276                     maximum: limit,
277                 });
278             }
279         }
280 
281         // Check mip_lod_bias value.
282         {
283             let limit = device
284                 .physical_device()
285                 .properties()
286                 .max_sampler_lod_bias;
287             if mip_lod_bias > limit {
288                 return Err(SamplerCreationError::MipLodBiasLimitExceeded {
289                     requested: mip_lod_bias,
290                     maximum: limit,
291                 });
292             }
293         }
294 
295         // Check MirrorClampToEdge extension support
296         if [address_u, address_v, address_w]
297             .iter()
298             .any(|&mode| mode == SamplerAddressMode::MirrorClampToEdge)
299         {
300             if !device.enabled_extensions().khr_sampler_mirror_clamp_to_edge {
301                 return Err(SamplerCreationError::SamplerMirrorClampToEdgeExtensionNotEnabled);
302             }
303         }
304 
305         // Handling border color.
306         let border_color = address_u.border_color();
307         let border_color = match (border_color, address_v.border_color()) {
308             (Some(b1), Some(b2)) => {
309                 assert_eq!(b1, b2);
310                 Some(b1)
311             }
312             (None, b) => b,
313             (b, None) => b,
314         };
315         let border_color = match (border_color, address_w.border_color()) {
316             (Some(b1), Some(b2)) => {
317                 assert_eq!(b1, b2);
318                 Some(b1)
319             }
320             (None, b) => b,
321             (b, None) => b,
322         };
323 
324         let fns = device.fns();
325         let sampler = unsafe {
326             let infos = ash::vk::SamplerCreateInfo {
327                 flags: ash::vk::SamplerCreateFlags::empty(),
328                 mag_filter: mag_filter.into(),
329                 min_filter: min_filter.into(),
330                 mipmap_mode: mipmap_mode.into(),
331                 address_mode_u: address_u.into(),
332                 address_mode_v: address_v.into(),
333                 address_mode_w: address_w.into(),
334                 mip_lod_bias: mip_lod_bias,
335                 anisotropy_enable: if max_anisotropy > 1.0 {
336                     ash::vk::TRUE
337                 } else {
338                     ash::vk::FALSE
339                 },
340                 max_anisotropy: max_anisotropy,
341                 compare_enable: if compare.is_some() {
342                     ash::vk::TRUE
343                 } else {
344                     ash::vk::FALSE
345                 },
346                 compare_op: compare
347                     .map(|c| c.into())
348                     .unwrap_or(ash::vk::CompareOp::NEVER),
349                 min_lod: min_lod,
350                 max_lod: max_lod,
351                 border_color: border_color
352                     .map(|b| b.into())
353                     .unwrap_or(ash::vk::BorderColor::FLOAT_TRANSPARENT_BLACK),
354                 unnormalized_coordinates: ash::vk::FALSE,
355                 ..Default::default()
356             };
357 
358             let mut output = MaybeUninit::uninit();
359             check_errors(fns.v1_0.create_sampler(
360                 device.internal_object(),
361                 &infos,
362                 ptr::null(),
363                 output.as_mut_ptr(),
364             ))?;
365             output.assume_init()
366         };
367 
368         Ok(Arc::new(Sampler {
369             sampler: sampler,
370             device: device.clone(),
371             compare_mode: compare.is_some(),
372             unnormalized: false,
373             usable_with_float_formats: match border_color {
374                 Some(BorderColor::FloatTransparentBlack) => true,
375                 Some(BorderColor::FloatOpaqueBlack) => true,
376                 Some(BorderColor::FloatOpaqueWhite) => true,
377                 Some(_) => false,
378                 None => true,
379             },
380             usable_with_int_formats: compare.is_none()
381                 && match border_color {
382                     Some(BorderColor::IntTransparentBlack) => true,
383                     Some(BorderColor::IntOpaqueBlack) => true,
384                     Some(BorderColor::IntOpaqueWhite) => true,
385                     Some(_) => false,
386                     None => true,
387                 },
388             usable_with_swizzling: match border_color {
389                 Some(BorderColor::FloatOpaqueBlack) => false,
390                 Some(BorderColor::IntOpaqueBlack) => false,
391                 _ => true,
392             },
393         }))
394     }
395 
396     /// Creates a sampler with unnormalized coordinates. This means that texture coordinates won't
397     /// range between `0.0` and `1.0` but use plain pixel offsets.
398     ///
399     /// Using an unnormalized sampler adds a few restrictions:
400     ///
401     /// - It can only be used with non-array 1D or 2D images.
402     /// - It can only be used with images with a single mipmap.
403     /// - Projection and offsets can't be used by shaders. Only the first mipmap can be accessed.
404     ///
405     /// # Panic
406     ///
407     /// - Panics if multiple `ClampToBorder` values are passed and the border color is different.
408     ///
unnormalized( device: Arc<Device>, filter: Filter, address_u: UnnormalizedSamplerAddressMode, address_v: UnnormalizedSamplerAddressMode, ) -> Result<Arc<Sampler>, SamplerCreationError>409     pub fn unnormalized(
410         device: Arc<Device>,
411         filter: Filter,
412         address_u: UnnormalizedSamplerAddressMode,
413         address_v: UnnormalizedSamplerAddressMode,
414     ) -> Result<Arc<Sampler>, SamplerCreationError> {
415         let fns = device.fns();
416 
417         let border_color = address_u.border_color();
418         let border_color = match (border_color, address_v.border_color()) {
419             (Some(b1), Some(b2)) => {
420                 assert_eq!(b1, b2);
421                 Some(b1)
422             }
423             (None, b) => b,
424             (b, None) => b,
425         };
426 
427         let sampler = unsafe {
428             let infos = ash::vk::SamplerCreateInfo {
429                 flags: ash::vk::SamplerCreateFlags::empty(),
430                 mag_filter: filter.into(),
431                 min_filter: filter.into(),
432                 mipmap_mode: ash::vk::SamplerMipmapMode::NEAREST,
433                 address_mode_u: address_u.into(),
434                 address_mode_v: address_v.into(),
435                 address_mode_w: ash::vk::SamplerAddressMode::CLAMP_TO_EDGE, // unused by the impl
436                 mip_lod_bias: 0.0,
437                 anisotropy_enable: ash::vk::FALSE,
438                 max_anisotropy: 1.0,
439                 compare_enable: ash::vk::FALSE,
440                 compare_op: ash::vk::CompareOp::NEVER,
441                 min_lod: 0.0,
442                 max_lod: 0.0,
443                 border_color: border_color
444                     .map(|b| b.into())
445                     .unwrap_or(ash::vk::BorderColor::FLOAT_TRANSPARENT_BLACK),
446                 unnormalized_coordinates: ash::vk::TRUE,
447                 ..Default::default()
448             };
449 
450             let mut output = MaybeUninit::uninit();
451             check_errors(fns.v1_0.create_sampler(
452                 device.internal_object(),
453                 &infos,
454                 ptr::null(),
455                 output.as_mut_ptr(),
456             ))?;
457             output.assume_init()
458         };
459 
460         Ok(Arc::new(Sampler {
461             sampler: sampler,
462             device: device.clone(),
463             compare_mode: false,
464             unnormalized: true,
465             usable_with_float_formats: match border_color {
466                 Some(BorderColor::FloatTransparentBlack) => true,
467                 Some(BorderColor::FloatOpaqueBlack) => true,
468                 Some(BorderColor::FloatOpaqueWhite) => true,
469                 Some(_) => false,
470                 None => true,
471             },
472             usable_with_int_formats: match border_color {
473                 Some(BorderColor::IntTransparentBlack) => true,
474                 Some(BorderColor::IntOpaqueBlack) => true,
475                 Some(BorderColor::IntOpaqueWhite) => true,
476                 Some(_) => false,
477                 None => true,
478             },
479             usable_with_swizzling: match border_color {
480                 Some(BorderColor::FloatOpaqueBlack) => false,
481                 Some(BorderColor::IntOpaqueBlack) => false,
482                 _ => true,
483             },
484         }))
485     }
486 
487     /// Returns true if the sampler is a compare-mode sampler.
488     #[inline]
compare_mode(&self) -> bool489     pub fn compare_mode(&self) -> bool {
490         self.compare_mode
491     }
492 
493     /// Returns true if the sampler is unnormalized.
494     #[inline]
is_unnormalized(&self) -> bool495     pub fn is_unnormalized(&self) -> bool {
496         self.unnormalized
497     }
498 
499     /// Returns true if the sampler can be used with floating-point image views. See the
500     /// documentation of the `sampler` module for more info.
501     #[inline]
usable_with_float_formats(&self) -> bool502     pub fn usable_with_float_formats(&self) -> bool {
503         self.usable_with_float_formats
504     }
505 
506     /// Returns true if the sampler can be used with integer image views. See the documentation of
507     /// the `sampler` module for more info.
508     #[inline]
usable_with_int_formats(&self) -> bool509     pub fn usable_with_int_formats(&self) -> bool {
510         self.usable_with_int_formats
511     }
512 
513     /// Returns true if the sampler can be used with image views that have non-identity swizzling.
514     /// See the documentation of the `sampler` module for more info.
515     #[inline]
usable_with_swizzling(&self) -> bool516     pub fn usable_with_swizzling(&self) -> bool {
517         self.usable_with_swizzling
518     }
519 }
520 
521 unsafe impl DeviceOwned for Sampler {
522     #[inline]
device(&self) -> &Arc<Device>523     fn device(&self) -> &Arc<Device> {
524         &self.device
525     }
526 }
527 
528 unsafe impl VulkanObject for Sampler {
529     type Object = ash::vk::Sampler;
530 
531     #[inline]
internal_object(&self) -> ash::vk::Sampler532     fn internal_object(&self) -> ash::vk::Sampler {
533         self.sampler
534     }
535 }
536 
537 impl fmt::Debug for Sampler {
538     #[inline]
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>539     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
540         write!(fmt, "<Vulkan sampler {:?}>", self.sampler)
541     }
542 }
543 
544 impl Drop for Sampler {
545     #[inline]
drop(&mut self)546     fn drop(&mut self) {
547         unsafe {
548             let fns = self.device.fns();
549             fns.v1_0
550                 .destroy_sampler(self.device.internal_object(), self.sampler, ptr::null());
551         }
552     }
553 }
554 
555 /// Describes how the color of each pixel should be determined.
556 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
557 #[repr(i32)]
558 pub enum Filter {
559     /// The four pixels whose center surround the requested coordinates are taken, then their
560     /// values are interpolated.
561     Linear = ash::vk::Filter::LINEAR.as_raw(),
562 
563     /// The pixel whose center is nearest to the requested coordinates is taken from the source
564     /// and its value is returned as-is.
565     Nearest = ash::vk::Filter::NEAREST.as_raw(),
566 }
567 
568 impl From<Filter> for ash::vk::Filter {
569     #[inline]
from(val: Filter) -> Self570     fn from(val: Filter) -> Self {
571         Self::from_raw(val as i32)
572     }
573 }
574 
575 /// Describes which mipmap from the source to use.
576 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
577 #[repr(i32)]
578 pub enum MipmapMode {
579     /// Use the mipmap whose dimensions are the nearest to the dimensions of the destination.
580     Nearest = ash::vk::SamplerMipmapMode::NEAREST.as_raw(),
581 
582     /// Take the mipmap whose dimensions are no greater than that of the destination together
583     /// with the next higher level mipmap, calculate the value for both, and interpolate them.
584     Linear = ash::vk::SamplerMipmapMode::LINEAR.as_raw(),
585 }
586 
587 impl From<MipmapMode> for ash::vk::SamplerMipmapMode {
588     #[inline]
from(val: MipmapMode) -> Self589     fn from(val: MipmapMode) -> Self {
590         Self::from_raw(val as i32)
591     }
592 }
593 
594 /// How the sampler should behave when it needs to access a pixel that is out of range of the
595 /// texture.
596 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
597 pub enum SamplerAddressMode {
598     /// Repeat the texture. In other words, the pixel at coordinate `x + 1.0` is the same as the
599     /// one at coordinate `x`.
600     Repeat,
601 
602     /// Repeat the texture but mirror it at every repetition. In other words, the pixel at
603     /// coordinate `x + 1.0` is the same as the one at coordinate `1.0 - x`.
604     MirroredRepeat,
605 
606     /// The coordinates are clamped to the valid range. Coordinates below 0.0 have the same value
607     /// as coordinate 0.0. Coordinates over 1.0 have the same value as coordinate 1.0.
608     ClampToEdge,
609 
610     /// Any pixel out of range is considered to be part of the "border" of the image, which has a
611     /// specific color of your choice.
612     ///
613     /// Note that if you use `ClampToBorder` multiple times, they must all have the same border
614     /// color.
615     ClampToBorder(BorderColor),
616 
617     /// Similar to `MirroredRepeat`, except that coordinates are clamped to the range
618     /// `[-1.0, 1.0]`.
619     MirrorClampToEdge,
620 }
621 
622 impl SamplerAddressMode {
623     #[inline]
border_color(self) -> Option<BorderColor>624     fn border_color(self) -> Option<BorderColor> {
625         match self {
626             SamplerAddressMode::ClampToBorder(c) => Some(c),
627             _ => None,
628         }
629     }
630 }
631 
632 impl From<SamplerAddressMode> for ash::vk::SamplerAddressMode {
633     #[inline]
from(val: SamplerAddressMode) -> Self634     fn from(val: SamplerAddressMode) -> Self {
635         match val {
636             SamplerAddressMode::Repeat => ash::vk::SamplerAddressMode::REPEAT,
637             SamplerAddressMode::MirroredRepeat => ash::vk::SamplerAddressMode::MIRRORED_REPEAT,
638             SamplerAddressMode::ClampToEdge => ash::vk::SamplerAddressMode::CLAMP_TO_EDGE,
639             SamplerAddressMode::ClampToBorder(_) => ash::vk::SamplerAddressMode::CLAMP_TO_BORDER,
640             SamplerAddressMode::MirrorClampToEdge => {
641                 ash::vk::SamplerAddressMode::MIRROR_CLAMP_TO_EDGE
642             }
643         }
644     }
645 }
646 
647 /// How the sampler should behave when it needs to access a pixel that is out of range of the
648 /// texture.
649 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
650 #[repr(u32)]
651 pub enum UnnormalizedSamplerAddressMode {
652     /// The coordinates are clamped to the valid range. Coordinates below 0 have the same value
653     /// as coordinate 0. Coordinates over *size of texture* have the same value as coordinate
654     /// *size of texture*.
655     ClampToEdge,
656 
657     /// Any pixel out of range is considered to be part of the "border" of the image, which has a
658     /// specific color of your choice.
659     ///
660     /// Note that if you use `ClampToBorder` multiple times, they must all have the same border
661     /// color.
662     ClampToBorder(BorderColor),
663 }
664 
665 impl UnnormalizedSamplerAddressMode {
666     #[inline]
border_color(self) -> Option<BorderColor>667     fn border_color(self) -> Option<BorderColor> {
668         match self {
669             UnnormalizedSamplerAddressMode::ClampToEdge => None,
670             UnnormalizedSamplerAddressMode::ClampToBorder(c) => Some(c),
671         }
672     }
673 }
674 
675 impl From<UnnormalizedSamplerAddressMode> for ash::vk::SamplerAddressMode {
676     #[inline]
from(val: UnnormalizedSamplerAddressMode) -> Self677     fn from(val: UnnormalizedSamplerAddressMode) -> Self {
678         match val {
679             UnnormalizedSamplerAddressMode::ClampToEdge => {
680                 ash::vk::SamplerAddressMode::CLAMP_TO_EDGE
681             }
682             UnnormalizedSamplerAddressMode::ClampToBorder(_) => {
683                 ash::vk::SamplerAddressMode::CLAMP_TO_BORDER
684             }
685         }
686     }
687 }
688 
689 /// The color to use for the border of an image.
690 ///
691 /// Only relevant if you use `ClampToBorder`.
692 ///
693 /// Using a border color restricts the sampler to either floating-point images or integer images.
694 /// See the documentation of the `sampler` module for more info.
695 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
696 #[repr(i32)]
697 pub enum BorderColor {
698     /// The value `(0.0, 0.0, 0.0, 0.0)`. Can only be used with floating-point images.
699     FloatTransparentBlack = ash::vk::BorderColor::FLOAT_TRANSPARENT_BLACK.as_raw(),
700 
701     /// The value `(0, 0, 0, 0)`. Can only be used with integer images.
702     IntTransparentBlack = ash::vk::BorderColor::INT_TRANSPARENT_BLACK.as_raw(),
703 
704     /// The value `(0.0, 0.0, 0.0, 1.0)`. Can only be used with floating-point identity-swizzled
705     /// images.
706     FloatOpaqueBlack = ash::vk::BorderColor::FLOAT_OPAQUE_BLACK.as_raw(),
707 
708     /// The value `(0, 0, 0, 1)`. Can only be used with integer identity-swizzled images.
709     IntOpaqueBlack = ash::vk::BorderColor::INT_OPAQUE_BLACK.as_raw(),
710 
711     /// The value `(1.0, 1.0, 1.0, 1.0)`. Can only be used with floating-point images.
712     FloatOpaqueWhite = ash::vk::BorderColor::FLOAT_OPAQUE_WHITE.as_raw(),
713 
714     /// The value `(1, 1, 1, 1)`. Can only be used with integer images.
715     IntOpaqueWhite = ash::vk::BorderColor::INT_OPAQUE_WHITE.as_raw(),
716 }
717 
718 impl From<BorderColor> for ash::vk::BorderColor {
719     #[inline]
from(val: BorderColor) -> Self720     fn from(val: BorderColor) -> Self {
721         Self::from_raw(val as i32)
722     }
723 }
724 
725 /// Error that can happen when creating an instance.
726 #[derive(Clone, Debug, PartialEq)]
727 pub enum SamplerCreationError {
728     /// Not enough memory.
729     OomError(OomError),
730 
731     /// Too many sampler objects have been created. You must destroy some before creating new ones.
732     /// Note the specs guarantee that at least 4000 samplers can exist simultaneously.
733     TooManyObjects,
734 
735     /// Using an anisotropy greater than 1.0 requires enabling the `sampler_anisotropy` feature
736     /// when creating the device.
737     SamplerAnisotropyFeatureNotEnabled,
738 
739     /// The requested anisotropy level exceeds the device's limits.
740     AnisotropyLimitExceeded {
741         /// The value that was requested.
742         requested: f32,
743         /// The maximum supported value.
744         maximum: f32,
745     },
746 
747     /// The requested mip lod bias exceeds the device's limits.
748     MipLodBiasLimitExceeded {
749         /// The value that was requested.
750         requested: f32,
751         /// The maximum supported value.
752         maximum: f32,
753     },
754 
755     /// Using `MirrorClampToEdge` requires enabling the `VK_KHR_sampler_mirror_clamp_to_edge`
756     /// extension when creating the device.
757     SamplerMirrorClampToEdgeExtensionNotEnabled,
758 }
759 
760 impl error::Error for SamplerCreationError {
761     #[inline]
source(&self) -> Option<&(dyn error::Error + 'static)>762     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
763         match *self {
764             SamplerCreationError::OomError(ref err) => Some(err),
765             _ => None,
766         }
767     }
768 }
769 
770 impl fmt::Display for SamplerCreationError {
771     #[inline]
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>772     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
773         write!(
774             fmt,
775             "{}",
776             match *self {
777                 SamplerCreationError::OomError(_) => "not enough memory available",
778                 SamplerCreationError::TooManyObjects => "too many simultaneous sampler objects",
779                 SamplerCreationError::SamplerAnisotropyFeatureNotEnabled => {
780                     "the `sampler_anisotropy` feature is not enabled"
781                 }
782                 SamplerCreationError::AnisotropyLimitExceeded { .. } => "anisotropy limit exceeded",
783                 SamplerCreationError::MipLodBiasLimitExceeded { .. } =>
784                     "mip lod bias limit exceeded",
785                 SamplerCreationError::SamplerMirrorClampToEdgeExtensionNotEnabled => {
786                     "the device extension `VK_KHR_sampler_mirror_clamp_to_edge` is not enabled"
787                 }
788             }
789         )
790     }
791 }
792 
793 impl From<OomError> for SamplerCreationError {
794     #[inline]
from(err: OomError) -> SamplerCreationError795     fn from(err: OomError) -> SamplerCreationError {
796         SamplerCreationError::OomError(err)
797     }
798 }
799 
800 impl From<Error> for SamplerCreationError {
801     #[inline]
from(err: Error) -> SamplerCreationError802     fn from(err: Error) -> SamplerCreationError {
803         match err {
804             err @ Error::OutOfHostMemory => SamplerCreationError::OomError(OomError::from(err)),
805             err @ Error::OutOfDeviceMemory => SamplerCreationError::OomError(OomError::from(err)),
806             Error::TooManyObjects => SamplerCreationError::TooManyObjects,
807             _ => panic!("unexpected error: {:?}", err),
808         }
809     }
810 }
811 
812 #[cfg(test)]
813 mod tests {
814     use crate::sampler;
815 
816     #[test]
create_regular()817     fn create_regular() {
818         let (device, queue) = gfx_dev_and_queue!();
819 
820         let s = sampler::Sampler::new(
821             device,
822             sampler::Filter::Linear,
823             sampler::Filter::Linear,
824             sampler::MipmapMode::Nearest,
825             sampler::SamplerAddressMode::Repeat,
826             sampler::SamplerAddressMode::Repeat,
827             sampler::SamplerAddressMode::Repeat,
828             1.0,
829             1.0,
830             0.0,
831             2.0,
832         )
833         .unwrap();
834         assert!(!s.compare_mode());
835         assert!(!s.is_unnormalized());
836     }
837 
838     #[test]
create_compare()839     fn create_compare() {
840         let (device, queue) = gfx_dev_and_queue!();
841 
842         let s = sampler::Sampler::compare(
843             device,
844             sampler::Filter::Linear,
845             sampler::Filter::Linear,
846             sampler::MipmapMode::Nearest,
847             sampler::SamplerAddressMode::Repeat,
848             sampler::SamplerAddressMode::Repeat,
849             sampler::SamplerAddressMode::Repeat,
850             1.0,
851             1.0,
852             0.0,
853             2.0,
854             sampler::Compare::Less,
855         )
856         .unwrap();
857 
858         assert!(s.compare_mode());
859         assert!(!s.is_unnormalized());
860     }
861 
862     #[test]
create_unnormalized()863     fn create_unnormalized() {
864         let (device, queue) = gfx_dev_and_queue!();
865 
866         let s = sampler::Sampler::unnormalized(
867             device,
868             sampler::Filter::Linear,
869             sampler::UnnormalizedSamplerAddressMode::ClampToEdge,
870             sampler::UnnormalizedSamplerAddressMode::ClampToEdge,
871         )
872         .unwrap();
873 
874         assert!(!s.compare_mode());
875         assert!(s.is_unnormalized());
876     }
877 
878     #[test]
simple_repeat_linear()879     fn simple_repeat_linear() {
880         let (device, queue) = gfx_dev_and_queue!();
881         let _ = sampler::Sampler::simple_repeat_linear(device);
882     }
883 
884     #[test]
simple_repeat_linear_no_mipmap()885     fn simple_repeat_linear_no_mipmap() {
886         let (device, queue) = gfx_dev_and_queue!();
887         let _ = sampler::Sampler::simple_repeat_linear_no_mipmap(device);
888     }
889 
890     #[test]
min_lod_inferior()891     fn min_lod_inferior() {
892         let (device, queue) = gfx_dev_and_queue!();
893 
894         assert_should_panic!({
895             let _ = sampler::Sampler::new(
896                 device,
897                 sampler::Filter::Linear,
898                 sampler::Filter::Linear,
899                 sampler::MipmapMode::Nearest,
900                 sampler::SamplerAddressMode::Repeat,
901                 sampler::SamplerAddressMode::Repeat,
902                 sampler::SamplerAddressMode::Repeat,
903                 1.0,
904                 1.0,
905                 5.0,
906                 2.0,
907             );
908         });
909     }
910 
911     #[test]
max_anisotropy()912     fn max_anisotropy() {
913         let (device, queue) = gfx_dev_and_queue!();
914 
915         assert_should_panic!({
916             let _ = sampler::Sampler::new(
917                 device,
918                 sampler::Filter::Linear,
919                 sampler::Filter::Linear,
920                 sampler::MipmapMode::Nearest,
921                 sampler::SamplerAddressMode::Repeat,
922                 sampler::SamplerAddressMode::Repeat,
923                 sampler::SamplerAddressMode::Repeat,
924                 1.0,
925                 0.5,
926                 0.0,
927                 2.0,
928             );
929         });
930     }
931 
932     #[test]
different_borders()933     fn different_borders() {
934         let (device, queue) = gfx_dev_and_queue!();
935 
936         let b1 = sampler::BorderColor::IntTransparentBlack;
937         let b2 = sampler::BorderColor::FloatOpaqueWhite;
938 
939         assert_should_panic!({
940             let _ = sampler::Sampler::new(
941                 device,
942                 sampler::Filter::Linear,
943                 sampler::Filter::Linear,
944                 sampler::MipmapMode::Nearest,
945                 sampler::SamplerAddressMode::ClampToBorder(b1),
946                 sampler::SamplerAddressMode::ClampToBorder(b2),
947                 sampler::SamplerAddressMode::Repeat,
948                 1.0,
949                 1.0,
950                 5.0,
951                 2.0,
952             );
953         });
954     }
955 
956     #[test]
anisotropy_feature()957     fn anisotropy_feature() {
958         let (device, queue) = gfx_dev_and_queue!();
959 
960         let r = sampler::Sampler::new(
961             device,
962             sampler::Filter::Linear,
963             sampler::Filter::Linear,
964             sampler::MipmapMode::Nearest,
965             sampler::SamplerAddressMode::Repeat,
966             sampler::SamplerAddressMode::Repeat,
967             sampler::SamplerAddressMode::Repeat,
968             1.0,
969             2.0,
970             0.0,
971             2.0,
972         );
973 
974         match r {
975             Err(sampler::SamplerCreationError::SamplerAnisotropyFeatureNotEnabled) => (),
976             _ => panic!(),
977         }
978     }
979 
980     #[test]
anisotropy_limit()981     fn anisotropy_limit() {
982         let (device, queue) = gfx_dev_and_queue!(sampler_anisotropy);
983 
984         let r = sampler::Sampler::new(
985             device,
986             sampler::Filter::Linear,
987             sampler::Filter::Linear,
988             sampler::MipmapMode::Nearest,
989             sampler::SamplerAddressMode::Repeat,
990             sampler::SamplerAddressMode::Repeat,
991             sampler::SamplerAddressMode::Repeat,
992             1.0,
993             100000000.0,
994             0.0,
995             2.0,
996         );
997 
998         match r {
999             Err(sampler::SamplerCreationError::AnisotropyLimitExceeded { .. }) => (),
1000             _ => panic!(),
1001         }
1002     }
1003 
1004     #[test]
mip_lod_bias_limit()1005     fn mip_lod_bias_limit() {
1006         let (device, queue) = gfx_dev_and_queue!();
1007 
1008         let r = sampler::Sampler::new(
1009             device,
1010             sampler::Filter::Linear,
1011             sampler::Filter::Linear,
1012             sampler::MipmapMode::Nearest,
1013             sampler::SamplerAddressMode::Repeat,
1014             sampler::SamplerAddressMode::Repeat,
1015             sampler::SamplerAddressMode::Repeat,
1016             100000000.0,
1017             1.0,
1018             0.0,
1019             2.0,
1020         );
1021 
1022         match r {
1023             Err(sampler::SamplerCreationError::MipLodBiasLimitExceeded { .. }) => (),
1024             _ => panic!(),
1025         }
1026     }
1027 
1028     #[test]
sampler_mirror_clamp_to_edge_extension()1029     fn sampler_mirror_clamp_to_edge_extension() {
1030         let (device, queue) = gfx_dev_and_queue!();
1031 
1032         let r = sampler::Sampler::new(
1033             device,
1034             sampler::Filter::Linear,
1035             sampler::Filter::Linear,
1036             sampler::MipmapMode::Nearest,
1037             sampler::SamplerAddressMode::MirrorClampToEdge,
1038             sampler::SamplerAddressMode::MirrorClampToEdge,
1039             sampler::SamplerAddressMode::MirrorClampToEdge,
1040             1.0,
1041             1.0,
1042             0.0,
1043             2.0,
1044         );
1045 
1046         match r {
1047             Err(sampler::SamplerCreationError::SamplerMirrorClampToEdgeExtensionNotEnabled) => (),
1048             _ => panic!(),
1049         }
1050     }
1051 }
1052