Here's a FBO "benchmark" I wrote a while ago. set_fbo_size()
has a creation sequence that Works For Me(TM).
/////////////////////////////////////////////////////////////////////////////
// INCLUDES /////////////////////////////////////////////////////////////////
#include <GL/glew.h>
#include <GL/glut.h>
#include <iostream>
#include <iomanip>
#include <sstream>
using namespace std;
/////////////////////////////////////////////////////////////////////////////
// CLASSES //////////////////////////////////////////////////////////////////
// http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
class ExpAvg
{
public:
ExpAvg(float initial, unsigned int time_periods) : avg(initial), alpha(2.0f / ( time_periods + 1 )) {}
void Update(float nextval) { avg = alpha*nextval + (1.0f-alpha)*avg; }
float Get() { return avg; }
private:
float avg;
float alpha;
};
class gl2D
{
public:
gl2D() {
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity();
gluOrtho2D(0, viewport[2], 0, viewport[3]);
glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
}
~gl2D() {
glMatrixMode(GL_PROJECTION); glPopMatrix();
glMatrixMode(GL_MODELVIEW); glPopMatrix();
}
};
/////////////////////////////////////////////////////////////////////////////
// GLOBALS //////////////////////////////////////////////////////////////////
int screen_width = 1024;
int screen_height = 768;
int mouse_x, mouse_y;
bool mouse_left, mouse_right;
float camera_angle_x = 45;
float camera_angle_y = 45;
float camera_distance = 0;
int texture_width, texture_height;
GLuint tex, fbo, rbo; // object IDs
/////////////////////////////////////////////////////////////////////////////
// UTILITIES ////////////////////////////////////////////////////////////////
bool get_fbo_status()
{
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
switch(status) {
case GL_FRAMEBUFFER_COMPLETE_EXT:
cout << "Framebuffer complete." << endl; return true;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
cerr << "[ERROR] Attachment is NOT complete." << endl; return false;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
cerr << "[ERROR] No image is attached to FBO." << endl; return false;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
cerr << "[ERROR] Attached images have different dimensions." << endl; return false;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
cerr << "[ERROR] Color attached images have different internal formats." << endl; return false;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
cerr << "[ERROR] Draw buffer." << endl; return false;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
cerr << "[ERROR] Read buffer." << endl; return false;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
cerr << "[ERROR] Unsupported by FBO implementation." << endl; return false;
default:
cerr << "[ERROR] Unknow error." << endl; return false;
}
}
void gl_print(const char *str, int x, int y, void *font)
{
glPushAttrib(GL_ENABLE_BIT);
glDisable(GL_LIGHTING); // need to disable lighting for proper text color
glDisable(GL_TEXTURE_2D);
glRasterPos2i(x, y); // place text position
while(*str) glutBitmapCharacter(font, *str++);
glPopAttrib();
}
void textured_cube()
{
glBegin(GL_QUADS);
glColor4f(1, 1, 1, 1);
// face v0-v1-v2-v3
glNormal3f(0,0,1);
glTexCoord2f(1, 1); glVertex3f(1,1,1);
glTexCoord2f(0, 1); glVertex3f(-1,1,1);
glTexCoord2f(0, 0); glVertex3f(-1,-1,1);
glTexCoord2f(1, 0); glVertex3f(1,-1,1);
// face v0-v3-v4-v5
glNormal3f(1,0,0);
glTexCoord2f(0, 1); glVertex3f(1,1,1);
glTexCoord2f(0, 0); glVertex3f(1,-1,1);
glTexCoord2f(1, 0); glVertex3f(1,-1,-1);
glTexCoord2f(1, 1); glVertex3f(1,1,-1);
// face v0-v5-v6-v1
glNormal3f(0,1,0);
glTexCoord2f(1, 0); glVertex3f(1,1,1);
glTexCoord2f(1, 1); glVertex3f(1,1,-1);
glTexCoord2f(0, 1); glVertex3f(-1,1,-1);
glTexCoord2f(0, 0); glVertex3f(-1,1,1);
// face v1-v6-v7-v2
glNormal3f(-1,0,0);
glTexCoord2f(1, 1); glVertex3f(-1,1,1);
glTexCoord2f(0, 1); glVertex3f(-1,1,-1);
glTexCoord2f(0, 0); glVertex3f(-1,-1,-1);
glTexCoord2f(1, 0); glVertex3f(-1,-1,1);
// face v7-v4-v3-v2
glNormal3f(0,-1,0);
glTexCoord2f(0, 0); glVertex3f(-1,-1,-1);
glTexCoord2f(1, 0); glVertex3f(1,-1,-1);
glTexCoord2f(1, 1); glVertex3f(1,-1,1);
glTexCoord2f(0, 1); glVertex3f(-1,-1,1);
// face v4-v7-v6-v5
glNormal3f(0,0,-1);
glTexCoord2f(0, 0); glVertex3f(1,-1,-1);
glTexCoord2f(1, 0); glVertex3f(-1,-1,-1);
glTexCoord2f(1, 1); glVertex3f(-1,1,-1);
glTexCoord2f(0, 1); glVertex3f(1,1,-1);
glEnd();
}
bool set_fbo_size(int width, int height)
{
int max_size;
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &max_size);
if(width > max_size) return false;
if(height > max_size) return false;
texture_width = width; texture_height = height;
// create FBO
if(fbo) glDeleteFramebuffersEXT(1, &fbo);
glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
// create and attach a new texture as the FBO's color buffer
if(tex) glDeleteTextures(1, &tex);
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);
// create and attach a new depth buffer to currently bound FBO
if(rbo) glDeleteRenderbuffersEXT(1, &rbo);
glGenRenderbuffersEXT(1, &rbo);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rbo);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // unbind fbo
if( !get_fbo_status() ) exit(1);
return true;
}
/////////////////////////////////////////////////////////////////////////////
// GLUT CALLBACKS ///////////////////////////////////////////////////////////
void CB_Idle()
{
glutPostRedisplay();
}
void CB_Reshape(int width, int height)
{
screen_width = width;
screen_height = height;
glViewport(0, 0, (GLsizei)width, (GLsizei)height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0f, (float)(width)/height, 1.0f, 1000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void CB_Mouse(int button, int state, int x, int y)
{
mouse_x = x; mouse_y = y;
if(button == GLUT_LEFT_BUTTON)
mouse_left = (state == GLUT_DOWN);
else if(button == GLUT_RIGHT_BUTTON)
mouse_right = (state == GLUT_DOWN);
}
void CB_Motion(int x, int y)
{
if(mouse_left) {
camera_angle_y += (x - mouse_x);
camera_angle_x += (y - mouse_y);
mouse_x = x; mouse_y = y;
}
if(mouse_right) {
camera_distance += (y - mouse_y) * 0.2f;
mouse_y = y;
}
}
void CB_Keyboard(unsigned char key, int x, int y)
{
static int drawMode = 0;
static int tex_size = 0;
bool ret = false;
switch(key) {
case 27: // ESCAPE
exit(0);
break;
case ' ':
while(!ret) {
tex_size = (tex_size+1) % 7;
switch(tex_size) {
case 0: ret = set_fbo_size(128,128); break;
case 1: ret = set_fbo_size(256,256); break;
case 2: ret = set_fbo_size(512,512); break;
case 3: ret = set_fbo_size(1024,1024); break;
case 4: ret = set_fbo_size(2048,2048); break;
case 5: ret = set_fbo_size(4096,4096); break;
case 6: ret = set_fbo_size(8192,8192); break;
default: ; break;
}
}
break;
default:
break;
}
glutPostRedisplay();
}
void CB_Init()
{
GLenum err = glewInit();
if(GLEW_OK != err) {
cerr << "Error: " << glewGetErrorString(err) << endl;
exit(1);
}
if(!GLEW_EXT_framebuffer_object) {
cerr << "Requires EXT_framebuffer_object" << endl;
exit(1);
}
tex = fbo = rbo = 0;
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glEnable(GL_CULL_FACE);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glClearColor(0, 0, 0, 0);
GLfloat lightKa[] = {.2f, .2f, .2f, 1.0f}; // ambient light
GLfloat lightKd[] = {.7f, .7f, .7f, 1.0f}; // diffuse light
GLfloat lightKs[] = {1, 1, 1, 1}; // specular light
glLightfv(GL_LIGHT0, GL_AMBIENT, lightKa);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightKd);
glLightfv(GL_LIGHT0, GL_SPECULAR, lightKs);
float lightPos[4] = {0, 0, 20, 1}; // positional light
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
glEnable(GL_LIGHT0);
set_fbo_size(128, 128);
}
void CB_Exit()
{
glDeleteTextures(1, &tex);
glDeleteFramebuffersEXT(1, &fbo);
glDeleteRenderbuffersEXT(1, &rbo);
}
void CB_Display()
{
static ExpAvg ft_fbo(0, 19);
static ExpAvg ft_overall(0, 19);
int before = glutGet(GLUT_ELAPSED_TIME);
// compute rotation angle
const float ANGLE_SPEED = 90; // degree/s
float angle = ANGLE_SPEED * (glutGet(GLUT_ELAPSED_TIME) / 1000.0f);
// render using fbo /////////////////////////////////////////////
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); // bind fbo
// adjust viewport and projection matrix to texture dimension
glViewport(0, 0, texture_width, texture_height);
glMatrixMode(GL_PROJECTION); glLoadIdentity();
gluPerspective(60.0f, (float)(texture_width)/texture_height, 1.0f, 100.0f);
glMatrixMode(GL_MODELVIEW); glLoadIdentity();
// clear buffer
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef(0,0,-3);
glPushMatrix();
glRotatef(angle*0.5f, 1, 0, 0);
glRotatef(angle, 0, 1, 0);
glRotatef(angle*0.7f, 0, 0, 1);
// set up teapot colors
float shininess = 15.0f;
float diffuseColor[3] = {0.929524f, 0.796542f, 0.178823f};
float specularColor[4] = {1.00000f, 0.980392f, 0.549020f, 1.0f};
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess); // range 0 ~ 128
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularColor);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glColor3fv(diffuseColor);
glBindTexture(GL_TEXTURE_2D, 0);
glFrontFace(GL_CW);
glutSolidTeapot(1.0);
glFrontFace(GL_CCW);
glPopMatrix();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // unbind fbo
glFinish();
ft_fbo.Update( (float)(glutGet(GLUT_ELAPSED_TIME) - before) );
// normal rendering ///////////////////////////////////
// back to normal viewport and projection matrix
glViewport(0, 0, screen_width, screen_height);
glMatrixMode(GL_PROJECTION); glLoadIdentity();
gluPerspective(60.0f, (float)(screen_width)/screen_height, 1.0f, 100.0f);
glMatrixMode(GL_MODELVIEW); glLoadIdentity();
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef(0,0,-4);
glPushMatrix();
glTranslatef(0, 0, camera_distance);
glRotatef(camera_angle_x, 1, 0, 0);
glRotatef(camera_angle_y, 0, 1, 0);
// draw a cube with the dynamic texture
glBindTexture(GL_TEXTURE_2D, tex);
textured_cube();
glPopMatrix();
{
gl2D two_dee; // set 2D mode
glDisable(GL_DEPTH_TEST);
stringstream ss;
glColor3f(1,1,0);
ss << fixed << setprecision(3);
int pos = 1;
ss.str(""); ss << "Texture size: " << texture_width << "x" << texture_height;
gl_print(ss.str().c_str(), 10, screen_height - (pos++ * 20), GLUT_BITMAP_8_BY_13);
ss.str(""); ss << "Overall frame time: " << ft_overall.Get() << " ms";
gl_print(ss.str().c_str(), 10, screen_height - (pos++ * 20), GLUT_BITMAP_8_BY_13);
ss.str(""); ss << " FBO frame time: " << ft_fbo.Get() << " ms";
gl_print(ss.str().c_str(), 10, screen_height - (pos++ * 20), GLUT_BITMAP_8_BY_13);
ss.str(""); ss << "Press space to change texture size; mouse moves/zooms cube";
gl_print(ss.str().c_str(), 10, 10, GLUT_BITMAP_8_BY_13);
glEnable(GL_DEPTH_TEST);
}
glutSwapBuffers();
ft_overall.Update( (float)(glutGet(GLUT_ELAPSED_TIME) - before) );
}
/////////////////////////////////////////////////////////////////////////////
// MAIN /////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(screen_width, screen_height);
glutInitWindowPosition(100, 100);
glutCreateWindow("FBO Test");
glutDisplayFunc(CB_Display);
glutIdleFunc(CB_Idle);
glutReshapeFunc(CB_Reshape);
glutKeyboardFunc(CB_Keyboard);
glutMouseFunc(CB_Mouse);
glutMotionFunc(CB_Motion);
atexit(CB_Exit);
CB_Init();
glutMainLoop();
return 0;
}
fbTexID
? Where do you usetex
? – Branton