FBO + multisample and blitting

Just bought myself a new MBP with Leopard and a shiny 8600gt. Trying to port some SDL/GLEW-based code but my app doesnt’t seem to find the fbo multisample and blitting functions… Weird. I googled and got the (hopefully wrong) impression that these functions are not exposed in the current OS/driver. Please tell me it’s not true!

They are supported on ATI Radeon X1k and Radeon HD 2k cards on 10.5.2 + LGU1, but not on NVidia. http://homepage.mac.com/arekkusu/bugs/GLInfo.html

Thanks for your answer. Sad that it had to be that way though… sob . So we’ll have to hope for better drivers then.
I remember this annoying thing with GL and Apple since I last tried to make something useful on the mac a few years back. Then it was the support for FBOs in general and before that it was the extremely late support for vertex and fragment programs… I really thought those days where over now but I guess I was hoping in vain.
In this case it’s extra confusing since Apple was instrumental in the development of the actual extensions ( I mean, just read in the opengl registry…)

Apple has some nice extensions though!

Do you mean that there are some special Apple extensions for multisampled storage and blitting in a renderbuffer? Or that Apple has some nice special extensions for other things? I’m all ears, that sounds interesting…

When you read that it was representatives from Apple and nVidia that wrote the extensions you get a little surprised that the extensions only work on windows with nVidia cards and on mac with ATI-cards… ironic isn’t it?

Time to go back to software rendering :slight_smile:

I meant extensions like APPLE_object_purgeable and APPLE_vertex_array_object. It is indeed puzzling that framebuffer_blit only works with ATI, as it was Nvidia who implemented it first on Windows. Still, Mac is not really famous as a graphics development platform, so I don’ t think Apple puts such things on high priority.

Ok, sounds interesting, I guess I should take a look on those. The problem for me is that I’m trying to develop OpenGL software and that platform dependant specialties is boring and unnecessary overhead that I would preferably not have to deal with.

It’s not a big deal, I can make a special code path for mac that doesn’t allow multisampling. It’s just that I love the Mac as a platform and that it feels so incredibly irritating to not be able to make it “best of breed”

You’d need that path anyway, for hardware that doesn’t support multisampling. Like the GMA 950.

True, I guess it’s just me wanting simplicity that might not ever be achievable… Anyway, I’ll better stop whining and start producing instead. Thank you all :slight_smile:

If you want Apple to improve something, request it at bugreport.apple.com (even if it’s not technically a bug). Of course that won’t make it happen immediately, but maybe when the next big cat comes around…

Bug/driver issue reported to apple as bug. In the meantime, what do you think would be the fastest workaround to use instead of glBlitFramebufferEXT ? I can live without multisampling in the framebuffer at the moment (during dev) but I still need fast copies from renderbuffer to renderbuffer. Do I have to revert to having textures instead of renderbuffers and use glCopyTexSubImage2D ?

Just wanted to bump this item. I haven’t heard anything from Apple and the 1.5.4 update sadly didn’t fix the issue.

10.5.5 seems to have added the extensions I need. Time to test if they work…

<s>Ok, I have made a few tests now and blitting now works on nvidia as well. But there are still problems. I can not use multisampling in an FBO and blit to another FBO using float (GL_RGBA16F_ARB) as pixel format. It effectively kills the machine and creates garbled graphics on the screen (even outside the rendering window!) So there is some kind of really nasty bug in there… In addition it seems like float fbo -> float fbo blitting is messed up as well. Obviously this needs further research.

As for now:

float FBO --(rendered as texture on quad)–> regular framebuffer = ok!
MS float FBO --(blit)–> RGBA8 FBO (rendered as texture on quad) --> regular framebuffer = ok!

float FBO --(blit)–> float FBO --(rendered as texture on quad)–> regular framebuffer = kills machine!
MS float FBO --(blit)–> float FBO (rendered as texture on quad) --> regular framebuffer = Kills machine!

It a little hard to debug since it requires a restart :frowning:
</s>

I have to make more investigations in my own code before I draw any further conclusions. I’ll be back!

Ported my code to windows and it ran flawlessly. Getting more and more disappointed on Apple. :frowning:

So tell them.

Ok, bug submitted.

I have attached a minimal example of a program that fails on my nvidia-based macs. Feel free to test it and report back :slight_smile:

The same code has been sent to apple as part of bug report…


#include <stdlib.h>
#include <GLUT/glut.h>

GLuint fbo1;
GLuint fbo1ColorBuffer;
GLuint fbo1DepthBuffer;

GLuint fboFinal;
GLuint fboFinalDepthTexture;
GLuint fboFinalColorTexture;

GLuint textureTarget = GL_TEXTURE_2D;
GLuint bufferWidth = 512;
GLuint bufferHeight = 512;
GLfloat gNearPlane = 0.1f;
GLfloat gFarPlane = 100.0f;
GLuint gWindowWidth = 800;
GLuint gWindowHeight = 800;

GLenum internalType = GL_RGBA16F_ARB;

int useBlit = 1; 

/******************************************************************************/
void initFBO1(void)
{
    /* Generate names */
    glGenFramebuffersEXT(1, &fbo1);
	glGenRenderbuffersEXT(1, &fbo1ColorBuffer);
	glGenRenderbuffersEXT(1, &fbo1DepthBuffer);
	
	/* Bind FBO */
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo1);
    
	/* Bind depth buffer, store as renderbuffer, not texture */
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo1DepthBuffer);
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 
                             bufferWidth, bufferHeight);
	
    /* attach to the fbo */
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, 
								 GL_DEPTH_ATTACHMENT_EXT, 
								 GL_RENDERBUFFER_EXT, 
								 fbo1DepthBuffer);
	
    /* Color buffer */
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo1ColorBuffer);
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalType, 
                             bufferWidth, bufferHeight);
	
    /* attach to the fbo */
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, 
								 GL_COLOR_ATTACHMENT0_EXT, 
								 GL_RENDERBUFFER_EXT, 
								 fbo1ColorBuffer);
	
    /* Unbind FBO */
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    
}
/******************************************************************************/
void initFBOFinal(void)
{
    /* Generate names */
    glGenFramebuffersEXT(1, &fboFinal);
    glGenTextures(1, &fboFinalColorTexture);
    glGenTextures(1, &fboFinalDepthTexture);
	
	/* Bind FBO */
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboFinal);
    
	/* Bind depth buffer, store as texture */
	//glActiveTexture(GL_TEXTURE0);
	glBindTexture(textureTarget, fboFinalDepthTexture);
	glTexParameterf( textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
	glTexParameterf( textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
	glTexParameteri( textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri( textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexImage2D(textureTarget, 0, GL_DEPTH_COMPONENT, bufferWidth, 
				 bufferHeight, 0, GL_DEPTH_COMPONENT, GL_INT, NULL);
    
    glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, 
                               textureTarget, fboFinalDepthTexture, 0);
	
    
    /* Color buffer */
	//glActiveTexture(GL_TEXTURE1);
	glBindTexture(textureTarget, fboFinalColorTexture);
	glTexParameterf( textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
	glTexParameterf( textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
	glTexParameteri( textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri( textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexImage2D(textureTarget, 0, internalType, bufferWidth, bufferHeight, 
                 0, GL_RGBA, GL_FLOAT, NULL); 
    
	
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, 
							  textureTarget, fboFinalColorTexture, 0);
	
    /* Unbind FBO */
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    
}
/******************************************************************************/
void initGL(void)
{
    glEnable(textureTarget);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glCullFace(GL_BACK);
    glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glDepthMask(GL_TRUE);
}

void reshapeOrtho(unsigned int width, unsigned int height)
{	
	glViewport(0, 0, (GLint) width, (GLint) height);
	glMatrixMode(GL_PROJECTION);
    glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(0, width, 0, height);

	
}
/******************************************************************************/
void display(void)
{
    /* spin the teapot to see that all is alive */
    
    static float angle = 0.0f;
    angle += 0.1f;
    
    GLuint renderTarget = fboFinal;
    if (useBlit)
    {
        renderTarget = fbo1;
    }
    
    /* render to fboFinal */
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, renderTarget);
    glPushMatrix();
    glLoadIdentity();
    glTranslatef(0.0f, 0.0f, -2.0f);
    glRotatef(angle, 1.0f, 1.0f, 1.0f);
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glEnable(GL_COLOR_MATERIAL);
    glColor3f(1.0f, 1.0f, 1.0f);
    glDisable(textureTarget);
    glutSolidTeapot(1.0f);
    
    
    if (useBlit)
    {
        printf("Blitting
");
        glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, fbo1);
        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fboFinal);
        
        glBlitFramebufferEXT(0, 0, bufferWidth, bufferHeight,
                             0, 0, bufferWidth, bufferHeight,
                             GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT,
                             GL_NEAREST);
        
        glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
        glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
    }
    
    
    
    /* render to regular back frame buffer */
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    /* Draw fboFinal color to a texture on a quad */
    glPopMatrix();
    
    reshapeOrtho(gWindowWidth, gWindowHeight);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glTranslatef(32.0f, 32.0f, 0.0f);
    glColor3f(1.0f, 1.0f, 1.0f);
    glActiveTexture(GL_TEXTURE0);
    glEnable(textureTarget);
    glBindTexture(textureTarget, fboFinalColorTexture);
    
    glBegin(GL_QUADS);
    glTexCoord2f( 1.0f, 1.0f );
    glVertex3f( bufferWidth, bufferHeight, 0.0f);
    glTexCoord2f( 0.0f, 1.0f );
    glVertex3f(0, bufferHeight, 0.0f);			
    glTexCoord2f( 0.0f, 0.0f );
    glVertex3f(0,0, 0.0f);			
    glTexCoord2f( 1.0f, 0.0f );
    glVertex3f( bufferWidth,0, 0.0f);		
	glEnd();
    
    glPopMatrix();
    
    /* return to perspective */
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    
    
    glutSwapBuffers();
}
/******************************************************************************/
void reshape(int width, int height)
{
    printf("Reshape called
");
    gWindowWidth = width;
    gWindowHeight = height;
    if (width ==0)
		width = 1;
	GLfloat h = (GLfloat) height / (GLfloat) width;
	glViewport(0, 0, (GLint) width, (GLint) height);
	glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    GLfloat aspect = 1.0f/h;
	glLoadIdentity();
	gluPerspective(90, aspect, gNearPlane, gFarPlane);
    glMatrixMode(GL_MODELVIEW);
    
}
/******************************************************************************/
void idle(void)
{
    glutPostRedisplay();
}
/******************************************************************************/
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(800, 800);
    
    glutCreateWindow("GLUT FBO test");
    
    initGL();
    initFBO1();
    initFBOFinal();
    
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutIdleFunc(idle);
    
    glutMainLoop();
    return EXIT_SUCCESS;
}
/******************************************************************************/

Ok, seems like it is solved in 10.5.6.