aboutsummaryrefslogtreecommitdiff
path: root/src/sampler.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sampler.rs')
-rw-r--r--src/sampler.rs1051
1 files changed, 1051 insertions, 0 deletions
diff --git a/src/sampler.rs b/src/sampler.rs
new file mode 100644
index 0000000..be7fc9b
--- /dev/null
+++ b/src/sampler.rs
@@ -0,0 +1,1051 @@
+// 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!(),
+ }
+ }
+}