pub use self::{
aspect::{ImageAspect, ImageAspects},
attachment::AttachmentImage,
immutable::ImmutableImage,
layout::{ImageDescriptorLayouts, ImageLayout},
storage::StorageImage,
swapchain::SwapchainImage,
sys::ImageError,
traits::{ImageAccess, ImageInner},
usage::ImageUsage,
view::{ImageViewAbstract, ImageViewType},
};
use crate::{
format::Format,
macros::{vulkan_bitflags, vulkan_enum},
memory::{ExternalMemoryHandleType, ExternalMemoryProperties},
DeviceSize,
};
use std::{cmp, ops::Range};
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;
vulkan_bitflags! {
#[non_exhaustive]
ImageCreateFlags = ImageCreateFlags(u32);
mutable_format = MUTABLE_FORMAT,
cube_compatible = CUBE_COMPATIBLE,
array_2d_compatible = TYPE_2D_ARRAY_COMPATIBLE {
api_version: V1_1,
device_extensions: [khr_maintenance1],
},
block_texel_view_compatible = BLOCK_TEXEL_VIEW_COMPATIBLE {
api_version: V1_1,
device_extensions: [khr_maintenance2],
},
disjoint = DISJOINT {
api_version: V1_1,
device_extensions: [khr_sampler_ycbcr_conversion],
},
}
vulkan_enum! {
#[non_exhaustive]
SampleCount = SampleCountFlags(u32);
Sample1 = TYPE_1,
Sample2 = TYPE_2,
Sample4 = TYPE_4,
Sample8 = TYPE_8,
Sample16 = TYPE_16,
Sample32 = TYPE_32,
Sample64 = TYPE_64,
}
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(()),
}
}
}
vulkan_bitflags! {
#[non_exhaustive]
SampleCounts = SampleCountFlags(u32);
sample1 = TYPE_1,
sample2 = TYPE_2,
sample4 = TYPE_4,
sample8 = TYPE_8,
sample16 = TYPE_16,
sample32 = TYPE_32,
sample64 = TYPE_64,
}
impl SampleCounts {
#[inline]
pub const fn contains_count(&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,
}
}
#[inline]
pub const fn max_count(&self) -> SampleCount {
match self {
Self { sample64: true, .. } => SampleCount::Sample64,
Self { sample32: true, .. } => SampleCount::Sample32,
Self { sample16: true, .. } => SampleCount::Sample16,
Self { sample8: true, .. } => SampleCount::Sample8,
Self { sample4: true, .. } => SampleCount::Sample4,
Self { sample2: true, .. } => SampleCount::Sample2,
_ => SampleCount::Sample1,
}
}
}
#[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)
}
}
vulkan_enum! {
#[non_exhaustive]
ImageType = ImageType(i32);
Dim1d = TYPE_1D,
Dim2d = TYPE_2D,
Dim3d = TYPE_3D,
}
vulkan_enum! {
#[non_exhaustive]
ImageTiling = ImageTiling(i32);
Optimal = OPTIMAL,
Linear = LINEAR,
}
#[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()
}
#[inline]
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, PartialEq, Eq, Hash)]
pub struct ImageSubresourceLayers {
pub aspects: ImageAspects,
pub mip_level: u32,
pub array_layers: Range<u32>,
}
impl ImageSubresourceLayers {
#[inline]
pub fn from_parameters(format: Format, array_layers: u32) -> Self {
Self {
aspects: {
let aspects = format.aspects();
if aspects.plane0 {
ImageAspects {
plane0: true,
..ImageAspects::empty()
}
} else {
aspects
}
},
mip_level: 0,
array_layers: 0..array_layers,
}
}
}
impl From<ImageSubresourceLayers> for ash::vk::ImageSubresourceLayers {
#[inline]
fn from(val: ImageSubresourceLayers) -> Self {
Self {
aspect_mask: val.aspects.into(),
mip_level: val.mip_level,
base_array_layer: val.array_layers.start,
layer_count: val.array_layers.end - val.array_layers.start,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ImageSubresourceRange {
pub aspects: ImageAspects,
pub mip_levels: Range<u32>,
pub array_layers: Range<u32>,
}
impl ImageSubresourceRange {
#[inline]
pub fn from_parameters(format: Format, mip_levels: u32, array_layers: u32) -> Self {
Self {
aspects: ImageAspects {
plane0: false,
plane1: false,
plane2: false,
..format.aspects()
},
mip_levels: 0..mip_levels,
array_layers: 0..array_layers,
}
}
}
impl From<ImageSubresourceRange> for ash::vk::ImageSubresourceRange {
#[inline]
fn from(val: ImageSubresourceRange) -> Self {
Self {
aspect_mask: val.aspects.into(),
base_mip_level: val.mip_levels.start,
level_count: val.mip_levels.end - val.mip_levels.start,
base_array_layer: val.array_layers.start,
layer_count: val.array_layers.end - val.array_layers.start,
}
}
}
impl From<ImageSubresourceLayers> for ImageSubresourceRange {
#[inline]
fn from(val: ImageSubresourceLayers) -> Self {
Self {
aspects: val.aspects,
mip_levels: val.mip_level..val.mip_level + 1,
array_layers: val.array_layers,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SubresourceLayout {
pub offset: DeviceSize,
pub size: DeviceSize,
pub row_pitch: DeviceSize,
pub array_pitch: DeviceSize,
pub depth_pitch: DeviceSize,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ImageFormatInfo {
pub flags: ImageCreateFlags,
pub format: Option<Format>,
pub image_type: ImageType,
pub tiling: ImageTiling,
pub usage: ImageUsage,
pub stencil_usage: ImageUsage,
pub external_memory_handle_type: Option<ExternalMemoryHandleType>,
pub image_view_type: Option<ImageViewType>,
pub _ne: crate::NonExhaustive,
}
impl Default for ImageFormatInfo {
#[inline]
fn default() -> Self {
Self {
flags: ImageCreateFlags::empty(),
format: None,
image_type: ImageType::Dim2d,
tiling: ImageTiling::Optimal,
usage: ImageUsage::empty(),
stencil_usage: ImageUsage::empty(),
external_memory_handle_type: None,
image_view_type: None,
_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 {
#[inline]
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,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SparseImageFormatInfo {
pub format: Option<Format>,
pub image_type: ImageType,
pub samples: SampleCount,
pub usage: ImageUsage,
pub tiling: ImageTiling,
pub _ne: crate::NonExhaustive,
}
impl Default for SparseImageFormatInfo {
#[inline]
fn default() -> Self {
Self {
format: None,
image_type: ImageType::Dim2d,
samples: SampleCount::Sample1,
usage: ImageUsage::empty(),
tiling: ImageTiling::Optimal,
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct SparseImageFormatProperties {
pub aspects: ImageAspects,
pub image_granularity: [u32; 3],
pub flags: SparseImageFormatFlags,
}
vulkan_bitflags! {
SparseImageFormatFlags = SparseImageFormatFlags(u32);
single_miptail = SINGLE_MIPTAIL,
aligned_mip_size = ALIGNED_MIP_SIZE,
nonstandard_block_size = NONSTANDARD_BLOCK_SIZE,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct SparseImageMemoryRequirements {
pub format_properties: SparseImageFormatProperties,
pub image_mip_tail_first_lod: u32,
pub image_mip_tail_size: DeviceSize,
pub image_mip_tail_offset: DeviceSize,
pub image_mip_tail_stride: Option<DeviceSize>,
}
#[cfg(test)]
mod tests {
use crate::{
command_buffer::{
allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage,
},
format::Format,
image::{ImageAccess, ImageDimensions, ImmutableImage, MipmapsCount},
memory::allocator::StandardMemoryAllocator,
};
#[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 cb_allocator = StandardCommandBufferAllocator::new(device.clone(), Default::default());
let mut cbb = AutoCommandBufferBuilder::primary(
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
let memory_allocator = StandardMemoryAllocator::new_default(device);
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(
&memory_allocator,
vec.into_iter(),
dimensions,
MipmapsCount::One,
Format::R8_UNORM,
&mut cbb,
)
.unwrap();
assert_eq!(image.mip_levels(), 1);
}
{
let mut vec = Vec::new();
vec.resize(512 * 512, 0u8);
let image = ImmutableImage::from_iter(
&memory_allocator,
vec.into_iter(),
dimensions,
MipmapsCount::Log2,
Format::R8_UNORM,
&mut cbb,
)
.unwrap();
assert_eq!(image.mip_levels(), 10);
}
}
}