// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // 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> = 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> = return; /// let _slice = BufferSlice::from(&buffer).slice(12 .. 14).unwrap(); /// ``` /// pub struct BufferSlice { marker: PhantomData, resource: B, offset: DeviceSize, size: DeviceSize, } // We need to implement `Clone` manually, otherwise the derive adds a `T: Clone` requirement. impl Clone for BufferSlice where B: Clone, { #[inline] fn clone(&self) -> Self { BufferSlice { marker: PhantomData, resource: self.resource.clone(), offset: self.offset, size: self.size, } } } impl BufferSlice { #[inline] pub fn from_typed_buffer_access(r: B) -> BufferSlice where B: TypedBufferAccess, { 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(self, f: F) -> BufferSlice 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>> = return; /// let vertex_slice: BufferSlice<[VertexImpl], Arc>> = 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(self) -> BufferSlice { BufferSlice { marker: PhantomData, resource: self.resource, offset: self.offset, size: self.size, } } } impl 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::() as DeviceSize, 0); self.size() / mem::size_of::() 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> { if index >= self.len() { return None; } Some(BufferSlice { marker: PhantomData, resource: self.resource, offset: self.offset + index * mem::size_of::() as DeviceSize, size: mem::size_of::() 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) -> Option> { if range.end > self.len() { return None; } Some(BufferSlice { marker: PhantomData, resource: self.resource, offset: self.offset + range.start * mem::size_of::() as DeviceSize, size: (range.end - range.start) * mem::size_of::() as DeviceSize, }) } } unsafe impl BufferAccess for BufferSlice 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 TypedBufferAccess for BufferSlice where B: BufferAccess, { type Content = T; } unsafe impl DeviceOwned for BufferSlice where B: DeviceOwned, { #[inline] fn device(&self) -> &Arc { self.resource.device() } } impl From> for BufferSlice<[T], B> { #[inline] fn from(r: BufferSlice) -> BufferSlice<[T], B> { BufferSlice { marker: PhantomData, resource: r.resource, offset: r.offset, size: r.size, } } } impl PartialEq for BufferSlice where B: BufferAccess, { #[inline] fn eq(&self, other: &Self) -> bool { self.inner() == other.inner() && self.size() == other.size() } } impl Eq for BufferSlice where B: BufferAccess {} impl Hash for BufferSlice where B: BufferAccess, { #[inline] fn hash(&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) } }; }