Поиск…


Вступление

Примеры различных способов работы OpenGL-объектов с C ++ RAII.

замечания

Инкапсуляция объектов OpenGL в RAII имеет свои опасности. Наиболее неизбежным является то, что объекты OpenGL связаны с контекстом OpenGL, который их создал. Таким образом, уничтожение объекта C ++ RAII должно выполняться в контексте OpenGL, который разделяет право собственности на объект OpenGL, управляемый этим объектом C ++.

Это также означает, что если все контексты, которые владеют объектом, будут уничтожены, то любые существующие объекты с открытым содержимым, содержащиеся в RAII, будут пытаться уничтожить объекты, которые больше не существуют.

Вы должны предпринять ручные шаги для решения таких проблем контекста, как это.

В C ++ 98/03

Инкапсуляция объекта OpenGL в C ++ 98/03 требует подчинения правилу 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 &);
};

Конструктор создаст объект и инициализирует данные объекта буфера. Деструктор уничтожит объект. Объявив конструктор / назначение копии без их определения, компоновщик выдаст ошибку, если какой-либо код попытается вызвать их. И, объявив их закрытыми, только члены 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 предлагает инструменты, которые улучшают функциональность объектов OpenGL, инкапсулированных RAII. Без возможностей 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_;
};

Обратите внимание, что объект перемещается конструктивно, но не может быть назначен переносом. Идея заключается в том, чтобы предотвратить повторное связывание привязки буфера с привязкой. После того, как он установлен, единственное, что может его отключить, перемещается из него.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow