blob: be7fc9bc78af8ee6e1ed93ab143151aeb86b339d [file] [log] [blame]
// 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!(),
}
}
}