diff options
Diffstat (limited to 'opengl-bindings/src/buffer.rs')
| -rw-r--r-- | opengl-bindings/src/buffer.rs | 167 | 
1 files changed, 167 insertions, 0 deletions
diff --git a/opengl-bindings/src/buffer.rs b/opengl-bindings/src/buffer.rs new file mode 100644 index 0000000..c64ec8d --- /dev/null +++ b/opengl-bindings/src/buffer.rs @@ -0,0 +1,167 @@ +use std::marker::PhantomData; +use std::mem::size_of_val; +use std::ptr::null; + +use safer_ffi::layout::ReprC; + +use crate::CurrentContextWithFns; + +#[derive(Debug)] +pub struct Buffer<Item: ReprC> +{ +    buf: crate::sys::types::GLuint, +    _pd: PhantomData<Item>, +} + +impl<Item: ReprC> Buffer<Item> +{ +    #[must_use] +    pub fn new(current_context: &CurrentContextWithFns<'_>) -> Self +    { +        let mut buffer = crate::sys::types::GLuint::default(); + +        unsafe { +            current_context.fns().CreateBuffers(1, &raw mut buffer); +        }; + +        Self { buf: buffer, _pd: PhantomData } +    } + +    /// Stores items in this buffer. +    /// +    /// # Errors +    /// Returns `Err` if the total size (in bytes) is too large. +    pub fn store( +        &self, +        current_context: &CurrentContextWithFns<'_>, +        items: &[Item], +        usage: Usage, +    ) -> Result<(), Error> +    { +        let total_size = size_of_val(items); + +        let total_size: crate::sys::types::GLsizeiptr = +            total_size +                .try_into() +                .map_err(|_| Error::TotalItemsSizeTooLarge { +                    total_size, +                    max_total_size: crate::sys::types::GLsizeiptr::MAX as usize, +                })?; + +        unsafe { +            current_context.fns().NamedBufferData( +                self.buf, +                total_size, +                items.as_ptr().cast(), +                usage.into_gl(), +            ); +        } + +        Ok(()) +    } + +    /// Maps the values in the `values` slice into `Item`s which is stored into this +    /// buffer. +    /// +    /// # Errors +    /// Returns `Err` if the total size (in bytes) is too large. +    pub fn store_mapped<Value>( +        &self, +        current_context: &CurrentContextWithFns<'_>, +        values: &[Value], +        usage: Usage, +        mut map_func: impl FnMut(&Value) -> Item, +    ) -> Result<(), Error> +    { +        let item_size: crate::sys::types::GLsizeiptr = const { +            assert!(size_of::<Item>() <= crate::sys::types::GLsizeiptr::MAX as usize); + +            size_of::<Item>().cast_signed() +        }; + +        let total_size = size_of::<Item>() * values.len(); + +        let total_size: crate::sys::types::GLsizeiptr = +            total_size +                .try_into() +                .map_err(|_| Error::TotalItemsSizeTooLarge { +                    total_size, +                    max_total_size: crate::sys::types::GLsizeiptr::MAX as usize, +                })?; + +        unsafe { +            current_context.fns().NamedBufferData( +                self.buf, +                total_size, +                null(), +                usage.into_gl(), +            ); +        } + +        for (index, value) in values.iter().enumerate() { +            let item = map_func(value); + +            let offset = index * size_of::<Item>(); + +            let Ok(offset_casted) = crate::sys::types::GLintptr::try_from(offset) else { +                unreachable!(); // Reason: The total size can be casted to a GLintptr +                                // (done above) so offsets should be castable as well +            }; + +            unsafe { +                current_context.fns().NamedBufferSubData( +                    self.buf, +                    offset_casted, +                    item_size, +                    (&raw const item).cast(), +                ); +            } +        } + +        Ok(()) +    } + +    pub(crate) fn object(&self) -> crate::sys::types::GLuint +    { +        self.buf +    } +} + +/// Buffer usage. +#[derive(Debug)] +pub enum Usage +{ +    /// The buffer data is set only once and used by the GPU at most a few times. +    Stream, + +    /// The buffer data is set only once and used many times. +    Static, + +    /// The buffer data is changed a lot and used many times. +    Dynamic, +} + +impl Usage +{ +    fn into_gl(self) -> crate::sys::types::GLenum +    { +        match self { +            Self::Stream => crate::sys::STREAM_DRAW, +            Self::Static => crate::sys::STATIC_DRAW, +            Self::Dynamic => crate::sys::DYNAMIC_DRAW, +        } +    } +} + +#[derive(Debug, thiserror::Error)] +pub enum Error +{ +    #[error( +        "Total size of items ({total_size}) is too large. Must be < {max_total_size}" +    )] +    TotalItemsSizeTooLarge +    { +        total_size: usize, +        max_total_size: usize, +    }, +}  | 
