aboutsummaryrefslogtreecommitdiff
path: root/src/buffer/immutable.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/buffer/immutable.rs')
-rw-r--r--src/buffer/immutable.rs728
1 files changed, 0 insertions, 728 deletions
diff --git a/src/buffer/immutable.rs b/src/buffer/immutable.rs
deleted file mode 100644
index bb9c8e5..0000000
--- a/src/buffer/immutable.rs
+++ /dev/null
@@ -1,728 +0,0 @@
-// 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.
-
-//! Buffer that is written once then read for as long as it is alive.
-//!
-//! Use this buffer when you have data that you never modify.
-//!
-//! Only the first ever command buffer that uses this buffer can write to it (for example by
-//! copying from another buffer). Any subsequent command buffer **must** only read from the buffer,
-//! or a panic will happen.
-//!
-//! The buffer will be stored in device-local memory if possible
-//!
-
-use crate::buffer::sys::BufferCreationError;
-use crate::buffer::sys::UnsafeBuffer;
-use crate::buffer::traits::BufferAccess;
-use crate::buffer::traits::BufferInner;
-use crate::buffer::traits::TypedBufferAccess;
-use crate::buffer::BufferUsage;
-use crate::buffer::CpuAccessibleBuffer;
-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::DeviceOwned;
-use crate::device::Queue;
-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::memory::DeviceMemoryAllocError;
-use crate::sync::AccessError;
-use crate::sync::NowFuture;
-use crate::sync::Sharing;
-use crate::DeviceSize;
-use smallvec::SmallVec;
-use std::hash::Hash;
-use std::hash::Hasher;
-use std::marker::PhantomData;
-use std::mem;
-use std::sync::atomic::AtomicBool;
-use std::sync::atomic::Ordering;
-use std::sync::Arc;
-
-/// Buffer that is written once then read for as long as it is alive.
-// TODO: implement Debug
-pub struct ImmutableBuffer<T: ?Sized, A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>> {
- // Inner content.
- inner: UnsafeBuffer,
-
- // Memory allocated for the buffer.
- memory: A,
-
- // True if the `ImmutableBufferInitialization` object was used by the GPU then dropped.
- // This means that the `ImmutableBuffer` can be used as much as we want without any restriction.
- initialized: AtomicBool,
-
- // Queue families allowed to access this buffer.
- queue_families: SmallVec<[u32; 4]>,
-
- // Necessary to have the appropriate template parameter.
- marker: PhantomData<Box<T>>,
-}
-
-// TODO: make this prettier
-type ImmutableBufferFromBufferFuture = CommandBufferExecFuture<NowFuture, PrimaryAutoCommandBuffer>;
-
-impl<T: ?Sized> ImmutableBuffer<T> {
- /// Builds an `ImmutableBuffer` from some data.
- ///
- /// This function builds a memory-mapped intermediate buffer, writes the data to it, builds a
- /// command buffer that copies from this intermediate buffer to the final buffer, and finally
- /// submits the command buffer as a future.
- ///
- /// This function returns two objects: the newly-created buffer, and a future representing
- /// the initial upload operation. In order to be allowed to use the `ImmutableBuffer`, you must
- /// either submit your operation after this future, or execute this future and wait for it to
- /// be finished before submitting your own operation.
- pub fn from_data(
- data: T,
- usage: BufferUsage,
- queue: Arc<Queue>,
- ) -> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferFromBufferFuture), DeviceMemoryAllocError>
- where
- T: 'static + Copy + Send + Sync + Sized,
- {
- let source = CpuAccessibleBuffer::from_data(
- queue.device().clone(),
- BufferUsage::transfer_source(),
- false,
- data,
- )?;
- ImmutableBuffer::from_buffer(source, usage, queue)
- }
-
- /// Builds an `ImmutableBuffer` that copies its data from another buffer.
- ///
- /// This function returns two objects: the newly-created buffer, and a future representing
- /// the initial upload operation. In order to be allowed to use the `ImmutableBuffer`, you must
- /// either submit your operation after this future, or execute this future and wait for it to
- /// be finished before submitting your own operation.
- pub fn from_buffer<B>(
- source: B,
- usage: BufferUsage,
- queue: Arc<Queue>,
- ) -> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferFromBufferFuture), DeviceMemoryAllocError>
- where
- B: BufferAccess + TypedBufferAccess<Content = T> + 'static + Clone + Send + Sync,
- T: 'static + Send + Sync,
- {
- unsafe {
- // We automatically set `transfer_destination` to true in order to avoid annoying errors.
- let actual_usage = BufferUsage {
- transfer_destination: true,
- ..usage
- };
-
- let (buffer, init) = ImmutableBuffer::raw(
- source.device().clone(),
- source.size(),
- actual_usage,
- source.device().active_queue_families(),
- )?;
-
- let mut cbb = AutoCommandBufferBuilder::primary(
- source.device().clone(),
- queue.family(),
- CommandBufferUsage::MultipleSubmit,
- )?;
- cbb.copy_buffer(source, init).unwrap(); // TODO: return error?
- let cb = cbb.build().unwrap(); // TODO: return OomError
-
- let future = match cb.execute(queue) {
- Ok(f) => f,
- Err(_) => unreachable!(),
- };
-
- Ok((buffer, future))
- }
- }
-}
-
-impl<T> ImmutableBuffer<T> {
- /// Builds a new buffer with uninitialized data. Only allowed for sized data.
- ///
- /// Returns two things: the buffer, and a special access that should be used for the initial
- /// upload to the buffer.
- ///
- /// You will get an error if you try to use the buffer before using the initial upload access.
- /// However this function doesn't check whether you actually used this initial upload to fill
- /// the buffer like you're supposed to do.
- ///
- /// You will also get an error if you try to get exclusive access to the final buffer.
- ///
- /// # Safety
- ///
- /// - The `ImmutableBufferInitialization` should be used to fill the buffer with some initial
- /// data, otherwise the content is undefined.
- ///
- #[inline]
- pub unsafe fn uninitialized(
- device: Arc<Device>,
- usage: BufferUsage,
- ) -> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferInitialization<T>), DeviceMemoryAllocError>
- {
- ImmutableBuffer::raw(
- device.clone(),
- mem::size_of::<T>() as DeviceSize,
- usage,
- device.active_queue_families(),
- )
- }
-}
-
-impl<T> ImmutableBuffer<[T]> {
- pub fn from_iter<D>(
- data: D,
- usage: BufferUsage,
- queue: Arc<Queue>,
- ) -> Result<(Arc<ImmutableBuffer<[T]>>, ImmutableBufferFromBufferFuture), DeviceMemoryAllocError>
- where
- D: ExactSizeIterator<Item = T>,
- T: 'static + Send + Sync + Sized,
- {
- let source = CpuAccessibleBuffer::from_iter(
- queue.device().clone(),
- BufferUsage::transfer_source(),
- false,
- data,
- )?;
- ImmutableBuffer::from_buffer(source, usage, queue)
- }
-
- /// Builds a new buffer with uninitialized data. Can be used for arrays.
- ///
- /// Returns two things: the buffer, and a special access that should be used for the initial
- /// upload to the buffer.
- ///
- /// You will get an error if you try to use the buffer before using the initial upload access.
- /// However this function doesn't check whether you actually used this initial upload to fill
- /// the buffer like you're supposed to do.
- ///
- /// You will also get an error if you try to get exclusive access to the final buffer.
- ///
- /// # Safety
- ///
- /// - The `ImmutableBufferInitialization` should be used to fill the buffer with some initial
- /// data, otherwise the content is undefined.
- ///
- #[inline]
- pub unsafe fn uninitialized_array(
- device: Arc<Device>,
- len: DeviceSize,
- usage: BufferUsage,
- ) -> Result<
- (
- Arc<ImmutableBuffer<[T]>>,
- ImmutableBufferInitialization<[T]>,
- ),
- DeviceMemoryAllocError,
- > {
- ImmutableBuffer::raw(
- device.clone(),
- len * mem::size_of::<T>() as DeviceSize,
- usage,
- device.active_queue_families(),
- )
- }
-}
-
-impl<T: ?Sized> ImmutableBuffer<T> {
- /// Builds a new buffer without checking the size and granting free access for the initial
- /// upload.
- ///
- /// Returns two things: the buffer, and a special access that should be used for the initial
- /// upload to the buffer.
- /// You will get an error if you try to use the buffer before using the initial upload access.
- /// However this function doesn't check whether you used this initial upload to fill the buffer.
- /// You will also get an error if you try to get exclusive access to the final buffer.
- ///
- /// # Safety
- ///
- /// - You must ensure that the size that you pass is correct for `T`.
- /// - The `ImmutableBufferInitialization` should be used to fill the buffer with some initial
- /// data.
- ///
- #[inline]
- pub unsafe fn raw<'a, I>(
- device: Arc<Device>,
- size: DeviceSize,
- usage: BufferUsage,
- queue_families: I,
- ) -> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferInitialization<T>), DeviceMemoryAllocError>
- 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)
- }
-
- // Internal implementation of `raw`. This is separated from `raw` so that it doesn't need to be
- // inlined.
- unsafe fn raw_impl(
- device: Arc<Device>,
- size: DeviceSize,
- usage: BufferUsage,
- queue_families: SmallVec<[u32; 4]>,
- ) -> Result<(Arc<ImmutableBuffer<T>>, ImmutableBufferInitialization<T>), DeviceMemoryAllocError>
- {
- 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, None) {
- Ok(b) => b,
- Err(BufferCreationError::AllocError(err)) => return Err(err),
- Err(_) => unreachable!(), // We don't use sparse binding, therefore the other
- // errors can't happen
- }
- };
-
- let mem = MemoryPool::alloc_from_requirements(
- &Device::standard_pool(&device),
- &mem_reqs,
- AllocLayout::Linear,
- MappingRequirement::DoNotMap,
- DedicatedAlloc::Buffer(&buffer),
- |t| {
- if t.is_device_local() {
- AllocFromRequirementsFilter::Preferred
- } else {
- AllocFromRequirementsFilter::Allowed
- }
- },
- )?;
- debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
- 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> {
- /// Returns the device used to create this buffer.
- #[inline]
- pub fn device(&self) -> &Arc<Device> {
- self.inner.device()
- }
-
- /// Returns the queue families this buffer can be used on.
- // TODO: use a custom iterator
- #[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> BufferAccess for ImmutableBuffer<T, A> {
- #[inline]
- fn inner(&self) -> BufferInner {
- BufferInner {
- buffer: &self.inner,
- offset: 0,
- }
- }
-
- #[inline]
- fn size(&self) -> DeviceSize {
- self.inner.size()
- }
-
- #[inline]
- fn conflict_key(&self) -> (u64, u64) {
- (self.inner.key(), 0)
- }
-
- #[inline]
- fn try_gpu_lock(&self, exclusive_access: bool, _: &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) {}
-
- #[inline]
- unsafe fn unlock(&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()
- }
-}
-
-impl<T: ?Sized, A> PartialEq for ImmutableBuffer<T, A> {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.inner() == other.inner() && self.size() == other.size()
- }
-}
-
-impl<T: ?Sized, A> Eq for ImmutableBuffer<T, A> {}
-
-impl<T: ?Sized, A> Hash for ImmutableBuffer<T, A> {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.inner().hash(state);
- self.size().hash(state);
- }
-}
-
-/// Access to the immutable buffer that can be used for the initial upload.
-//#[derive(Debug)] // TODO:
-pub struct ImmutableBufferInitialization<
- T: ?Sized,
- A = PotentialDedicatedAllocation<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 size(&self) -> DeviceSize {
- self.buffer.size()
- }
-
- #[inline]
- fn conflict_key(&self) -> (u64, u64) {
- (self.buffer.inner.key(), 0)
- }
-
- #[inline]
- fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
- if self.buffer.initialized.load(Ordering::Relaxed) {
- return Err(AccessError::AlreadyInUse);
- }
-
- 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) {
- self.buffer.initialized.store(true, Ordering::Relaxed);
- }
-}
-
-unsafe impl<T: ?Sized, A> TypedBufferAccess 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> PartialEq for ImmutableBufferInitialization<T, A> {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.inner() == other.inner() && self.size() == other.size()
- }
-}
-
-impl<T: ?Sized, A> Eq for ImmutableBufferInitialization<T, A> {}
-
-impl<T: ?Sized, A> Hash for ImmutableBufferInitialization<T, A> {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.inner().hash(state);
- self.size().hash(state);
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::buffer::cpu_access::CpuAccessibleBuffer;
- use crate::buffer::immutable::ImmutableBuffer;
- use crate::buffer::BufferUsage;
- use crate::command_buffer::AutoCommandBufferBuilder;
- use crate::command_buffer::CommandBufferUsage;
- use crate::command_buffer::PrimaryCommandBuffer;
- use crate::sync::GpuFuture;
-
- #[test]
- fn from_data_working() {
- let (device, queue) = gfx_dev_and_queue!();
-
- let (buffer, _) =
- ImmutableBuffer::from_data(12u32, BufferUsage::all(), queue.clone()).unwrap();
-
- let destination =
- CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
-
- let mut cbb = AutoCommandBufferBuilder::primary(
- device.clone(),
- queue.family(),
- CommandBufferUsage::MultipleSubmit,
- )
- .unwrap();
- cbb.copy_buffer(buffer, destination.clone()).unwrap();
- let _ = cbb
- .build()
- .unwrap()
- .execute(queue.clone())
- .unwrap()
- .then_signal_fence_and_flush()
- .unwrap();
-
- let destination_content = destination.read().unwrap();
- assert_eq!(*destination_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(),
- queue.clone(),
- )
- .unwrap();
-
- let destination = CpuAccessibleBuffer::from_iter(
- device.clone(),
- BufferUsage::all(),
- false,
- (0..512).map(|_| 0u32),
- )
- .unwrap();
-
- let mut cbb = AutoCommandBufferBuilder::primary(
- device.clone(),
- queue.family(),
- CommandBufferUsage::MultipleSubmit,
- )
- .unwrap();
- cbb.copy_buffer(buffer, destination.clone()).unwrap();
- let _ = cbb
- .build()
- .unwrap()
- .execute(queue.clone())
- .unwrap()
- .then_signal_fence_and_flush()
- .unwrap();
-
- let destination_content = destination.read().unwrap();
- for (n, &v) in destination_content.iter().enumerate() {
- assert_eq!(n * 2, v as usize);
- }
- }
-
- #[test]
- fn writing_forbidden() {
- let (device, queue) = gfx_dev_and_queue!();
-
- let (buffer, _) =
- ImmutableBuffer::from_data(12u32, BufferUsage::all(), queue.clone()).unwrap();
-
- assert_should_panic!({
- // TODO: check Result error instead of panicking
- let mut cbb = AutoCommandBufferBuilder::primary(
- device.clone(),
- queue.family(),
- CommandBufferUsage::MultipleSubmit,
- )
- .unwrap();
- cbb.fill_buffer(buffer, 50).unwrap();
- let _ = cbb
- .build()
- .unwrap()
- .execute(queue.clone())
- .unwrap()
- .then_signal_fence_and_flush()
- .unwrap();
- });
- }
-
- #[test]
- fn read_uninitialized_forbidden() {
- let (device, queue) = gfx_dev_and_queue!();
-
- let (buffer, _) = unsafe {
- ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all()).unwrap()
- };
-
- let source =
- CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
-
- assert_should_panic!({
- // TODO: check Result error instead of panicking
- let mut cbb = AutoCommandBufferBuilder::primary(
- device.clone(),
- queue.family(),
- CommandBufferUsage::MultipleSubmit,
- )
- .unwrap();
- cbb.copy_buffer(source, buffer).unwrap();
- let _ = cbb
- .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()).unwrap()
- };
-
- let source =
- CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
-
- let mut cbb = AutoCommandBufferBuilder::primary(
- device.clone(),
- queue.family(),
- CommandBufferUsage::MultipleSubmit,
- )
- .unwrap();
- cbb.copy_buffer(source.clone(), init)
- .unwrap()
- .copy_buffer(buffer, source.clone())
- .unwrap();
- let _ = cbb
- .build()
- .unwrap()
- .execute(queue.clone())
- .unwrap()
- .then_signal_fence_and_flush()
- .unwrap();
- }
-
- #[test]
- #[ignore] // TODO: doesn't work because the submit sync layer isn't properly implemented
- fn init_then_read_same_future() {
- let (device, queue) = gfx_dev_and_queue!();
-
- let (buffer, init) = unsafe {
- ImmutableBuffer::<u32>::uninitialized(device.clone(), BufferUsage::all()).unwrap()
- };
-
- let source =
- CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, 0).unwrap();
-
- let mut cbb = AutoCommandBufferBuilder::primary(
- device.clone(),
- queue.family(),
- CommandBufferUsage::MultipleSubmit,
- )
- .unwrap();
- cbb.copy_buffer(source.clone(), init).unwrap();
- let cb1 = cbb.build().unwrap();
-
- let mut cbb = AutoCommandBufferBuilder::primary(
- device.clone(),
- queue.family(),
- CommandBufferUsage::MultipleSubmit,
- )
- .unwrap();
- cbb.copy_buffer(buffer, source.clone()).unwrap();
- let cb2 = cbb.build().unwrap();
-
- let _ = cb1
- .execute(queue.clone())
- .unwrap()
- .then_execute(queue.clone(), cb2)
- .unwrap()
- .then_signal_fence_and_flush()
- .unwrap();
- }
-
- #[test]
- fn create_buffer_zero_size_data() {
- let (device, queue) = gfx_dev_and_queue!();
-
- let _ = ImmutableBuffer::from_data((), BufferUsage::all(), queue.clone());
- }
-
- // TODO: write tons of tests that try to exploit loopholes
- // this isn't possible yet because checks aren't correctly implemented yet
-}