pub use self::aspect::ImageAspect;
pub use self::aspect::ImageAspects;
pub use self::attachment::AttachmentImage;
pub use self::immutable::ImmutableImage;
pub use self::layout::ImageDescriptorLayouts;
pub use self::layout::ImageLayout;
pub use self::storage::StorageImage;
pub use self::swapchain::SwapchainImage;
pub use self::sys::ImageCreationError;
pub use self::traits::ImageAccess;
pub use self::traits::ImageInner;
pub use self::usage::ImageUsage;
pub use self::view::ImageViewAbstract;
use self::view::ImageViewType;
use crate::format::Format;
use crate::memory::ExternalMemoryHandleType;
use crate::memory::ExternalMemoryProperties;
use crate::DeviceSize;
use std::cmp;
mod aspect;
pub mod attachment; pub mod immutable; mod layout;
mod storage;
pub mod swapchain; pub mod sys;
pub mod traits;
mod usage;
pub mod view;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum SampleCount {
Sample1 = ash::vk::SampleCountFlags::TYPE_1.as_raw(),
Sample2 = ash::vk::SampleCountFlags::TYPE_2.as_raw(),
Sample4 = ash::vk::SampleCountFlags::TYPE_4.as_raw(),
Sample8 = ash::vk::SampleCountFlags::TYPE_8.as_raw(),
Sample16 = ash::vk::SampleCountFlags::TYPE_16.as_raw(),
Sample32 = ash::vk::SampleCountFlags::TYPE_32.as_raw(),
Sample64 = ash::vk::SampleCountFlags::TYPE_64.as_raw(),
}
impl From<SampleCount> for ash::vk::SampleCountFlags {
#[inline]
fn from(val: SampleCount) -> Self {
Self::from_raw(val as u32)
}
}
impl TryFrom<ash::vk::SampleCountFlags> for SampleCount {
type Error = ();
#[inline]
fn try_from(val: ash::vk::SampleCountFlags) -> Result<Self, Self::Error> {
match val {
ash::vk::SampleCountFlags::TYPE_1 => Ok(Self::Sample1),
ash::vk::SampleCountFlags::TYPE_2 => Ok(Self::Sample2),
ash::vk::SampleCountFlags::TYPE_4 => Ok(Self::Sample4),
ash::vk::SampleCountFlags::TYPE_8 => Ok(Self::Sample8),
ash::vk::SampleCountFlags::TYPE_16 => Ok(Self::Sample16),
ash::vk::SampleCountFlags::TYPE_32 => Ok(Self::Sample32),
ash::vk::SampleCountFlags::TYPE_64 => Ok(Self::Sample64),
_ => Err(()),
}
}
}
impl TryFrom<u32> for SampleCount {
type Error = ();
#[inline]
fn try_from(val: u32) -> Result<Self, Self::Error> {
match val {
1 => Ok(Self::Sample1),
2 => Ok(Self::Sample2),
4 => Ok(Self::Sample4),
8 => Ok(Self::Sample8),
16 => Ok(Self::Sample16),
32 => Ok(Self::Sample32),
64 => Ok(Self::Sample64),
_ => Err(()),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SampleCounts {
pub sample1: bool,
pub sample2: bool,
pub sample4: bool,
pub sample8: bool,
pub sample16: bool,
pub sample32: bool,
pub sample64: bool,
}
impl SampleCounts {
#[inline]
pub fn contains(&self, sample_count: SampleCount) -> bool {
match sample_count {
SampleCount::Sample1 => self.sample1,
SampleCount::Sample2 => self.sample2,
SampleCount::Sample4 => self.sample4,
SampleCount::Sample8 => self.sample8,
SampleCount::Sample16 => self.sample16,
SampleCount::Sample32 => self.sample32,
SampleCount::Sample64 => self.sample64,
}
}
}
impl From<ash::vk::SampleCountFlags> for SampleCounts {
fn from(sample_counts: ash::vk::SampleCountFlags) -> SampleCounts {
SampleCounts {
sample1: !(sample_counts & ash::vk::SampleCountFlags::TYPE_1).is_empty(),
sample2: !(sample_counts & ash::vk::SampleCountFlags::TYPE_2).is_empty(),
sample4: !(sample_counts & ash::vk::SampleCountFlags::TYPE_4).is_empty(),
sample8: !(sample_counts & ash::vk::SampleCountFlags::TYPE_8).is_empty(),
sample16: !(sample_counts & ash::vk::SampleCountFlags::TYPE_16).is_empty(),
sample32: !(sample_counts & ash::vk::SampleCountFlags::TYPE_32).is_empty(),
sample64: !(sample_counts & ash::vk::SampleCountFlags::TYPE_64).is_empty(),
}
}
}
impl From<SampleCounts> for ash::vk::SampleCountFlags {
fn from(val: SampleCounts) -> ash::vk::SampleCountFlags {
let mut sample_counts = ash::vk::SampleCountFlags::default();
if val.sample1 {
sample_counts |= ash::vk::SampleCountFlags::TYPE_1;
}
if val.sample2 {
sample_counts |= ash::vk::SampleCountFlags::TYPE_2;
}
if val.sample4 {
sample_counts |= ash::vk::SampleCountFlags::TYPE_4;
}
if val.sample8 {
sample_counts |= ash::vk::SampleCountFlags::TYPE_8;
}
if val.sample16 {
sample_counts |= ash::vk::SampleCountFlags::TYPE_16;
}
if val.sample32 {
sample_counts |= ash::vk::SampleCountFlags::TYPE_32;
}
if val.sample64 {
sample_counts |= ash::vk::SampleCountFlags::TYPE_64;
}
sample_counts
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum MipmapsCount {
Log2,
One,
Specific(u32),
}
impl From<u32> for MipmapsCount {
#[inline]
fn from(num: u32) -> MipmapsCount {
MipmapsCount::Specific(num)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub struct ImageCreateFlags {
pub sparse_binding: bool,
pub sparse_residency: bool,
pub sparse_aliased: bool,
pub mutable_format: bool,
pub cube_compatible: bool,
pub array_2d_compatible: bool,
pub block_texel_view_compatible: bool,
}
impl ImageCreateFlags {
pub fn none() -> Self {
Self::default()
}
}
impl From<ImageCreateFlags> for ash::vk::ImageCreateFlags {
fn from(flags: ImageCreateFlags) -> Self {
let ImageCreateFlags {
sparse_binding,
sparse_residency,
sparse_aliased,
mutable_format,
cube_compatible,
array_2d_compatible,
block_texel_view_compatible,
} = flags;
let mut vk_flags = Self::default();
if sparse_binding {
vk_flags |= ash::vk::ImageCreateFlags::SPARSE_BINDING
};
if sparse_residency {
vk_flags |= ash::vk::ImageCreateFlags::SPARSE_RESIDENCY
};
if sparse_aliased {
vk_flags |= ash::vk::ImageCreateFlags::SPARSE_ALIASED
};
if mutable_format {
vk_flags |= ash::vk::ImageCreateFlags::MUTABLE_FORMAT
};
if cube_compatible {
vk_flags |= ash::vk::ImageCreateFlags::CUBE_COMPATIBLE
};
if array_2d_compatible {
vk_flags |= ash::vk::ImageCreateFlags::TYPE_2D_ARRAY_COMPATIBLE
};
if block_texel_view_compatible {
vk_flags |= ash::vk::ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE
};
vk_flags
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum ImageType {
Dim1d = ash::vk::ImageType::TYPE_1D.as_raw(),
Dim2d = ash::vk::ImageType::TYPE_2D.as_raw(),
Dim3d = ash::vk::ImageType::TYPE_3D.as_raw(),
}
impl From<ImageType> for ash::vk::ImageType {
fn from(val: ImageType) -> Self {
ash::vk::ImageType::from_raw(val as i32)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum ImageTiling {
Optimal = ash::vk::ImageTiling::OPTIMAL.as_raw(),
Linear = ash::vk::ImageTiling::LINEAR.as_raw(),
}
impl From<ImageTiling> for ash::vk::ImageTiling {
fn from(val: ImageTiling) -> Self {
ash::vk::ImageTiling::from_raw(val as i32)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ImageDimensions {
Dim1d {
width: u32,
array_layers: u32,
},
Dim2d {
width: u32,
height: u32,
array_layers: u32,
},
Dim3d {
width: u32,
height: u32,
depth: u32,
},
}
impl ImageDimensions {
#[inline]
pub fn width(&self) -> u32 {
match *self {
ImageDimensions::Dim1d { width, .. } => width,
ImageDimensions::Dim2d { width, .. } => width,
ImageDimensions::Dim3d { width, .. } => width,
}
}
#[inline]
pub fn height(&self) -> u32 {
match *self {
ImageDimensions::Dim1d { .. } => 1,
ImageDimensions::Dim2d { height, .. } => height,
ImageDimensions::Dim3d { height, .. } => height,
}
}
#[inline]
pub fn width_height(&self) -> [u32; 2] {
[self.width(), self.height()]
}
#[inline]
pub fn depth(&self) -> u32 {
match *self {
ImageDimensions::Dim1d { .. } => 1,
ImageDimensions::Dim2d { .. } => 1,
ImageDimensions::Dim3d { depth, .. } => depth,
}
}
#[inline]
pub fn width_height_depth(&self) -> [u32; 3] {
[self.width(), self.height(), self.depth()]
}
#[inline]
pub fn array_layers(&self) -> u32 {
match *self {
ImageDimensions::Dim1d { array_layers, .. } => array_layers,
ImageDimensions::Dim2d { array_layers, .. } => array_layers,
ImageDimensions::Dim3d { .. } => 1,
}
}
#[inline]
pub fn num_texels(&self) -> u32 {
self.width() * self.height() * self.depth() * self.array_layers()
}
#[inline]
pub fn image_type(&self) -> ImageType {
match *self {
ImageDimensions::Dim1d { .. } => ImageType::Dim1d,
ImageDimensions::Dim2d { .. } => ImageType::Dim2d,
ImageDimensions::Dim3d { .. } => ImageType::Dim3d,
}
}
#[inline]
pub fn max_mip_levels(&self) -> u32 {
let max = match *self {
ImageDimensions::Dim1d { width, .. } => width,
ImageDimensions::Dim2d { width, height, .. } => width | height,
ImageDimensions::Dim3d {
width,
height,
depth,
} => width | height | depth,
};
32 - max.leading_zeros()
}
pub fn mip_level_dimensions(&self, level: u32) -> Option<ImageDimensions> {
if level == 0 {
return Some(*self);
}
if level >= self.max_mip_levels() {
return None;
}
Some(match *self {
ImageDimensions::Dim1d {
width,
array_layers,
} => {
debug_assert_ne!(width, 0);
ImageDimensions::Dim1d {
array_layers,
width: cmp::max(1, width >> level),
}
}
ImageDimensions::Dim2d {
width,
height,
array_layers,
} => {
debug_assert_ne!(width, 0);
debug_assert_ne!(height, 0);
ImageDimensions::Dim2d {
width: cmp::max(1, width >> level),
height: cmp::max(1, height >> level),
array_layers,
}
}
ImageDimensions::Dim3d {
width,
height,
depth,
} => {
debug_assert_ne!(width, 0);
debug_assert_ne!(height, 0);
ImageDimensions::Dim3d {
width: cmp::max(1, width >> level),
height: cmp::max(1, height >> level),
depth: cmp::max(1, depth >> level),
}
}
})
}
}
#[derive(Clone, Debug)]
pub struct ImageFormatInfo {
pub format: Option<Format>,
pub image_type: ImageType,
pub tiling: ImageTiling,
pub usage: ImageUsage,
pub external_memory_handle_type: Option<ExternalMemoryHandleType>,
pub image_view_type: Option<ImageViewType>,
pub mutable_format: bool,
pub cube_compatible: bool,
pub array_2d_compatible: bool,
pub block_texel_view_compatible: bool,
pub _ne: crate::NonExhaustive,
}
impl Default for ImageFormatInfo {
#[inline]
fn default() -> Self {
Self {
format: None,
image_type: ImageType::Dim2d,
tiling: ImageTiling::Optimal,
usage: ImageUsage::none(),
external_memory_handle_type: None,
image_view_type: None,
mutable_format: false,
cube_compatible: false,
array_2d_compatible: false,
block_texel_view_compatible: false,
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct ImageFormatProperties {
pub max_extent: [u32; 3],
pub max_mip_levels: u32,
pub max_array_layers: u32,
pub sample_counts: SampleCounts,
pub max_resource_size: DeviceSize,
pub external_memory_properties: ExternalMemoryProperties,
pub filter_cubic: bool,
pub filter_cubic_minmax: bool,
}
impl From<ash::vk::ImageFormatProperties> for ImageFormatProperties {
fn from(props: ash::vk::ImageFormatProperties) -> Self {
Self {
max_extent: [
props.max_extent.width,
props.max_extent.height,
props.max_extent.depth,
],
max_mip_levels: props.max_mip_levels,
max_array_layers: props.max_array_layers,
sample_counts: props.sample_counts.into(),
max_resource_size: props.max_resource_size,
external_memory_properties: Default::default(),
filter_cubic: false,
filter_cubic_minmax: false,
}
}
}
#[cfg(test)]
mod tests {
use crate::format::Format;
use crate::image::ImageAccess;
use crate::image::ImageDimensions;
use crate::image::ImmutableImage;
use crate::image::MipmapsCount;
#[test]
fn max_mip_levels() {
let dims = ImageDimensions::Dim2d {
width: 2,
height: 1,
array_layers: 1,
};
assert_eq!(dims.max_mip_levels(), 2);
let dims = ImageDimensions::Dim2d {
width: 2,
height: 3,
array_layers: 1,
};
assert_eq!(dims.max_mip_levels(), 2);
let dims = ImageDimensions::Dim2d {
width: 512,
height: 512,
array_layers: 1,
};
assert_eq!(dims.max_mip_levels(), 10);
}
#[test]
fn mip_level_dimensions() {
let dims = ImageDimensions::Dim2d {
width: 283,
height: 175,
array_layers: 1,
};
assert_eq!(dims.mip_level_dimensions(0), Some(dims));
assert_eq!(
dims.mip_level_dimensions(1),
Some(ImageDimensions::Dim2d {
width: 141,
height: 87,
array_layers: 1,
})
);
assert_eq!(
dims.mip_level_dimensions(2),
Some(ImageDimensions::Dim2d {
width: 70,
height: 43,
array_layers: 1,
})
);
assert_eq!(
dims.mip_level_dimensions(3),
Some(ImageDimensions::Dim2d {
width: 35,
height: 21,
array_layers: 1,
})
);
assert_eq!(
dims.mip_level_dimensions(4),
Some(ImageDimensions::Dim2d {
width: 17,
height: 10,
array_layers: 1,
})
);
assert_eq!(
dims.mip_level_dimensions(5),
Some(ImageDimensions::Dim2d {
width: 8,
height: 5,
array_layers: 1,
})
);
assert_eq!(
dims.mip_level_dimensions(6),
Some(ImageDimensions::Dim2d {
width: 4,
height: 2,
array_layers: 1,
})
);
assert_eq!(
dims.mip_level_dimensions(7),
Some(ImageDimensions::Dim2d {
width: 2,
height: 1,
array_layers: 1,
})
);
assert_eq!(
dims.mip_level_dimensions(8),
Some(ImageDimensions::Dim2d {
width: 1,
height: 1,
array_layers: 1,
})
);
assert_eq!(dims.mip_level_dimensions(9), None);
}
#[test]
fn mipmap_working_immutable_image() {
let (device, queue) = gfx_dev_and_queue!();
let dimensions = ImageDimensions::Dim2d {
width: 512,
height: 512,
array_layers: 1,
};
{
let mut vec = Vec::new();
vec.resize(512 * 512, 0u8);
let (image, _) = ImmutableImage::from_iter(
vec.into_iter(),
dimensions,
MipmapsCount::One,
Format::R8_UNORM,
queue.clone(),
)
.unwrap();
assert_eq!(image.mip_levels(), 1);
}
{
let mut vec = Vec::new();
vec.resize(512 * 512, 0u8);
let (image, _) = ImmutableImage::from_iter(
vec.into_iter(),
dimensions,
MipmapsCount::Log2,
Format::R8_UNORM,
queue.clone(),
)
.unwrap();
assert_eq!(image.mip_levels(), 10);
}
}
}