webgl-rc 0.1.12

WebGL wrapper with resources reference counting
Documentation
use crate::buffer_usage::BufferUsage;
use std::cell::Cell;
use std::marker::PhantomData;
use std::rc::Rc;
use web_sys::{WebGlBuffer, WebGlRenderingContext as Context, WebGlRenderingContext};

use super::gl::{Gl, GlError};
use super::settings::Settings;
use super::types::DataType;

pub trait Writable: Copy {
    fn write(&self, output: &mut Vec<f32>);
    fn stride() -> usize;
}

#[derive(Debug, Clone)]
pub struct ArrayBufferData {
    pub(self) gl: Gl,
    pub(self) handle: WebGlBuffer,
    pub(self) length: Cell<usize>,
}

impl Drop for ArrayBufferData {
    fn drop(&mut self) {
        self.gl.context().delete_buffer(Some(&self.handle));
    }
}

#[derive(Debug, Clone)]
pub struct ArrayBuffer {
    pub(self) data: Rc<ArrayBufferData>,
}

impl PartialEq<ArrayBuffer> for ArrayBuffer {
    fn eq(&self, other: &ArrayBuffer) -> bool {
        self.data.handle == other.data.handle
    }
}

impl Eq for ArrayBuffer {}

impl ArrayBuffer {
    pub fn new<T: Writable>(
        gl: Gl,
        data: &[T],
        usage: BufferUsage,
    ) -> Result<ArrayBuffer, GlError> {
        let ref context: &WebGlRenderingContext = gl.context();
        let buffer = context
            .create_buffer()
            .ok_or(GlError::BufferAllocationError)?;

        let result = ArrayBuffer {
            data: Rc::new(ArrayBufferData {
                gl: gl.clone(),
                handle: buffer,
                length: Default::default(),
            }),
        };

        result.set_content(data, usage);

        return Ok(result);
    }

    pub(crate) fn handle(&self) -> WebGlBuffer {
        self.data.handle.clone()
    }

    pub fn set_content<T: Writable>(&self, items: &[T], usage: BufferUsage) {
        let mut data: Vec<f32> = Vec::with_capacity(T::stride() * items.len());
        for i in items {
            i.write(&mut data);
        }

        self.data
            .gl
            .apply(Gl::settings().array_buffer(self.clone()), || {
                let bytes = unsafe {
                    std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4)
                };
                self.data.gl.context().buffer_data_with_u8_array(
                    Context::ARRAY_BUFFER,
                    &bytes,
                    usage.into(),
                );
            });

        self.data.length.set(items.len());
    }

    pub fn len(&self) -> usize {
        self.data.length.get()
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Layout {
    pub name: &'static str,
    pub data_type: DataType,
}

pub trait Item: Writable {
    fn layout() -> Vec<Layout>;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ItemsBuffer<T: Item> {
    pub(self) phantom: PhantomData<T>,
    pub(crate) buffer: ArrayBuffer,
}

impl<T: Item> ItemsBuffer<T> {
    pub fn new(gl: Gl, data: &[T], usage: BufferUsage) -> Result<ItemsBuffer<T>, GlError> {
        Ok(ItemsBuffer {
            phantom: Default::default(),
            buffer: ArrayBuffer::new(gl, data, usage)?,
        })
    }

    pub fn set_content(&self, items: &[T], usage: BufferUsage) {
        self.buffer.set_content(items, usage);
    }

    pub fn len(&self) -> usize {
        self.buffer.len()
    }
}