GL Framebuffer Completeness & Blitting Issues
Hi,
I have the following piece of test code that I made after encountering a blitting issue on the application I was working on.
There are 3 issues that are illustrated by this test code:
1) glCheckFramebufferStatus gives reversed status on the read and draw buffer. If the buffers are incomplete, it returns 0x8CDB for READ and 0x8CDC for DRAW which is flipped according to the definition in the header file.
2) I am unable to make the FBO complete unless I attach a color attachment, even though I called glDrawBuffer(GL_NONE), glReadBuffer(GL_NONE), which according to my understanding, is supposed to do away with this requirement for completeness.
3) On my ATI Radeon HD 5770 Mac Pro, even if I use the dummy attachment, the blitting fails on a D24S8 but succeeds D32. "Fail" here means failing a source and destination comparison verification check. I have done this check using bytes, ints and floats and the results are consistent. When I tested this on my MacBook Pro with a Nvidia 560m, there were no errors for either D32 or D24S8.
Is there anyone out there who can shed some light on this?
Are these bugs or did I miss something obvious? I will like to do depth-buffer-only blitting without using a dummy attachment if possible.
If these are known issues, what is the recommended solution?
In this test code, I am trying to blit a depth-only FBO to another depth-only FBO. Depending on the parameters passed in, the test can be performed with D24S8 or D32, and with/without a dummy color attachment.
To ensure that OpenGL is initialized properly, the rest of the test application displays a colored rotating cube when run.
- (void) RunTest_DepthBufferBlit:(bool)isPackedStencil useDummyAttachment:(bool)useDummy
{
int err = glGetError(); //Clears out previous errors
NSLog(@"DepthBufferBlit %@ %@", isPackedStencil ? @"DepthStencil" : @"Depth", useDummy ? @"With Dummy Color Attachment" : @"");
/*
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB
#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC
#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
*/
int width = 256;
int height= 256;
// Generates 2 framebuffers, one as READ_FRAMEBUFFER and the other as DRAW_FRAMEBFFER.
GLuint framebuffers[2] = {0, 0};
glGenFramebuffers(2, framebuffers);
err = glGetError();
if (err != 0)
NSLog(@"DepthBufferBlit - Gen FBO Fail: %d", err);
GLuint texName[3] = { 0, 0, 0 };
// Create source, destination and dummy buffer
{
glGenTextures (3, texName);
err = glGetError();
if (err != 0)
NSLog(@"DepthBufferBlit - Gen Textures Fail: %d", err);
unsigned char* buffer = (unsigned char*)malloc(4 * width * height);
memset(buffer, 0, 4 * width * height);
float* depthBuffer = (float*)malloc(4 * width * height);
for (int i = 0; i < width*height; i++)
{
if (i%2 == 0)
depthBuffer[i] = 2.0f;
if (i%5 == 0)
depthBuffer[i] = 5.0f;
}
for (int i = 0; i < 3; i++)
{
glBindTexture (GL_TEXTURE_RECTANGLE, texName[i]);
err = glGetError();
if (err != 0)
NSLog(@"DepthBufferBlit - Bind Texture %d Fail: %d", texName[i], err);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GLint internalFormat = isPackedStencil ? GL_DEPTH_STENCIL : GL_DEPTH_COMPONENT32;
if (i == 0)
{
glTexImage2D(GL_TEXTURE_RECTANGLE, 0, internalFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, depthBuffer);
}
else if (i == 2)
{
glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA , width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
}
else
{
glTexImage2D(GL_TEXTURE_RECTANGLE, 0, internalFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, buffer);
}
err = glGetError();
if (err != 0)
NSLog(@"DepthBufferBlit - Create Texture %d Fail: %d", texName[i], err);
}
free(buffer);
free(depthBuffer);
}
GLint attachment = isPackedStencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[0]);
if (useDummy)
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, texName[2], 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, attachment, GL_TEXTURE_RECTANGLE, texName[0], 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[1]);
if (useDummy)
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, texName[2], 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_RECTANGLE, texName[1], 0);
glReadBuffer(GL_NONE);
glDrawBuffer(GL_NONE);
err = glGetError();
if (err != 0)
NSLog(@"DepthBufferBlit - glFramebufferTexture2D: %d", err);
int dfbo = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); //Apple Bug: Flipped status
int rfbo = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); //Apple Bug: Flipped status
if (dfbo != GL_FRAMEBUFFER_COMPLETE || rfbo != GL_FRAMEBUFFER_COMPLETE)
{
NSLog(@"DepthBufferBlit FBO {%i,%i} Fail - Read = %x, Write = %x\n", width, height, rfbo, dfbo);
// Restore the framebuffer binding.
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Deletes the 2 framebuffers.
glDeleteFramebuffers(2, framebuffers);
// Deletes the 3 textures
glDeleteTextures(3, texName);
return;
}
if (isPackedStencil)
{
glBlitFramebuffer(0, 0, width, height,
0, 0, width, height, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
}
else {
glBlitFramebuffer(0, 0, width, height,
0, 0, width, height, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
}
err = glGetError();
if (err != 0)
NSLog(@"DepthBufferBlit - Blitting: %d", err);
int errCount = 0;
// Check texture contents
// 256 x 256 = 65536
float tex[65536] = { 0 };
float tex2[65536] = { 0 };
glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[0]);
glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, tex);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[1]);
glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, tex2);
for (int i = 0; i < 65536; i++)
{
float val = abs(tex[i] - tex2[i]);
if (val > 0.000001)
errCount++;
}
if (errCount > 0)
NSLog(@"DepthBufferBlit - Differences = %d", errCount);
else {
NSLog(@"DepthBufferBlit %@ Completed Successfully.", isPackedStencil ? @"DepthStencil" : @"Depth");
}
// Restore the framebuffer binding.
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Deletes the 2 framebuffers.
glDeleteFramebuffers(2, framebuffers);
// Deletes the 3 textures
glDeleteTextures(3, texName);
return;
}
- (void) RunSingleShotTests
{
/* glBlitFrameBuffer */
[self RunTest_DepthBufferBlit:NO useDummyAttachment:NO];NSLog(@" ");
[self RunTest_DepthBufferBlit:NO useDummyAttachment:YES];NSLog(@" ");
[self RunTest_DepthBufferBlit:YES useDummyAttachment:NO];NSLog(@" ");
[self RunTest_DepthBufferBlit:YES useDummyAttachment:YES];
}
Results after running the above sample on 10.7.3 with both CoreProfile and LegacyProfile (with a few minor changes to make it GL 2.1 compatible):
2012-04-27 15:52:51.871 CocoaGLCoreProfile[56222:403] DepthBufferBlit Depth (D32)
2012-04-27 15:52:51.874 CocoaGLCoreProfile[56222:403] DepthBufferBlit FBO {256,256} Fail - Read = 8cdc, Write = 8cdb
2012-04-27 15:52:51.874 CocoaGLCoreProfile[56222:403]
2012-04-27 15:52:51.875 CocoaGLCoreProfile[56222:403] DepthBufferBlit Depth (D32) With Dummy Color Attachment
2012-04-27 15:52:51.886 CocoaGLCoreProfile[56222:403] DepthBufferBlit Depth Completed Successfully.
2012-04-27 15:52:51.887 CocoaGLCoreProfile[56222:403]
2012-04-27 15:52:51.887 CocoaGLCoreProfile[56222:403] DepthBufferBlit DepthStencil (D24S8)
2012-04-27 15:52:51.888 CocoaGLCoreProfile[56222:403] DepthBufferBlit FBO {256,256} Fail - Read = 8cdc, Write = 8cdb
2012-04-27 15:52:51.889 CocoaGLCoreProfile[56222:403]
2012-04-27 15:52:51.889 CocoaGLCoreProfile[56222:403] DepthBufferBlit DepthStencil (D24S8) With Dummy Color Attachment
2012-04-27 15:52:51.892 CocoaGLCoreProfile[56222:403] DepthBufferBlit - Differences = 9832
Thanks in advance.
Mac Pro, Mac OS X (10.7.3), ATI Radeon HD 5770