| // Copyright (c) 2016 The vulkano developers |
| // Licensed under the Apache License, Version 2.0 |
| // <LICENSE-APACHE or |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT |
| // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, |
| // at your option. All files in the project carrying such |
| // notice may not be copied, modified, or distributed except |
| // according to those terms. |
| |
| //! How to retrieve data from an image within a shader. |
| //! |
| //! When you retrieve data from an image, you have to pass the coordinates of the pixel you want |
| //! to retrieve. The implementation then performs various calculations, and these operations are |
| //! what the `Sampler` struct describes. |
| //! |
| //! Sampling is a very complex topic but that hasn't changed much since the beginnings of 3D |
| //! rendering. Documentation here is missing, but any tutorial about OpenGL or DirectX can teach |
| //! you how it works. |
| //! |
| //! # Examples |
| //! |
| //! A simple sampler for most usages: |
| //! |
| //! ``` |
| //! use vulkano::sampler::Sampler; |
| //! |
| //! # let device: std::sync::Arc<vulkano::device::Device> = return; |
| //! let _sampler = Sampler::simple_repeat_linear_no_mipmap(device.clone()); |
| //! ``` |
| //! |
| //! More detailed sampler creation: |
| //! |
| //! ``` |
| //! use vulkano::sampler; |
| //! |
| //! # let device: std::sync::Arc<vulkano::device::Device> = return; |
| //! let _sampler = sampler::Sampler::new(device.clone(), sampler::Filter::Linear, |
| //! sampler::Filter::Linear, |
| //! sampler::MipmapMode::Nearest, |
| //! sampler::SamplerAddressMode::Repeat, |
| //! sampler::SamplerAddressMode::Repeat, |
| //! sampler::SamplerAddressMode::Repeat, 1.0, 1.0, |
| //! 0.0, 100.0).unwrap();; |
| //! ``` |
| //! |
| //! # About border colors |
| //! |
| //! One of the possible values of `SamplerAddressMode` and `UnnormalizedSamplerAddressMode` is |
| //! `ClampToBorder`. This value indicates that accessing an image outside of its range must return |
| //! the specified color. |
| //! |
| //! However this comes with restrictions. When using a floating-point border color, the sampler can |
| //! only be used with floating-point or depth image views. When using an integer border color, the |
| //! sampler can only be used with integer or stencil image views. In addition to this, you can't |
| //! use an opaque black border color with an image view that uses components swizzling. |
| //! |
| //! > **Note**: The reason for this restriction about opaque black borders is that the value of the |
| //! > alpha is 1.0 while the value of the color components is 0.0. In the other border colors, the |
| //! > value of all the components is the same. |
| //! |
| //! Samplers that don't use `ClampToBorder` are not concerned by these restrictions. |
| //! |
| // FIXME: restrictions aren't checked yet |
| |
| use crate::check_errors; |
| use crate::device::Device; |
| use crate::device::DeviceOwned; |
| pub use crate::pipeline::depth_stencil::Compare; |
| use crate::Error; |
| use crate::OomError; |
| use crate::VulkanObject; |
| use std::error; |
| use std::fmt; |
| use std::mem::MaybeUninit; |
| use std::ptr; |
| use std::sync::Arc; |
| |
| /// Describes how to retrieve data from an image within a shader. |
| pub struct Sampler { |
| sampler: ash::vk::Sampler, |
| device: Arc<Device>, |
| compare_mode: bool, |
| unnormalized: bool, |
| usable_with_float_formats: bool, |
| usable_with_int_formats: bool, |
| usable_with_swizzling: bool, |
| } |
| |
| impl Sampler { |
| /// Shortcut for creating a sampler with linear sampling, linear mipmaps, and with the repeat |
| /// mode for borders. |
| /// |
| /// Useful for prototyping, but can also be used in real projects. |
| /// |
| /// # Panic |
| /// |
| /// - Panics if out of memory or the maximum number of samplers has exceeded. |
| /// |
| #[inline] |
| pub fn simple_repeat_linear(device: Arc<Device>) -> Arc<Sampler> { |
| Sampler::new( |
| device, |
| Filter::Linear, |
| Filter::Linear, |
| MipmapMode::Linear, |
| SamplerAddressMode::Repeat, |
| SamplerAddressMode::Repeat, |
| SamplerAddressMode::Repeat, |
| 0.0, |
| 1.0, |
| 0.0, |
| 1_000.0, |
| ) |
| .unwrap() |
| } |
| |
| /// Shortcut for creating a sampler with linear sampling, that only uses the main level of |
| /// images, and with the repeat mode for borders. |
| /// |
| /// Useful for prototyping, but can also be used in real projects. |
| /// |
| /// # Panic |
| /// |
| /// - Panics if out of memory or the maximum number of samplers has exceeded. |
| /// |
| #[inline] |
| pub fn simple_repeat_linear_no_mipmap(device: Arc<Device>) -> Arc<Sampler> { |
| Sampler::new( |
| device, |
| Filter::Linear, |
| Filter::Linear, |
| MipmapMode::Nearest, |
| SamplerAddressMode::Repeat, |
| SamplerAddressMode::Repeat, |
| SamplerAddressMode::Repeat, |
| 0.0, |
| 1.0, |
| 0.0, |
| 1.0, |
| ) |
| .unwrap() |
| } |
| |
| /// Creates a new `Sampler` with the given behavior. |
| /// |
| /// `mag_filter` and `min_filter` define how the implementation should sample from the image |
| /// when it is respectively larger and smaller than the original. |
| /// |
| /// `mipmap_mode` defines how the implementation should choose which mipmap to use. |
| /// |
| /// `address_u`, `address_v` and `address_w` define how the implementation should behave when |
| /// sampling outside of the texture coordinates range `[0.0, 1.0]`. |
| /// |
| /// `mip_lod_bias` is a value to add to . |
| /// |
| /// `max_anisotropy` must be greater than or equal to 1.0. If greater than 1.0, the |
| /// implementation will use anisotropic filtering. Using a value greater than 1.0 requires |
| /// the `sampler_anisotropy` feature to be enabled when creating the device. |
| /// |
| /// `min_lod` and `max_lod` are respectively the minimum and maximum mipmap level to use. |
| /// `max_lod` must always be greater than or equal to `min_lod`. |
| /// |
| /// # Panic |
| /// |
| /// - Panics if multiple `ClampToBorder` values are passed and the border color is different. |
| /// - Panics if `max_anisotropy < 1.0`. |
| /// - Panics if `min_lod > max_lod`. |
| /// |
| #[inline(always)] |
| pub fn 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> { |
| Sampler::new_impl( |
| device, |
| mag_filter, |
| min_filter, |
| mipmap_mode, |
| address_u, |
| address_v, |
| address_w, |
| mip_lod_bias, |
| max_anisotropy, |
| min_lod, |
| max_lod, |
| None, |
| ) |
| } |
| |
| /// Creates a new `Sampler` with the given behavior. |
| /// |
| /// Contrary to `new`, this creates a sampler that is used to compare depth values. |
| /// |
| /// A sampler like this can only operate on depth or depth-stencil textures. Instead of |
| /// returning the value of the texture, this sampler will return a value between 0.0 and 1.0 |
| /// indicating how much the reference value (passed by the shader) compares to the value in the |
| /// texture. |
| /// |
| /// Note that it doesn't make sense to create a compare-mode sampler with an integer border |
| /// color, as such a sampler would be unusable. |
| /// |
| /// # Panic |
| /// |
| /// Same panic reasons as `new`. |
| /// |
| #[inline(always)] |
| pub fn 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> { |
| Sampler::new_impl( |
| device, |
| mag_filter, |
| min_filter, |
| mipmap_mode, |
| address_u, |
| address_v, |
| address_w, |
| mip_lod_bias, |
| max_anisotropy, |
| min_lod, |
| max_lod, |
| Some(compare), |
| ) |
| } |
| |
| fn 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> { |
| assert!(max_anisotropy >= 1.0); |
| assert!(min_lod <= max_lod); |
| |
| // Check max anisotropy. |
| if max_anisotropy > 1.0 { |
| if !device.enabled_features().sampler_anisotropy { |
| return Err(SamplerCreationError::SamplerAnisotropyFeatureNotEnabled); |
| } |
| |
| let limit = device |
| .physical_device() |
| .properties() |
| .max_sampler_anisotropy; |
| if max_anisotropy > limit { |
| return Err(SamplerCreationError::AnisotropyLimitExceeded { |
| requested: max_anisotropy, |
| maximum: limit, |
| }); |
| } |
| } |
| |
| // Check mip_lod_bias value. |
| { |
| let limit = device |
| .physical_device() |
| .properties() |
| .max_sampler_lod_bias; |
| if mip_lod_bias > limit { |
| return Err(SamplerCreationError::MipLodBiasLimitExceeded { |
| requested: mip_lod_bias, |
| maximum: limit, |
| }); |
| } |
| } |
| |
| // Check MirrorClampToEdge extension support |
| if [address_u, address_v, address_w] |
| .iter() |
| .any(|&mode| mode == SamplerAddressMode::MirrorClampToEdge) |
| { |
| if !device.enabled_extensions().khr_sampler_mirror_clamp_to_edge { |
| return Err(SamplerCreationError::SamplerMirrorClampToEdgeExtensionNotEnabled); |
| } |
| } |
| |
| // Handling border color. |
| let border_color = address_u.border_color(); |
| let border_color = match (border_color, address_v.border_color()) { |
| (Some(b1), Some(b2)) => { |
| assert_eq!(b1, b2); |
| Some(b1) |
| } |
| (None, b) => b, |
| (b, None) => b, |
| }; |
| let border_color = match (border_color, address_w.border_color()) { |
| (Some(b1), Some(b2)) => { |
| assert_eq!(b1, b2); |
| Some(b1) |
| } |
| (None, b) => b, |
| (b, None) => b, |
| }; |
| |
| let fns = device.fns(); |
| let sampler = unsafe { |
| let infos = ash::vk::SamplerCreateInfo { |
| flags: ash::vk::SamplerCreateFlags::empty(), |
| mag_filter: mag_filter.into(), |
| min_filter: min_filter.into(), |
| mipmap_mode: mipmap_mode.into(), |
| address_mode_u: address_u.into(), |
| address_mode_v: address_v.into(), |
| address_mode_w: address_w.into(), |
| mip_lod_bias: mip_lod_bias, |
| anisotropy_enable: if max_anisotropy > 1.0 { |
| ash::vk::TRUE |
| } else { |
| ash::vk::FALSE |
| }, |
| max_anisotropy: max_anisotropy, |
| compare_enable: if compare.is_some() { |
| ash::vk::TRUE |
| } else { |
| ash::vk::FALSE |
| }, |
| compare_op: compare |
| .map(|c| c.into()) |
| .unwrap_or(ash::vk::CompareOp::NEVER), |
| min_lod: min_lod, |
| max_lod: max_lod, |
| border_color: border_color |
| .map(|b| b.into()) |
| .unwrap_or(ash::vk::BorderColor::FLOAT_TRANSPARENT_BLACK), |
| unnormalized_coordinates: ash::vk::FALSE, |
| ..Default::default() |
| }; |
| |
| let mut output = MaybeUninit::uninit(); |
| check_errors(fns.v1_0.create_sampler( |
| device.internal_object(), |
| &infos, |
| ptr::null(), |
| output.as_mut_ptr(), |
| ))?; |
| output.assume_init() |
| }; |
| |
| Ok(Arc::new(Sampler { |
| sampler: sampler, |
| device: device.clone(), |
| compare_mode: compare.is_some(), |
| unnormalized: false, |
| usable_with_float_formats: match border_color { |
| Some(BorderColor::FloatTransparentBlack) => true, |
| Some(BorderColor::FloatOpaqueBlack) => true, |
| Some(BorderColor::FloatOpaqueWhite) => true, |
| Some(_) => false, |
| None => true, |
| }, |
| usable_with_int_formats: compare.is_none() |
| && match border_color { |
| Some(BorderColor::IntTransparentBlack) => true, |
| Some(BorderColor::IntOpaqueBlack) => true, |
| Some(BorderColor::IntOpaqueWhite) => true, |
| Some(_) => false, |
| None => true, |
| }, |
| usable_with_swizzling: match border_color { |
| Some(BorderColor::FloatOpaqueBlack) => false, |
| Some(BorderColor::IntOpaqueBlack) => false, |
| _ => true, |
| }, |
| })) |
| } |
| |
| /// Creates a sampler with unnormalized coordinates. This means that texture coordinates won't |
| /// range between `0.0` and `1.0` but use plain pixel offsets. |
| /// |
| /// Using an unnormalized sampler adds a few restrictions: |
| /// |
| /// - It can only be used with non-array 1D or 2D images. |
| /// - It can only be used with images with a single mipmap. |
| /// - Projection and offsets can't be used by shaders. Only the first mipmap can be accessed. |
| /// |
| /// # Panic |
| /// |
| /// - Panics if multiple `ClampToBorder` values are passed and the border color is different. |
| /// |
| pub fn unnormalized( |
| device: Arc<Device>, |
| filter: Filter, |
| address_u: UnnormalizedSamplerAddressMode, |
| address_v: UnnormalizedSamplerAddressMode, |
| ) -> Result<Arc<Sampler>, SamplerCreationError> { |
| let fns = device.fns(); |
| |
| let border_color = address_u.border_color(); |
| let border_color = match (border_color, address_v.border_color()) { |
| (Some(b1), Some(b2)) => { |
| assert_eq!(b1, b2); |
| Some(b1) |
| } |
| (None, b) => b, |
| (b, None) => b, |
| }; |
| |
| let sampler = unsafe { |
| let infos = ash::vk::SamplerCreateInfo { |
| flags: ash::vk::SamplerCreateFlags::empty(), |
| mag_filter: filter.into(), |
| min_filter: filter.into(), |
| mipmap_mode: ash::vk::SamplerMipmapMode::NEAREST, |
| address_mode_u: address_u.into(), |
| address_mode_v: address_v.into(), |
| address_mode_w: ash::vk::SamplerAddressMode::CLAMP_TO_EDGE, // unused by the impl |
| mip_lod_bias: 0.0, |
| anisotropy_enable: ash::vk::FALSE, |
| max_anisotropy: 1.0, |
| compare_enable: ash::vk::FALSE, |
| compare_op: ash::vk::CompareOp::NEVER, |
| min_lod: 0.0, |
| max_lod: 0.0, |
| border_color: border_color |
| .map(|b| b.into()) |
| .unwrap_or(ash::vk::BorderColor::FLOAT_TRANSPARENT_BLACK), |
| unnormalized_coordinates: ash::vk::TRUE, |
| ..Default::default() |
| }; |
| |
| let mut output = MaybeUninit::uninit(); |
| check_errors(fns.v1_0.create_sampler( |
| device.internal_object(), |
| &infos, |
| ptr::null(), |
| output.as_mut_ptr(), |
| ))?; |
| output.assume_init() |
| }; |
| |
| Ok(Arc::new(Sampler { |
| sampler: sampler, |
| device: device.clone(), |
| compare_mode: false, |
| unnormalized: true, |
| usable_with_float_formats: match border_color { |
| Some(BorderColor::FloatTransparentBlack) => true, |
| Some(BorderColor::FloatOpaqueBlack) => true, |
| Some(BorderColor::FloatOpaqueWhite) => true, |
| Some(_) => false, |
| None => true, |
| }, |
| usable_with_int_formats: match border_color { |
| Some(BorderColor::IntTransparentBlack) => true, |
| Some(BorderColor::IntOpaqueBlack) => true, |
| Some(BorderColor::IntOpaqueWhite) => true, |
| Some(_) => false, |
| None => true, |
| }, |
| usable_with_swizzling: match border_color { |
| Some(BorderColor::FloatOpaqueBlack) => false, |
| Some(BorderColor::IntOpaqueBlack) => false, |
| _ => true, |
| }, |
| })) |
| } |
| |
| /// Returns true if the sampler is a compare-mode sampler. |
| #[inline] |
| pub fn compare_mode(&self) -> bool { |
| self.compare_mode |
| } |
| |
| /// Returns true if the sampler is unnormalized. |
| #[inline] |
| pub fn is_unnormalized(&self) -> bool { |
| self.unnormalized |
| } |
| |
| /// Returns true if the sampler can be used with floating-point image views. See the |
| /// documentation of the `sampler` module for more info. |
| #[inline] |
| pub fn usable_with_float_formats(&self) -> bool { |
| self.usable_with_float_formats |
| } |
| |
| /// Returns true if the sampler can be used with integer image views. See the documentation of |
| /// the `sampler` module for more info. |
| #[inline] |
| pub fn usable_with_int_formats(&self) -> bool { |
| self.usable_with_int_formats |
| } |
| |
| /// Returns true if the sampler can be used with image views that have non-identity swizzling. |
| /// See the documentation of the `sampler` module for more info. |
| #[inline] |
| pub fn usable_with_swizzling(&self) -> bool { |
| self.usable_with_swizzling |
| } |
| } |
| |
| unsafe impl DeviceOwned for Sampler { |
| #[inline] |
| fn device(&self) -> &Arc<Device> { |
| &self.device |
| } |
| } |
| |
| unsafe impl VulkanObject for Sampler { |
| type Object = ash::vk::Sampler; |
| |
| #[inline] |
| fn internal_object(&self) -> ash::vk::Sampler { |
| self.sampler |
| } |
| } |
| |
| impl fmt::Debug for Sampler { |
| #[inline] |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| write!(fmt, "<Vulkan sampler {:?}>", self.sampler) |
| } |
| } |
| |
| impl Drop for Sampler { |
| #[inline] |
| fn drop(&mut self) { |
| unsafe { |
| let fns = self.device.fns(); |
| fns.v1_0 |
| .destroy_sampler(self.device.internal_object(), self.sampler, ptr::null()); |
| } |
| } |
| } |
| |
| /// Describes how the color of each pixel should be determined. |
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
| #[repr(i32)] |
| pub enum Filter { |
| /// The four pixels whose center surround the requested coordinates are taken, then their |
| /// values are interpolated. |
| Linear = ash::vk::Filter::LINEAR.as_raw(), |
| |
| /// The pixel whose center is nearest to the requested coordinates is taken from the source |
| /// and its value is returned as-is. |
| Nearest = ash::vk::Filter::NEAREST.as_raw(), |
| } |
| |
| impl From<Filter> for ash::vk::Filter { |
| #[inline] |
| fn from(val: Filter) -> Self { |
| Self::from_raw(val as i32) |
| } |
| } |
| |
| /// Describes which mipmap from the source to use. |
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
| #[repr(i32)] |
| pub enum MipmapMode { |
| /// Use the mipmap whose dimensions are the nearest to the dimensions of the destination. |
| Nearest = ash::vk::SamplerMipmapMode::NEAREST.as_raw(), |
| |
| /// Take the mipmap whose dimensions are no greater than that of the destination together |
| /// with the next higher level mipmap, calculate the value for both, and interpolate them. |
| Linear = ash::vk::SamplerMipmapMode::LINEAR.as_raw(), |
| } |
| |
| impl From<MipmapMode> for ash::vk::SamplerMipmapMode { |
| #[inline] |
| fn from(val: MipmapMode) -> Self { |
| Self::from_raw(val as i32) |
| } |
| } |
| |
| /// How the sampler should behave when it needs to access a pixel that is out of range of the |
| /// texture. |
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
| pub enum SamplerAddressMode { |
| /// Repeat the texture. In other words, the pixel at coordinate `x + 1.0` is the same as the |
| /// one at coordinate `x`. |
| Repeat, |
| |
| /// Repeat the texture but mirror it at every repetition. In other words, the pixel at |
| /// coordinate `x + 1.0` is the same as the one at coordinate `1.0 - x`. |
| MirroredRepeat, |
| |
| /// The coordinates are clamped to the valid range. Coordinates below 0.0 have the same value |
| /// as coordinate 0.0. Coordinates over 1.0 have the same value as coordinate 1.0. |
| ClampToEdge, |
| |
| /// Any pixel out of range is considered to be part of the "border" of the image, which has a |
| /// specific color of your choice. |
| /// |
| /// Note that if you use `ClampToBorder` multiple times, they must all have the same border |
| /// color. |
| ClampToBorder(BorderColor), |
| |
| /// Similar to `MirroredRepeat`, except that coordinates are clamped to the range |
| /// `[-1.0, 1.0]`. |
| MirrorClampToEdge, |
| } |
| |
| impl SamplerAddressMode { |
| #[inline] |
| fn border_color(self) -> Option<BorderColor> { |
| match self { |
| SamplerAddressMode::ClampToBorder(c) => Some(c), |
| _ => None, |
| } |
| } |
| } |
| |
| impl From<SamplerAddressMode> for ash::vk::SamplerAddressMode { |
| #[inline] |
| fn from(val: SamplerAddressMode) -> Self { |
| match val { |
| SamplerAddressMode::Repeat => ash::vk::SamplerAddressMode::REPEAT, |
| SamplerAddressMode::MirroredRepeat => ash::vk::SamplerAddressMode::MIRRORED_REPEAT, |
| SamplerAddressMode::ClampToEdge => ash::vk::SamplerAddressMode::CLAMP_TO_EDGE, |
| SamplerAddressMode::ClampToBorder(_) => ash::vk::SamplerAddressMode::CLAMP_TO_BORDER, |
| SamplerAddressMode::MirrorClampToEdge => { |
| ash::vk::SamplerAddressMode::MIRROR_CLAMP_TO_EDGE |
| } |
| } |
| } |
| } |
| |
| /// How the sampler should behave when it needs to access a pixel that is out of range of the |
| /// texture. |
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
| #[repr(u32)] |
| pub enum UnnormalizedSamplerAddressMode { |
| /// The coordinates are clamped to the valid range. Coordinates below 0 have the same value |
| /// as coordinate 0. Coordinates over *size of texture* have the same value as coordinate |
| /// *size of texture*. |
| ClampToEdge, |
| |
| /// Any pixel out of range is considered to be part of the "border" of the image, which has a |
| /// specific color of your choice. |
| /// |
| /// Note that if you use `ClampToBorder` multiple times, they must all have the same border |
| /// color. |
| ClampToBorder(BorderColor), |
| } |
| |
| impl UnnormalizedSamplerAddressMode { |
| #[inline] |
| fn border_color(self) -> Option<BorderColor> { |
| match self { |
| UnnormalizedSamplerAddressMode::ClampToEdge => None, |
| UnnormalizedSamplerAddressMode::ClampToBorder(c) => Some(c), |
| } |
| } |
| } |
| |
| impl From<UnnormalizedSamplerAddressMode> for ash::vk::SamplerAddressMode { |
| #[inline] |
| fn from(val: UnnormalizedSamplerAddressMode) -> Self { |
| match val { |
| UnnormalizedSamplerAddressMode::ClampToEdge => { |
| ash::vk::SamplerAddressMode::CLAMP_TO_EDGE |
| } |
| UnnormalizedSamplerAddressMode::ClampToBorder(_) => { |
| ash::vk::SamplerAddressMode::CLAMP_TO_BORDER |
| } |
| } |
| } |
| } |
| |
| /// The color to use for the border of an image. |
| /// |
| /// Only relevant if you use `ClampToBorder`. |
| /// |
| /// Using a border color restricts the sampler to either floating-point images or integer images. |
| /// See the documentation of the `sampler` module for more info. |
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
| #[repr(i32)] |
| pub enum BorderColor { |
| /// The value `(0.0, 0.0, 0.0, 0.0)`. Can only be used with floating-point images. |
| FloatTransparentBlack = ash::vk::BorderColor::FLOAT_TRANSPARENT_BLACK.as_raw(), |
| |
| /// The value `(0, 0, 0, 0)`. Can only be used with integer images. |
| IntTransparentBlack = ash::vk::BorderColor::INT_TRANSPARENT_BLACK.as_raw(), |
| |
| /// The value `(0.0, 0.0, 0.0, 1.0)`. Can only be used with floating-point identity-swizzled |
| /// images. |
| FloatOpaqueBlack = ash::vk::BorderColor::FLOAT_OPAQUE_BLACK.as_raw(), |
| |
| /// The value `(0, 0, 0, 1)`. Can only be used with integer identity-swizzled images. |
| IntOpaqueBlack = ash::vk::BorderColor::INT_OPAQUE_BLACK.as_raw(), |
| |
| /// The value `(1.0, 1.0, 1.0, 1.0)`. Can only be used with floating-point images. |
| FloatOpaqueWhite = ash::vk::BorderColor::FLOAT_OPAQUE_WHITE.as_raw(), |
| |
| /// The value `(1, 1, 1, 1)`. Can only be used with integer images. |
| IntOpaqueWhite = ash::vk::BorderColor::INT_OPAQUE_WHITE.as_raw(), |
| } |
| |
| impl From<BorderColor> for ash::vk::BorderColor { |
| #[inline] |
| fn from(val: BorderColor) -> Self { |
| Self::from_raw(val as i32) |
| } |
| } |
| |
| /// Error that can happen when creating an instance. |
| #[derive(Clone, Debug, PartialEq)] |
| pub enum SamplerCreationError { |
| /// Not enough memory. |
| OomError(OomError), |
| |
| /// Too many sampler objects have been created. You must destroy some before creating new ones. |
| /// Note the specs guarantee that at least 4000 samplers can exist simultaneously. |
| TooManyObjects, |
| |
| /// Using an anisotropy greater than 1.0 requires enabling the `sampler_anisotropy` feature |
| /// when creating the device. |
| SamplerAnisotropyFeatureNotEnabled, |
| |
| /// The requested anisotropy level exceeds the device's limits. |
| AnisotropyLimitExceeded { |
| /// The value that was requested. |
| requested: f32, |
| /// The maximum supported value. |
| maximum: f32, |
| }, |
| |
| /// The requested mip lod bias exceeds the device's limits. |
| MipLodBiasLimitExceeded { |
| /// The value that was requested. |
| requested: f32, |
| /// The maximum supported value. |
| maximum: f32, |
| }, |
| |
| /// Using `MirrorClampToEdge` requires enabling the `VK_KHR_sampler_mirror_clamp_to_edge` |
| /// extension when creating the device. |
| SamplerMirrorClampToEdgeExtensionNotEnabled, |
| } |
| |
| impl error::Error for SamplerCreationError { |
| #[inline] |
| fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
| match *self { |
| SamplerCreationError::OomError(ref err) => Some(err), |
| _ => None, |
| } |
| } |
| } |
| |
| impl fmt::Display for SamplerCreationError { |
| #[inline] |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| write!( |
| fmt, |
| "{}", |
| match *self { |
| SamplerCreationError::OomError(_) => "not enough memory available", |
| SamplerCreationError::TooManyObjects => "too many simultaneous sampler objects", |
| SamplerCreationError::SamplerAnisotropyFeatureNotEnabled => { |
| "the `sampler_anisotropy` feature is not enabled" |
| } |
| SamplerCreationError::AnisotropyLimitExceeded { .. } => "anisotropy limit exceeded", |
| SamplerCreationError::MipLodBiasLimitExceeded { .. } => |
| "mip lod bias limit exceeded", |
| SamplerCreationError::SamplerMirrorClampToEdgeExtensionNotEnabled => { |
| "the device extension `VK_KHR_sampler_mirror_clamp_to_edge` is not enabled" |
| } |
| } |
| ) |
| } |
| } |
| |
| impl From<OomError> for SamplerCreationError { |
| #[inline] |
| fn from(err: OomError) -> SamplerCreationError { |
| SamplerCreationError::OomError(err) |
| } |
| } |
| |
| impl From<Error> for SamplerCreationError { |
| #[inline] |
| fn from(err: Error) -> SamplerCreationError { |
| match err { |
| err @ Error::OutOfHostMemory => SamplerCreationError::OomError(OomError::from(err)), |
| err @ Error::OutOfDeviceMemory => SamplerCreationError::OomError(OomError::from(err)), |
| Error::TooManyObjects => SamplerCreationError::TooManyObjects, |
| _ => panic!("unexpected error: {:?}", err), |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use crate::sampler; |
| |
| #[test] |
| fn create_regular() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| |
| let s = sampler::Sampler::new( |
| device, |
| sampler::Filter::Linear, |
| sampler::Filter::Linear, |
| sampler::MipmapMode::Nearest, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| 1.0, |
| 1.0, |
| 0.0, |
| 2.0, |
| ) |
| .unwrap(); |
| assert!(!s.compare_mode()); |
| assert!(!s.is_unnormalized()); |
| } |
| |
| #[test] |
| fn create_compare() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| |
| let s = sampler::Sampler::compare( |
| device, |
| sampler::Filter::Linear, |
| sampler::Filter::Linear, |
| sampler::MipmapMode::Nearest, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| 1.0, |
| 1.0, |
| 0.0, |
| 2.0, |
| sampler::Compare::Less, |
| ) |
| .unwrap(); |
| |
| assert!(s.compare_mode()); |
| assert!(!s.is_unnormalized()); |
| } |
| |
| #[test] |
| fn create_unnormalized() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| |
| let s = sampler::Sampler::unnormalized( |
| device, |
| sampler::Filter::Linear, |
| sampler::UnnormalizedSamplerAddressMode::ClampToEdge, |
| sampler::UnnormalizedSamplerAddressMode::ClampToEdge, |
| ) |
| .unwrap(); |
| |
| assert!(!s.compare_mode()); |
| assert!(s.is_unnormalized()); |
| } |
| |
| #[test] |
| fn simple_repeat_linear() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| let _ = sampler::Sampler::simple_repeat_linear(device); |
| } |
| |
| #[test] |
| fn simple_repeat_linear_no_mipmap() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| let _ = sampler::Sampler::simple_repeat_linear_no_mipmap(device); |
| } |
| |
| #[test] |
| fn min_lod_inferior() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| |
| assert_should_panic!({ |
| let _ = sampler::Sampler::new( |
| device, |
| sampler::Filter::Linear, |
| sampler::Filter::Linear, |
| sampler::MipmapMode::Nearest, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| 1.0, |
| 1.0, |
| 5.0, |
| 2.0, |
| ); |
| }); |
| } |
| |
| #[test] |
| fn max_anisotropy() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| |
| assert_should_panic!({ |
| let _ = sampler::Sampler::new( |
| device, |
| sampler::Filter::Linear, |
| sampler::Filter::Linear, |
| sampler::MipmapMode::Nearest, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| 1.0, |
| 0.5, |
| 0.0, |
| 2.0, |
| ); |
| }); |
| } |
| |
| #[test] |
| fn different_borders() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| |
| let b1 = sampler::BorderColor::IntTransparentBlack; |
| let b2 = sampler::BorderColor::FloatOpaqueWhite; |
| |
| assert_should_panic!({ |
| let _ = sampler::Sampler::new( |
| device, |
| sampler::Filter::Linear, |
| sampler::Filter::Linear, |
| sampler::MipmapMode::Nearest, |
| sampler::SamplerAddressMode::ClampToBorder(b1), |
| sampler::SamplerAddressMode::ClampToBorder(b2), |
| sampler::SamplerAddressMode::Repeat, |
| 1.0, |
| 1.0, |
| 5.0, |
| 2.0, |
| ); |
| }); |
| } |
| |
| #[test] |
| fn anisotropy_feature() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| |
| let r = sampler::Sampler::new( |
| device, |
| sampler::Filter::Linear, |
| sampler::Filter::Linear, |
| sampler::MipmapMode::Nearest, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| 1.0, |
| 2.0, |
| 0.0, |
| 2.0, |
| ); |
| |
| match r { |
| Err(sampler::SamplerCreationError::SamplerAnisotropyFeatureNotEnabled) => (), |
| _ => panic!(), |
| } |
| } |
| |
| #[test] |
| fn anisotropy_limit() { |
| let (device, queue) = gfx_dev_and_queue!(sampler_anisotropy); |
| |
| let r = sampler::Sampler::new( |
| device, |
| sampler::Filter::Linear, |
| sampler::Filter::Linear, |
| sampler::MipmapMode::Nearest, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| 1.0, |
| 100000000.0, |
| 0.0, |
| 2.0, |
| ); |
| |
| match r { |
| Err(sampler::SamplerCreationError::AnisotropyLimitExceeded { .. }) => (), |
| _ => panic!(), |
| } |
| } |
| |
| #[test] |
| fn mip_lod_bias_limit() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| |
| let r = sampler::Sampler::new( |
| device, |
| sampler::Filter::Linear, |
| sampler::Filter::Linear, |
| sampler::MipmapMode::Nearest, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| sampler::SamplerAddressMode::Repeat, |
| 100000000.0, |
| 1.0, |
| 0.0, |
| 2.0, |
| ); |
| |
| match r { |
| Err(sampler::SamplerCreationError::MipLodBiasLimitExceeded { .. }) => (), |
| _ => panic!(), |
| } |
| } |
| |
| #[test] |
| fn sampler_mirror_clamp_to_edge_extension() { |
| let (device, queue) = gfx_dev_and_queue!(); |
| |
| let r = sampler::Sampler::new( |
| device, |
| sampler::Filter::Linear, |
| sampler::Filter::Linear, |
| sampler::MipmapMode::Nearest, |
| sampler::SamplerAddressMode::MirrorClampToEdge, |
| sampler::SamplerAddressMode::MirrorClampToEdge, |
| sampler::SamplerAddressMode::MirrorClampToEdge, |
| 1.0, |
| 1.0, |
| 0.0, |
| 2.0, |
| ); |
| |
| match r { |
| Err(sampler::SamplerCreationError::SamplerMirrorClampToEdgeExtensionNotEnabled) => (), |
| _ => panic!(), |
| } |
| } |
| } |