diff options
Diffstat (limited to 'src/image/storage.rs')
-rw-r--r-- | src/image/storage.rs | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/src/image/storage.rs b/src/image/storage.rs new file mode 100644 index 0000000..627d43a --- /dev/null +++ b/src/image/storage.rs @@ -0,0 +1,336 @@ +// 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(); + } +} |