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 GLuint
s, 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 ...))
: theIF
makes no sense.e
will always be an object.- many
PROGN
are not needed. Where needed, it could be removed whenIF
is replaced byWHEN
.
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
Where Is C Not a Subset of C++
Undefined Reference to Static Constexpr Char[]
Is There a Difference Between Foo(Void) and Foo() in C++ or C
Why Isn't Vector≪Bool≫ a Stl Container
Cout ≪≪ Order of Call to Functions It Prints
How to Find the Location of the Executable in C
How to Use Queryperformancecounter
Why Can't the Template Argument Be Deduced When It Is Used as Template Parameter to Another Template
A Confusing Detail About the Most Vexing Parse
What Does the Question Mark Character ('') Mean in C++
Installing Opencv 2.4.3 in Visual C++ 2010 Express
C++ Static Polymorphism (Crtp) and Using Typedefs from Derived Classes