Using WGL_ARB_pbuffer and glGetTexImage

Hello all!
I’m trying to render the scene to a 512x512 pbuffer texture, read it into system RAM using glGetTexImage and then write to a .TGA just to test things out.
The problem is that glGetTexImage seems to always return image that’s completely black (ie. all zeros), as if nothing was actually rendered to the texture.

My pbuffer (it’s size 512x512) is set up and running properly without errors, so the problem must lie in somewhere else.
I also registered the pbuffer texture via glGenTextures(1, &pbufferTexID) in pbuffer init function.

Below is the code I’m using. renderTarget is set when the screenshot key is pressed:

if(pbufferReady && renderTarget == RENDER_TARGET_PBUFFER)
{
  int flag;

  // Make sure it's not lost due to a display mode change
  gle.wglQueryPbufferARB(hPbuffer, WGL_PBUFFER_LOST_ARB, &flag);

  if(flag != 0)
  {
    gConsole.Printf("ERROR: lost pbuffer!
");
    renderTarget = RENDER_TARGET_FRAMEBUFFER;
  }
  else
  {
    // Store the current contexts
    hOldDC = wglGetCurrentDC();
    hOldRC = wglGetCurrentContext();
    gle.wglMakeContextCurrentARB(hPbufferDC, hPbufferDC, hPbufferRC);

    glDrawBuffer(GL_FRONT);
    glReadBuffer(GL_FRONT);
    glViewport(0, 0, 512, 512);
  }
}

// Code to render the scene goes here
// ...

if(renderTarget == RENDER_TARGET_PBUFFER)
{
  float startTime = gGame.GetTime();
  byte  *data = new byte[512*512*4];

  glBindTexture(GL_TEXTURE_2D, pbufferTexID);
  gle.wglBindTexImageARB(hPbuffer, WGL_FRONT_LEFT_ARB);

  glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
  // data is just all zeros <--
  delete [] data;

  gle.wglReleaseTexImageARB(hPbuffer, WGL_FRONT_LEFT_ARB);

  // Restore framebuffer render target state
  gle.wglMakeContextCurrentARB(hOldDC, hOldDC, hOldRC);
  glDrawBuffer(GL_BACK);
  glReadBuffer(GL_FRONT);
  glViewport(0, 0, viewWidth, viewHeight);

  renderTarget = RENDER_TARGET_FRAMEBUFFER;
}

Is this the right way? Maybe I misunderstood how pbuffers work.
TIA.

Before I answer this, I make no guarantee I have the right answer.

In the part following

if(renderTarget == RENDER_TARGET_PBUFFER)

the context should be the main context, not the p-buffer context. You should create the texture object in the main context or apply resource sharing.

I don’t see why you are using glGetTexImage when you can use glReadPixels, and why you use a render able texture.

Another issue is that when you release the p-buffer, the contents become undefined. You have to re-render everything.

Ok, I tried what you suggested, but it still comes out as completely black image.

Also, what’s the difference between wglMakeContextCurrentARB() and wglMakeCurrent()? Almost all examples seem to use the latter only, but it doesn’t help me either.

I have ATI Radeon 9700 with newest drivers installed.

Here’s the new code:

void Render()
{
	if(renderTarget == RENDER_TARGET_SCREENSHOT)
		EnablePBuffer(512, 512);
	
        // Render the scene here
        // ...

        // Disable PBuffer after rendering to it once and set context back to framebuffer
	if(renderTarget == RENDER_TARGET_SCREENSHOT)
	{
		byte *data = new byte[512*512*3];

		DisablePBuffer(); // <-- set context back to the framebuffer
		renderTarget = RENDER_TARGET_FRAMEBUFFER;
		
                // Get the data
		glBindTexture(GL_TEXTURE_2D, pbufferTexID);
		gle.wglBindTexImageARB(hPbuffer, WGL_FRONT_LEFT_ARB);

		glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		
                // It's still all zeros
		delete [] data;
		gle.wglReleaseTexImageARB(hPbuffer, WGL_FRONT_LEFT_ARB);
	}
}

void EnablePBuffer(int width, int height)
{
	if(!pbufferReady)
		return;

	int flag = 0;
	gle.wglQueryPbufferARB(hPbuffer, WGL_PBUFFER_LOST_ARB, &flag);

	if(flag != 0)
	{
		gConsole.Printf("ERROR: lost P-buffer!
");
		return;
	}

	// Store the window contexts
	hOldDC = wglGetCurrentDC();
	hOldRC = wglGetCurrentContext();

	// set read/write context to pbuffer
	wglMakeCurrent(hPbufferDC, hPbufferRC);

	// draw & read the front buffer of pbuffer
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_DEPTH_TEST);
	glDrawBuffer(GL_FRONT);
	glReadBuffer(GL_FRONT);
	glViewport(0.0, 0.0, width, height);
}

void DisablePBuffer()
{
	if(!pbufferReady)
		return;

	wglMakeCurrent(hOldDC, hOldRC);

	glDrawBuffer(GL_BACK);
	glReadBuffer(GL_FRONT);
	glViewport(0, 0, viewWidth, viewHeight);
}

I do pretty much the same thing, except I check what wglBindTexImageARB and wglReleaseTexImageARB return. Use GetLastError on them if they return error.

Are you able to apply the texture to a quad or something to see if it doesn’t come out black?

Also, ATI drivers seem sensitive. Make sure all other parts of your code are error free. Make sure no other WGL function fail.

I figured out the reason for this behavior. The problem is that the textures used in the scene don’t seem to be shared between the framebuffer rendering context and the pbuffer rendering context. Thus, only the framebuffer context can use them initially.

Using wglShareLists() fixes the problem. In MSDN OpenGL reference they only talk about display lists, but it’s seems to share textures too.

FYI, MSDN was outdated a long time ago.