Suche…


Einführung

Beispiele für verschiedene Möglichkeiten, OpenGL-Objekte mit C ++ RAII zusammenarbeiten

Bemerkungen

Die RAII-Kapselung von OpenGL-Objekten birgt Gefahren. Am unvermeidlichsten ist, dass OpenGL-Objekte dem OpenGL-Kontext zugeordnet sind, mit dem sie erstellt wurden. Die Zerstörung eines C ++ - RAII-Objekts muss daher in einem OpenGL-Kontext erfolgen, der den Besitz des von diesem C ++ - Objekt verwalteten OpenGL-Objekts teilt.

Das bedeutet auch, dass bei Zerstörung aller Kontexte, zu denen das Objekt gehört, vorhandene RAII-gekapselte OpenGL-Objekte versuchen, Objekte zu zerstören, die nicht mehr vorhanden sind.

Sie müssen manuelle Schritte ausführen, um solche Kontextprobleme zu lösen.

In C ++ 98/03

Das Einkapseln eines OpenGL-Objekts in C ++ 98/03 erfordert die Einhaltung der C ++ - Regel von 3. Dies bedeutet, dass Sie einen Kopierkonstruktor, einen Kopierzuweisungsoperator und einen Destruktor hinzufügen.

Kopierkonstruktoren sollten das Objekt jedoch logisch kopieren. Das Kopieren eines OpenGL-Objekts ist ein nicht triviales Unternehmen. Ebenso wichtig ist es fast sicher, dass der Benutzer dies nicht tun möchte.

Also machen wir das Objekt stattdessen nicht kopierbar:

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 &);
};

Der Konstruktor erstellt das Objekt und initialisiert die Daten des Pufferobjekts. Der Zerstörer zerstört das Objekt. Durch die Deklaration des Kopierkonstruktors / der Zuordnung, ohne sie zu definieren, gibt der Linker einen Fehler aus, wenn ein Code versucht, sie aufzurufen. BufferObject sie für privat erklären, können sie nur von Mitgliedern von BufferObject aufgerufen werden.

Beachten Sie, dass BufferObject das an den Konstruktor übergebene target nicht BufferObject . Das liegt daran, dass ein OpenGL-Pufferobjekt mit jedem Ziel verwendet werden kann, nicht nur mit dem, mit dem es ursprünglich erstellt wurde. Dies unterscheidet sich von Texturobjekten, die immer an das Ziel gebunden sein müssen, mit dem sie ursprünglich erstellt wurden.

Da OpenGL für verschiedene Zwecke sehr stark von der Bindung von Objekten an den Kontext abhängt, ist es häufig auch sinnvoll, RAOB-artige Objektbindungen zu verwenden. Da verschiedene Objekte unterschiedliche Bindungsbedürfnisse haben (einige haben Ziele, andere nicht), müssen wir für jedes Objekt einen eigenen implementieren.

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 ist nicht kopierbar, da das Kopieren keinen Sinn macht. Beachten Sie, dass der Zugriff auf das BufferObject nicht erhalten BufferObject . Das ist weil es unnötig ist.

In C ++ 11 und höher

C ++ 11 bietet Tools, die die Funktionalität von RAII-gekapselten OpenGL-Objekten erweitern. Ohne C ++ 11-Features wie die Move-Semantik müssen solche Objekte dynamisch zugewiesen werden, wenn Sie sie weitergeben möchten, da sie nicht kopiert werden können. Move-Unterstützung ermöglicht es, sie wie normale Werte hin und her zu übergeben, jedoch nicht durch Kopieren:

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_;
};

Ein solcher Typ kann von einer Funktion zurückgegeben werden:

BufferObject CreateStaticBuffer(GLsizeiptr byteSize) {return BufferObject(GL_ARRAY_BUFFER, byteSize, nullptr, GL_STATIC_DRAW);}

Damit können Sie sie in Ihren eigenen (implizit nur zum Verschieben bestimmten) Typen speichern:

struct Mesh
{
public:
private:
    //Default member initializer.
    BufferObject buff_ = CreateStaticBuffer(someSize);
};

Eine bereichsbindende Klasse kann auch eine Bewegungssemantik aufweisen, sodass der Binder von Funktionen zurückgegeben und in C ++ - Bibliothekscontainern gespeichert werden kann:

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_;
};

Beachten Sie, dass das Objekt bewegbar ist, aber nicht zuweisbar ist. Die Idee dabei ist, das erneute Binden einer Pufferbindung mit Umfang zu verhindern. Wenn es einmal eingestellt ist, wird das Einzige, von dem es entfernt werden kann, verschoben.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow