use super::*;
pub type GpuBufferAddress = wgpu::BufferAddress;
pub trait GpuBufferNew<T>
{
fn new(value: &[T], desc: GpuBufferDesc) -> Self;
fn with_capacity(capacity: usize, desc: GpuBufferDesc) -> Self;
}
pub trait GpuBufferByte
{
fn bytes_len(&self) -> usize { self.wgpu_bytes_len() as _ }
fn wgpu_bytes_len(&self) -> GpuBufferAddress;
fn bytes_capacity(&self) -> usize { self.wgpu_bytes_capacity() as _ }
fn wgpu_bytes_capacity(&self) -> GpuBufferAddress { self.wgpu_bytes_len() }
}
pub trait GpuBufferAsWgpuSlice
{
fn as_wgpu_slice(&self) -> wgpu::BufferSlice<'_>;
}
pub trait GpuAsUntypedSlice
{
fn untyped_slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuUntypedSlice<'_>;
fn untyped_update<T>(&mut self, values: &[T])
where
T: BitAllUsed,
{
self.try_untyped_update(values)
.expect("failed to update the gpu buffer")
}
fn try_untyped_update<T>(&mut self, values: &[T]) -> Result<(), ()>
where
T: BitAllUsed,
{
self.untyped_slice(..).update(values)
}
}
pub trait GpuBufferRead<T>: GpuBufferByte
{
fn len(&self) -> usize { self.wgpu_len() as _ }
fn wgpu_len(&self) -> GpuBufferAddress
{
self.wgpu_bytes_len() / std::mem::size_of::<T>() as GpuBufferAddress
}
fn capacity(&self) -> usize { self.wgpu_capacity() as _ }
fn wgpu_capacity(&self) -> GpuBufferAddress
{
self.wgpu_bytes_capacity() / std::mem::size_of::<T>() as GpuBufferAddress
}
fn read(&self) -> GpuResult<Vec<T>>
where
T: BitZero + BitPattern,
{
let mut v = Vec::new();
self.read_in(&mut v)?;
Ok(v)
}
fn read_in(&self, vec: &mut Vec<T>) -> GpuResult
where
T: BitZero + BitPattern;
fn slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuSlice<'_, T>;
fn update(&mut self, values: &[T])
where
T: BitAllUsed,
{
self.try_update(values)
.expect("failed to update the gpu buffer")
}
fn try_update(&mut self, values: &[T]) -> Result<(), ()>
where
T: BitAllUsed,
{
self.slice(..).update(values)
}
}
impl<T> GpuBufferNew<T> for wgpu::Buffer
where
T: BitAllUsed,
{
fn new(value: &[T], desc: GpuBufferDesc) -> Self
{
Gpu.wgpu
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: bit::transmute_slice(value),
usage: desc.usages.into(),
})
}
fn with_capacity(capacity: usize, desc: GpuBufferDesc) -> Self
{
Gpu.wgpu.device.create_buffer(&wgpu::BufferDescriptor {
label: None,
usage: desc.usages.into(),
size: (capacity * std::mem::size_of::<T>()) as _,
mapped_at_creation: false,
})
}
}
impl GpuBufferByte for wgpu::Buffer
{
fn wgpu_bytes_len(&self) -> GpuBufferAddress { self.size() }
}
impl GpuAsUntypedSlice for wgpu::Buffer
{
fn untyped_slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuUntypedSlice<'_>
{
let bytes_len = self.size();
let start = match bounds.start_bound()
{
Bound::Included(&v) => v as GpuBufferAddress,
Bound::Excluded(&v) => (v + 1) as GpuBufferAddress,
Bound::Unbounded => 0,
}
.min(bytes_len);
let end = match bounds.end_bound()
{
Bound::Included(&v) => (v + 1) as GpuBufferAddress,
Bound::Excluded(&v) => v as GpuBufferAddress,
Bound::Unbounded => bytes_len,
}
.min(bytes_len);
assert!(
start <= end,
"GpuUntypedSlice::untyped_slice: invalid range"
);
GpuUntypedSlice {
buffer: self,
offset: start,
size: end - start,
}
}
}
fn create_staging_and_copy_slice(slice: &wgpu::BufferSlice) -> wgpu::Buffer
{
let size = slice.size().get();
let staging = Gpu.wgpu.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("staging buffer"),
size,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let mut encoder = Gpu
.wgpu
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
encoder.copy_buffer_to_buffer(slice.buffer(), slice.offset(), &staging, 0, size);
Gpu.wgpu.queue.submit(Some(encoder.finish()));
staging
}
impl<'a> GpuBufferByte for wgpu::BufferSlice<'a>
{
fn wgpu_bytes_len(&self) -> GpuBufferAddress { self.size().get() }
}
impl<'a, T> GpuBufferRead<T> for wgpu::BufferSlice<'a>
{
fn read_in(&self, vec: &mut Vec<T>) -> GpuResult
where
T: BitZero + BitPattern,
{
let byte_len = self.size().get() as GpuBufferAddress as usize;
let elem_count = byte_len / std::mem::size_of::<T>();
if vec.len() < elem_count
{
vec.resize(elem_count, T::zeroed());
}
let staging = create_staging_and_copy_slice(self);
let slice = staging.slice(..);
let status = std::sync::Arc::new(std::sync::Mutex::new(None));
let status_clone = status.clone();
slice.map_async(wgpu::MapMode::Read, move |res| {
*status_clone.lock().unwrap() = Some(res);
});
while status.lock().unwrap().is_none()
{
Gpu.wgpu.device.poll(wgpu::PollType::Wait);
}
match status.lock().unwrap().take().unwrap()
{
Ok(()) =>
{
let data = slice.get_mapped_range();
let typed = bit::transmute_slice(&data);
vec[..typed.len()].copy_from_slice(typed);
drop(data);
staging.unmap();
Ok(())
}
Err(e) => Err(e.into()),
}
}
fn slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuSlice<'_, T> { todo!() }
}
impl<T> GpuBufferRead<T> for wgpu::Buffer
{
fn read_in(&self, mut vec: &mut Vec<T>) -> GpuResult
where
T: BitZero + BitPattern,
{
self.slice(..).read_in(vec)
}
fn slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuSlice<'_, T>
{
let untyped_slice = GpuUntypedSlice {
buffer: self,
offset: 0,
size: self.size(),
};
let slice: GpuSlice<'_, T> = untyped_slice.slice(bounds);
unsafe { GpuSlice::from_raw_parts(self, slice.offset, slice.size) }
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct GpuSlice<'a, T>
{
buffer: &'a wgpu::Buffer,
offset: GpuBufferAddress,
size: GpuBufferAddress,
phantom: PhantomData<T>,
}
impl<'a, T> From<GpuSlice<'a, T>> for GpuUntypedSlice<'a>
{
fn from(value: GpuSlice<'a, T>) -> Self
{
let size = std::mem::size_of::<T>() as GpuBufferAddress;
unsafe {
GpuUntypedSlice::from_raw_parts(value.buffer, value.offset * size, value.size * size)
}
}
}
impl<'a, T> GpuSlice<'a, T>
{
pub unsafe fn from_raw_parts(
buffer: &'a wgpu::Buffer,
offset: GpuBufferAddress,
size: GpuBufferAddress,
) -> Self
{
Self {
buffer,
offset,
size,
phantom: PhantomData,
}
}
pub unsafe fn from_untyped_slice_unchecked(slice: GpuUntypedSlice<'a>) -> Self
{
let size = std::mem::size_of::<T>().max(1) as GpuBufferAddress;
unsafe { Self::from_raw_parts(slice.buffer, slice.offset / size, slice.size / size) }
}
fn update(&self, values: &[T]) -> Result<(), ()>
where
T: BitAllUsed,
{
let untyped: GpuUntypedSlice<'_> = (*self).into();
let elem_size = std::mem::size_of::<T>();
if (values.len() as GpuBufferAddress) > self.size
{
return Err(()); }
untyped.update(values)
}
}
impl<'a, T> GpuBufferAsWgpuSlice for GpuSlice<'a, T>
{
fn as_wgpu_slice(&self) -> wgpu::BufferSlice<'a>
{
let size = std::mem::size_of::<T>() as GpuBufferAddress;
self.buffer
.slice((self.offset * size)..((self.offset + self.size) * size))
}
}
impl<'a, T> From<&'a wgpu::Buffer> for GpuSlice<'a, T>
where
T: BitAnyPattern,
{
fn from(buffer: &'a wgpu::Buffer) -> Self
{
assert_eq!(
buffer.bytes_len() % std::mem::size_of::<T>(),
0,
"wrong size"
);
unsafe { Self::from_untyped_slice_unchecked(GpuUntypedSlice::from(buffer)) }
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct GpuUntypedSlice<'a>
{
buffer: &'a wgpu::Buffer,
offset: GpuBufferAddress,
size: GpuBufferAddress,
}
impl<'a> GpuUntypedSlice<'a>
{
pub unsafe fn from_raw_parts(
buffer: &'a wgpu::Buffer,
offset: GpuBufferAddress,
size: GpuBufferAddress,
) -> Self
{
Self {
buffer,
offset,
size,
}
}
fn update<T>(&self, values: &[T]) -> Result<(), ()>
where
T: BitAllUsed,
{
let value_bytes = values.len() * size_of::<T>();
if value_bytes as GpuBufferAddress > self.size
{
return Err(()); }
let staging = Gpu
.wgpu
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("update_bytes staging"),
contents: bit::try_transmute_slice(values).map_err(|_| ())?,
usage: wgpu::BufferUsages::COPY_SRC,
});
let mut encoder = Gpu
.wgpu
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("update_bytes encoder"),
});
encoder.copy_buffer_to_buffer(
&staging,
0,
self.buffer,
self.offset,
value_bytes as GpuBufferAddress,
);
Gpu.wgpu.queue.submit(Some(encoder.finish()));
Ok(())
}
}
impl<'a> GpuBufferAsWgpuSlice for GpuUntypedSlice<'a>
{
fn as_wgpu_slice(&self) -> wgpu::BufferSlice<'a>
{
self.buffer.slice(self.offset..self.offset + self.size)
}
}
impl<'a> From<&'a wgpu::Buffer> for GpuUntypedSlice<'a>
{
fn from(buffer: &'a wgpu::Buffer) -> Self
{
Self {
buffer,
offset: 0,
size: buffer.size(),
}
}
}
impl<'a> GpuBufferByte for GpuUntypedSlice<'a>
{
fn wgpu_bytes_len(&self) -> GpuBufferAddress { self.size }
}
impl<'a> GpuAsUntypedSlice for GpuUntypedSlice<'a>
{
fn untyped_slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuUntypedSlice<'_>
{
let start = match bounds.start_bound()
{
Bound::Included(&v) => v as GpuBufferAddress,
Bound::Excluded(&v) => (v + 1) as GpuBufferAddress,
Bound::Unbounded => 0,
};
let end = match bounds.end_bound()
{
Bound::Included(&v) => (v + 1) as GpuBufferAddress,
Bound::Excluded(&v) => v as GpuBufferAddress,
Bound::Unbounded => self.size,
};
assert!(
start <= end && end <= self.size,
"GpuUntypedSlice::untyped_slice out of bounds"
);
GpuUntypedSlice {
buffer: self.buffer,
offset: self.offset + start,
size: end - start,
}
}
}
impl<'a, T> GpuBufferRead<T> for GpuUntypedSlice<'a>
{
fn read_in(&self, vec: &mut Vec<T>) -> GpuResult
where
T: BitZero + BitPattern,
{
let elem_size = std::mem::size_of::<T>().max(1);
let elem_count = (self.size / elem_size as GpuBufferAddress) as usize;
if vec.len() < elem_count
{
vec.resize(elem_count, T::zeroed());
}
let staging = create_staging_and_copy_slice(&self.as_wgpu_slice());
let slice = staging.slice(..);
let status = std::sync::Arc::new(std::sync::Mutex::new(None));
let status_clone = status.clone();
slice.map_async(wgpu::MapMode::Read, move |res| {
*status_clone.lock().unwrap() = Some(res);
});
while status.lock().unwrap().is_none()
{
Gpu.wgpu.device.poll(wgpu::PollType::Wait);
}
match status.lock().unwrap().take().unwrap()
{
Ok(()) =>
{
let data = slice.get_mapped_range();
let typed = bit::transmute_slice(&data);
vec[..typed.len()].copy_from_slice(typed);
drop(data);
staging.unmap();
Ok(())
}
Err(e) => Err(e.into()),
}
}
fn slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuSlice<'_, T>
{
let elem_size = std::mem::size_of::<T>().max(1) as GpuBufferAddress;
let bytes_len = self.size;
let start = match bounds.start_bound()
{
std::ops::Bound::Included(&v) => (v as GpuBufferAddress) * elem_size,
std::ops::Bound::Excluded(&v) => ((v + 1) as GpuBufferAddress) * elem_size,
std::ops::Bound::Unbounded => 0,
}
.min(bytes_len);
let end = match bounds.end_bound()
{
std::ops::Bound::Included(&v) => ((v + 1) as GpuBufferAddress) * elem_size,
std::ops::Bound::Excluded(&v) => (v as GpuBufferAddress) * elem_size,
std::ops::Bound::Unbounded => bytes_len,
}
.min(bytes_len);
let size = end - start;
assert!(
start <= end && size % elem_size == 0,
"GpuSlice<T>::slice: requested range not aligned with element size"
);
let untyped = GpuUntypedSlice {
buffer: self.buffer,
offset: self.offset + start,
size,
};
unsafe { GpuSlice::from_untyped_slice_unchecked(untyped) }
}
}
#[repr(transparent)]
#[derive(Clone, Debug, PartialEq, Hash)]
pub struct GpuUntypedBuffer
{
pub(crate) wgpu: wgpu::Buffer,
private_constructor: (),
}
impl Handle for GpuUntypedBuffer {}
impl<T> GpuBufferNew<T> for GpuUntypedBuffer
where
T: BitAllUsed,
{
fn new(value: &[T], desc: GpuBufferDesc) -> Self
{
Self {
wgpu: wgpu::Buffer::new(value, desc),
private_constructor: (),
}
}
fn with_capacity(capacity: usize, desc: GpuBufferDesc) -> Self
{
Self {
wgpu: <wgpu::Buffer as GpuBufferNew<T>>::with_capacity(capacity, desc),
private_constructor: (),
}
}
}
impl From<wgpu::Buffer> for GpuUntypedBuffer
{
fn from(buffer: wgpu::Buffer) -> Self
{
Self {
wgpu: buffer,
private_constructor: (),
}
}
}
impl From<GpuUntypedBuffer> for wgpu::Buffer
{
fn from(value: GpuUntypedBuffer) -> Self { value.wgpu }
}
impl GpuBufferByte for GpuUntypedBuffer
{
fn wgpu_bytes_len(&self) -> GpuBufferAddress { self.wgpu.wgpu_bytes_len() }
}
impl GpuAsUntypedSlice for GpuUntypedBuffer
{
fn untyped_slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuUntypedSlice<'_>
{
self.wgpu.untyped_slice(bounds)
}
}
impl<T> GpuBufferRead<T> for GpuUntypedBuffer
where
wgpu::Buffer: GpuBufferRead<T>,
{
fn read_in(&self, vec: &mut Vec<T>) -> GpuResult
where
T: BitZero + BitPattern,
{
self.wgpu.read_in(vec)
}
fn slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuSlice<'_, T>
{
GpuBufferRead::<T>::slice(&self.wgpu, bounds)
}
}
#[repr(transparent)]
#[derive(Clone, Debug, PartialEq, Hash)]
pub struct GpuBuffer<T>
{
pub(crate) wgpu: wgpu::Buffer,
phantom: PhantomData<T>,
}
impl<T> Handle for GpuBuffer<T> where T: BitAllUsed {}
impl<T> GpuBufferNew<T> for GpuBuffer<T>
where
T: BitAllUsed,
{
fn new(value: &[T], desc: GpuBufferDesc) -> Self
{
Self {
wgpu: wgpu::Buffer::new(value, desc),
phantom: PhantomData,
}
}
fn with_capacity(capacity: usize, desc: GpuBufferDesc) -> Self
{
Self {
wgpu: <wgpu::Buffer as GpuBufferNew<T>>::with_capacity(capacity, desc),
phantom: PhantomData,
}
}
}
impl<T> From<wgpu::Buffer> for GpuBuffer<T>
where
T: BitAnyPattern,
{
fn from(buffer: wgpu::Buffer) -> Self
{
assert_eq!(
buffer.bytes_len() % std::mem::size_of::<T>(),
0,
"wrong gpu buffer size"
);
Self {
wgpu: buffer,
phantom: PhantomData,
}
}
}
impl<T> From<GpuUntypedBuffer> for GpuBuffer<T>
where
T: BitAnyPattern,
{
fn from(buffer: GpuUntypedBuffer) -> Self { Self::from(buffer.wgpu) }
}
impl<T> From<GpuBuffer<T>> for wgpu::Buffer
{
fn from(value: GpuBuffer<T>) -> Self { value.wgpu }
}
impl<T> From<GpuBuffer<T>> for GpuUntypedBuffer
{
fn from(value: GpuBuffer<T>) -> Self { GpuUntypedBuffer::from(value.wgpu) }
}
impl<T> GpuBufferByte for GpuBuffer<T>
{
fn wgpu_bytes_len(&self) -> GpuBufferAddress { self.wgpu.wgpu_bytes_len() }
}
impl<T> GpuAsUntypedSlice for GpuBuffer<T>
{
fn untyped_slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuUntypedSlice<'_>
{
self.wgpu.untyped_slice(bounds)
}
fn try_untyped_update<T2>(&mut self, values: &[T2]) -> Result<(), ()>
where
T2: BitAllUsed,
{
if std::mem::size_of::<T>() != std::mem::size_of::<T2>()
{
return Err(());
}
self.untyped_slice(..).update(values)
}
}
impl<T> GpuBufferRead<T> for GpuBuffer<T>
where
wgpu::Buffer: GpuBufferRead<T>,
{
fn read_in(&self, vec: &mut Vec<T>) -> GpuResult
where
T: BitZero + BitPattern,
{
self.wgpu.read_in(vec)
}
fn slice<S: RangeBounds<usize>>(&self, bounds: S) -> GpuSlice<'_, T>
{
GpuBufferRead::<T>::slice(&self.wgpu, bounds)
}
}
#[bit_index]
#[repr(u32)]
pub enum BufferUsage
{
MapReap = 0,
MapWrite = 1,
CopySrc = 2,
CopyDst = 3,
Index = 4,
Vertex = 5,
Uniform = 6,
Storage = 7,
Indirect = 8,
QueryResolve = 9,
BlasInput = 10,
TlasInput = 11,
}
impl BufferUsageFlags
{
pub const fn from_wgpu(value: wgpu::BufferUsages) -> Self
{
unsafe { Self::from_bits_unchecked(value.bits()) }
}
}
impl From<wgpu::BufferUsages> for BufferUsageFlags
{
#[inline(always)]
fn from(value: wgpu::BufferUsages) -> Self { Self::from_wgpu(value) }
}
impl From<BufferUsageFlags> for wgpu::BufferUsages
{
fn from(value: BufferUsageFlags) -> Self { Self::from_bits(value.bits()).expect("") }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GpuBufferDesc
{
pub usages: BufferUsageFlags,
pub name: Option<&'static str>,
}
impl Default for GpuBufferDesc
{
fn default() -> Self { Self::new() }
}
impl GpuBufferDesc
{
pub const fn new() -> Self
{
Self {
usages: BufferUsageFlags::CopyDst.union(BufferUsageFlags::CopySrc),
name: None,
}
}
pub const fn add_usage(mut self, usage: BufferUsageFlags) -> Self
{
self.usages = self.usages.union(usage);
self
}
pub const fn with_usages(mut self, usages: BufferUsageFlags) -> Self
{
self.usages = usages;
self
}
pub const fn with_label(mut self, label: Option<&'static str>) -> Self
{
self.name = label;
self
}
pub const VERTEX: Self = Self::new().add_usage(BufferUsageFlags::Vertex);
pub const INDEX: Self = Self::new().add_usage(BufferUsageFlags::Index);
}
pub trait ToGpuBuffer<T>
where
T: BitAllUsed,
{
fn to_gpu_buffer(self, desc: GpuBufferDesc) -> GpuBuffer<T>;
}
impl<T> ToGpuBuffer<T> for &[T]
where
T: BitAllUsed,
{
fn to_gpu_buffer(self, desc: GpuBufferDesc) -> GpuBuffer<T> { GpuBuffer::new(self, desc) }
}