blob: 6a6e0933fc8192fd44d4b00907a9dffde01786dc [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 crate::buffer::BufferAccess;
use crate::buffer::BufferUsage;
use crate::buffer::CpuAccessibleBuffer;
use crate::buffer::TypedBufferAccess;
use crate::command_buffer::AutoCommandBufferBuilder;
use crate::command_buffer::CommandBufferExecFuture;
use crate::command_buffer::CommandBufferUsage;
use crate::command_buffer::PrimaryAutoCommandBuffer;
use crate::command_buffer::PrimaryCommandBuffer;
use crate::device::physical::QueueFamily;
use crate::device::Device;
use crate::device::Queue;
use crate::format::Format;
use crate::format::Pixel;
use crate::image::sys::ImageCreationError;
use crate::image::sys::UnsafeImage;
use crate::image::traits::ImageAccess;
use crate::image::traits::ImageContent;
use crate::image::ImageCreateFlags;
use crate::image::ImageDescriptorLayouts;
use crate::image::ImageDimensions;
use crate::image::ImageInner;
use crate::image::ImageLayout;
use crate::image::ImageUsage;
use crate::image::MipmapsCount;
use crate::image::SampleCount;
use crate::memory::pool::AllocFromRequirementsFilter;
use crate::memory::pool::AllocLayout;
use crate::memory::pool::MappingRequirement;
use crate::memory::pool::MemoryPool;
use crate::memory::pool::MemoryPoolAlloc;
use crate::memory::pool::PotentialDedicatedAllocation;
use crate::memory::pool::StdMemoryPoolAlloc;
use crate::memory::DedicatedAlloc;
use crate::sampler::Filter;
use crate::sync::AccessError;
use crate::sync::NowFuture;
use crate::sync::Sharing;
use smallvec::SmallVec;
use std::hash::Hash;
use std::hash::Hasher;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::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<A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>> {
image: UnsafeImage,
dimensions: ImageDimensions,
memory: A,
format: Format,
initialized: AtomicBool,
layout: ImageLayout,
}
/// Image whose purpose is to access only a part of one image, for any kind of access
/// We define a part of one image here by a level of mipmap, or a layer of an array
/// The image attribute must be an implementation of ImageAccess
/// The mip_levels_access must be a range showing which mipmaps will be accessed
/// The layer_levels_access must be a range showing which layers will be accessed
/// The layout must be the layout of the image at the beginning and at the end of the command buffer
pub struct SubImage {
image: Arc<dyn ImageAccess + Sync + Send>,
mip_levels_access: std::ops::Range<u32>,
layer_levels_access: std::ops::Range<u32>,
layout: ImageLayout,
}
impl SubImage {
pub fn new(
image: Arc<dyn ImageAccess + Sync + Send>,
mip_level: u32,
mip_level_count: u32,
layer_level: u32,
layer_level_count: u32,
layout: ImageLayout,
) -> Arc<SubImage> {
debug_assert!(mip_level + mip_level_count <= image.mipmap_levels());
debug_assert!(layer_level + layer_level_count <= image.dimensions().array_layers());
let last_level = mip_level + mip_level_count;
let mip_levels_access = mip_level..last_level;
let last_level = layer_level + layer_level_count;
let layer_levels_access = layer_level..last_level;
Arc::new(SubImage {
image,
mip_levels_access,
layer_levels_access,
layout: ImageLayout::ShaderReadOnlyOptimal,
})
}
}
// Must not implement Clone, as that would lead to multiple `used` values.
pub struct ImmutableImageInitialization<A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>> {
image: Arc<ImmutableImage<A>>,
used: AtomicBool,
mip_levels_access: std::ops::Range<u32>,
layer_levels_access: std::ops::Range<u32>,
}
fn has_mipmaps(mipmaps: MipmapsCount) -> bool {
match mipmaps {
MipmapsCount::One => false,
MipmapsCount::Log2 => true,
MipmapsCount::Specific(x) => x > 1,
}
}
fn generate_mipmaps<L, Img>(
cbb: &mut AutoCommandBufferBuilder<L>,
image: Arc<Img>,
dimensions: ImageDimensions,
layout: ImageLayout,
) where
Img: ImageAccess + Send + Sync + 'static,
{
for level in 1..image.mipmap_levels() {
let [xs, ys, ds] = dimensions
.mipmap_dimensions(level - 1)
.unwrap()
.width_height_depth();
let [xd, yd, dd] = dimensions
.mipmap_dimensions(level)
.unwrap()
.width_height_depth();
let src = SubImage::new(
image.clone(),
level - 1,
1,
0,
dimensions.array_layers(),
layout,
);
let dst = SubImage::new(
image.clone(),
level,
1,
0,
dimensions.array_layers(),
layout,
);
cbb.blit_image(
src, //source
[0, 0, 0], //source_top_left
[xs as i32, ys as i32, ds as i32], //source_bottom_right
0, //source_base_array_layer
level - 1, //source_mip_level
dst, //destination
[0, 0, 0], //destination_top_left
[xd as i32, yd as i32, dd as i32], //destination_bottom_right
0, //destination_base_array_layer
level, //destination_mip_level
1, //layer_count
Filter::Linear, //filter
)
.expect("failed to blit a mip map to image!");
}
}
impl ImmutableImage {
#[deprecated(note = "use ImmutableImage::uninitialized instead")]
#[inline]
pub fn new<'a, I>(
device: Arc<Device>,
dimensions: ImageDimensions,
format: Format,
queue_families: I,
) -> Result<Arc<ImmutableImage>, ImageCreationError>
where
I: IntoIterator<Item = QueueFamily<'a>>,
{
#[allow(deprecated)]
ImmutableImage::with_mipmaps(
device,
dimensions,
format,
MipmapsCount::One,
queue_families,
)
}
#[deprecated(note = "use ImmutableImage::uninitialized instead")]
#[inline]
pub fn with_mipmaps<'a, I, M>(
device: Arc<Device>,
dimensions: ImageDimensions,
format: Format,
mipmaps: M,
queue_families: I,
) -> Result<Arc<ImmutableImage>, ImageCreationError>
where
I: IntoIterator<Item = QueueFamily<'a>>,
M: Into<MipmapsCount>,
{
let usage = ImageUsage {
transfer_source: true, // for blits
transfer_destination: true,
sampled: true,
..ImageUsage::none()
};
let flags = ImageCreateFlags::none();
let (image, _) = ImmutableImage::uninitialized(
device,
dimensions,
format,
mipmaps,
usage,
flags,
ImageLayout::ShaderReadOnlyOptimal,
queue_families,
)?;
image.initialized.store(true, Ordering::Relaxed); // Allow uninitialized access for backwards compatibility
Ok(image)
}
/// 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<'a, I, M>(
device: Arc<Device>,
dimensions: ImageDimensions,
format: Format,
mipmaps: M,
usage: ImageUsage,
flags: ImageCreateFlags,
layout: ImageLayout,
queue_families: I,
) -> Result<(Arc<ImmutableImage>, ImmutableImageInitialization), ImageCreationError>
where
I: IntoIterator<Item = QueueFamily<'a>>,
M: Into<MipmapsCount>,
{
let queue_families = queue_families
.into_iter()
.map(|f| f.id())
.collect::<SmallVec<[u32; 4]>>();
let (image, mem_reqs) = unsafe {
let sharing = if queue_families.len() >= 2 {
Sharing::Concurrent(queue_families.iter().cloned())
} else {
Sharing::Exclusive
};
UnsafeImage::new(
device.clone(),
usage,
format,
flags,
dimensions,
SampleCount::Sample1,
mipmaps,
sharing,
false,
false,
)?
};
let memory = MemoryPool::alloc_from_requirements(
&Device::standard_pool(&device),
&mem_reqs,
AllocLayout::Optimal,
MappingRequirement::DoNotMap,
DedicatedAlloc::Image(&image),
|t| {
if t.is_device_local() {
AllocFromRequirementsFilter::Preferred
} else {
AllocFromRequirementsFilter::Allowed
}
},
)?;
debug_assert!((memory.offset() % mem_reqs.alignment) == 0);
unsafe {
image.bind_memory(memory.memory(), memory.offset())?;
}
let image = Arc::new(ImmutableImage {
image,
memory,
dimensions,
format,
initialized: AtomicBool::new(false),
layout,
});
let init = ImmutableImageInitialization {
image: image.clone(),
used: AtomicBool::new(false),
mip_levels_access: 0..image.mipmap_levels(),
layer_levels_access: 0..image.dimensions().array_layers(),
};
Ok((image, init))
}
/// Construct an ImmutableImage from the contents of `iter`.
#[inline]
pub fn from_iter<Px, I>(
iter: I,
dimensions: ImageDimensions,
mipmaps: MipmapsCount,
format: Format,
queue: Arc<Queue>,
) -> Result<
(
Arc<Self>,
CommandBufferExecFuture<NowFuture, PrimaryAutoCommandBuffer>,
),
ImageCreationError,
>
where
Px: Pixel + Send + Sync + Clone + 'static,
I: ExactSizeIterator<Item = Px>,
{
let source = CpuAccessibleBuffer::from_iter(
queue.device().clone(),
BufferUsage::transfer_source(),
false,
iter,
)?;
ImmutableImage::from_buffer(source, dimensions, mipmaps, format, queue)
}
/// Construct an ImmutableImage containing a copy of the data in `source`.
pub fn from_buffer<B, Px>(
source: B,
dimensions: ImageDimensions,
mipmaps: MipmapsCount,
format: Format,
queue: Arc<Queue>,
) -> Result<
(
Arc<Self>,
CommandBufferExecFuture<NowFuture, PrimaryAutoCommandBuffer>,
),
ImageCreationError,
>
where
B: BufferAccess + TypedBufferAccess<Content = [Px]> + 'static + Clone + Send + Sync,
Px: Pixel + Send + Sync + Clone + 'static,
{
let need_to_generate_mipmaps = has_mipmaps(mipmaps);
let usage = ImageUsage {
transfer_destination: true,
transfer_source: need_to_generate_mipmaps,
sampled: true,
..ImageUsage::none()
};
let flags = ImageCreateFlags::none();
let layout = ImageLayout::ShaderReadOnlyOptimal;
let (image, initializer) = ImmutableImage::uninitialized(
source.device().clone(),
dimensions,
format,
mipmaps,
usage,
flags,
layout,
source.device().active_queue_families(),
)?;
let init = SubImage::new(
Arc::new(initializer),
0,
1,
0,
1,
ImageLayout::ShaderReadOnlyOptimal,
);
let mut cbb = AutoCommandBufferBuilder::primary(
source.device().clone(),
queue.family(),
CommandBufferUsage::MultipleSubmit,
)?;
cbb.copy_buffer_to_image_dimensions(
source,
init,
[0, 0, 0],
dimensions.width_height_depth(),
0,
dimensions.array_layers(),
0,
)
.unwrap();
if need_to_generate_mipmaps {
generate_mipmaps(
&mut cbb,
image.clone(),
image.dimensions,
ImageLayout::ShaderReadOnlyOptimal,
);
}
let cb = cbb.build().unwrap();
let future = match cb.execute(queue) {
Ok(f) => f,
Err(e) => unreachable!("{:?}", e),
};
image.initialized.store(true, Ordering::Relaxed);
Ok((image, future))
}
}
impl<A> ImmutableImage<A> {
/// Returns the dimensions of the image.
#[inline]
pub fn dimensions(&self) -> ImageDimensions {
self.dimensions
}
/// Returns the number of mipmap levels of the image.
#[inline]
pub fn mipmap_levels(&self) -> u32 {
self.image.mipmap_levels()
}
}
unsafe impl<A> ImageAccess for ImmutableImage<A> {
#[inline]
fn inner(&self) -> ImageInner {
ImageInner {
image: &self.image,
first_layer: 0,
num_layers: self.image.dimensions().array_layers() as usize,
first_mipmap_level: 0,
num_mipmap_levels: self.image.mipmap_levels() as usize,
}
}
#[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: self.layout,
combined_image_sampler: self.layout,
sampled_image: self.layout,
input_attachment: self.layout,
})
}
#[inline]
fn conflict_key(&self) -> u64 {
self.image.key()
}
#[inline]
fn try_gpu_lock(
&self,
exclusive_access: bool,
uninitialized_safe: bool,
expected_layout: ImageLayout,
) -> Result<(), AccessError> {
if expected_layout != self.layout && expected_layout != ImageLayout::Undefined {
return Err(AccessError::UnexpectedImageLayout {
requested: expected_layout,
allowed: self.layout,
});
}
if exclusive_access {
return Err(AccessError::ExclusiveDenied);
}
if !self.initialized.load(Ordering::Relaxed) {
return Err(AccessError::BufferNotInitialized);
}
Ok(())
}
#[inline]
unsafe fn increase_gpu_lock(&self) {}
#[inline]
unsafe fn unlock(&self, new_layout: Option<ImageLayout>) {
debug_assert!(new_layout.is_none());
}
#[inline]
fn current_miplevels_access(&self) -> std::ops::Range<u32> {
0..self.mipmap_levels()
}
#[inline]
fn current_layer_levels_access(&self) -> std::ops::Range<u32> {
0..self.dimensions().array_layers()
}
}
unsafe impl<P, A> ImageContent<P> for ImmutableImage<A> {
#[inline]
fn matches_format(&self) -> bool {
true // FIXME:
}
}
unsafe impl ImageAccess for SubImage {
#[inline]
fn inner(&self) -> ImageInner {
self.image.inner()
}
#[inline]
fn initial_layout_requirement(&self) -> ImageLayout {
self.image.initial_layout_requirement()
}
#[inline]
fn final_layout_requirement(&self) -> ImageLayout {
self.image.final_layout_requirement()
}
#[inline]
fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> {
None
}
fn current_miplevels_access(&self) -> std::ops::Range<u32> {
self.mip_levels_access.clone()
}
fn current_layer_levels_access(&self) -> std::ops::Range<u32> {
self.layer_levels_access.clone()
}
#[inline]
fn conflict_key(&self) -> u64 {
self.image.conflict_key()
}
#[inline]
fn try_gpu_lock(
&self,
exclusive_access: bool,
uninitialized_safe: bool,
expected_layout: ImageLayout,
) -> Result<(), AccessError> {
if expected_layout != self.layout && expected_layout != ImageLayout::Undefined {
return Err(AccessError::UnexpectedImageLayout {
requested: expected_layout,
allowed: self.layout,
});
}
Ok(())
}
#[inline]
unsafe fn increase_gpu_lock(&self) {
self.image.increase_gpu_lock()
}
#[inline]
unsafe fn unlock(&self, new_layout: Option<ImageLayout>) {
self.image.unlock(new_layout)
}
}
impl<A> PartialEq for ImmutableImage<A> {
#[inline]
fn eq(&self, other: &Self) -> bool {
ImageAccess::inner(self) == ImageAccess::inner(other)
}
}
impl<A> Eq for ImmutableImage<A> {}
impl<A> Hash for ImmutableImage<A> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
ImageAccess::inner(self).hash(state);
}
}
unsafe impl<A> ImageAccess for ImmutableImageInitialization<A> {
#[inline]
fn inner(&self) -> ImageInner {
ImageAccess::inner(&self.image)
}
#[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
}
#[inline]
fn conflict_key(&self) -> u64 {
self.image.image.key()
}
#[inline]
fn try_gpu_lock(
&self,
_: bool,
uninitialized_safe: bool,
expected_layout: ImageLayout,
) -> Result<(), AccessError> {
if expected_layout != ImageLayout::Undefined {
return Err(AccessError::UnexpectedImageLayout {
requested: expected_layout,
allowed: ImageLayout::Undefined,
});
}
if self.image.initialized.load(Ordering::Relaxed) {
return Err(AccessError::AlreadyInUse);
}
// FIXME: Mipmapped textures require multiple writes to initialize
if !self
.used
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.unwrap_or_else(|e| e)
{
Ok(())
} else {
Err(AccessError::AlreadyInUse)
}
}
#[inline]
unsafe fn increase_gpu_lock(&self) {
debug_assert!(self.used.load(Ordering::Relaxed));
}
#[inline]
unsafe fn unlock(&self, new_layout: Option<ImageLayout>) {
assert_eq!(new_layout, Some(self.image.layout));
self.image.initialized.store(true, Ordering::Relaxed);
}
#[inline]
fn current_miplevels_access(&self) -> std::ops::Range<u32> {
self.mip_levels_access.clone()
}
#[inline]
fn current_layer_levels_access(&self) -> std::ops::Range<u32> {
self.layer_levels_access.clone()
}
}
impl<A> PartialEq for ImmutableImageInitialization<A> {
#[inline]
fn eq(&self, other: &Self) -> bool {
ImageAccess::inner(self) == ImageAccess::inner(other)
}
}
impl<A> Eq for ImmutableImageInitialization<A> {}
impl<A> Hash for ImmutableImageInitialization<A> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
ImageAccess::inner(self).hash(state);
}
}