aboutsummaryrefslogtreecommitdiff
path: root/src/buffer/slice.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/buffer/slice.rs')
-rw-r--r--src/buffer/slice.rs315
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) }
+ };
+}