use bytemuck::cast_slice;
use std::fmt::{Debug, Formatter};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use wgpu::{BufferAddress, BufferDescriptor, BufferUsages, Device, Queue};
#[cfg(feature = "lua")]
pub type IndexBufferObj = fey_lua::UserDataOf<IndexBuffer>;
#[cfg(feature = "lua")]
pub type IndexBufferRef = mlua::UserDataRef<IndexBuffer>;
#[derive(Clone)]
pub struct IndexBuffer(Arc<Inner>);
impl Debug for IndexBuffer {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("IndexBuffer").finish_non_exhaustive()
}
}
impl PartialEq for IndexBuffer {
#[inline]
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl PartialOrd for IndexBuffer {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Arc::as_ptr(&self.0).partial_cmp(&Arc::as_ptr(&other.0))
}
}
#[derive(Debug)]
struct Inner {
buffer: wgpu::Buffer,
queue: Queue,
count: AtomicUsize,
}
impl IndexBuffer {
pub(crate) fn new(device: &Device, queue: Queue, capacity: usize) -> Self {
let buffer = device.create_buffer(&BufferDescriptor {
label: None,
size: (capacity * size_of::<u32>()) as BufferAddress,
usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
Self(Arc::new(Inner {
buffer,
queue,
count: AtomicUsize::new(0),
}))
}
#[inline]
pub fn upload(&self, indices: &[u32]) -> Result<(), IndexBufferUploadError> {
assert!(indices.len() <= self.capacity());
if indices.len() > self.capacity() {
return Err(IndexBufferUploadError::InsufficientSpace {
expected: self.capacity(),
got: indices.len(),
});
}
self.0.count.store(indices.len(), Ordering::Relaxed);
self.0
.queue
.write_buffer(&self.0.buffer, 0, cast_slice(indices));
Ok(())
}
#[inline]
pub(crate) fn buffer(&self) -> &wgpu::Buffer {
&self.0.buffer
}
#[inline]
pub fn capacity(&self) -> usize {
self.capacity_in_bytes() / size_of::<u32>()
}
#[inline]
pub fn capacity_in_bytes(&self) -> usize {
self.0.buffer.size() as usize
}
#[inline]
pub fn count(&self) -> usize {
self.0.count.load(Ordering::Relaxed)
}
#[inline]
pub fn size_in_bytes(&self) -> usize {
self.0.count.load(Ordering::Relaxed) * size_of::<u32>()
}
}
#[derive(Debug, thiserror::Error)]
pub enum IndexBufferUploadError {
#[error("attempted to upload {got} indices to buffer with a capacity of {expected}")]
InsufficientSpace { expected: usize, got: usize },
}