OpenGL 对象的 RAII 包装器

2021-12-19 00:00:00 opengl visual-studio c++ dry raii

我想为我注意到的 OpenGL 对象(纹理、帧缓冲区等)编写一个简单的 RAII 包装器,所有 glGen*glDelete* 函数共享相同的签名,所以我的第一次尝试是这样的:

I want to write a simple RAII wrapper for OpenGL objects (textures, frame buffers, etc.) I have noticed, that all glGen* and glDelete* functions share the same signature, so my first attempt was like this:

typedef void (__stdcall *GLGenFunction)(GLsizei, GLuint *);
typedef void (__stdcall *GLDelFunction)(GLsizei, const GLuint *);

template <GLGenFunction glGenFunction, GLDelFunction glDelFunction>
class GLObject
{
    GLuint m_name;
public:
    GLObject() 
    {  
        glGenFunction(1, &m_name);
    }

    ~GLObject()
    {
        glDelFunction(1, &m_name);
    }

    GLuint getName() {return m_name;}
};

typedef GLObject<glGenTextures, glDeleteTextures> GLTexture;

它适用于纹理,但不适用于帧缓冲区:glGenFramebuffersglDeleteFramebuffers 函数地址在编译时未知,不能用作模板参数.所以我做了第二个版本:

It works fine for textures, but fails for frame buffers: glGenFramebuffers and glDeleteFramebuffers function addresses are not known at compile time, and cannot be used as template arguments. So I made second version:

class GLObjectBase
{
    GLuint m_name;
    GLDelFunction m_delFunction;

public:
    GLObjectBase(GLGenFunction genFunc, GLDelFunction delFunction)
        : m_delFunction(delFunction)
    {
        genFunc(1, &m_name);
    }

    GLuint getName()
    {
        return m_name;
    }

protected:
    ~GLObjectBase()
    {
        m_delFunction(1, &m_name);
    }
};

class GLFrameBuffer : public GLObjectBase
{
public:
    GLFrameBuffer() : GLObjectBase(glGenFramebuffers, glDeleteFramebuffers) {}
};

但我不喜欢它,因为我必须在每个实例中存储在运行时不会改变的 del 函数指针.

But I don't like it since I have to store del function pointer in each instance that will not change at run-time.

如何制作仅在每个实例中存储对象名称的包装类,而无需创建一堆几乎复制粘贴的类?

How do I make wrapper class that stores only object name in each instance without resorting to create a bunch of almost copy-pasted classes?

我可以这样做:

template <int N>
class GLObject2
{
    GLuint m_name;
    static GLDelFunction glDelFunction;
public:
    GLObject2(GLGenFunction genFunction, GLDelFunction delFunc)
    {  
        genFunction(1, &m_name);
        if ( glDelFunction == nullptr )
            glDelFunction = delFunc;
        ASSERT(glDelFunction == delFunc);
    }

    GLuint getName() {return m_name;}

protected:
    ~GLObject2()
    {
        glDelFunction(1, &m_name);
    }
};

template <int N>
GLDelFunction GLObject2<N>::glDelFunction = nullptr;

class GLTexture: public GLObject2<1>
{
public:
    GLTexture(): GLObject2<1>(glGenTextures, glDeleteTextures) {}
};

class GLRenderBuffer: public GLObject2<2>
{
public:
    GLRenderBuffer(): GLObject2<2>(glGenRenderbuffers, glDeleteRenderbuffers) {}
};

谁能提出更优雅的解决方案?

Can anyone suggest more elegant solution?

推荐答案

真的,您会像 C 程序员一样思考这个问题.您正在使用 C++,因此请按照 C++ 程序员的方式解决它.使用特征类:

Really, you're thinking about this like a C programmer. You're using C++, so solve it the way a C++ programmer would. With a traits class:

struct VertexArrayObjectTraits
{
  typedef GLuint value_type;
  static value_type Create();
  static void Destroy(value_type);
};

就像一个适当的 C++ 特征类,我们让每个对象声明它自己的 value_type.这将允许您使其适应不使用 GLuint 的 OpenGL 对象,例如 同步对象(尽管创建/销毁界面无论如何都不适合它们,所以你可能不应该打扰).

Like a proper C++ traits class, we have each object declare it's own value_type. This will allow you to adapt it to OpenGL objects that don't use GLuints, like sync objects (though the Create/Destroy interface wouldn't be good for them anyway, so you probably shouldn't bother).

因此,您为每种类型的 OpenGL 对象编写了一个特征类.您的 CreateDestroy 函数会将调用适当地转发到 C API.

So you write one traits class for each type of OpenGL object. Your Create and Destroy functions will forward the calls on to the C API appropriately.

这样做之后,您只需要一个围绕那些接口的 RAII 包装器:

After doing that, all you need is a RAII-wrapper around those interfaces:

template<typename T>
class OpenGLObject
{
public:
  OpenGLObject() : m_obj(T::Create()) {}
  ~OpenGLObject() {T::Destroy(m_obj);}

  operator typename T::value_type() {return m_obj;}

private:
  typename T::value_type m_obj;
};

OpenGLObject 将持有 VAO.

相关文章