fbo, pbo, vbo & render_to_vertex_array

I’ve been asking about my problems with render_to_vertex_array in another thread of this forum, but since my implementation has changed considerably (and so did the problem), I thought I should reformulate the question.

I’m doing the following:

  1. Render a quad with a texture containing the geometry to an offscreen color buffer using a fbo (from the EXT_framebuffer_object extension).
  2. Read back the content of the color buffer to a buffer object on the graphics card (from the EXT_pixel_buffer_object extension) calling glReadPixels (see code below).
  3. Bind this buffer and use it as source for rendering a vertex array (probably better called a vbo in this case since the geometry is already allocated on the graphics card).

The problem is that when I bind the buffer and try to render the vertex array with glDrawElements, the application crashes with the error “Access violation reading location 0x00000000”. :frowning:

I’m using the ForceWare 76.41 driver for my GeForce 6600 GT, obviously on Windows.

Here is some code to clarify what I’m actually doing:
(I’ve updated the code to change the unbind and bind the buffers more explicitly)

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

GLuint *fboTexIDs;
GLuint *fboIDs;
GLuint *vboIDs;

void initialize()
{
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDisable(GL_CULL_FACE);
    glDisable(GL_LIGHTING);
    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glClearColor( 1.0, 1.0, 1.0, 1.0 );
    glClear( GL_COLOR_BUFFER_BIT );

    // initialize compile and link the shader program/-s
    initializeGLSL();

     // allocate space for the target (floating point) texture to be rendered to 
    fboTexIDs = new GLuint[1];
    glGenTextures( 1, fboTexIDs );
    glBindTexture( GL_TEXTURE_2D, fboTexIDs[i] );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB32F_ARB, textureSize, textureSize, 0, GL_RGB, GL_FLOAT, 0 );

    // Create and bind the framebuffer and add the (target) textures (fbo)
    fboIDs = new GLuint[1];
    glGenFramebuffersEXT( 1, fboIDs );
    glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fboIDs[0] );
    glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fboTexIDs[0], 0 );


    // Create the texture containing the geometry
    glEnable( GL_TEXTURE_2D );
    textureIDs = new GLuint[1];
    glGenTextures( 1, textureIDs );
    glBindTexture( GL_TEXTURE_2D, textureIDs[0] );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
    // Upload the content of the texture
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB32F_ARB, textureSize, textureSize, 
                  0, GL_RGB, GL_FLOAT, (float *)textureData );
    glBindTexture( GL_TEXTURE_2D, 0 );
    glDisable( GL_TEXTURE_2D );

    // Generate the VBO for rendering the FBO content as vertex array
    vboIDs = new GLuint[1];
    glGenBuffers( 1, vboIDs );
    glBindBuffer( GL_PIXEL_PACK_BUFFER_EXT, vboIDs[0] );
    glBufferData( GL_PIXEL_PACK_BUFFER_EXT, textureSize*textureSize*3*sizeof(float), NULL, GL_STATIC_COPY );
    glBindBuffer( GL_PIXEL_PACK_BUFFER_EXT, 0 );
}

void render()
{
    int viewport[4];
    // FIRST PASS
    if( firstPass ) {
        firstPass = false;
        // save the viewport so that it can be restored later
        glGetIntegerv( GL_VIEWPORT, viewport );
        glViewport( 0, 0, textureSize, textureSize ); // set viewport to size of texture

        // draw to texture
        glMatrixMode( GL_PROJECTION );
        glPushMatrix();
        glLoadIdentity();
        gluOrtho2D( 0, textureSize, 0, textureSize );
        glMatrixMode( GL_MODELVIEW );
        glPushMatrix();
        glLoadIdentity();
        glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, fboIDs[0] );
        GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT };
        glDrawBuffersARB(1,buffers);    // enable attachment for drawing

        firstPassSM->useProgram();      // enable the shader program
        
        // render 2D textured quad
        glEnable( GL_TEXTURE_2D );
        glBindTexture( GL_TEXTURE_2D, textureIDs[0] );
        glBegin( GL_QUADS );
            glTexCoord2f( 0.0,  0.0); glVertex2f( 0.0,  0.0 );
            glTexCoord2f( 1.0,  0.0); glVertex2f( textureSize,  0.0 );
            glTexCoord2f( 1.0,  1.0); glVertex2f( textureSize,  textureSize );
            glTexCoord2f( 0.0,  1.0); glVertex2f( 0.0,  textureSize );
        glEnd();
        glBindTexture( GL_TEXTURE_2D, 0 );
        glDisable( GL_TEXTURE_2D );

        firstPassSM->useNoProgram();    // disable the shader program
        
        glMatrixMode( GL_PROJECTION );
        glPopMatrix();
        glMatrixMode( GL_MODELVIEW );
        glPopMatrix();

        // reset the viewport
        glViewport( viewport[0], viewport[1], viewport[2], viewport[3] );

        // bind the vertex array source to be BUFFER_OFFSET(0)
        glBindBuffer( GL_PIXEL_PACK_BUFFER_EXT, vboIDs[0] );
        glReadBuffer( GL_COLOR_ATTACHMENT0_EXT );
        glReadPixels( 0, 0, textureSize, textureSize, GL_RGB32F_ARB, GL_FLOAT, BUFFER_OFFSET(0) );
        glBindBuffer( GL_PIXEL_PACK_BUFFER_EXT, 0 );
   
        // Change the binding point of the buffer object to the vertex array binding point
        glBindBuffer( GL_ARRAY_BUFFER, vboIDs[0] );
        glEnableClientState( GL_VERTEX_ARRAY );
    
        glInterleavedArrays( GL_V3F, 0, BUFFER_OFFSET(0) );
    
        glDisableClientState( GL_VERTEX_ARRAY );
        glBindBuffer( GL_ARRAY_BUFFER, 0 );
    }

    // SECOND PASS
    glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
    glBindBuffer( GL_PIXEL_PACK_BUFFER_EXT, 0 );

    // Render vertex array
    glBindBuffer( GL_ARRAY_BUFFER, vboIDs[0] );
    glEnableClientState( GL_VERTEX_ARRAY );
    // Render triangle strips with indexed vertices
    glClearColor( 0.0, 0.0, 0.0, 1.0 );
    glClear( GL_COLOR_BUFFER_BIT );

    glTranslated( 0.0, 0.0, -1.0 );
    glColor3d( 0.7, 0.7, 0.8 );
    for( int stripNr = 0; stripNr < isoSurface->getNumOfStrips(); stripNr++ ) {
        glDrawElements( GL_TRIANGLE_STRIP, getStripNumOfVerts(stripNr),
                        GL_UNSIGNED_INT, getStripIndices(stripNr) );
    }
    glDisableClientState( GL_VERTEX_ARRAY );
    glBindBuffer( GL_ARRAY_BUFFER, 0 );
}

I know it’s a bit long to be posted, but I didn’t know how to leave out some part and still make it understandable :rolleyes:

I’m quite sure the FBO is working fine, because I’ve tried to use its content as a texture and render it on screen and it looks just as it should… but the last part is still unsuccessful.

Can anyone tell me what I’m doing wrong? Could this be a problem of the beta driver or am I missing the point?

Thanks in advance for any help and for reading this long post :wink:

Originally posted by schaaade:
[b]

    // Generate the VBO for rendering the FBO content as vertex array
    vboIDs = new GLuint[1];
    glGenBuffers( 1, vboIDs );
    glBindBuffer( GL_PIXEL_PACK_BUFFER_EXT, vboIDs[0] );
    glBufferData( GL_PIXEL_PACK_BUFFER_EXT, textureSize*textureSize*3*sizeof(float), NULL, GL_STATIC_DRAW );
    glBindBuffer( GL_PIXEL_PACK_BUFFER_EXT, 0 );

[/b]
Possibly try GL_ARRAY_BUFFER instead of GL_PIXEL_PACK_BUFFER_EXT?
Also, you might want GL_STREAM_COPY instead of GL_STATIC_DRAW, but that shouldn’t make it crash.

Thanks for your fast reply 3B and for your suggestions.
However, I think in this case that wouldn’t help because I want the content of the fbo to be reinterpreted as the source for a vertex array on board, this is without being copied back to main memory and forth again to video memory. As far as I understand, this is only possible making use of the pixel_buffer_object extension that defines the PIXEL_PACK_BUFFER target. According to the extension specs.:

When a buffer object is bound to the PIXEL_PACK_BUFFER target, commands such as ReadPixels write their data into a buffer object.
And this is exactly what I need, my buffer to be written with the content of the fbo by doing ReadPixels. But it seems I can’t make use of this buffer. I guess the question is whether I’m doing something wrong that prevents ReadPixels from working or if I’m not binding the created buffer in a proper way… Has anyone experience with ReadPixels from a buffer generated as PIXEL_PACK_BUFFER target?

About GL_STREAM_COPY and GL_STATIC_DRAW, since my scene is static, I only have to upload the geometry the first time, then if everything works, the buffer shouldn’t be modified during the following frames. That’s why I thought GL_STATIC_DRAW would fit better to my problem. But anyway, I agree this shouldn’t make the application crash.

I guess that in

 
    // SECOND PASS
    glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
    glBindBuffer( GL_PIXEL_PACK_BUFFER_EXT, vboIDs[0] );
 

You should replace vboIDs[0] with NULL, as I don’t know if the spec allows binding the same buffer to several targets at the same time.

edit: the same after you read the data

Does glgetError say anything?

I meant just do the BufferData() using the VBO instead of the PBO, not dropping PBO completely. Looks like the PBO examples do it the same way you did though, so that should work…
Only other suggestions are to check for GL errors, and make sure getStripIndices(stripNr) never returns 0…

I’ve tried both suggestions, but still no success.
I’ve updated the code in my original post, so that it reflexes the current status (explicitly bind to NULL the vbo before binding it again).

I’ve also checked on glErrors and glReadPixels produces one indeed. Directly after the call to glReadPixels, glGetError returns 1, but it doesn’t seem to be any of the GLenum errors I know and check for (GL_INVALID_ENUM, GL_INVALID_VALUE, GL_INVALID_OPERATION, GL_STACK_OVERFLOW, GL_STACK_UNDERFLOW, GL_OUT_OF_MEMORY).

I’ve also made a minor change on the usage flag of the buffer object in glBufferData, replacing GL_STATIC_DRAW with GL_STATIC_COPY. After reviewing the spec I think this should improve the performance in my case because the data is not coming from the application, but from GL, and just as I said before, only once during initialization. In any case this should be only a performance problem not related to whether the whole thing works or not.

3B, I’m still not sure if I get you right. Do you mean something like this?

// Generate the VBO for rendering the FBO content as vertex array
vboIDs = new GLuint[1];
glGenBuffers( 1, vboIDs );
glBindBuffer( GL_ARRAY_BUFFER, vboIDs[0] );
glBufferData( GL_ARRAY_BUFFER, textureSize*textureSize*3*sizeof(float), NULL, GL_STATIC_COPY );
glBindBuffer( GL_ARRAY_BUFFER, 0 );

and then using GL_PIXEL_PACK_BUFFER_EXT for glReadPixels just as in the code above?

I’m not sure what this actually does because then I wouldn’t set GL_PIXEL_PACK_BUFFER_EXT as target for the buffer object and, according to the spec, this is necessary in order to use it as source for any gl command getting a pointer as a parameter (am I wrong about this? if so, please correct me)

Even so, I’ve tried it anyway and to my surprise, it didn’t make any difference. I’m still getting an error (glGetError() returns 1) right after the glReadPixels call. Does anyone understand this?

Ok, I’ve found the reason for the glError, but it’s still not working.
If I replace GL_RGB32F_ARB with GL_RGB in glReadPixels, the error is gone (glGetError returns GL_NO_ERROR).
However, the application still crashes when I try to render the vertex array using glDrawElements (Access violation reading location 0x00000000).
Could anyone explain the implications of this? Which would be the format/precision of the data if I manage to actually use it?

By the way, I forgot to mention this in my previeous post, but I’m sure the indices for the vertex array are OK (getStripIndices(stripNr)). I’ve tried to render the whole thing with a normal VBO (uploading the vertices from main memory) with the same procedure and it works fine.

Lookin forward to new suggestions and thanks again for your help so far :wink:

Yeah, that was what I meant about the GL_ARRAY_BUFFER stuff, but like I said before, it should work either way…

Tried running your original code, and with GL_RGB32F_ARB in the ReadBuffer call, I get a segfault, with GL_RGB it works fine on my 6800 GT with 76.10 drivers.

Changes I made to run it :
added global firstPass, initialize to true
added #define textureSize 128
added global GLuint textureIDs;
added textureData
change ‘i’ to 0 in “glBindTexture( GL_TEXTURE_2D, fboTexIDs[i] );”
replaced "for( int stripNr = 0; …) " loop with glDrawArrays(GL_POINTS,0,textureSize
textureSize);

Following code works. Note that it isn’t FBO related, it is just a example of “render to vertex array”.
First it create some “wave” texture, and it render this texture into pbuffer, do readback into PBO and reuse PBO as VBO.

// GLCALL macro is only for debuging...  
#include "StdAfx.h"
#include ".\Test_PBO_VBO.h"

#define TEXX 512
#define TEXY 512

CPVEngine::CPVEngine(void)
{
}

CPVEngine::~CPVEngine(void)
{
}


bool CPVEngine::Init()
{
	__super::Init();

// make a wave texture
	char* data = new char[TEXX*TEXY*4];
	{
		int x,y;
		for (y=0; y<TEXY; y++)
		{
			for (x=0; x<TEXX; x++)
			{
				float tx = (x-TEXX/2)/10.0f;
				float ty = (y-TEXY/2)/10.0f;
				float d = tx*tx + ty*ty;

				data[y*TEXX*4 + x*4 + 0] = x/2;
				data[y*TEXX*4 + x*4 + 1] = ((1.0+cos(sqrt(d)))) * 32;
				data[y*TEXX*4 + x*4 + 2] = y/2;
				data[y*TEXX*4 + x*4 + 3] = 255;
			}
		}

		glGenTextures(1, &texture);
		glBindTexture(GL_TEXTURE_2D, texture);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXX, TEXX, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
		GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
		GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
	}

	delete [] data;

	m_pFaces = new FACE[TEXX*TEXX*2];
	{
		int fx, fy, a = 0;

		for (fy = 0; fy < (TEXX - 1); fy ++)
		{
			for (fx = 0; fx < (TEXX - 1); fx ++)
			{
				a = (fx + fy * (TEXX - 1));
				int p[4];
				p[0] = fy*TEXX + fx;
				p[1] = p[0] + 1;
				p[2] = p[0] + TEXX;
				p[3] = p[2] + 1;

				int strana = ( fx+fy)%2;
				if (strana == 0)	
				{
					m_pFaces[2*a + 0][0] = p[2];
					m_pFaces[2*a + 0][1] = p[3];
					m_pFaces[2*a + 0][2] = p[0];

					m_pFaces[2*a + 1][0] = p[3];
					m_pFaces[2*a + 1][1] = p[1];
					m_pFaces[2*a + 1][2] = p[0];
				}
				else				// sporedna dijagonala
				{
					m_pFaces[2*a + 0][0] = p[2];
					m_pFaces[2*a + 0][1] = p[1];
					m_pFaces[2*a + 0][2] = p[0];

					m_pFaces[2*a + 1][0] = p[2];
					m_pFaces[2*a + 1][1] = p[3];
					m_pFaces[2*a + 1][2] = p[1];
				}
			}
		}

	}

	glGenBuffers(1, &TRI_buffer);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, TRI_buffer);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, TEXX*TEXX*2*sizeof(FACE), m_pFaces, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	


	rtt = new RenderTexture("rgba");
	rtt->Initialize(TEXX,TEXY);
	rtt->BeginCapture();
	{
		glMatrixMode(GL_PROJECTION);	glLoadIdentity();
		glMatrixMode(GL_MODELVIEW); 	glLoadIdentity();
		GLCALL(glFrontFace(GL_CCW));
		GLCALL(glEnable(GL_CULL_FACE));

		GLCALL(glShadeModel(GL_SMOOTH));
		GLCALL(glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST));
	}
	rtt->EndCapture();

	glGenBuffers(1, &PV_buffer);
	glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, PV_buffer);
	glBufferData(GL_PIXEL_PACK_BUFFER_EXT, TEXX*TEXY*4*sizeof(float), NULL, GL_DYNAMIC_DRAW);
	glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, 0);

	RenderRTT();

	return true;
}

// called before app exit
void CPVEngine::Done()
{
	delete rtt;
	glDeleteTextures(1, &texture);
	glDeleteBuffers(1, &PV_buffer);
	glDeleteBuffers(1, &TRI_buffer);

	__super::Done();
}



void CPVEngine::Update(float time_sec, float delta_time)
{
	__super::Update(time_sec, delta_time);
	m_time = time_sec;
}

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

void CPVEngine::RenderRTT()
{
	rtt->BeginCapture();
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		GLCALL(glEnable(GL_TEXTURE_2D));
		GLCALL(glBindTexture(GL_TEXTURE_2D, texture));
		//glColor4f(1,1,1,1);

		glColor4f(1.0,(1.0+cos(m_time))/2.0,1.0,1);

		glBegin(GL_QUADS);
		{
			glTexCoord2f(0,0); glVertex3f(-1,-1,0);
			glTexCoord2f(1,0); glVertex3f( 1,-1,0);
			glTexCoord2f(1,1); glVertex3f( 1, 1,0);
			glTexCoord2f(0,1); glVertex3f(-1, 1,0);
		}
		glEnd();

		GLCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, PV_buffer));
		GLCALL(glReadPixels(0,0,TEXX, TEXY, GL_BGRA, GL_FLOAT, BUFFER_OFFSET(0)));
		GLCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, 0));
	}
	rtt->EndCapture();
}

void CPVEngine::RenderVBO()
{
	GLCALL(glBindBuffer(GL_ARRAY_BUFFER, PV_buffer));

	GLCALL(glEnableClientState(GL_VERTEX_ARRAY));
	GLCALL(glVertexPointer(4, GL_FLOAT, 0, BUFFER_OFFSET(0)));
	GLCALL(glEnableClientState(GL_COLOR_ARRAY));
	GLCALL(glColorPointer(4, GL_FLOAT, 0, BUFFER_OFFSET(0)));

	glColor4f(1,1,1,1);
	glPushMatrix();
	glScalef(20,20,20);
	glTranslatef(-0.5, 0, -0.5);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, TRI_buffer);
	glDrawElements(GL_TRIANGLES, 2*3*TEXX*TEXX, GL_UNSIGNED_INT, 0);
	glPopMatrix();
	GLCALL(glDisableClientState(GL_VERTEX_ARRAY));
	GLCALL(glDisableClientState(GL_COLOR_ARRAY));

	GLCALL(glBindBuffer(GL_ARRAY_BUFFER, 0));
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

}

void CPVEngine::Render()
{
	BeginRender();

	RenderRTT();
	RenderVBO();

	EndRender();
}

yooyo

OK, It´s working now. It seems like the format (GL_RGB32F initially and GL_RGB in the working version) was the problem here. After I corrected it I still was having some other problem not present in the code from my original post (just some double initialization issue for the vbo). Anyway, thanks a lot for your help, guys.

Care to post a little demo? I’d be interested…

Tnx

Dirk

Originally posted by dirk:
Care to post a little demo? I’d be interested…

I’m quite under pressure right now with my work and probably won’t have the time to put a demo together till in about two weeks’ time, sorry :frowning:
But it will be pretty much as the code I’ve posted, just adding the necessary data for the texture/geometry. So, if you’re in a hurry you can give it a try and post if you have any difficulties.
Hope that helps :wink: