use super::{BufferAccess, BufferAccessObject, BufferContents, BufferInner, TypedBufferAccess};
use crate::{
device::{Device, DeviceOwned},
DeviceSize,
};
use std::{
hash::{Hash, Hasher},
marker::PhantomData,
mem::{size_of, size_of_val, MaybeUninit},
ops::Range,
sync::Arc,
};
#[derive(Debug)]
pub struct BufferSlice<T: ?Sized, B> {
marker: PhantomData<T>,
resource: Arc<B>,
offset: DeviceSize,
size: DeviceSize,
}
impl<T: ?Sized, B> Clone for BufferSlice<T, B> {
fn clone(&self) -> Self {
BufferSlice {
marker: PhantomData,
resource: self.resource.clone(),
offset: self.offset,
size: self.size,
}
}
}
impl<T: ?Sized, B> BufferSlice<T, B> {
pub fn from_typed_buffer_access(r: Arc<B>) -> Arc<BufferSlice<T, B>>
where
B: TypedBufferAccess<Content = T>,
{
let size = r.size();
Arc::new(BufferSlice {
marker: PhantomData,
resource: r,
offset: 0,
size,
})
}
pub fn buffer(&self) -> &Arc<B> {
&self.resource
}
pub fn offset(&self) -> DeviceSize {
self.offset
}
pub fn size(&self) -> DeviceSize {
self.size
}
pub unsafe fn slice_custom<F, R: ?Sized>(&self, f: F) -> Arc<BufferSlice<R, B>>
where
F: for<'r> FnOnce(&'r T) -> &'r R, {
let data: MaybeUninit<&T> = MaybeUninit::zeroed();
let result = f(data.assume_init());
let size = 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());
Arc::new(BufferSlice {
marker: PhantomData,
resource: self.resource.clone(),
offset: self.offset + result,
size,
})
}
pub unsafe fn reinterpret<R: ?Sized>(&self) -> Arc<BufferSlice<R, B>> {
Arc::new(BufferSlice {
marker: PhantomData,
resource: self.resource.clone(),
offset: self.offset,
size: self.size,
})
}
}
impl<T, B> BufferSlice<[T], B> {
pub fn len(&self) -> DeviceSize {
debug_assert_eq!(self.size() % size_of::<T>() as DeviceSize, 0);
self.size() / size_of::<T>() as DeviceSize
}
pub fn index(&self, index: DeviceSize) -> Option<Arc<BufferSlice<T, B>>> {
if index >= self.len() {
return None;
}
Some(Arc::new(BufferSlice {
marker: PhantomData,
resource: self.resource.clone(),
offset: self.offset + index * size_of::<T>() as DeviceSize,
size: size_of::<T>() as DeviceSize,
}))
}
pub fn slice(&self, range: Range<DeviceSize>) -> Option<Arc<BufferSlice<[T], B>>> {
if range.end > self.len() {
return None;
}
Some(Arc::new(BufferSlice {
marker: PhantomData,
resource: self.resource.clone(),
offset: self.offset + range.start * size_of::<T>() as DeviceSize,
size: (range.end - range.start) * size_of::<T>() as DeviceSize,
}))
}
}
unsafe impl<T, B> BufferAccess for BufferSlice<T, B>
where
B: BufferAccess,
T: Send + Sync + ?Sized,
{
fn inner(&self) -> BufferInner<'_> {
let inner = self.resource.inner();
BufferInner {
buffer: inner.buffer,
offset: inner.offset + self.offset,
}
}
fn size(&self) -> DeviceSize {
self.size
}
}
impl<T, B> BufferAccessObject for Arc<BufferSlice<T, B>>
where
B: BufferAccess + 'static,
T: Send + Sync + ?Sized + 'static,
{
fn as_buffer_access_object(&self) -> Arc<dyn BufferAccess> {
self.clone()
}
}
unsafe impl<T, B> TypedBufferAccess for BufferSlice<T, B>
where
T: BufferContents + ?Sized,
B: BufferAccess,
{
type Content = T;
}
unsafe impl<T: ?Sized, B> DeviceOwned for BufferSlice<T, B>
where
B: DeviceOwned,
{
fn device(&self) -> &Arc<Device> {
self.resource.device()
}
}
impl<T, B> From<BufferSlice<T, B>> for BufferSlice<[T], B> {
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
T: Send + Sync,
B: BufferAccess,
{
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,
T: Send + Sync,
{
}
impl<T: ?Sized, B> Hash for BufferSlice<T, B>
where
T: Send + Sync,
B: BufferAccess,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner().hash(state);
self.size().hash(state);
}
}
#[macro_export]
macro_rules! buffer_slice_field {
($slice:expr, $field:ident) => {
unsafe { $slice.slice_custom(|s| &s.$field) }
};
}