blob: 42503796b3c0b1918b309ed541955f644b5de1df [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.
use super::{
sys::{Image, RawImage},
traits::ImageContent,
ImageAccess, ImageCreateFlags, ImageDescriptorLayouts, ImageDimensions, ImageError, ImageInner,
ImageLayout, ImageSubresourceLayers, ImageUsage, MipmapsCount,
};
use crate::{
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferError, BufferUsage, Subbuffer},
command_buffer::{
allocator::CommandBufferAllocator, AutoCommandBufferBuilder, BlitImageInfo,
BufferImageCopy, CommandBufferBeginError, CopyBufferToImageInfo, ImageBlit,
},
device::{Device, DeviceOwned},
format::Format,
image::sys::ImageCreateInfo,
memory::{
allocator::{
AllocationCreateInfo, AllocationCreationError, AllocationType,
MemoryAllocatePreference, MemoryAllocator, MemoryUsage,
},
is_aligned, DedicatedAllocation,
},
sampler::Filter,
sync::Sharing,
DeviceSize, VulkanError,
};
use smallvec::{smallvec, SmallVec};
use std::{
error::Error,
fmt::{Display, Error as FmtError, Formatter},
hash::{Hash, Hasher},
sync::Arc,
};
/// Image whose purpose is to be used for read-only purposes. You can write to the image once,
/// but then you must only ever read from it.
// TODO: type (2D, 3D, array, etc.) as template parameter
#[derive(Debug)]
pub struct ImmutableImage {
inner: Arc<Image>,
layout: ImageLayout,
}
fn has_mipmaps(mipmaps: MipmapsCount) -> bool {
match mipmaps {
MipmapsCount::One => false,
MipmapsCount::Log2 => true,
MipmapsCount::Specific(x) => x > 1,
}
}
fn generate_mipmaps<L, Cba>(
cbb: &mut AutoCommandBufferBuilder<L, Cba>,
image: Arc<dyn ImageAccess>,
dimensions: ImageDimensions,
_layout: ImageLayout,
) where
Cba: CommandBufferAllocator,
{
for level in 1..image.mip_levels() {
let src_size = dimensions
.mip_level_dimensions(level - 1)
.unwrap()
.width_height_depth();
let dst_size = dimensions
.mip_level_dimensions(level)
.unwrap()
.width_height_depth();
cbb.blit_image(BlitImageInfo {
regions: [ImageBlit {
src_subresource: ImageSubresourceLayers {
mip_level: level - 1,
..image.subresource_layers()
},
src_offsets: [[0; 3], src_size],
dst_subresource: ImageSubresourceLayers {
mip_level: level,
..image.subresource_layers()
},
dst_offsets: [[0; 3], dst_size],
..Default::default()
}]
.into(),
filter: Filter::Linear,
..BlitImageInfo::images(image.clone(), image.clone())
})
.expect("failed to blit a mip map to image!");
}
}
impl ImmutableImage {
/// Builds an uninitialized immutable image.
///
/// Returns two things: the image, and a special access that should be used for the initial
/// upload to the image.
pub fn uninitialized(
allocator: &(impl MemoryAllocator + ?Sized),
dimensions: ImageDimensions,
format: Format,
mip_levels: impl Into<MipmapsCount>,
usage: ImageUsage,
flags: ImageCreateFlags,
layout: ImageLayout,
queue_family_indices: impl IntoIterator<Item = u32>,
) -> Result<(Arc<ImmutableImage>, Arc<ImmutableImageInitialization>), ImmutableImageCreationError>
{
let queue_family_indices: SmallVec<[_; 4]> = queue_family_indices.into_iter().collect();
assert!(!flags.intersects(ImageCreateFlags::DISJOINT)); // TODO: adjust the code below to make this safe
let raw_image = RawImage::new(
allocator.device().clone(),
ImageCreateInfo {
flags,
dimensions,
format: Some(format),
mip_levels: match mip_levels.into() {
MipmapsCount::Specific(num) => num,
MipmapsCount::Log2 => dimensions.max_mip_levels(),
MipmapsCount::One => 1,
},
usage,
sharing: if queue_family_indices.len() >= 2 {
Sharing::Concurrent(queue_family_indices)
} else {
Sharing::Exclusive
},
..Default::default()
},
)?;
let requirements = raw_image.memory_requirements()[0];
let res = unsafe {
allocator.allocate_unchecked(
requirements,
AllocationType::NonLinear,
AllocationCreateInfo {
usage: MemoryUsage::DeviceOnly,
allocate_preference: MemoryAllocatePreference::Unknown,
_ne: crate::NonExhaustive(()),
},
Some(DedicatedAllocation::Image(&raw_image)),
)
};
match res {
Ok(alloc) => {
debug_assert!(is_aligned(alloc.offset(), requirements.layout.alignment()));
debug_assert!(alloc.size() == requirements.layout.size());
let inner = Arc::new(
unsafe { raw_image.bind_memory_unchecked([alloc]) }
.map_err(|(err, _, _)| err)?,
);
let image = Arc::new(ImmutableImage { inner, layout });
let init = Arc::new(ImmutableImageInitialization {
image: image.clone(),
});
Ok((image, init))
}
Err(err) => Err(err.into()),
}
}
/// Construct an ImmutableImage from the contents of `iter`.
///
/// This is a convenience function, equivalent to creating a `CpuAccessibleBuffer`, writing
/// `iter` to it, then calling [`from_buffer`](ImmutableImage::from_buffer) to copy the data
/// over.
pub fn from_iter<Px, I, L, A>(
allocator: &(impl MemoryAllocator + ?Sized),
iter: I,
dimensions: ImageDimensions,
mip_levels: MipmapsCount,
format: Format,
command_buffer_builder: &mut AutoCommandBufferBuilder<L, A>,
) -> Result<Arc<Self>, ImmutableImageCreationError>
where
Px: BufferContents,
I: IntoIterator<Item = Px>,
I::IntoIter: ExactSizeIterator,
A: CommandBufferAllocator,
{
let source = Buffer::from_iter(
allocator,
BufferCreateInfo {
usage: BufferUsage::TRANSFER_SRC,
..Default::default()
},
AllocationCreateInfo {
usage: MemoryUsage::Upload,
..Default::default()
},
iter,
)
.map_err(|err| match err {
BufferError::AllocError(err) => err,
// We don't use sparse-binding, concurrent sharing or external memory, therefore the
// other errors can't happen.
_ => unreachable!(),
})?;
ImmutableImage::from_buffer(
allocator,
source,
dimensions,
mip_levels,
format,
command_buffer_builder,
)
}
/// Construct an ImmutableImage containing a copy of the data in `source`.
///
/// This is a convenience function, equivalent to calling
/// [`uninitialized`](ImmutableImage::uninitialized) with the queue family index of
/// `command_buffer_builder`, then recording a `copy_buffer_to_image` command to
/// `command_buffer_builder`.
///
/// `command_buffer_builder` can then be used to record other commands, built, and executed as
/// normal. If it is not executed, the image contents will be left undefined.
pub fn from_buffer<L, A>(
allocator: &(impl MemoryAllocator + ?Sized),
source: Subbuffer<impl ?Sized>,
dimensions: ImageDimensions,
mip_levels: MipmapsCount,
format: Format,
command_buffer_builder: &mut AutoCommandBufferBuilder<L, A>,
) -> Result<Arc<Self>, ImmutableImageCreationError>
where
A: CommandBufferAllocator,
{
let region = BufferImageCopy {
image_subresource: ImageSubresourceLayers::from_parameters(
format,
dimensions.array_layers(),
),
image_extent: dimensions.width_height_depth(),
..Default::default()
};
let required_size = region.buffer_copy_size(format);
if source.size() < required_size {
return Err(ImmutableImageCreationError::SourceTooSmall {
source_size: source.size(),
required_size,
});
}
let need_to_generate_mipmaps = has_mipmaps(mip_levels);
let usage = ImageUsage::TRANSFER_DST
| ImageUsage::SAMPLED
| if need_to_generate_mipmaps {
ImageUsage::TRANSFER_SRC
} else {
ImageUsage::empty()
};
let flags = ImageCreateFlags::empty();
let layout = ImageLayout::ShaderReadOnlyOptimal;
let (image, initializer) = ImmutableImage::uninitialized(
allocator,
dimensions,
format,
mip_levels,
usage,
flags,
layout,
source
.device()
.active_queue_family_indices()
.iter()
.copied(),
)?;
command_buffer_builder
.copy_buffer_to_image(CopyBufferToImageInfo {
regions: smallvec![region],
..CopyBufferToImageInfo::buffer_image(source, initializer)
})
.unwrap();
if need_to_generate_mipmaps {
generate_mipmaps(
command_buffer_builder,
image.clone(),
image.inner.dimensions(),
ImageLayout::ShaderReadOnlyOptimal,
);
}
Ok(image)
}
}
unsafe impl DeviceOwned for ImmutableImage {
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
unsafe impl ImageAccess for ImmutableImage {
#[inline]
fn inner(&self) -> ImageInner<'_> {
ImageInner {
image: &self.inner,
first_layer: 0,
num_layers: self.inner.dimensions().array_layers(),
first_mipmap_level: 0,
num_mipmap_levels: self.inner.mip_levels(),
}
}
#[inline]
fn is_layout_initialized(&self) -> bool {
true
}
#[inline]
fn initial_layout_requirement(&self) -> ImageLayout {
self.layout
}
#[inline]
fn final_layout_requirement(&self) -> ImageLayout {
self.layout
}
#[inline]
fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> {
Some(ImageDescriptorLayouts {
storage_image: ImageLayout::General,
combined_image_sampler: self.layout,
sampled_image: self.layout,
input_attachment: self.layout,
})
}
}
unsafe impl<P> ImageContent<P> for ImmutableImage {
fn matches_format(&self) -> bool {
true // FIXME:
}
}
impl PartialEq for ImmutableImage {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner() == other.inner()
}
}
impl Eq for ImmutableImage {}
impl Hash for ImmutableImage {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner().hash(state);
}
}
// Must not implement Clone, as that would lead to multiple `used` values.
pub struct ImmutableImageInitialization {
image: Arc<ImmutableImage>,
}
unsafe impl DeviceOwned for ImmutableImageInitialization {
#[inline]
fn device(&self) -> &Arc<Device> {
self.image.device()
}
}
unsafe impl ImageAccess for ImmutableImageInitialization {
#[inline]
fn inner(&self) -> ImageInner<'_> {
self.image.inner()
}
#[inline]
fn initial_layout_requirement(&self) -> ImageLayout {
ImageLayout::Undefined
}
#[inline]
fn final_layout_requirement(&self) -> ImageLayout {
self.image.layout
}
#[inline]
fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> {
None
}
}
impl PartialEq for ImmutableImageInitialization {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner() == other.inner()
}
}
impl Eq for ImmutableImageInitialization {}
impl Hash for ImmutableImageInitialization {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner().hash(state);
}
}
/// Error that can happen when creating an `ImmutableImage`.
#[derive(Clone, Debug)]
pub enum ImmutableImageCreationError {
ImageCreationError(ImageError),
AllocError(AllocationCreationError),
CommandBufferBeginError(CommandBufferBeginError),
/// The size of the provided source data is less than the required size for an image with the
/// given format and dimensions.
SourceTooSmall {
source_size: DeviceSize,
required_size: DeviceSize,
},
}
impl Error for ImmutableImageCreationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::ImageCreationError(err) => Some(err),
Self::AllocError(err) => Some(err),
Self::CommandBufferBeginError(err) => Some(err),
_ => None,
}
}
}
impl Display for ImmutableImageCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::ImageCreationError(err) => err.fmt(f),
Self::AllocError(err) => err.fmt(f),
Self::CommandBufferBeginError(err) => err.fmt(f),
Self::SourceTooSmall {
source_size,
required_size,
} => write!(
f,
"the size of the provided source data ({} bytes) is less than the required size \
for an image of the given format and dimensions ({} bytes)",
source_size, required_size,
),
}
}
}
impl From<ImageError> for ImmutableImageCreationError {
fn from(err: ImageError) -> Self {
Self::ImageCreationError(err)
}
}
impl From<AllocationCreationError> for ImmutableImageCreationError {
fn from(err: AllocationCreationError) -> Self {
Self::AllocError(err)
}
}
impl From<VulkanError> for ImmutableImageCreationError {
fn from(err: VulkanError) -> Self {
Self::AllocError(err.into())
}
}
impl From<CommandBufferBeginError> for ImmutableImageCreationError {
fn from(err: CommandBufferBeginError) -> Self {
Self::CommandBufferBeginError(err)
}
}