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