| // 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) } |
| }; |
| } |