Search code examples
c++openglrenderer

How to use RAII appropriately/reduce memory leaks in this situation


I'm not sure how I can use RAII to my fullest in the situation that I have. This is the situation:

I am creating a basic renderer. Geometry is described by a Geometry class, which may have vertices added to it. In order for Geometry objects to be used for rendering, it must first be compiled (i.e. a VBO will be created for the Geometry). Compiliation (and decompiliation) of geometery objects is done via a Renderer object, decompilation must be done once you are done with geometery objects. If decompilation is not done, there will be memory leaks.

Here is an example of what I'm describing:

Renderer renderer; // the renderer object

{
    Geometry geometry;

    // add vertices
    // and play around with material, etc.

    if(!renderer.compile(geometery); // compile the geometery, so we can use it
    {
        cerr << "Failed to compile geometry!\n";
    }

    // now we can use it...
    renderer.render(geometry, RenderType::TriangleStrips);

} // if I don't call renderer.decompile(geometry) here, I will get a leak

What I'm trying to do is decompile my geometery, without explicitly telling the renderer to decompile it. This is to simply reduce memory leaks. My first thought was to use RAII, but if I do so the Geometry class would require the Renderer class, which seems quite messy. As I would require a reference to the Renderer object which compiled the geometery.

Another alternative I thought of was to make the renderer create geometry, but this will cause geometry objects to allocated dynamically (i.e. with new), and it also seems quite messy.

I also thought of placing a handle object within the geometery, such as a unique_ptr to an abstract handle object.

e.g.

class GeometeryHandle
{
   virtual ~GeometeryHandle() = 0; 
};

Which might actually work, as it may also be used to store the GLuint within the handle. I am unsure if this is appropriate though, as I could simply decompile the geometry directly via a Renderer reference. i.e. it will do the same thing if I call it directly through the destructor.

How should I design this appropriately so I don't accidentally not decompile geometry?


Solution

  • It's not clear who is supposed to be responsible for what in your design. That's step 1 in figuring out how to employ any form of resource management: deciding who's responsible for what.

    For example, you say that "Geometry is described by a Geometry class, which may have vertices added to it." OK, but how does this relate to the post-compilation data? If the user adds vertices to Geometry after it's compiled, do those vertices automatically get placed in the compiled data? Or is the compiled data completely separate from the Geometry class after compiling it, such that changes to the Geometry class don't update the compiled data?

    It sounds to me like you're conflating two very different ideas: GeometryBuilder and Renderable. GeometryBuilder is what you put vertex data into. Then you take that and create a Renderable out of it. The Renderable is an optimized form of the data stored in a GeometryBuilder. The object is completely independent of the GeometryBuilder that created it. The Renderable is what you can actually render with.

    So you would do this:

    GeometryBuilder meshBuilder;
    
    meshBuilder.AddVertex(...);
    ...
    
    Renderable renderMesh = render.CreateRenderable(meshBuilder);
    

    After this point, meshBuilder is independent of renderMesh. You can delete one and the other's fine. You can "compile" renderMesh multiple times and get identical copies of the same data. And so forth.