From ea1d70c8c28e3b96da6264021fa1c62e28fcd8e4 Mon Sep 17 00:00:00 2001 From: HampusM Date: Fri, 19 Sep 2025 16:36:57 +0200 Subject: feat: add OpenGL bindings crate --- opengl-bindings/src/buffer.rs | 167 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 opengl-bindings/src/buffer.rs (limited to 'opengl-bindings/src/buffer.rs') 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 +{ + buf: crate::sys::types::GLuint, + _pd: PhantomData, +} + +impl Buffer +{ + #[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( + &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::() <= crate::sys::types::GLsizeiptr::MAX as usize); + + size_of::().cast_signed() + }; + + let total_size = size_of::() * 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::(); + + 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, + }, +} -- cgit v1.2.3-18-g5258