Opengl Object in C++ Raii Class No Longer Works

OpenGL object in C++ RAII class no longer works

All of those operations copy the C++ object. Since your class did not define a copy constructor, you get the compiler-generated copy constructor. This simply copies all of the members of the object.

Consider the first example:

vector<BufferObject> bufVec;
{
BufferObject some_buffer;
//Initialize some_buffer;
bufVec.push_back(some_buffer);
}
bufVec.back(); //buffer doesn't work.

When you call push_back, it copies some_buffer into a BufferObject in the vector. So, right before we exit that scope, there are two BufferObject objects.

But what OpenGL buffer object do they store? Well, they store the same one. After all, to C++, we just copied an integer. So both C++ objects store the same integer value.

When we exit that scope, some_buffer will be destroyed. Therefore, it will call glDeleteBuffers on this OpenGL object. But the object in the vector will still have its own copy of that OpenGL object name. Which has been destroyed.

So you can't use it anymore; hence the errors.

The same thing happens with your InitBuffer function. buff will be destroyed after it is copied into the return value, which makes the returned object worthless.

This is all due to a violation of the so-called "Rule of 3/5" in C++. You created a destructor without creating copy/move constructors/assignment operators. That's bad.

To solve this, your OpenGL object wrappers should be move-only types. You should delete the copy constructor and copy assignment operator, and provide move equivalents that set the moved-from object to object 0:

class BufferObject
{
private:
GLuint buff_;

public:
BufferObject()
{
glGenBuffers(1, &buff_);
}

BufferObject(const BufferObject &) = delete;
BufferObject &operator=(const BufferObject &) = delete;

BufferObject(BufferObject &&other) : buff_(other.buff_)
{
other.buff_ = 0;
}

BufferObject &operator=(BufferObject &&other)
{
//ALWAYS check for self-assignment
if(this != &other)
{
Release();
buff_ = other.buff_;
other.buff_ = 0;
}

return *this;
}

~BufferObject() {Release();}

void Release();
{
if(buff_)
glDeleteBuffers(1, &buff_);
}

//Other members.
};

There are various other techniques for making move-only RAII wrappers for OpenGL objects.

RAII wrapper for OpenGL objects

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

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).

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.

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

An OpenGLObject<VertexArrayObjectTraits> would hold a VAO.

OpenGL Blank Screen Within Class Procedure

The problem pointed out by Rabbid76, was that once the procedure got out of scope the destructors for the verex buffer and vertex buffer array got called which deleted the buffers causing the screen to be blank as the id's of the vertex buffer and vertex array were now invalid.

OpenGL + Object-Orientation?

I actually have a design similar to what you were discussing for a project of mine, only instead of using a global array I have a class that serves as the interface to the state machine and provides some functionality similar to the deprecated state stack.

Example binding state:

class FrameBufferBindState {
friend class eTB_RenderContext;
protected:
FrameBufferBindState (void) {
reset ();
}

void reset (void) {
bound = NULL;
swap_count = 0;
req_count = 0;
}

void bind (eTB_FrameBuffer* fbo);

void push (void);
void pop (void);

eTB_FrameBuffer* bound;
int swap_count;
int req_count;

std::stack <eTB_FrameBuffer *> stack;
};

I have a class called eTB_RenderContext that contains instances of classes like this for each fundamental class of object the engine can bind (note that some of these classes also contain bindings themselves, for instance program objects have shader objects bound to them).

I keep track of the number of times bind (...) was called (req_count) versus the number of times the binding actually had to be changed (swap_count) to measure batch efficiency. I also have a stack mechanism for bindings and states to make some algorithms easier.

Binding classes:

  VertexArrayBindState vertex_array_;
ProgramBindState program_;
FrameBufferBindState framebuffer_;
SamplerBindState* samplers_; // Minimum: 80 in GL4
TextureBindState* texture_images_; // Minimum: 80 in GL4

My OpenGL Vertex Array no longer works when abstracted out into a class

The problem is that you "call" sizeof(vertex_buffer) on this line:

GLCall(glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_buffer), vertex_buffer, GL_STATIC_DRAW));

vertex_buffer is a pointer to the array so if you "call" sizeof on it you will get the size of a float* and not the number of elements in the array times sizeof(float).

One way to solve this is passing the size of the array as a parameter.
Another thing you could do is send in the amount of elements in the array and multiply it by sizeof(float).

Managing external resources (similar to RAII in C++?)

If you want to handle a dynamic extent scope use UNWIND-PROTECT. If the program leaves that scope - normally or on error - the clean-up form is called. Just deallocate there or do whatever you want.

Sometimes Lisps use some kind of 'resource' mechanism to keep track of used and unused objects. Such a library provides fast allocation from a pool, allocating, initialization, deallocation, mapping of resourced object. CLIM defines a primitive version: Resources. CCL has a similar primitive version of it.

In a Lisp with 'timers', you can run a function periodically which looks for 'objects' to deallocate. In CCL you can use a thread which sleeps for a certain time using PROCESS-WAIT.

A bit about your coding style:

  • FORMAT can directly output to a stream, no PRINT needed
  • (defmethod foo ((e bar)) (if e ...)) : the IF makes no sense. e will always be an object.
  • many PROGN are not needed. Where needed, it could be removed when IF is replaced by WHEN.

c++ OpenGL Issue drawing mesh with vbo and vao stored within an object

Maybe that's a shot from the hip, but since you use glGenVertexArrays in the constructor of the class Mesh, I assume that you use glDeleteVertexArrays in the destructor of the class Mesh.

In this line:

mesh = Mesh(verticies, indicies);

the following happens:

You create a temporary Mesh object, which generates the vertex array object in its constructor. At this point all the data are valid and the vertex array object is generated (GPU). When you assign the object, the members of the temporary object are copied to the target object. Now 2 identical objects exists. Immediately after this is done, the temporary Mesh object gets destroyed and the vertex array object get deleted (glDeleteVertexArrays) by the destructor Mesh::~Mesh of the temporary object. At this point all the data are gone.

Put a breakpoint in the destrutor Mesh::~Mesh, then you can simply track the expiration.

Make the class not copyable and not copy constructable, but specify a move constructor and move operator.

class Mesh 
{
Mesh(const Mesh &) = delete;
Mesh & operator = (const Mesh &) = delete;

Mesh( Mesh && );
Mesh & operator = ( Mesh && );

....
};

See:

  • Move assignment operator
  • Move constructors
  • C++11 Tutorial: Introducing the Move Constructor and the Move Assignment Operator

Texture binding isn't working / C++ / OpenGL

OK, let's look at this:

Texture Texture::CreateTexture(std::string const& texPath) {

Texture tex(texPath);
return (tex);
}

I'm going to assume that this is a static function. So it creates a Texture object on the stack. And tex contains an OpenGL texture object. The function then returns this object.

By the rules of C++, the lifetime of tex is limited to the scope in which it is created. Namely, Texture::CreateTexture. Which means that, at the end of this function, tex will be destroyed by having its destructor invoked.

But since you returned tex, before that happens, tex will be used to initialize the return value of the function. That return value happens to be an object of type Texture, so the compiler will invoke Texture's copy constructor to initialize the return value.

So, right before tex is destroyed, there are two Texture objects: tex itself and the return value of type Texture that was copied from tex. So far, so good.

Now, tex is destroyed. Texture::~Texture calls glDestroyTexture on the texture object contained within it. That destroys the texture created in the constructor. Fine.

So... what happens now? Well, let's back up to the creation of the return value from CreateTexture. I said that it would invoke the copy constructor of Texture to construct it, passing tex as the object to copy from.

You did not post your complete code, but given the nature of the other code you've written, I'd bet that you didn't write a copy constructor for Texture. That's fine, because the compiler will make one for you.

Only that's not fine. Why? Because right before tex gets destroyed, there are two Texture objects. And both of them store the same OpenGL texture object name. How did that happen?

Because you copied the texture object from tex into the return value. That's what the compiler-generated copy constructor does: it copies everything in the class.

So when tex is destroyed, it is destroying the OpenGL texture it just returned.

Texture should not be a copyable class. It should be move-only, just like many resource-containing classes in C++.

OpenGL 3.3: GL_INVALID_OPERATION when calling glBindBuffer

It the constructor of the calss OpenGLMesh the object buffers are generated (glGenBuffers). In the destructor OpenGLMesh::~OpenGLMesh the object buffers are destroyed (glDeleteBuffers).

In the following line:

meshes.push_back(OpenGLMesh(renderer, 0, "./assets/dune_glitch.png"));

you push_back the OpenGLMesh to a std::vector. This means that a temporary OpenGLMesh object is generated, an the object bffers are generated in its constructor. At this point all the data are valid and the object buffers are generated (GPU). When calling std::vector::push_back, then a new OpenGLMesh object is is generated, in the std::vector. The object is constructed by the default copy constructor and gets a copy of all the members of the first OpenGLMesh object. Immediately after this is done, the temporary OpenGLMesh object gets destroyed and the object buffers get deleted (glDeleteBuffers) by the destructor OpenGLMesh::~OpenGLMesh of the temporary object. At this point all the data are gone.

See std::vector::push_back. Put a breakpoint in the destrutor OpenGLMesh::~OpenGLMesh, then you can simply track the expiration.

You should make the class not copyable and not copy constructable, but specify a move constructor and move operator.

class OpenGLMesh 
{
OpenGLMesh(const OpenGLMesh &) = delete;
OpenGLMesh & operator = (const OpenGLMesh &) = delete;

OpenGLMesh( OpenGLMesh && );
OpenGLMesh & operator = ( OpenGLMesh && );

....
};

You can quickly fix this behavior for debug reasons, by replacing

meshes.push_back(OpenGLMesh(renderer, 0, "./assets/dune_glitch.png"));

by

meshes.emplace_back(renderer, 0, "./assets/dune_glitch.png");

(see std::vector::emplace_back)



For the implementation of a move constructor and move operator see:

  • Move assignment operator
  • Move constructors
  • C++11 Tutorial: Introducing the Move Constructor and the Move Assignment Operator

Using OpenGL functions in object constructor without context loaded

Change vertices and mesh to be (smart) pointers and construct the objects in main() after you secure a GL context.

If you don't want to change your existing code (. to -> for member access) you can call them verticesPtr and meshPtr and create local references (Mesh& mesh = *meshPtr;) at the top of the functions where you reference them.



Related Topics



Leave a reply



Submit