From 6249b9e2f450257e71baafb7af4a47c4eccaeb9e Mon Sep 17 00:00:00 2001
From: HampusM <hampus@hampusmat.com>
Date: Tue, 21 Jan 2025 02:45:23 +0100
Subject: refactor(engine): add GlObjects component later in opengl renderer

---
 engine/src/opengl/buffer.rs       | 13 -------
 engine/src/opengl/vertex_array.rs | 11 ------
 engine/src/renderer/opengl.rs     | 49 ++++++++------------------
 engine/src/util.rs                | 73 +++++++++++++++++++++++++++++----------
 4 files changed, 69 insertions(+), 77 deletions(-)

diff --git a/engine/src/opengl/buffer.rs b/engine/src/opengl/buffer.rs
index 2be7f12..68a75fb 100644
--- a/engine/src/opengl/buffer.rs
+++ b/engine/src/opengl/buffer.rs
@@ -39,19 +39,6 @@ impl<Item> Buffer<Item>
     {
         self.buf
     }
-
-    /// Does a weak clone of this buffer. The buffer itself is NOT copied in any way this
-    /// function only copies the internal buffer ID.
-    ///
-    /// # Safety
-    /// The returned `Buffer` must not be dropped if another `Buffer` referencing the
-    /// same buffer ID is used later or if a [`VertexArray`] is used later.
-    ///
-    /// [`VertexArray`]: crate::opengl::vertex_array::VertexArray
-    pub unsafe fn clone_weak(&self) -> Self
-    {
-        Self { buf: self.buf, _pd: PhantomData }
-    }
 }
 
 impl<Item> Drop for Buffer<Item>
diff --git a/engine/src/opengl/vertex_array.rs b/engine/src/opengl/vertex_array.rs
index da5d91e..e1e1a15 100644
--- a/engine/src/opengl/vertex_array.rs
+++ b/engine/src/opengl/vertex_array.rs
@@ -125,17 +125,6 @@ impl VertexArray
     {
         unsafe { gl::BindVertexArray(self.array) }
     }
-
-    /// Does a weak clone of this vertex array. The vertex array itself is NOT copied in
-    /// any way this function only copies the internal vertex array ID.
-    ///
-    /// # Safety
-    /// The returned `VertexArray` must not be dropped if another `VertexArray`
-    /// referencing the same vertex array ID is used later.
-    pub unsafe fn clone_unsafe(&self) -> Self
-    {
-        Self { array: self.array }
-    }
 }
 
 impl Drop for VertexArray
diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs
index c098548..6a571eb 100644
--- a/engine/src/renderer/opengl.rs
+++ b/engine/src/renderer/opengl.rs
@@ -64,7 +64,7 @@ use crate::opengl::{
 use crate::projection::{ClipVolume, Projection};
 use crate::texture::{Id as TextureId, Texture};
 use crate::transform::{Position, Scale};
-use crate::util::NeverDrop;
+use crate::util::{defer, Defer, RefOrValue};
 use crate::vector::{Vec2, Vec3};
 use crate::vertex::{AttributeComponentType, Vertex};
 use crate::window::Window;
@@ -174,21 +174,17 @@ fn render(
             .map(|material_flags| material_flags.clone())
             .unwrap_or_default();
 
-        let new_gl_objects;
-
-        let gl_objects = if let Some(gl_objects) = gl_objects.as_deref() {
-            gl_objects
-        } else {
-            // TODO: Account for when meshes are changed
-            let gl_objects = GlObjects::new(&mesh);
-
-            new_gl_objects = Some(gl_objects.clone());
-
-            actions.add_components(euid, (gl_objects,));
-
-            &*new_gl_objects.unwrap()
+        let gl_objs = match gl_objects.as_deref() {
+            Some(gl_objs) => RefOrValue::Ref(gl_objs),
+            None => RefOrValue::Value(Some(GlObjects::new(&mesh))),
         };
 
+        defer!(|gl_objs| {
+            if let RefOrValue::Value(opt_gl_objs) = gl_objs {
+                actions.add_components(euid, (opt_gl_objs.take().unwrap(),));
+            };
+        });
+
         apply_transformation_matrices(
             Transformation {
                 position: position.map(|pos| *pos).unwrap_or_default().position,
@@ -235,7 +231,7 @@ fn render(
             );
         }
 
-        draw_mesh(gl_objects);
+        draw_mesh(gl_objs.get().unwrap());
 
         if draw_flags.is_some() {
             let default_polygon_mode_config = PolygonModeConfig::default();
@@ -360,7 +356,7 @@ fn get_glsl_shader_content(path: &Path) -> Result<Vec<u8>, std::io::Error>
 struct GlObjects
 {
     /// Vertex and index buffer has to live as long as the vertex array
-    vertex_buffer: Buffer<Vertex>,
+    _vertex_buffer: Buffer<Vertex>,
     index_buffer: Option<Buffer<u32>>,
     element_cnt: u32,
 
@@ -415,7 +411,7 @@ impl GlObjects
             vertex_arr.bind_element_buffer(&index_buffer);
 
             return Self {
-                vertex_buffer,
+                _vertex_buffer: vertex_buffer,
                 index_buffer: Some(index_buffer),
                 element_cnt: indices
                     .len()
@@ -426,7 +422,7 @@ impl GlObjects
         }
 
         Self {
-            vertex_buffer,
+            _vertex_buffer: vertex_buffer,
             index_buffer: None,
             element_cnt: mesh
                 .vertices()
@@ -436,23 +432,6 @@ impl GlObjects
             vertex_arr,
         }
     }
-
-    pub fn clone(&self) -> NeverDrop<Self>
-    {
-        NeverDrop::new(Self {
-            // SAFETY: The vertex buffer will never become dropped (NeverDrop ensures it)
-            vertex_buffer: unsafe { self.vertex_buffer.clone_weak() },
-            index_buffer: self
-                .index_buffer
-                .as_ref()
-                // SAFETY: The index buffer will never become dropped (NeverDrop ensures
-                // it)
-                .map(|index_buffer| unsafe { index_buffer.clone_weak() }),
-            element_cnt: self.element_cnt,
-            // SAFETY: The vertex array will never become dropped (NeverDrop ensures it)
-            vertex_arr: unsafe { self.vertex_arr.clone_unsafe() },
-        })
-    }
 }
 
 fn apply_transformation_matrices(
diff --git a/engine/src/util.rs b/engine/src/util.rs
index a505a38..0f6c78c 100644
--- a/engine/src/util.rs
+++ b/engine/src/util.rs
@@ -1,3 +1,5 @@
+use std::marker::PhantomData;
+
 macro_rules! try_option {
     ($expr: expr) => {
         match $expr {
@@ -9,9 +11,6 @@ macro_rules! try_option {
     };
 }
 
-use std::mem::ManuallyDrop;
-use std::ops::{Deref, DerefMut};
-
 pub(crate) use try_option;
 
 macro_rules! or {
@@ -97,36 +96,74 @@ macro_rules! builder {
 
 pub(crate) use builder;
 
-/// Wrapper that ensures the contained value will never be dropped.
-#[derive(Debug)]
-pub struct NeverDrop<Value>
+pub enum RefOrValue<'a, T>
 {
-    value: ManuallyDrop<Value>,
+    Ref(&'a T),
+    Value(Option<T>),
 }
 
-impl<Value> NeverDrop<Value>
+impl<'a, T> RefOrValue<'a, T>
 {
-    #[must_use]
-    pub fn new(value: Value) -> Self
+    pub fn get(&self) -> Option<&T>
     {
-        Self { value: ManuallyDrop::new(value) }
+        match self {
+            Self::Ref(val_ref) => Some(val_ref),
+            Self::Value(val_cell) => val_cell.as_ref(),
+        }
     }
 }
 
-impl<Value> Deref for NeverDrop<Value>
+#[derive(Debug)]
+pub struct Defer<'func, Func, Data>
+where
+    Func: FnMut(&mut Data) + 'func,
 {
-    type Target = Value;
+    func: Func,
+    pub data: Data,
+    _pd: PhantomData<&'func ()>,
+}
 
-    fn deref(&self) -> &Self::Target
+impl<'func, Func, Data> Defer<'func, Func, Data>
+where
+    Func: FnMut(&mut Data) + 'func,
+{
+    pub fn new(data: Data, func: Func) -> Self
     {
-        &self.value
+        Self { func, data, _pd: PhantomData }
     }
 }
 
-impl<Value> DerefMut for NeverDrop<Value>
+impl<'func, Func, Data> Drop for Defer<'func, Func, Data>
+where
+    Func: FnMut(&mut Data) + 'func,
 {
-    fn deref_mut(&mut self) -> &mut Self::Target
+    fn drop(&mut self)
     {
-        &mut self.value
+        (self.func)(&mut self.data)
     }
 }
+
+/// Defines a function that will be called at the end of the current scope.
+///
+/// Only captured variables that are later mutably borrowed needs to specified as
+/// captures.
+macro_rules! defer {
+    (|$capture: ident| {$($tt: tt)*}) => {
+        // This uses the automatic temporary lifetime extension behaviour introduced
+        // in Rust 1.79.0 (https://blog.rust-lang.org/2024/06/13/Rust-1.79.0.html) to
+        // create a unnamable variable for the Defer struct. The variable should be
+        // unnamable so that it cannot be missused and so that this macro can be used
+        // multiple times without having to give it a identifier for the Defer struct
+        // variable
+        let Defer { data: $capture, .. } = if true {
+            &Defer::new($capture, |$capture| {
+                $($tt)*
+            })
+        }
+        else {
+            unreachable!();
+        };
+    };
+}
+
+pub(crate) use defer;
-- 
cgit v1.2.3-18-g5258