blob: 627d43a683224ce479302acf372811014d9aaf76 [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::device::physical::QueueFamily;
use crate::device::Device;
use crate::format::ClearValue;
use crate::format::Format;
use crate::format::FormatTy;
use crate::image::sys::ImageCreationError;
use crate::image::sys::UnsafeImage;
use crate::image::traits::ImageAccess;
use crate::image::traits::ImageClearValue;
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::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::StdMemoryPool;
use crate::memory::DedicatedAlloc;
use crate::sync::AccessError;
use crate::sync::Sharing;
use smallvec::SmallVec;
use std::hash::Hash;
use std::hash::Hasher;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
/// General-purpose image in device memory. Can be used for any usage, but will be slower than a
/// specialized image.
#[derive(Debug)]
pub struct StorageImage<A = Arc<StdMemoryPool>>
where
A: MemoryPool,
{
// Inner implementation.
image: UnsafeImage,
// Memory used to back the image.
memory: PotentialDedicatedAllocation<A::Alloc>,
// Dimensions of the image.
dimensions: ImageDimensions,
// Format.
format: Format,
// Queue families allowed to access this image.
queue_families: SmallVec<[u32; 4]>,
// Number of times this image is locked on the GPU side.
gpu_lock: AtomicUsize,
}
impl StorageImage {
/// Creates a new image with the given dimensions and format.
#[inline]
pub fn new<'a, I>(
device: Arc<Device>,
dimensions: ImageDimensions,
format: Format,
queue_families: I,
) -> Result<Arc<StorageImage>, ImageCreationError>
where
I: IntoIterator<Item = QueueFamily<'a>>,
{
let is_depth = match format.ty() {
FormatTy::Depth => true,
FormatTy::DepthStencil => true,
FormatTy::Stencil => true,
FormatTy::Compressed => panic!(),
_ => false,
};
let usage = ImageUsage {
transfer_source: true,
transfer_destination: true,
sampled: true,
storage: true,
color_attachment: !is_depth,
depth_stencil_attachment: is_depth,
input_attachment: true,
transient_attachment: false,
};
let flags = ImageCreateFlags::none();
StorageImage::with_usage(device, dimensions, format, usage, flags, queue_families)
}
/// Same as `new`, but allows specifying the usage.
pub fn with_usage<'a, I>(
device: Arc<Device>,
dimensions: ImageDimensions,
format: Format,
usage: ImageUsage,
flags: ImageCreateFlags,
queue_families: I,
) -> Result<Arc<StorageImage>, ImageCreationError>
where
I: IntoIterator<Item = QueueFamily<'a>>,
{
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,
1,
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())?;
}
Ok(Arc::new(StorageImage {
image,
memory,
dimensions,
format,
queue_families,
gpu_lock: AtomicUsize::new(0),
}))
}
}
impl<A> StorageImage<A>
where
A: MemoryPool,
{
/// Returns the dimensions of the image.
#[inline]
pub fn dimensions(&self) -> ImageDimensions {
self.dimensions
}
}
unsafe impl<A> ImageAccess for StorageImage<A>
where
A: MemoryPool,
{
#[inline]
fn inner(&self) -> ImageInner {
ImageInner {
image: &self.image,
first_layer: 0,
num_layers: self.dimensions.array_layers() as usize,
first_mipmap_level: 0,
num_mipmap_levels: 1,
}
}
#[inline]
fn initial_layout_requirement(&self) -> ImageLayout {
ImageLayout::General
}
#[inline]
fn final_layout_requirement(&self) -> ImageLayout {
ImageLayout::General
}
#[inline]
fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> {
Some(ImageDescriptorLayouts {
storage_image: ImageLayout::General,
combined_image_sampler: ImageLayout::General,
sampled_image: ImageLayout::General,
input_attachment: ImageLayout::General,
})
}
#[inline]
fn conflict_key(&self) -> u64 {
self.image.key()
}
#[inline]
fn try_gpu_lock(
&self,
_: bool,
uninitialized_safe: bool,
expected_layout: ImageLayout,
) -> Result<(), AccessError> {
// TODO: handle initial layout transition
if expected_layout != ImageLayout::General && expected_layout != ImageLayout::Undefined {
return Err(AccessError::UnexpectedImageLayout {
requested: expected_layout,
allowed: ImageLayout::General,
});
}
let val = self
.gpu_lock
.compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst)
.unwrap_or_else(|e| e);
if val == 0 {
Ok(())
} else {
Err(AccessError::AlreadyInUse)
}
}
#[inline]
unsafe fn increase_gpu_lock(&self) {
let val = self.gpu_lock.fetch_add(1, Ordering::SeqCst);
debug_assert!(val >= 1);
}
#[inline]
unsafe fn unlock(&self, new_layout: Option<ImageLayout>) {
assert!(new_layout.is_none() || new_layout == Some(ImageLayout::General));
self.gpu_lock.fetch_sub(1, Ordering::SeqCst);
}
#[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<A> ImageClearValue<ClearValue> for StorageImage<A>
where
A: MemoryPool,
{
#[inline]
fn decode(&self, value: ClearValue) -> Option<ClearValue> {
Some(self.format.decode_clear_value(value))
}
}
unsafe impl<P, A> ImageContent<P> for StorageImage<A>
where
A: MemoryPool,
{
#[inline]
fn matches_format(&self) -> bool {
true // FIXME:
}
}
impl<A> PartialEq for StorageImage<A>
where
A: MemoryPool,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
ImageAccess::inner(self) == ImageAccess::inner(other)
}
}
impl<A> Eq for StorageImage<A> where A: MemoryPool {}
impl<A> Hash for StorageImage<A>
where
A: MemoryPool,
{
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
ImageAccess::inner(self).hash(state);
}
}
#[cfg(test)]
mod tests {
use super::StorageImage;
use crate::format::Format;
use crate::image::ImageDimensions;
#[test]
fn create() {
let (device, queue) = gfx_dev_and_queue!();
let _img = StorageImage::new(
device,
ImageDimensions::Dim2d {
width: 32,
height: 32,
array_layers: 1,
},
Format::R8G8B8A8Unorm,
Some(queue.family()),
)
.unwrap();
}
}