use std::iter::Empty;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use smallvec::SmallVec;
use device::Device;
use device::Queue;
use format::ClearValue;
use format::FormatDesc;
use format::FormatTy;
use format::Format;
use image::Dimensions;
use image::ImageDimensions;
use image::sys::ImageCreationError;
use image::ImageLayout;
use image::ImageUsage;
use image::sys::UnsafeImage;
use image::sys::UnsafeImageView;
use image::traits::ImageAccess;
use image::traits::ImageClearValue;
use image::traits::ImageContent;
use image::traits::ImageViewAccess;
use image::traits::Image;
use image::traits::ImageView;
use instance::QueueFamily;
use memory::pool::AllocLayout;
use memory::pool::MemoryPool;
use memory::pool::MemoryPoolAlloc;
use memory::pool::StdMemoryPool;
use sync::AccessError;
use sync::Sharing;
#[derive(Debug)]
pub struct StorageImage<F, A = Arc<StdMemoryPool>> where A: MemoryPool {
image: UnsafeImage,
view: UnsafeImageView,
memory: A::Alloc,
dimensions: Dimensions,
format: F,
queue_families: SmallVec<[u32; 4]>,
gpu_lock: AtomicUsize,
}
impl<F> StorageImage<F> {
pub fn new<'a, I>(device: Arc<Device>, dimensions: Dimensions, format: F, queue_families: I)
-> Result<Arc<StorageImage<F>>, ImageCreationError>
where F: FormatDesc,
I: IntoIterator<Item = QueueFamily<'a>>
{
let is_depth = match format.format().ty() {
FormatTy::Depth => true,
FormatTy::DepthStencil => true,
FormatTy::Stencil => true,
FormatTy::Compressed => panic!(),
_ => false
};
let usage = ImageUsage {
transfer_source: true,
transfer_dest: true,
sampled: true,
storage: true,
color_attachment: !is_depth,
depth_stencil_attachment: is_depth,
input_attachment: true,
transient_attachment: false,
};
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
};
try!(UnsafeImage::new(device.clone(), usage, format.format(), dimensions.to_image_dimensions(),
1, 1, Sharing::Exclusive::<Empty<u32>>, false, false))
};
let mem_ty = {
let device_local = device.physical_device().memory_types()
.filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0)
.filter(|t| t.is_device_local());
let any = device.physical_device().memory_types()
.filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0);
device_local.chain(any).next().unwrap()
};
let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty,
mem_reqs.size, mem_reqs.alignment, AllocLayout::Optimal));
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
unsafe { try!(image.bind_memory(mem.memory(), mem.offset())); }
let view = unsafe {
try!(UnsafeImageView::raw(&image, dimensions.to_view_type(), 0 .. image.mipmap_levels(),
0 .. image.dimensions().array_layers()))
};
Ok(Arc::new(StorageImage {
image: image,
view: view,
memory: mem,
dimensions: dimensions,
format: format,
queue_families: queue_families,
gpu_lock: AtomicUsize::new(0),
}))
}
}
impl<F, A> StorageImage<F, A> where A: MemoryPool {
#[inline]
pub fn dimensions(&self) -> Dimensions {
self.dimensions
}
}
unsafe impl<F, A> Image for Arc<StorageImage<F, A>>
where F: 'static + Send + Sync, A: MemoryPool
{
type Access = Self;
#[inline]
fn access(self) -> Self {
self
}
#[inline]
fn format(&self) -> Format {
self.image.format()
}
#[inline]
fn samples(&self) -> u32 {
self.image.samples()
}
#[inline]
fn dimensions(&self) -> ImageDimensions {
self.image.dimensions()
}
}
unsafe impl<F, A> ImageView for Arc<StorageImage<F, A>>
where F: 'static + Send + Sync, A: MemoryPool
{
type Access = Self;
#[inline]
fn access(self) -> Self {
self
}
}
unsafe impl<F, A> ImageAccess for StorageImage<F, A> where F: 'static + Send + Sync, A: MemoryPool {
#[inline]
fn inner(&self) -> &UnsafeImage {
&self.image
}
#[inline]
fn initial_layout_requirement(&self) -> ImageLayout {
ImageLayout::General
}
#[inline]
fn final_layout_requirement(&self) -> ImageLayout {
ImageLayout::General
}
#[inline]
fn conflict_key(&self, _: u32, _: u32, _: u32, _: u32) -> u64 {
self.image.key()
}
#[inline]
fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
let val = self.gpu_lock.fetch_add(1, Ordering::SeqCst);
if val == 1 {
Ok(())
} else {
self.gpu_lock.fetch_sub(1, Ordering::SeqCst);
Err(AccessError::AlreadyInUse)
}
}
#[inline]
unsafe fn increase_gpu_lock(&self) {
let val = self.gpu_lock.fetch_add(1, Ordering::SeqCst);
debug_assert!(val >= 1);
}
}
unsafe impl<F, A> ImageClearValue<F::ClearValue> for StorageImage<F, A>
where F: FormatDesc + 'static + Send + Sync, A: MemoryPool
{
#[inline]
fn decode(&self, value: F::ClearValue) -> Option<ClearValue> {
Some(self.format.decode_clear_value(value))
}
}
unsafe impl<P, F, A> ImageContent<P> for StorageImage<F, A>
where F: 'static + Send + Sync, A: MemoryPool
{
#[inline]
fn matches_format(&self) -> bool {
true }
}
unsafe impl<F, A> ImageViewAccess for StorageImage<F, A>
where F: 'static + Send + Sync, A: MemoryPool
{
#[inline]
fn parent(&self) -> &ImageAccess {
self
}
#[inline]
fn dimensions(&self) -> Dimensions {
self.dimensions
}
#[inline]
fn inner(&self) -> &UnsafeImageView {
&self.view
}
#[inline]
fn descriptor_set_storage_image_layout(&self) -> ImageLayout {
ImageLayout::General
}
#[inline]
fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout {
ImageLayout::General
}
#[inline]
fn descriptor_set_sampled_image_layout(&self) -> ImageLayout {
ImageLayout::General
}
#[inline]
fn descriptor_set_input_attachment_layout(&self) -> ImageLayout {
ImageLayout::General
}
#[inline]
fn identity_swizzle(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::StorageImage;
use format::Format;
use image::Dimensions;
#[test]
fn create() {
let (device, queue) = gfx_dev_and_queue!();
let _img = StorageImage::new(device, Dimensions::Dim2d { width: 32, height: 32 },
Format::R8G8B8A8Unorm, Some(queue.family())).unwrap();
}
}