opengl
OpenGLオブジェクトをC ++ RAIIでカプセル化する
サーチ…
前書き
OpenGLオブジェクトを持つさまざまな方法の例は、C ++ RAIIで動作します。
備考
OpenGLオブジェクトのRAIIカプセル化には危険性があります。最も避けられないことは、OpenGLオブジェクトがそれらを作成したOpenGLコンテキストに関連付けられていることです。したがって、C ++ RAIIオブジェクトの破壊は、そのC ++オブジェクトによって管理されるOpenGLオブジェクトの所有権を共有するOpenGLコンテキストで行われなければなりません。
つまり、オブジェクトを所有するすべてのコンテキストが破棄された場合、既存のRAIIカプセル化OpenGLオブジェクトは存在しなくなったオブジェクトを破棄しようとします。
このような文脈の問題に対処するには、手作業で取る必要があります。
C ++ 98/03では
C ++ 98/03でOpenGLオブジェクトをカプセル化するには、C ++ルール3に従う必要があります。これは、コピーコンストラクタ、コピー代入演算子、およびデストラクタを追加することを意味します。
ただし、コピーコンストラクタは論理的にオブジェクトをコピーする必要があります。 OpenGLオブジェクトをコピーすることは、自明ではありません。同様に重要なのは、ユーザーがしたくないことはほぼ確実です。
だから私たちはオブジェクトをコピー不可能にします:
class BufferObject
{
public:
BufferObject(GLenum target, GLsizeiptr size, const void *data, GLenum usage)
{
glGenBuffers(1, &object_);
glBindBuffer(target, object_);
glBufferData(target, size, data, usage);
glBindBuffer(target, 0);
}
~BufferObject()
{
glDeleteBuffers(1, &object_);
}
//Accessors and manipulators
void Bind(GLenum target) const {glBindBuffer(target, object_);}
GLuint GetObject() const {return object_;}
private:
GLuint object_;
//Prototypes, but no implementation.
BufferObject(const BufferObject &);
BufferObject &operator=(const BufferObject &);
};
コンストラクタはオブジェクトを作成し、バッファオブジェクトのデータを初期化します。デストラクタはオブジェクトを破壊します。それらを定義せずにコピーコンストラクタ/代入を宣言することによって、リンカはコードを呼び出そうとするとエラーを返します。また、privateを宣言することで、 BufferObject
メンバーだけでもそれらを呼び出すことができます。
BufferObject
は、コンストラクタに渡されたtarget
を保持しないことに注意してください。これは、OpenGLのバッファオブジェクトは、最初に作成されたものだけでなく、どのターゲットでも使用できるためです。これは、 最初に作成されたターゲットに常にバインドされていなければならないテクスチャオブジェクトとは異なります。
OpenGLはさまざまな目的のためにオブジェクトをコンテキストにバインドすることに非常に依存しているので、RAII形式のスコープ付きオブジェクトバインディングを持つこともしばしば役に立ちます。異なるオブジェクトは異なるバインディングのニーズ(ターゲットを持つものとそうでないものがあります)があるため、オブジェクトごとに個別に実装する必要があります。
class BindBuffer
{
public:
BindBuffer(GLenum target, const BufferObject &buff) : target_(target)
{
buff.Bind(target_);
}
~BindBuffer()
{
glBindBuffer(target_, 0);
}
private:
GLenum target_;
//Also non-copyable.
BindBuffer(const BindBuffer &);
BindBuffer &operator=(const BindBuffer &);
};
BindBuffer
はコピーできないのでコピーできません。バインドされたBufferObject
へのアクセスは保持されません。これは不要なためです。
C ++ 11以降
C ++ 11は、RAIIでカプセル化されたOpenGLオブジェクトの機能を強化するツールを提供します。移動セマンティクスのようなC ++ 11の機能がなければ、コピーすることができないので、それらのオブジェクトを渡す必要がある場合、そのようなオブジェクトを動的に割り当てる必要があります。移動をサポートすると、コピーするのではなく、通常の値のように前後に渡すことができます。
class BufferObject
{
public:
BufferObject(GLenum target, GLsizeiptr size, const void *data, GLenum usage)
{
glGenBuffers(1, &object_);
glBindBuffer(target, object_);
glBufferData(target, size, data, usage);
glBindBuffer(target, 0);
}
//Cannot be copied.
BufferObject(const BufferObject &) = delete;
BufferObject &operator=(const BufferObject &) = delete;
//Can be moved
BufferObject(BufferObject &&other) noexcept : object_(other.Release())
{}
//Self-assignment is OK with this implementation.
BufferObject &operator=(BufferObject &&other) noexcept
{
Reset(other.Release());
}
//Destroys the old buffer and claims ownership of a new buffer object.
//It's OK to call glDeleteBuffers on buffer object 0.
GLuint Reset(GLuint object = 0)
{
glDeleteBuffers(1, &object_);
object_ = object;
}
//Relinquishes ownership of the object without destroying it
GLuint Release()
{
GLuint ret = object_;
object_ = 0;
return ret;
}
~BufferObject()
{
Reset();
}
//Accessors and manipulators
void Bind(GLenum target) const {glBindBuffer(target, object_);}
GLuint GetObject() const {return object_;}
private:
GLuint object_;
};
そのような型は関数によって返すことができます:
BufferObject CreateStaticBuffer(GLsizeiptr byteSize) {return BufferObject(GL_ARRAY_BUFFER, byteSize, nullptr, GL_STATIC_DRAW);}
これはあなた自身の(暗黙的に移動のみ)タイプでそれらを格納することを可能にします:
struct Mesh
{
public:
private:
//Default member initializer.
BufferObject buff_ = CreateStaticBuffer(someSize);
};
スコープ付きバインダークラスでは、移動セマンティクスも使用できるため、バインダーを関数から戻したり、C ++標準ライブラリコンテナに格納することができます。
class BindBuffer
{
public:
BindBuffer(GLenum target, const BufferObject &buff) : target_(target)
{
buff.Bind(target_);
}
//Non-copyable.
BindBuffer(const BindBuffer &) = delete;
BindBuffer &operator=(const BindBuffer &) = delete;
//Move-constructible.
BindBuffer(BindBuffer &&other) noexcept : target_(other.target_)
{
other.target_ = 0;
}
//Not move-assignable.
BindBuffer &operator=(BindBuffer &&) = delete;
~BindBuffer()
{
//Only unbind if not moved from.
if(target_)
glBindBuffer(target_, 0);
}
private:
GLenum target_;
};
オブジェクトは移動可能であるが、移動可能ではないことに注意してください。この考え方は、スコープ付きバッファー・バインディングの再バインドを防ぐことです。それが設定されると、それを設定解除できる唯一のものが移動されます。