blob: 03995d5e2088f0f226fd798c1d52120419a518f8 [file] [log] [blame]
// 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) }
};
}