PDA

View Full Version : Bug?- glGenBuffer reassigns IDs in vector<myclass>



bonsalty
08-31-2011, 03:20 PM
Im using VBO for fast rendering, now I have an issue I cant solve:
glGenBuffer is behaving idiotic when assigning IDs for the vector<MyClass> objects. I get always the same values for normalsID, verticesID and the colorsID. To see clear what Im talking about :
/////////////////////////////////////////////////
//---------MyClass------------
class MyClass{

Gluint verticesID,normalsID,colorsID;
vector <GLfloat> vertices_vbo,colors_vbo,normals_vbo; // holding vertice,color components...

public:
void Init(..);
.
.
void CreateVBO();
void Draw();
.
.
};

void MyClass::CreateVBO(){
.
.
// here I was pushing vertices into vertices_vbo before,etc its ok till this point...
glGenBuffersARB(1, &amp;verticesID);
glBindBufferARB( GL_ARRAY_BUFFER_ARB, verticesID);

glBufferDataARB( GL_ARRAY_BUFFER_ARB, vertices_vbo.size()*sizeof(GL_FLOAT), &amp;vertices_vbo[0], GL_STATIC_DRAW_ARB);

//--- and the same for colors and normals
.
.
}


// ----- In the main ! ---
void main(..){
.
.
// Creating openGL context
.
.
g_fVBOSupported = IsExtensionSupported( "GL_ARB_vertex_buffer_object" );
//g_fVBOSupported =FALSE;
if( g_fVBOSupported )
{
// Get Pointers To The GL Functions
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");
// Load Vertex Data Into The Graphics Card Memory

}
.
.
vector<MyClass> pipelines;

for (int i=0;i<10;i++){
pipelines.pushback(MyClass());

pipelines[i].Init(..);
pipelines[i].CreateVBO();

}


}// end main
//////////////////////////////////////////
What you expect is having 10 pipelines objects in VBO ready to
render. But what you get is 10 pipelines objects all the same color , vertices, normals. I checked with the Visual 2008 debugger,surprisingly I had different valid IDs for colorsID,verticesID,normalsID and this was ok, but in each object they were the same IDs. So the 10 pipelines drawn were actually only one: the last created VBO.

Before you ask:
I think the call routins are mapped correctly:
#define GL_ARRAY_BUFFER_ARB 0x8892
#define GL_STATIC_DRAW_ARB 0x88E4
typedef void (APIENTRY * PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer);
typedef void (APIENTRY * PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers);
typedef void (APIENTRY * PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers);
typedef void (APIENTRY * PFNGLBUFFERDATAARBPROC) (GLenum target, int size, const GLvoid *data, GLenum usage);

// VBO Extension Function Pointers
PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL; // VBO Name Generation Procedure
PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL; // VBO Bind Procedure
PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL; // VBO Data Loading Procedure
PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL; // VBO Deletion Procedure
BOOL g_fVBOSupported = FALSE;

When I push my objects into the vector the OpenGL context is ready to use.I have no access violations or so. I tried to run the program on different very modern computers all supporting OpenGL over 3,.0 like Intel Graphics HD, NVidia . The drivers are all updated, this cant be the problem. The platform is win7 64bit.
Im very unhappy with this and have to solve this in a few days, I would appreciate if you could give me some hints.
Thanks! K.T

marshats
08-31-2011, 07:51 PM
I cannot recreate your error on linux. With glew and glut as a test the following code gave reasonable results



#include <GL/glew.h>
#include <GL/glut.h>

#include <vector>
#include <iostream>
#include <cstdlib>
#include <cstdio>

using namespace std;

class MyClass{

GLuint verticesID,normalsID,colorsID;
vector <GLfloat> vertices_vbo,colors_vbo,normals_vbo; // holding vertice,color components...

public:
void Init();
void CreateVBO();
void Draw();
};

void MyClass::Init() {
// of course I don't have your vertices, colors, normal etc so
// you would have to place different values depending on index
// i. this was just to have something to buffer to the GPU to
// follow your post.
vertices_vbo.push_back(-1.0); vertices_vbo.push_back( 1.0); vertices_vbo.push_back( 1.0);
colors_vbo.push_back(-1.0); colors_vbo.push_back( 1.0); colors_vbo.push_back( 1.0);
normals_vbo.push_back(-1.0); normals_vbo.push_back( 1.0); normals_vbo.push_back( 1.0);
};

void MyClass::CreateVBO() {
glGenBuffers(1, &amp;verticesID);
glBindBuffer( GL_ARRAY_BUFFER, verticesID);
glBufferData( GL_ARRAY_BUFFER, vertices_vbo.size()*sizeof(GLfloat), &amp;vertices_vbo[0], GL_STATIC_DRAW);

glGenBuffers(1, &amp;normalsID);
glBindBuffer( GL_ARRAY_BUFFER, normalsID);
glBufferData( GL_ARRAY_BUFFER, normals_vbo.size()*sizeof(GLfloat), &amp;normals_vbo[0], GL_STATIC_DRAW);

glGenBuffers(1, &amp;colorsID);
glBindBuffer( GL_ARRAY_BUFFER, colorsID);
glBufferData( GL_ARRAY_BUFFER, colors_vbo.size()*sizeof(GLfloat), &amp;colors_vbo[0], GL_STATIC_DRAW);
};

void MyClass::Draw() {cout << verticesID << "\t" << normalsID << "\t" << colorsID << endl;};

int main(int argc, char **argv)
{
glutInit(&amp;argc, argv);

glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);
glutCreateWindow("test");

glewInit();

printf("OpenGL %s, GLSL %s\n",
glGetString(GL_VERSION),
glGetString(GL_SHADING_LANGUAGE_VERSION));

vector<MyClass> pipelines;

for (int i=0;i<10;i++){
pipelines.push_back(MyClass());
pipelines[i].Init();
pipelines[i].CreateVBO();
}

for (int i=0;i<10;i++){
pipelines[i].Draw();
}

}

gave the following output


OpenGL 4.1.0 NVIDIA 280.13, GLSL 4.10 NVIDIA via Cg compiler
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18
19 20 21
22 23 24
25 26 27
28 29 30


Maybe you could try modifying the above for windows and see if you get different results or try the code as is by linking with glew and glut.

I don't know if you had a typo or are using std::vector which does not have a method named "pushback()". Rather std:vector has a method "push_back()"

In your actual draw() command are you remembering to bind each of the specific buffer IDs before your glDraw command is executed? Expect your MyClass:Draw() function to look like


// the gl2+ way
glBindBuffer( GL_ARRAY_BUFFER, verticesID);
glVertexAttribPointer(index_vertex...)

glBindBuffer( GL_ARRAY_BUFFER, normalsID);
glVertexAttribPointer(index_normal...)

glBindBuffer( GL_ARRAY_BUFFER, colorsID);
glVertexAttribPointer(index_color...)

glDraw*(...)

or


//this deprecated fixed pipeline way

glBindBuffer(GL_ARRAY_BUFFER, verticesID);
glVertexPointer(...);

glBindBuffer(GL_ARRAY_BUFFER, normalsID);
glNormalPointer(...);

glBindBuffer(GL_ARRAY_BUFFER, colorsID);
glColorPointer(...);

glDraw*();

for EACH object. If not then that is a likely cause why you have different IDs but only the last BOUND ones are actually drawn.

bonsalty
09-01-2011, 05:21 AM
Thanks for your response.
a)Well, I didnt mention , Im using freeglut for windowing if this counts.
b)Yes I bind those before drawing. I think my code is just right,
but Windows is messing up something, there is also no drawing because the binding happens in the initialising section called by main:
//////////////////////////////////////////////////////////////
// -------------------main section--------
int main(int argc, char **argv)
{
glutInit(&amp;argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);

glutInitWindowPosition(100, 100);
glutInitWindowSize(1000, 600);
glutCreateWindow("LeoAPI Sample");
//glutCreateSubWindow( 0,100, 100, 1000, 600 );

glutKeyboardFunc(Key);
glutDisplayFunc(Display);
glutIdleFunc(Idle);
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE,GLUT_ACT ION_GLUTMAINLOOP_RETURNS);// GLUT_ACTION_CONTINUE_EXECUTION);

if (!Initialize()) // this is where our VBO creation occur {
return -1;
}
gluPerspective(75.0f,800/600.0,1.01f,100.0f);
glutMainLoop();
.
.
}
//-------------------------------
vector<MyClass> pipelines;
bool Initialize()
{
// initialize and connect the application to Leonar3Do System Software
if (!leoInitialize()) return false;
lglInitialize();
lgl_ctx = lglCreateContext(NULL, NULL);
if (!lgl_ctx) return false;
.
.
g_fVBOSupported = IsExtensionSupported( "GL_ARB_vertex_buffer_object" );
//g_fVBOSupported =FALSE;
if( g_fVBOSupported )
{
// Get Pointers To The GL Functions
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");
// Load Vertex Data Into The Graphics Card Memory

}
.
.

for (int i=0;i<10;i++){
pipelines.pushback(MyClass());
pipelines[i].Init(..);
pipelines[i].CreateVBO();

}
return true;
}

/////////////////////////////////////////

c) But here is the interesting part:
If I create the vector elements first

for (int i=0;i<10;i++) pipelines.push_back(MyClass());
for (int i=0;i<10;i++){
pipelines[i].Init();
pipelines[i].CreateVBO();
}

then GlGenBuffer creates the right IDs as for you. But this behavior is strange and Im not sure its safe in all cases(Note:MyClass constructor is empty, does nothing with OpenGL).

Is there anything we know about glGenBuffers, might be here some threading problem ??

marshats
09-01-2011, 07:45 AM
freeglut is perfectly good. freeglut and windows/linux have been very reliable in my experience. If you are using the lastest snapshot you can replace "#include <GL/glut.h>" with "#include <GL/freeglut.h>" and let freeglut do the context creation for you with the two new commands glutInitContextVersion and glutInitContextProfile. Here is what works on my Linux box



#include <GL/glew.h>
#include <GL/freeglut.h>

#include <vector>
#include <iostream>
#include <cstdlib>
#include <cstdio>

using namespace std;

class MyClass{

GLuint verticesID,normalsID,colorsID;
vector <GLfloat> vertices_vbo,colors_vbo,normals_vbo; // holding vertice,color components...

public:
void Init();
void CreateVBO();
void Draw();
};

void MyClass::Init() {
// of course I don't have your vertices, colors, normal etc so
// you would have to place different values depending on index
// i. this was just to have something to buffer to the GPU to
// follow your post.
vertices_vbo.push_back(-1.0); vertices_vbo.push_back( 1.0); vertices_vbo.push_back( 1.0);
colors_vbo.push_back(-1.0); colors_vbo.push_back( 1.0); colors_vbo.push_back( 1.0);
normals_vbo.push_back(-1.0); normals_vbo.push_back( 1.0); normals_vbo.push_back( 1.0);
};

void MyClass::CreateVBO() {
glGenBuffers(1, &amp;verticesID);
glBindBuffer( GL_ARRAY_BUFFER, verticesID);
glBufferData( GL_ARRAY_BUFFER, vertices_vbo.size()*sizeof(GLfloat), &amp;vertices_vbo[0], GL_STATIC_DRAW);

glGenBuffers(1, &amp;normalsID);
glBindBuffer( GL_ARRAY_BUFFER, normalsID);
glBufferData( GL_ARRAY_BUFFER, normals_vbo.size()*sizeof(GLfloat), &amp;normals_vbo[0], GL_STATIC_DRAW);

glGenBuffers(1, &amp;colorsID);
glBindBuffer( GL_ARRAY_BUFFER, colorsID);
glBufferData( GL_ARRAY_BUFFER, colors_vbo.size()*sizeof(GLfloat), &amp;colors_vbo[0], GL_STATIC_DRAW);
};

void MyClass::Draw() {cout << verticesID << "\t" << normalsID << "\t" << colorsID << endl;};

int main(int argc, char **argv)
{
glutInit(&amp;argc, argv);
glutInitContextVersion(4, 1);
glutInitContextProfile(GLUT_CORE_PROFILE);
glutCreateWindow("test");

glewInit();

printf("OpenGL %s, GLSL %s\n",
glGetString(GL_VERSION),
glGetString(GL_SHADING_LANGUAGE_VERSION));

vector<MyClass> pipelines;

for (int i=0;i<10;i++){
pipelines.push_back(MyClass());
pipelines[i].Init();
pipelines[i].CreateVBO();
}

for (int i=0;i<10;i++){
pipelines[i].Draw();
}

}

I suggest this to try and eliminate any possible discrepancies when you do context creation.

Are you doing something with multiple threads on VBO creation? It is not at all obvious from your snippet of code.

I'll ask it again: do you really have "pushback" as in


for (int i=0;i<10;i++){
pipelines.pushback(MyClass());
pipelines[i].Init(..);
pipelines[i].CreateVBO();
}
where it fails but works with "push_back" as in


for (int i=0;i<10;i++)
pipelines.push_back(MyClass());
for (int i=0;i<10;i++){
pipelines[i].Init();
pipelines[i].CreateVBO();
}

bonsalty
09-01-2011, 08:04 AM
No sorry, pipelines.pushback was type mismatch in my code its ok:
push_back.
I replaced everything to glew, its not working either, the same issue.
I dont use any threads.

marshats
09-01-2011, 08:30 AM
Is it possible to publish a bare minimum code based on glut/glew that shows the problem for you? I could then compile it and run it on linux to confirm if the problem persists.

bonsalty
09-01-2011, 09:17 AM
PROBLEM SOLVED:
Ok I think I found the problem, its nothing with VBO its a fun with vector memory allocation. The folowing thing happens in this code:

for (int i=0;i<10;i++){
pipelines.pushback(MyClass());
pipelines[i].Init(..);
pipelines[i].CreateVBO();
}

where - pipelines.pushback(MyClass()); - creates a temporary MyClass object. Immediately after the call a destructor is called.
I had set glDeletBuffer in the destructor function if the vertexID is nonzero:

MyClass::~MyClass(){
if (vertexID>0)glDeleteBuffers(...)
}

(the vertexID is set to 0 in the constructor so this should be never called by a temporary object)

after the first loop pipelines.pushback(MyClass()); is
called again. This time strange thing happens because of unknown reason :the previously created and VBO initialized pipeline object's destructor is called for the vector, however the vector element doesnt get destructed , it remains properly! So the vector is filled up with MyClass objects,
but at each cycle it receives a fake destructor call.
The destructor function then erases my VBO since the IDs are not zero.

marshats
09-01-2011, 11:37 AM
Yes, now when I put a destructor in the above code the problem shows up. A fix may be to use c++ new/delete as in the following working example. The vector contains pointers to MyClass objects so the destructor is not called until explicitly requested (as in onExitCleanup).



#include <GL/glew.h>
#include <GL/glut.h>

#include <vector>
#include <iostream>
#include <cstdlib>
#include <cstdio>

using namespace std;

class MyClass{

GLuint verticesID,normalsID,colorsID;
vector <GLfloat> vertices_vbo,colors_vbo,normals_vbo; // holding vertice,color components...

public:
~MyClass();
void Init(int i);
void CreateVBO();
void Draw();
};

MyClass::~MyClass(){
cout << "MyClass::~MyClass() " << verticesID << "\t" << normalsID << "\t" << colorsID << endl;
if (verticesID>0) glDeleteBuffers(1,&amp;verticesID);
if ( normalsID>0) glDeleteBuffers(1,&amp;normalsID);
if ( colorsID>0) glDeleteBuffers(1,&amp;colorsID);
}

void MyClass::Init(int i) {
float R = float(i+1)/10.0;

vertices_vbo.push_back( 0); vertices_vbo.push_back( 0); vertices_vbo.push_back( 0);
vertices_vbo.push_back( R); vertices_vbo.push_back( 0); vertices_vbo.push_back( 0);
vertices_vbo.push_back( 0); vertices_vbo.push_back( R); vertices_vbo.push_back( 0);

colors_vbo.push_back(1.0); colors_vbo.push_back(0.0); colors_vbo.push_back(0.0);
colors_vbo.push_back(0.0); colors_vbo.push_back(1.0); colors_vbo.push_back(0.0);
colors_vbo.push_back(0.0); colors_vbo.push_back(0.0); colors_vbo.push_back(1.0);

normals_vbo.push_back(0.0); normals_vbo.push_back( 0.0); normals_vbo.push_back( 1.0);
normals_vbo.push_back(0.0); normals_vbo.push_back( 0.0); normals_vbo.push_back( 1.0);
normals_vbo.push_back(0.0); normals_vbo.push_back( 0.0); normals_vbo.push_back( 1.0);
}

void MyClass::CreateVBO() {
glGenBuffers(1, &amp;verticesID);
glBindBuffer( GL_ARRAY_BUFFER, verticesID);
glBufferData( GL_ARRAY_BUFFER, vertices_vbo.size()*sizeof(GLfloat), &amp;vertices_vbo[0], GL_STATIC_DRAW);

glGenBuffers(1, &amp;normalsID);
glBindBuffer( GL_ARRAY_BUFFER, normalsID);
glBufferData( GL_ARRAY_BUFFER, normals_vbo.size()*sizeof(GLfloat), &amp;normals_vbo[0], GL_STATIC_DRAW);

glGenBuffers(1, &amp;colorsID);
glBindBuffer( GL_ARRAY_BUFFER, colorsID);
glBufferData( GL_ARRAY_BUFFER, colors_vbo.size()*sizeof(GLfloat), &amp;colors_vbo[0], GL_STATIC_DRAW);
}

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

void MyClass::Draw() {
glBindBuffer(GL_ARRAY_BUFFER, verticesID);
glVertexPointer(3,GL_FLOAT,3*sizeof(GLfloat),BUFFE R_OFFSET(0));

glBindBuffer(GL_ARRAY_BUFFER, normalsID);
glNormalPointer(GL_FLOAT,3*sizeof(GLfloat),BUFFER_ OFFSET(0));

glBindBuffer(GL_ARRAY_BUFFER, colorsID);
glColorPointer(3,GL_FLOAT,3*sizeof(GLfloat),BUFFER _OFFSET(0));

glDrawArrays(GL_TRIANGLES,0,3);
}

vector<MyClass*> pipelines;

void displayCB();
void idleCB();
void reshapeCB(int w, int h);
void keyboardCB(unsigned char key, int x, int y);
void onExitCleanup(void);

int main(int argc, char **argv)
{
glutInit(&amp;argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("test");
glutDisplayFunc(displayCB);
glutIdleFunc(idleCB);
glutReshapeFunc(reshapeCB);
glutKeyboardFunc(keyboardCB);

glewInit();

printf("OpenGL %s, GLSL %s\n",
glGetString(GL_VERSION),
glGetString(GL_SHADING_LANGUAGE_VERSION));

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_CULL_FACE);

for (int i=0;i<10;i++){
pipelines.push_back(new MyClass());
pipelines[i]->Init(i);
pipelines[i]->CreateVBO();
}

atexit(onExitCleanup);

glutMainLoop();

return 0;
}

void displayCB()
{
glClear(GL_COLOR_BUFFER_BIT);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
for(vector<MyClass*>::iterator it = pipelines.begin(); it != pipelines.end(); it++)
{
(*it)->Draw();
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);

glutSwapBuffers();
}

void onExitCleanup(void)
{
for(vector<MyClass*>::iterator it = pipelines.begin(); it != pipelines.end(); it++)
{
if (*it) delete *it;
}
}

void reshapeCB(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void idleCB()
{
glutPostRedisplay();
}

void keyboardCB(unsigned char key, int x, int y)
{
switch(key)
{
case 27: // ESCAPE
exit(0);
break;
default:
break;
}
}

bonsalty
09-03-2011, 04:43 AM
Thanks, I didnt post the solution , but this is exactly what I did and it fixes the problem , using vector< class pointer > instead of just vector <class>. Besides this is a c++ vector pitfall its related with the fact that temporary constructed objects are getting hooked with the created one.