diff options
Diffstat (limited to 'src/buffer/slice.rs')
-rw-r--r-- | src/buffer/slice.rs | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/src/buffer/slice.rs b/src/buffer/slice.rs new file mode 100644 index 0000000..03995d5 --- /dev/null +++ b/src/buffer/slice.rs @@ -0,0 +1,315 @@ +// 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::buffer::traits::BufferAccess; +use crate::buffer::traits::BufferInner; +use crate::buffer::traits::TypedBufferAccess; +use crate::device::Device; +use crate::device::DeviceOwned; +use crate::device::Queue; +use crate::sync::AccessError; +use crate::DeviceSize; +use std::hash::Hash; +use std::hash::Hasher; +use std::marker::PhantomData; +use std::mem; +use std::mem::MaybeUninit; +use std::ops::Range; +use std::sync::Arc; + +/// A subpart of a buffer. +/// +/// This object doesn't correspond to any Vulkan object. It exists for API convenience. +/// +/// # Example +/// +/// Creating a slice: +/// +/// ```ignore // FIXME: unignore +/// use vulkano::buffer::BufferSlice; +/// # let buffer: std::sync::Arc<vulkano::buffer::DeviceLocalBuffer<[u8]>> = return; +/// let _slice = BufferSlice::from(&buffer); +/// ``` +/// +/// Selecting a slice of a buffer that contains `[T]`: +/// +/// ```ignore // FIXME: unignore +/// use vulkano::buffer::BufferSlice; +/// # let buffer: std::sync::Arc<vulkano::buffer::DeviceLocalBuffer<[u8]>> = return; +/// let _slice = BufferSlice::from(&buffer).slice(12 .. 14).unwrap(); +/// ``` +/// +pub struct BufferSlice<T: ?Sized, B> { + marker: PhantomData<T>, + resource: B, + offset: DeviceSize, + size: DeviceSize, +} + +// We need to implement `Clone` manually, otherwise the derive adds a `T: Clone` requirement. +impl<T: ?Sized, B> Clone for BufferSlice<T, B> +where + B: Clone, +{ + #[inline] + fn clone(&self) -> Self { + BufferSlice { + marker: PhantomData, + resource: self.resource.clone(), + offset: self.offset, + size: self.size, + } + } +} + +impl<T: ?Sized, B> BufferSlice<T, B> { + #[inline] + pub fn from_typed_buffer_access(r: B) -> BufferSlice<T, B> + where + B: TypedBufferAccess<Content = T>, + { + let size = r.size(); + + BufferSlice { + marker: PhantomData, + resource: r, + offset: 0, + size: size, + } + } + + /// Returns the buffer that this slice belongs to. + pub fn buffer(&self) -> &B { + &self.resource + } + + /// Returns the offset of that slice within the buffer. + #[inline] + pub fn offset(&self) -> DeviceSize { + self.offset + } + + /// Returns the size of that slice in bytes. + #[inline] + pub fn size(&self) -> DeviceSize { + self.size + } + + /// Builds a slice that contains an element from inside the buffer. + /// + /// This method builds an object that represents a slice of the buffer. No actual operation + /// is performed. + /// + /// # Example + /// + /// TODO + /// + /// # Safety + /// + /// The object whose reference is passed to the closure is uninitialized. Therefore you + /// **must not** access the content of the object. + /// + /// You **must** return a reference to an element from the parameter. The closure **must not** + /// panic. + #[inline] + pub unsafe fn slice_custom<F, R: ?Sized>(self, f: F) -> BufferSlice<R, B> + where + F: for<'r> FnOnce(&'r T) -> &'r R, // TODO: bounds on R + { + let data: MaybeUninit<&T> = MaybeUninit::zeroed(); + let result = f(data.assume_init()); + let size = mem::size_of_val(result) as DeviceSize; + let result = result as *const R as *const () as DeviceSize; + + assert!(result <= self.size()); + assert!(result + size <= self.size()); + + BufferSlice { + marker: PhantomData, + resource: self.resource, + offset: self.offset + result, + size, + } + } + + /// Changes the `T` generic parameter of the `BufferSlice` to the desired type. This can be + /// useful when you have a buffer with various types of data and want to create a typed slice + /// of a region that contains a single type of data. + /// + /// # Example + /// + /// ``` + /// # use std::sync::Arc; + /// # use vulkano::buffer::BufferSlice; + /// # use vulkano::buffer::immutable::ImmutableBuffer; + /// # struct VertexImpl; + /// let blob_slice: BufferSlice<[u8], Arc<ImmutableBuffer<[u8]>>> = return; + /// let vertex_slice: BufferSlice<[VertexImpl], Arc<ImmutableBuffer<[u8]>>> = unsafe { + /// blob_slice.reinterpret::<[VertexImpl]>() + /// }; + /// ``` + /// + /// # Safety + /// + /// Correct `offset` and `size` must be ensured before using this `BufferSlice` on the device. + /// See `BufferSlice::slice` for adjusting these properties. + #[inline] + pub unsafe fn reinterpret<R: ?Sized>(self) -> BufferSlice<R, B> { + BufferSlice { + marker: PhantomData, + resource: self.resource, + offset: self.offset, + size: self.size, + } + } +} + +impl<T, B> BufferSlice<[T], B> { + /// Returns the number of elements in this slice. + #[inline] + pub fn len(&self) -> DeviceSize { + debug_assert_eq!(self.size() % mem::size_of::<T>() as DeviceSize, 0); + self.size() / mem::size_of::<T>() as DeviceSize + } + + /// Reduces the slice to just one element of the array. + /// + /// Returns `None` if out of range. + #[inline] + pub fn index(self, index: DeviceSize) -> Option<BufferSlice<T, B>> { + if index >= self.len() { + return None; + } + + Some(BufferSlice { + marker: PhantomData, + resource: self.resource, + offset: self.offset + index * mem::size_of::<T>() as DeviceSize, + size: mem::size_of::<T>() as DeviceSize, + }) + } + + /// Reduces the slice to just a range of the array. + /// + /// Returns `None` if out of range. + #[inline] + pub fn slice(self, range: Range<DeviceSize>) -> Option<BufferSlice<[T], B>> { + if range.end > self.len() { + return None; + } + + Some(BufferSlice { + marker: PhantomData, + resource: self.resource, + offset: self.offset + range.start * mem::size_of::<T>() as DeviceSize, + size: (range.end - range.start) * mem::size_of::<T>() as DeviceSize, + }) + } +} + +unsafe impl<T: ?Sized, B> BufferAccess for BufferSlice<T, B> +where + B: BufferAccess, +{ + #[inline] + fn inner(&self) -> BufferInner { + let inner = self.resource.inner(); + BufferInner { + buffer: inner.buffer, + offset: inner.offset + self.offset, + } + } + + #[inline] + fn size(&self) -> DeviceSize { + self.size + } + + #[inline] + fn conflict_key(&self) -> (u64, u64) { + self.resource.conflict_key() + } + + #[inline] + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> { + self.resource.try_gpu_lock(exclusive_access, queue) + } + + #[inline] + unsafe fn increase_gpu_lock(&self) { + self.resource.increase_gpu_lock() + } + + #[inline] + unsafe fn unlock(&self) { + self.resource.unlock() + } +} + +unsafe impl<T: ?Sized, B> TypedBufferAccess for BufferSlice<T, B> +where + B: BufferAccess, +{ + type Content = T; +} + +unsafe impl<T: ?Sized, B> DeviceOwned for BufferSlice<T, B> +where + B: DeviceOwned, +{ + #[inline] + fn device(&self) -> &Arc<Device> { + self.resource.device() + } +} + +impl<T, B> From<BufferSlice<T, B>> for BufferSlice<[T], B> { + #[inline] + fn from(r: BufferSlice<T, B>) -> BufferSlice<[T], B> { + BufferSlice { + marker: PhantomData, + resource: r.resource, + offset: r.offset, + size: r.size, + } + } +} + +impl<T: ?Sized, B> PartialEq for BufferSlice<T, B> +where + B: BufferAccess, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + self.inner() == other.inner() && self.size() == other.size() + } +} + +impl<T: ?Sized, B> Eq for BufferSlice<T, B> where B: BufferAccess {} + +impl<T: ?Sized, B> Hash for BufferSlice<T, B> +where + B: BufferAccess, +{ + #[inline] + fn hash<H: Hasher>(&self, state: &mut H) { + self.inner().hash(state); + self.size().hash(state); + } +} + +/// Takes a `BufferSlice` that points to a struct, and returns a `BufferSlice` that points to +/// a specific field of that struct. +#[macro_export] +macro_rules! buffer_slice_field { + ($slice:expr, $field:ident) => { + // TODO: add #[allow(unsafe_code)] when that's allowed + unsafe { $slice.slice_custom(|s| &s.$field) } + }; +} |