use crate::webgl2::{
state::{Bind, WebGL2State},
WebGL2,
};
use core::fmt;
use luminance::tess::TessError;
use std::{
cell::RefCell,
error,
marker::PhantomData,
mem,
ops::{Deref, DerefMut},
rc::Rc,
slice,
};
use web_sys::{WebGl2RenderingContext, WebGlBuffer};
#[derive(Clone, Debug)]
pub enum BufferError {
CannotCreate,
}
impl fmt::Display for BufferError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
BufferError::CannotCreate => f.write_str("cannot create buffer on the backend"),
}
}
}
impl error::Error for BufferError {}
impl From<BufferError> for TessError {
fn from(e: BufferError) -> Self {
TessError::cannot_create(e.to_string())
}
}
#[derive(Clone, Debug)]
struct BufferWrapper<const TARGET: u32> {
handle: WebGlBuffer,
state: Rc<RefCell<WebGL2State>>,
}
impl<const TARGET: u32> Drop for BufferWrapper<TARGET> {
fn drop(&mut self) {
let mut state = self.state.borrow_mut();
state.unbind_buffer(&self.handle);
state.ctx.delete_buffer(Some(&self.handle));
}
}
#[derive(Clone, Debug)]
pub struct Buffer<T, const TARGET: u32> {
pub(crate) buf: Vec<T>,
gl_buf: BufferWrapper<TARGET>,
}
impl<T, const TARGET: u32> Buffer<T, TARGET>
where
WebGL2State: BindBuffer<TARGET>,
{
pub(crate) fn from_vec(webgl2: &mut WebGL2, vec: Vec<T>) -> Result<Self, BufferError> {
let mut state = webgl2.state.borrow_mut();
let len = vec.len();
let handle = state
.create_buffer()
.ok_or_else(|| BufferError::CannotCreate)?;
state.bind_buffer(&handle, Bind::Forced);
let bytes = mem::size_of::<T>() * len;
let data = unsafe { slice::from_raw_parts(vec.as_ptr() as *const _, bytes) };
state
.ctx
.buffer_data_with_u8_array(TARGET, data, WebGl2RenderingContext::STREAM_DRAW);
let gl_buf = BufferWrapper {
handle,
state: webgl2.state.clone(),
};
Ok(Buffer { gl_buf, buf: vec })
}
pub(crate) fn handle(&self) -> &WebGlBuffer {
&self.gl_buf.handle
}
pub(crate) fn slice_buffer(&self) -> BufferSlice<T> {
BufferSlice {
handle: &self.gl_buf.handle,
ptr: self.buf.as_ptr(),
len: self.buf.len(),
state: self.gl_buf.state.clone(),
}
}
pub(crate) fn slice_buffer_mut(&mut self) -> BufferSliceMut<T, TARGET> {
let raw = BufferSliceMutWrapper {
handle: &self.gl_buf.handle,
ptr: self.buf.as_mut_ptr() as *mut u8,
bytes: self.buf.len() * mem::size_of::<T>(),
state: self.gl_buf.state.clone(),
};
BufferSliceMut {
raw,
_phantom: PhantomData,
}
}
}
pub struct BufferSlice<'a, T> {
handle: &'a WebGlBuffer,
ptr: *const T,
len: usize,
state: Rc<RefCell<WebGL2State>>,
}
impl<'a> BufferSlice<'a, u8> {
pub(crate) unsafe fn transmute<T>(self) -> BufferSlice<'a, T> {
let handle = self.handle;
let ptr = self.ptr as *const T;
let len = self.len / mem::size_of::<T>();
let state = self.state;
BufferSlice {
handle,
ptr,
len,
state,
}
}
}
impl<T> Deref for BufferSlice<'_, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.ptr, self.len) }
}
}
pub struct BufferSliceMutWrapper<'a, const TARGET: u32>
where
WebGL2State: BindBuffer<TARGET>,
{
handle: &'a WebGlBuffer,
ptr: *mut u8,
bytes: usize,
state: Rc<RefCell<WebGL2State>>,
}
impl<const TARGET: u32> Drop for BufferSliceMutWrapper<'_, TARGET>
where
WebGL2State: BindBuffer<TARGET>,
{
fn drop(&mut self) {
let mut state = self.state.borrow_mut();
let _ = update_webgl_buffer::<TARGET>(&mut state, &self.handle, self.ptr, self.bytes, 0);
}
}
pub struct BufferSliceMut<'a, T, const TARGET: u32>
where
WebGL2State: BindBuffer<TARGET>,
{
raw: BufferSliceMutWrapper<'a, TARGET>,
_phantom: PhantomData<T>,
}
impl<'a, const TARGET: u32> BufferSliceMut<'a, u8, TARGET>
where
WebGL2State: BindBuffer<TARGET>,
{
pub(crate) unsafe fn transmute<T>(self) -> BufferSliceMut<'a, T, TARGET> {
BufferSliceMut {
raw: self.raw,
_phantom: PhantomData,
}
}
}
impl<T, const TARGET: u32> Deref for BufferSliceMut<'_, T, TARGET>
where
WebGL2State: BindBuffer<TARGET>,
{
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe {
slice::from_raw_parts(
self.raw.ptr as *const T,
self.raw.bytes / mem::size_of::<T>(),
)
}
}
}
impl<T, const TARGET: u32> DerefMut for BufferSliceMut<'_, T, TARGET>
where
WebGL2State: BindBuffer<TARGET>,
{
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe {
slice::from_raw_parts_mut(self.raw.ptr as *mut T, self.raw.bytes / mem::size_of::<T>())
}
}
}
pub trait BindBuffer<const TARGET: u32> {
fn bind_buffer(&mut self, handle: &WebGlBuffer, bind_mode: Bind);
}
impl BindBuffer<{ WebGl2RenderingContext::ARRAY_BUFFER }> for WebGL2State {
fn bind_buffer(&mut self, handle: &WebGlBuffer, bind_mode: Bind) {
self.bind_array_buffer(Some(handle), bind_mode);
}
}
impl BindBuffer<{ WebGl2RenderingContext::ELEMENT_ARRAY_BUFFER }> for WebGL2State {
fn bind_buffer(&mut self, handle: &WebGlBuffer, bind_mode: Bind) {
self.bind_element_array_buffer(Some(handle), bind_mode);
}
}
impl BindBuffer<{ WebGl2RenderingContext::UNIFORM_BUFFER }> for WebGL2State {
fn bind_buffer(&mut self, handle: &WebGlBuffer, bind: Bind) {
self.bind_uniform_buffer(Some(handle), bind);
}
}
fn update_webgl_buffer<const TARGET: u32>(
state: &mut WebGL2State,
handle: &WebGlBuffer,
data: *const u8,
bytes: usize,
offset: usize,
) -> Result<(), BufferError>
where
WebGL2State: BindBuffer<TARGET>,
{
state.bind_buffer(handle, Bind::Cached);
let data = unsafe { slice::from_raw_parts(data as _, bytes) };
state
.ctx
.buffer_sub_data_with_i32_and_u8_array_and_src_offset(TARGET, offset as _, data, 0);
Ok(())
}