use std::error;
use std::fmt;
use std::iter;
use std::marker::PhantomData;
use std::mem;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use smallvec::SmallVec;
use buffer::CpuAccessibleBuffer;
use buffer::sys::BufferCreationError;
use buffer::sys::SparseLevel;
use buffer::sys::UnsafeBuffer;
use buffer::BufferUsage;
use buffer::traits::BufferAccess;
use buffer::traits::BufferInner;
use buffer::traits::Buffer;
use buffer::traits::TypedBuffer;
use buffer::traits::TypedBufferAccess;
use command_buffer::cb::AddCommand;
use command_buffer::commands_raw::CmdCopyBuffer;
use command_buffer::commands_raw::CmdCopyBufferError;
use command_buffer::AutoCommandBufferBuilder;
use command_buffer::CommandBuffer;
use command_buffer::CommandBufferBuilder;
use command_buffer::CommandBufferBuilderError;
use command_buffer::CommandBufferExecFuture;
use device::Device;
use device::DeviceOwned;
use device::Queue;
use instance::QueueFamily;
use memory::pool::AllocLayout;
use memory::pool::MemoryPool;
use memory::pool::MemoryPoolAlloc;
use memory::pool::StdMemoryPoolAlloc;
use sync::AccessError;
use sync::NowFuture;
use sync::Sharing;
use OomError;
pub struct ImmutableBuffer<T: ?Sized, A = StdMemoryPoolAlloc> {
inner: UnsafeBuffer,
memory: A,
initialized: AtomicBool,
queue_families: SmallVec<[u32; 4]>,
marker: PhantomData<Box<T>>,
}
type ImmutableBufferFromBufferFuture = CommandBufferExecFuture<NowFuture, ::command_buffer::cb::SubmitSyncLayer<::command_buffer::cb::AbstractStorageLayer<::command_buffer::cb::UnsafeCommandBuffer<Arc<::command_buffer::pool::standard::StandardCommandPool>>>>>;
impl<T: ?Sized> ImmutableBuffer<T> {
pub fn from_data<'a, I>(data: T, usage: BufferUsage, queue_families: I, queue: Arc<Queue>)
-> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferFromBufferFuture), OomError>
where I: IntoIterator<Item = QueueFamily<'a>>,
T: 'static + Send + Sync + Sized,
{
let source = CpuAccessibleBuffer::from_data(queue.device().clone(), BufferUsage::transfer_source(),
iter::once(queue.family()), data)?;
ImmutableBuffer::from_buffer(source, usage, queue_families, queue)
}
pub fn from_buffer<'a, B, I>(source: B, usage: BufferUsage, queue_families: I, queue: Arc<Queue>)
-> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferFromBufferFuture), OomError>
where B: Buffer + TypedBuffer<Content = T> + DeviceOwned, B::Access: 'static + Clone + Send + Sync,
I: IntoIterator<Item = QueueFamily<'a>>,
T: 'static + Send + Sync,
{
let cb = AutoCommandBufferBuilder::new(source.device().clone(), queue.family())?;
let (buf, cb) = match ImmutableBuffer::from_buffer_with_builder(source, usage,
queue_families, cb)
{
Ok(v) => v,
Err(ImmutableBufferFromBufferWithBuilderError::OomError(err)) => return Err(err),
Err(ImmutableBufferFromBufferWithBuilderError::CommandBufferBuilderError(_)) => {
unreachable!()
},
};
let future = match cb.build()?.execute(queue) {
Ok(f) => f,
Err(_) => unreachable!()
};
Ok((buf, future))
}
pub fn from_buffer_with_builder<'a, B, I, Cb, O>(source: B, usage: BufferUsage, queue_families: I,
builder: Cb)
-> Result<(Arc<ImmutableBuffer<T>>, O), ImmutableBufferFromBufferWithBuilderError>
where B: Buffer + TypedBuffer<Content = T> + DeviceOwned, I: IntoIterator<Item = QueueFamily<'a>>,
Cb: CommandBufferBuilder +
AddCommand<CmdCopyBuffer<B::Access, ImmutableBufferInitialization<T>>, Out = O>,
{
unsafe {
let actual_usage = BufferUsage {
transfer_dest: true,
.. usage
};
let (buffer, init) = ImmutableBuffer::raw(source.device().clone(), source.size(),
actual_usage, queue_families)?;
let builder = builder.copy_buffer(source, init)?;
Ok((buffer, builder))
}
}
}
impl<T> ImmutableBuffer<T> {
#[inline]
pub unsafe fn uninitialized<'a, I>(device: Arc<Device>, usage: BufferUsage, queue_families: I)
-> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferInitialization<T>), OomError>
where I: IntoIterator<Item = QueueFamily<'a>>
{
ImmutableBuffer::raw(device, mem::size_of::<T>(), usage, queue_families)
}
}
impl<T> ImmutableBuffer<[T]> {
pub fn from_iter<'a, D, I>(data: D, usage: BufferUsage, queue_families: I, queue: Arc<Queue>)
-> Result<(Arc<ImmutableBuffer<[T]>>, ImmutableBufferFromBufferFuture), OomError>
where I: IntoIterator<Item = QueueFamily<'a>>,
D: ExactSizeIterator<Item = T>,
T: 'static + Send + Sync + Sized,
{
let source = CpuAccessibleBuffer::from_iter(queue.device().clone(), BufferUsage::transfer_source(),
iter::once(queue.family()), data)?;
ImmutableBuffer::from_buffer(source, usage, queue_families, queue)
}
#[inline]
pub unsafe fn uninitialized_array<'a, I>(device: Arc<Device>, len: usize, usage: BufferUsage,
queue_families: I)
-> Result<(Arc<ImmutableBuffer<[T]>>, ImmutableBufferInitialization<[T]>), OomError>
where I: IntoIterator<Item = QueueFamily<'a>>
{
ImmutableBuffer::raw(device, len * mem::size_of::<T>(), usage, queue_families)
}
}
impl<T: ?Sized> ImmutableBuffer<T> {
#[inline]
pub unsafe fn raw<'a, I>(device: Arc<Device>, size: usize, usage: BufferUsage, queue_families: I)
-> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferInitialization<T>), OomError>
where I: IntoIterator<Item = QueueFamily<'a>>
{
let queue_families = queue_families.into_iter().map(|f| f.id()).collect();
ImmutableBuffer::raw_impl(device, size, usage, queue_families)
}
unsafe fn raw_impl(device: Arc<Device>, size: usize, usage: BufferUsage,
queue_families: SmallVec<[u32; 4]>)
-> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferInitialization<T>), OomError>
{
let (buffer, mem_reqs) = {
let sharing = if queue_families.len() >= 2 {
Sharing::Concurrent(queue_families.iter().cloned())
} else {
Sharing::Exclusive
};
match UnsafeBuffer::new(device.clone(), size, usage, sharing, SparseLevel::none()) {
Ok(b) => b,
Err(BufferCreationError::OomError(err)) => return Err(err),
Err(_) => unreachable!() }
};
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::Linear));
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
try!(buffer.bind_memory(mem.memory(), mem.offset()));
let final_buf = Arc::new(ImmutableBuffer {
inner: buffer,
memory: mem,
queue_families: queue_families,
initialized: AtomicBool::new(false),
marker: PhantomData,
});
let initialization = ImmutableBufferInitialization {
buffer: final_buf.clone(),
used: Arc::new(AtomicBool::new(false)),
};
Ok((final_buf, initialization))
}
}
impl<T: ?Sized, A> ImmutableBuffer<T, A> {
#[inline]
pub fn device(&self) -> &Arc<Device> {
self.inner.device()
}
#[inline]
pub fn queue_families(&self) -> Vec<QueueFamily> {
self.queue_families.iter().map(|&num| {
self.device().physical_device().queue_family_by_id(num).unwrap()
}).collect()
}
}
unsafe impl<T: ?Sized, A> Buffer for Arc<ImmutableBuffer<T, A>> {
type Access = Self;
#[inline]
fn access(self) -> Self {
self
}
#[inline]
fn size(&self) -> usize {
self.inner.size()
}
}
unsafe impl<T: ?Sized, A> TypedBuffer for Arc<ImmutableBuffer<T, A>> {
type Content = T;
}
unsafe impl<T: ?Sized, A> BufferAccess for ImmutableBuffer<T, A> {
#[inline]
fn inner(&self) -> BufferInner {
BufferInner {
buffer: &self.inner,
offset: 0,
}
}
#[inline]
fn conflict_key(&self, self_offset: usize, self_size: usize) -> u64 {
self.inner.key()
}
#[inline]
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
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) {
}
}
unsafe impl<T: ?Sized, A> TypedBufferAccess for ImmutableBuffer<T, A> {
type Content = T;
}
unsafe impl<T: ?Sized, A> DeviceOwned for ImmutableBuffer<T, A> {
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
pub struct ImmutableBufferInitialization<T: ?Sized, A = StdMemoryPoolAlloc> {
buffer: Arc<ImmutableBuffer<T, A>>,
used: Arc<AtomicBool>,
}
unsafe impl<T: ?Sized, A> BufferAccess for ImmutableBufferInitialization<T, A> {
#[inline]
fn inner(&self) -> BufferInner {
self.buffer.inner()
}
#[inline]
fn conflict_key(&self, self_offset: usize, self_size: usize) -> u64 {
self.buffer.inner.key()
}
#[inline]
fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> {
if !self.used.compare_and_swap(false, true, Ordering::Relaxed) {
Ok(())
} else {
Err(AccessError::AlreadyInUse)
}
}
#[inline]
unsafe fn increase_gpu_lock(&self) {
debug_assert!(self.used.load(Ordering::Relaxed));
}
}
unsafe impl<T: ?Sized, A> TypedBufferAccess for ImmutableBufferInitialization<T, A> {
type Content = T;
}
unsafe impl<T: ?Sized, A> Buffer for ImmutableBufferInitialization<T, A> {
type Access = Self;
#[inline]
fn access(self) -> Self {
self
}
#[inline]
fn size(&self) -> usize {
self.buffer.inner.size()
}
}
unsafe impl<T: ?Sized, A> TypedBuffer for ImmutableBufferInitialization<T, A> {
type Content = T;
}
unsafe impl<T: ?Sized, A> DeviceOwned for ImmutableBufferInitialization<T, A> {
#[inline]
fn device(&self) -> &Arc<Device> {
self.buffer.inner.device()
}
}
impl<T: ?Sized, A> Clone for ImmutableBufferInitialization<T, A> {
#[inline]
fn clone(&self) -> ImmutableBufferInitialization<T, A> {
ImmutableBufferInitialization {
buffer: self.buffer.clone(),
used: self.used.clone(),
}
}
}
impl<T: ?Sized, A> Drop for ImmutableBufferInitialization<T, A> {
#[inline]
fn drop(&mut self) {
if self.used.load(Ordering::Relaxed) {
self.buffer.initialized.store(true, Ordering::Relaxed);
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum ImmutableBufferFromBufferWithBuilderError {
OomError(OomError),
CommandBufferBuilderError(CommandBufferBuilderError<CmdCopyBufferError>),
}
impl error::Error for ImmutableBufferFromBufferWithBuilderError {
#[inline]
fn description(&self) -> &str {
match *self {
ImmutableBufferFromBufferWithBuilderError::OomError(_) => {
"out of memory"
},
ImmutableBufferFromBufferWithBuilderError::CommandBufferBuilderError(_) => {
"error while adding the command to the builder"
},
}
}
#[inline]
fn cause(&self) -> Option<&error::Error> {
match *self {
ImmutableBufferFromBufferWithBuilderError::OomError(ref err) => Some(err),
ImmutableBufferFromBufferWithBuilderError::CommandBufferBuilderError(ref err) => Some(err),
}
}
}
impl fmt::Display for ImmutableBufferFromBufferWithBuilderError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
impl From<OomError> for ImmutableBufferFromBufferWithBuilderError {
#[inline]
fn from(err: OomError) -> ImmutableBufferFromBufferWithBuilderError {
ImmutableBufferFromBufferWithBuilderError::OomError(err)
}
}
impl From<CommandBufferBuilderError<CmdCopyBufferError>> for ImmutableBufferFromBufferWithBuilderError {
#[inline]
fn from(err: CommandBufferBuilderError<CmdCopyBufferError>) -> ImmutableBufferFromBufferWithBuilderError {
ImmutableBufferFromBufferWithBuilderError::CommandBufferBuilderError(err)
}
}
#[cfg(test)]
mod tests {
use std::iter;
use buffer::cpu_access::CpuAccessibleBuffer;
use buffer::immutable::ImmutableBuffer;
use buffer::BufferUsage;
use command_buffer::AutoCommandBufferBuilder;
use command_buffer::CommandBuffer;
use command_buffer::CommandBufferBuilder;
use sync::GpuFuture;
#[test]
fn from_data_working() {
let (device, queue) = gfx_dev_and_queue!();
let (buffer, _) = ImmutableBuffer::from_data(12u32, BufferUsage::all(),
iter::once(queue.family()),
queue.clone()).unwrap();
let dest = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(),
iter::once(queue.family()), 0).unwrap();
let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
.copy_buffer(buffer, dest.clone()).unwrap()
.build().unwrap()
.execute(queue.clone()).unwrap()
.then_signal_fence_and_flush().unwrap();
let dest_content = dest.read().unwrap();
assert_eq!(*dest_content, 12);
}
#[test]
fn from_iter_working() {
let (device, queue) = gfx_dev_and_queue!();
let (buffer, _) = ImmutableBuffer::from_iter((0 .. 512u32).map(|n| n * 2), BufferUsage::all(),
iter::once(queue.family()),
queue.clone()).unwrap();
let dest = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(),
iter::once(queue.family()),
(0 .. 512).map(|_| 0u32)).unwrap();
let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
.copy_buffer(buffer, dest.clone()).unwrap()
.build().unwrap()
.execute(queue.clone()).unwrap()
.then_signal_fence_and_flush().unwrap();
let dest_content = dest.read().unwrap();
for (n, &v) in dest_content.iter().enumerate() {
assert_eq!(n * 2, v as usize);
}
}
#[test]
#[should_panic] fn writing_forbidden() {
let (device, queue) = gfx_dev_and_queue!();
let (buffer, _) = ImmutableBuffer::from_data(12u32, BufferUsage::all(),
iter::once(queue.family()),
queue.clone()).unwrap();
let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
.fill_buffer(buffer, 50).unwrap()
.build().unwrap()
.execute(queue.clone()).unwrap()
.then_signal_fence_and_flush().unwrap();
}
#[test]
#[should_panic] fn read_uninitialized_forbidden() {
let (device, queue) = gfx_dev_and_queue!();
let (buffer, _) = unsafe {
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all(),
iter::once(queue.family())).unwrap()
};
let src = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(),
iter::once(queue.family()), 0).unwrap();
let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
.copy_buffer(src, buffer).unwrap()
.build().unwrap()
.execute(queue.clone()).unwrap()
.then_signal_fence_and_flush().unwrap();
}
#[test]
fn init_then_read_same_cb() {
let (device, queue) = gfx_dev_and_queue!();
let (buffer, init) = unsafe {
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all(),
iter::once(queue.family())).unwrap()
};
let src = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(),
iter::once(queue.family()), 0).unwrap();
let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
.copy_buffer(src.clone(), init).unwrap()
.copy_buffer(buffer, src.clone()).unwrap()
.build().unwrap()
.execute(queue.clone()).unwrap()
.then_signal_fence_and_flush().unwrap();
}
#[test]
#[ignore] fn init_then_read_same_future() {
let (device, queue) = gfx_dev_and_queue!();
let (buffer, init) = unsafe {
ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all(),
iter::once(queue.family())).unwrap()
};
let src = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(),
iter::once(queue.family()), 0).unwrap();
let cb1 = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
.copy_buffer(src.clone(), init).unwrap()
.build().unwrap();
let cb2 = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap()
.copy_buffer(buffer, src.clone()).unwrap()
.build().unwrap();
let _ = cb1.execute(queue.clone()).unwrap()
.then_execute(queue.clone(), cb2).unwrap()
.then_signal_fence_and_flush().unwrap();
}
}