about glReadPixels

I am not sure this can be considered an advanced question, but it certainly is not easy for me to understand it, therefore I will consider it advanced.

I am trying to use OpenGL to calculate quickly some distance fields of sets of points. The theory is the same as the one that allows using graphic hardware to draw Voronoi diagrams of a set of points, by placing cones whose tips are positioned at each point coordinates.

I am doing something similar.
First I initialize the pipeline:

  
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0,0,w(),h());
glOrtho(0,w(),0,h(),1.5*w(),4.5*w());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glClearColor(1,1,1,1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFlush();

Then I send down the pipeline a cone for each of the points:

int numOfPoints = gui->app->getNumberOfPointsInStroke(s);
for(int p=0; p<numOfPoints; p++) {
	Point3D* current = gui->app->getPointInStroke(p,s);
	glPushMatrix();
	glColor3f(0.0f,1.0f,0.5f);
	glTranslatef( current->x, current->y, -4*w());
	glutSolidCone( 2*w(), 2*w(), 21, 20);
	glPopMatrix();
	glColor3f(1.0f,0.0f,0.0f);
}

Then I read the depth buffer in:

glReadPixels( 0, 0, WIN_WIDTH, WIN_HEIGHT, GL_DEPTH_COMPONENT, GL_FLOAT, gui->app->dfstrokes->getBitmap());

Then I transform the depth value into real world z coordinates:

for(int y=0; y<WIN_HEIGHT; y++) {
	for(int x=0; x<WIN_WIDTH; x++) {
		GLfloat depth_comp = gui->app->dfstrokes->getPixel(x,y);
		GLfloat clip_z = (depth_comp - 0.5f) * 2.0f;
		GLfloat world_z = 1.5*w()*clip_z + 3*w() - 2*w();
		gui->app->dfstrokes->setPixel(x,y,world_z);
	}
}

So far so good? Well yes, because this works just fine and I do get the correct values and everything.

Now here comes the question, finally: how do I do this more than one time?? Can I send again new geometry down the pipeline with a new set of point coordnates and create another distance field?
No user interaction has to happen in between the creation of these distance fields. I simply need to do this TWICE, but when I tried it I see my application crashes…
Do I have to WAIT for something to happen? Do I have to stop the application until I am sure that the Z buffer has been read by the glReadPixel command? How do I do that?

– begin edit ---------------------------
Let me add something more. The code I have described is located inside the draw() function. This function has a structure like:

void DisplayWindow::draw() {
    if(gui->app->appMode == CREATE_MODE) {
    ........
    ........
    }
    else if(gui->app->appMode == PROCESS_MODE) {
    ........
    ........
    }
    else if(gui->app->appMode == PROBE_MODE) {
    ........
    ........
    }
}

Specifically the code I have shown is located in the PROCESS_MODE section, called by the main application by saying redraw().

This is just a little more info just to let anyone who was interested in helping to get a little better idea of what I am working with…
– end edit -----------------------------

Please help me!

Francesco

Originally posted by franz1999:
Now here comes the question, finally: how do I do this more than one time?? Can I send again new geometry down the pipeline with a new set of point coordnates and create another distance field?
Yes, absolutely.
Because you’re depth testing, you probably want to glClear(GL_DEPTH_BUFFER_BIT) to erase the previous contents. But that does not explain the crash.

No user interaction has to happen in between the creation of these distance fields. I simply need to do this TWICE, but when I tried it I see my application crashes…
It shouldn’t …

Do I have to WAIT for something to happen? Do I have to stop the application until I am sure that the Z buffer has been read by the glReadPixel command? How do I do that?
You don’t have to do anything, really. ReadPixels will handle all of the required synchronization. I.e. it will not start the transfer before all pending rendering commands have finished, and it won’t return before the data is fully transferred.

Questions:
1)Are you absolutely sure that the memory region pointed to by gui->app->dfstrokes->getBitmap() is large enough, under any circumstances?
You need four bytes per pixel, obviously. If you handle window resizing, whenever WIN_WIDTH or WIN_HEIGHT change, the allocated memory for the bitmap must be reallocated to match.

2)Does the crash also happen if you simply call glReadPixels twice in a row instead of going through your draw function twice? You might be hitting thread issues here, and this is the easiest way to find out.

3)What’s your graphics card? Are your drivers up to date?

Originally posted by zeckensack:
1)Are you absolutely sure that the memory region pointed to by gui->app->dfstrokes->getBitmap() is large enough, under any circumstances?
You need four bytes per pixel, obviously. If you handle window resizing, whenever WIN_WIDTH or WIN_HEIGHT change, the allocated memory for the bitmap must be reallocated to match.

2)Does the crash also happen if you simply call glReadPixels twice in a row instead of going through your draw function twice? You might be hitting thread issues here, and this is the easiest way to find out.

3)What’s your graphics card? Are your drivers up to date?[/QB]

  1. No resizing is allowed and the array is big enough to contain the data the first time.

  2. I tried:

		printf("reading depth components...
");
		glReadPixels( 0, 0, WIN_WIDTH, WIN_HEIGHT, GL_DEPTH_COMPONENT, GL_FLOAT, gui->app->dfstrokes->getBitmap());
		glFinish();
		glReadPixels( 0, 0, WIN_WIDTH, WIN_HEIGHT, GL_DEPTH_COMPONENT, GL_FLOAT, gui->app->dfstrokes->getBitmap());
		glFinish();
		printf("finished
");

and it doesn’t crash

  1. Radeon 9000 I believe (DELL Laptop) I have the original drivers installed

Thanks for your input… Now I need to find a way to do inside draw() (pseudocode):

  • send cones
  • read the depth buffer
  • clear depth buffer
  • send new cones at diff positions
  • read the depth buffer

Traditional glReadPixels is a synchronous, blocking operation. When glReadPixels returns, the read operation has been completed and it’s immediately safe to read the “GLvoid *pixels” buffer you passed in as the final argument to glReadPixels.

So there’s no need to call glFinish(). In truth, calling glFinish() is almost always a mistake – it causes a synchronization between the CPU and the GPU that is typically unnecessary.

If you want asynchronous readback behavior (where glReadPixels initiates a transfer and returns immediately), you can use the Pixel Buffer Object extension to accomplish this.

Also: I’d debug this by moving the second glReadPixels call gradually up in the call order. Since you can call glReadPixels twice in a row without causing an error, there must be some point in your operation list where the crash first happens. For instance, if the crash happens after the glClear, but not before, then that would be quite enlightening (probably an ATI driver bug).

I’d wager the problem here is with the event handling system. The event that eventually leads to the second glReadPixels is somehow handled by a thread that doesn’t have the GL context.

If it is a problem with making OpenGL calls with threads, I would suggest running the GLIntercept ( http://glintercept.nutty.org ) tool with the FullDebug profile. (I am a bit biased however :slight_smile: )

Then check the gliLog.txt for any errors.