blob: 894a34337dd14d11358b3683ed48d354d0d6e1f9 [file] [log] [blame]
// Copyright (c) 2021 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.
//! Image views.
//!
//! This module contains types related to image views. An image view wraps around
//! an image and describes how the GPU should interpret the data. It is needed when an image is
//! to be used in a shader descriptor or as a framebuffer attachment.
use crate::check_errors;
use crate::device::Device;
use crate::format::Format;
use crate::format::FormatTy;
use crate::image::sys::UnsafeImage;
use crate::image::ImageAccess;
use crate::image::ImageDimensions;
use crate::memory::DeviceMemoryAllocError;
use crate::sampler::Sampler;
use crate::OomError;
use crate::SafeDeref;
use crate::VulkanObject;
use std::error;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::mem::MaybeUninit;
use std::ops::Range;
use std::ptr;
use std::sync::Arc;
/// A safe image view that checks for validity and keeps its attached image alive.
pub struct ImageView<I>
where
I: ImageAccess,
{
image: I,
inner: UnsafeImageView,
format: Format,
ty: ImageViewType,
component_mapping: ComponentMapping,
array_layers: Range<u32>,
}
impl<I> ImageView<I>
where
I: ImageAccess,
{
/// Creates a default `ImageView`. Equivalent to `ImageView::start(image).build()`.
#[inline]
pub fn new(image: I) -> Result<Arc<ImageView<I>>, ImageViewCreationError> {
Self::start(image).build()
}
/// Begins building an `ImageView`.
pub fn start(image: I) -> ImageViewBuilder<I> {
let ty = match image.dimensions() {
ImageDimensions::Dim1d {
array_layers: 1, ..
} => ImageViewType::Dim1d,
ImageDimensions::Dim1d { .. } => ImageViewType::Dim1dArray,
ImageDimensions::Dim2d {
array_layers: 1, ..
} => ImageViewType::Dim2d,
ImageDimensions::Dim2d { .. } => ImageViewType::Dim2dArray,
ImageDimensions::Dim3d { .. } => ImageViewType::Dim3d,
};
let mipmap_levels = 0..image.mipmap_levels();
let array_layers = 0..image.dimensions().array_layers();
ImageViewBuilder {
image,
ty,
component_mapping: ComponentMapping::default(),
mipmap_levels,
array_layers,
}
}
/// Returns the wrapped image that this image view was created from.
pub fn image(&self) -> &I {
&self.image
}
}
#[derive(Debug)]
pub struct ImageViewBuilder<I> {
image: I,
ty: ImageViewType,
component_mapping: ComponentMapping,
mipmap_levels: Range<u32>,
array_layers: Range<u32>,
}
impl<I> ImageViewBuilder<I>
where
I: ImageAccess,
{
/// Sets the image view type.
///
/// By default, this is determined from the image, based on its dimensions and number of layers.
/// The value of `ty` must be compatible with the dimensions of the image and the selected
/// array layers.
#[inline]
pub fn with_type(mut self, ty: ImageViewType) -> Self {
self.ty = ty;
self
}
/// Sets how to map components of each pixel.
///
/// By default, this is the identity mapping, with every component mapped directly.
#[inline]
pub fn with_component_mapping(mut self, component_mapping: ComponentMapping) -> Self {
self.component_mapping = component_mapping;
self
}
/// Sets the range of mipmap levels that the view should cover.
///
/// By default, this is the full range of mipmaps present in the image.
#[inline]
pub fn with_mipmap_levels(mut self, mipmap_levels: Range<u32>) -> Self {
self.mipmap_levels = mipmap_levels;
self
}
/// Sets the range of array layers that the view should cover.
///
/// By default, this is the full range of array layers present in the image.
#[inline]
pub fn with_array_layers(mut self, array_layers: Range<u32>) -> Self {
self.array_layers = array_layers;
self
}
/// Builds the `ImageView`.
pub fn build(self) -> Result<Arc<ImageView<I>>, ImageViewCreationError> {
let dimensions = self.image.dimensions();
let format = self.image.format();
let image_inner = self.image.inner().image;
let usage = image_inner.usage();
let flags = image_inner.flags();
if self.mipmap_levels.end <= self.mipmap_levels.start
|| self.mipmap_levels.end > image_inner.mipmap_levels()
{
return Err(ImageViewCreationError::MipMapLevelsOutOfRange);
}
if self.array_layers.end <= self.array_layers.start
|| self.array_layers.end > dimensions.array_layers()
{
return Err(ImageViewCreationError::ArrayLayersOutOfRange);
}
if !(usage.sampled
|| usage.storage
|| usage.color_attachment
|| usage.depth_stencil_attachment
|| usage.input_attachment
|| usage.transient_attachment)
{
return Err(ImageViewCreationError::InvalidImageUsage);
}
// Check for compatibility with the image
match (
self.ty,
self.image.dimensions(),
self.array_layers.end - self.array_layers.start,
self.mipmap_levels.end - self.mipmap_levels.start,
) {
(ImageViewType::Dim1d, ImageDimensions::Dim1d { .. }, 1, _) => (),
(ImageViewType::Dim1dArray, ImageDimensions::Dim1d { .. }, _, _) => (),
(ImageViewType::Dim2d, ImageDimensions::Dim2d { .. }, 1, _) => (),
(ImageViewType::Dim2dArray, ImageDimensions::Dim2d { .. }, _, _) => (),
(ImageViewType::Cubemap, ImageDimensions::Dim2d { .. }, 6, _)
if flags.cube_compatible =>
{
()
}
(ImageViewType::CubemapArray, ImageDimensions::Dim2d { .. }, n, _)
if flags.cube_compatible && n % 6 == 0 =>
{
()
}
(ImageViewType::Dim3d, ImageDimensions::Dim3d { .. }, 1, _) => (),
(ImageViewType::Dim2d, ImageDimensions::Dim3d { .. }, 1, 1)
if flags.array_2d_compatible =>
{
()
}
(ImageViewType::Dim2dArray, ImageDimensions::Dim3d { .. }, _, 1)
if flags.array_2d_compatible =>
{
()
}
_ => return Err(ImageViewCreationError::IncompatibleType),
}
let inner = unsafe {
UnsafeImageView::new(
image_inner,
self.ty,
self.component_mapping,
self.mipmap_levels,
self.array_layers.clone(),
)?
};
Ok(Arc::new(ImageView {
image: self.image,
inner,
format,
ty: self.ty,
component_mapping: self.component_mapping,
array_layers: self.array_layers,
}))
}
}
/// Error that can happen when creating an image view.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ImageViewCreationError {
/// Allocating memory failed.
AllocError(DeviceMemoryAllocError),
/// The specified range of array layers was out of range for the image.
ArrayLayersOutOfRange,
/// The specified range of mipmap levels was out of range for the image.
MipMapLevelsOutOfRange,
/// The requested [`ImageViewType`] was not compatible with the image, or with the specified ranges of array layers and mipmap levels.
IncompatibleType,
/// The image was not created with
/// [one of the required usages](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#valid-imageview-imageusage)
/// for image views.
InvalidImageUsage,
}
impl error::Error for ImageViewCreationError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
ImageViewCreationError::AllocError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for ImageViewCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
ImageViewCreationError::AllocError(err) => "allocating memory failed",
ImageViewCreationError::ArrayLayersOutOfRange => "array layers are out of range",
ImageViewCreationError::MipMapLevelsOutOfRange => "mipmap levels are out of range",
ImageViewCreationError::IncompatibleType =>
"image view type is not compatible with image, array layers or mipmap levels",
ImageViewCreationError::InvalidImageUsage =>
"the usage of the image is not compatible with image views",
}
)
}
}
impl From<OomError> for ImageViewCreationError {
#[inline]
fn from(err: OomError) -> ImageViewCreationError {
ImageViewCreationError::AllocError(DeviceMemoryAllocError::OomError(err))
}
}
/// A low-level wrapper around a `vkImageView`.
pub struct UnsafeImageView {
view: ash::vk::ImageView,
device: Arc<Device>,
}
impl UnsafeImageView {
/// Creates a new view from an image.
///
/// # Safety
/// - The returned `UnsafeImageView` must not outlive `image`.
/// - `image` must have a usage that is compatible with image views.
/// - `ty` must be compatible with the dimensions and flags of the image.
/// - `mipmap_levels` must not be empty, must be within the range of levels of the image, and be compatible with the requested `ty`.
/// - `array_layers` must not be empty, must be within the range of layers of the image, and be compatible with the requested `ty`.
///
/// # Panics
/// Panics if the image is a YcbCr image, since the Vulkano API is not yet flexible enough to
/// specify the aspect of image.
pub unsafe fn new(
image: &UnsafeImage,
ty: ImageViewType,
component_mapping: ComponentMapping,
mipmap_levels: Range<u32>,
array_layers: Range<u32>,
) -> Result<UnsafeImageView, OomError> {
let fns = image.device().fns();
debug_assert!(mipmap_levels.end > mipmap_levels.start);
debug_assert!(mipmap_levels.end <= image.mipmap_levels());
debug_assert!(array_layers.end > array_layers.start);
debug_assert!(array_layers.end <= image.dimensions().array_layers());
if image.format().ty() == FormatTy::Ycbcr {
unimplemented!();
}
// TODO: Let user choose
let aspects = image.format().aspects();
let view = {
let infos = ash::vk::ImageViewCreateInfo {
flags: ash::vk::ImageViewCreateFlags::empty(),
image: image.internal_object(),
view_type: ty.into(),
format: image.format().into(),
components: component_mapping.into(),
subresource_range: ash::vk::ImageSubresourceRange {
aspect_mask: aspects.into(),
base_mip_level: mipmap_levels.start,
level_count: mipmap_levels.end - mipmap_levels.start,
base_array_layer: array_layers.start,
layer_count: array_layers.end - array_layers.start,
},
..Default::default()
};
let mut output = MaybeUninit::uninit();
check_errors(fns.v1_0.create_image_view(
image.device().internal_object(),
&infos,
ptr::null(),
output.as_mut_ptr(),
))?;
output.assume_init()
};
Ok(UnsafeImageView {
view,
device: image.device().clone(),
})
}
}
unsafe impl VulkanObject for UnsafeImageView {
type Object = ash::vk::ImageView;
#[inline]
fn internal_object(&self) -> ash::vk::ImageView {
self.view
}
}
impl fmt::Debug for UnsafeImageView {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "<Vulkan image view {:?}>", self.view)
}
}
impl Drop for UnsafeImageView {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
fns.v1_0
.destroy_image_view(self.device.internal_object(), self.view, ptr::null());
}
}
}
impl PartialEq for UnsafeImageView {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.view == other.view && self.device == other.device
}
}
impl Eq for UnsafeImageView {}
impl Hash for UnsafeImageView {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.view.hash(state);
self.device.hash(state);
}
}
/// The geometry type of an image view.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum ImageViewType {
Dim1d = ash::vk::ImageViewType::TYPE_1D.as_raw(),
Dim1dArray = ash::vk::ImageViewType::TYPE_1D_ARRAY.as_raw(),
Dim2d = ash::vk::ImageViewType::TYPE_2D.as_raw(),
Dim2dArray = ash::vk::ImageViewType::TYPE_2D_ARRAY.as_raw(),
Dim3d = ash::vk::ImageViewType::TYPE_3D.as_raw(),
Cubemap = ash::vk::ImageViewType::CUBE.as_raw(),
CubemapArray = ash::vk::ImageViewType::CUBE_ARRAY.as_raw(),
}
impl From<ImageViewType> for ash::vk::ImageViewType {
fn from(val: ImageViewType) -> Self {
Self::from_raw(val as i32)
}
}
/// Specifies how the components of an image must be mapped.
///
/// When creating an image view, it is possible to ask the implementation to modify the value
/// returned when accessing a given component from within a shader.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct ComponentMapping {
/// First component.
pub r: ComponentSwizzle,
/// Second component.
pub g: ComponentSwizzle,
/// Third component.
pub b: ComponentSwizzle,
/// Fourth component.
pub a: ComponentSwizzle,
}
impl ComponentMapping {
/// Returns `true` if the component mapping is identity swizzled,
/// meaning that all the members are `Identity`.
///
/// Certain operations require views that are identity swizzled, and will return an error
/// otherwise. For example, attaching a view to a framebuffer is only possible if the view is
/// identity swizzled.
#[inline]
pub fn is_identity(&self) -> bool {
self.r == ComponentSwizzle::Identity
&& self.g == ComponentSwizzle::Identity
&& self.b == ComponentSwizzle::Identity
&& self.a == ComponentSwizzle::Identity
}
}
impl From<ComponentMapping> for ash::vk::ComponentMapping {
#[inline]
fn from(value: ComponentMapping) -> Self {
Self {
r: value.r.into(),
g: value.g.into(),
b: value.b.into(),
a: value.a.into(),
}
}
}
/// Describes the value that an individual component must return when being accessed.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(i32)]
pub enum ComponentSwizzle {
/// Returns the value that this component should normally have.
///
/// This is the `Default` value.
Identity = ash::vk::ComponentSwizzle::IDENTITY.as_raw(),
/// Always return zero.
Zero = ash::vk::ComponentSwizzle::ZERO.as_raw(),
/// Always return one.
One = ash::vk::ComponentSwizzle::ONE.as_raw(),
/// Returns the value of the first component.
Red = ash::vk::ComponentSwizzle::R.as_raw(),
/// Returns the value of the second component.
Green = ash::vk::ComponentSwizzle::G.as_raw(),
/// Returns the value of the third component.
Blue = ash::vk::ComponentSwizzle::B.as_raw(),
/// Returns the value of the fourth component.
Alpha = ash::vk::ComponentSwizzle::A.as_raw(),
}
impl From<ComponentSwizzle> for ash::vk::ComponentSwizzle {
#[inline]
fn from(val: ComponentSwizzle) -> Self {
Self::from_raw(val as i32)
}
}
impl Default for ComponentSwizzle {
#[inline]
fn default() -> ComponentSwizzle {
ComponentSwizzle::Identity
}
}
/// Trait for types that represent the GPU can access an image view.
pub unsafe trait ImageViewAbstract {
/// Returns the wrapped image that this image view was created from.
fn image(&self) -> &dyn ImageAccess;
/// Returns the inner unsafe image view object used by this image view.
fn inner(&self) -> &UnsafeImageView;
/// Returns the range of array layers of the wrapped image that this view exposes.
fn array_layers(&self) -> Range<u32>;
/// Returns the format of this view. This can be different from the parent's format.
fn format(&self) -> Format;
/// Returns the component mapping of this view.
fn component_mapping(&self) -> ComponentMapping;
/// Returns the [`ImageViewType`] of this image view.
fn ty(&self) -> ImageViewType;
/// Returns true if the given sampler can be used with this image view.
///
/// This method should check whether the sampler's configuration can be used with the format
/// of the view.
// TODO: return a Result and propagate it when binding to a descriptor set
fn can_be_sampled(&self, _sampler: &Sampler) -> bool {
true /* FIXME */
}
}
unsafe impl<I> ImageViewAbstract for ImageView<I>
where
I: ImageAccess,
{
#[inline]
fn image(&self) -> &dyn ImageAccess {
&self.image
}
#[inline]
fn inner(&self) -> &UnsafeImageView {
&self.inner
}
#[inline]
fn array_layers(&self) -> Range<u32> {
self.array_layers.clone()
}
#[inline]
fn format(&self) -> Format {
// TODO: remove this default impl
self.format
}
#[inline]
fn component_mapping(&self) -> ComponentMapping {
self.component_mapping
}
#[inline]
fn ty(&self) -> ImageViewType {
self.ty
}
}
unsafe impl<T> ImageViewAbstract for T
where
T: SafeDeref,
T::Target: ImageViewAbstract,
{
#[inline]
fn image(&self) -> &dyn ImageAccess {
(**self).image()
}
#[inline]
fn inner(&self) -> &UnsafeImageView {
(**self).inner()
}
#[inline]
fn array_layers(&self) -> Range<u32> {
(**self).array_layers()
}
#[inline]
fn format(&self) -> Format {
(**self).format()
}
#[inline]
fn component_mapping(&self) -> ComponentMapping {
(**self).component_mapping()
}
#[inline]
fn ty(&self) -> ImageViewType {
(**self).ty()
}
#[inline]
fn can_be_sampled(&self, sampler: &Sampler) -> bool {
(**self).can_be_sampled(sampler)
}
}
impl PartialEq for dyn ImageViewAbstract + Send + Sync {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner() == other.inner()
}
}
impl Eq for dyn ImageViewAbstract + Send + Sync {}
impl Hash for dyn ImageViewAbstract + Send + Sync {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner().hash(state);
}
}