I'm writing a 2D OpenGL engine and I recently included a framebuffer wrapper class that I use to generate texture atlas at runtime. It worked pretty great and improved performance a lot, until I tested it on my laptop's integrated graphics.
Apparently, when running on integrated graphics glCheckFramebufferStatus returns zero. No, it doesn't return GL_FRAMEBUFFER_UNSUPPORTED. It returns ZERO. I really don't think that my integrated graphics don't support framebuffers since they're not that old. I also tried to delay the initialization of the framebuffer to do it inside the main GLUT loop but it didn't solve the problem. I also tried using framebufferEXT instead of framebuffer, but nothing changed. If I ignore the error it doesn't draw anything when I attempt drawing the framebuffer's texture. Also: what should I fallback to if the video card doesn't support framebuffers and I absolutely need them?
Here's the code of my framebuffer class:
framebuffer.hpp:
#pragma once
#include "sprite.hpp"
namespace core
{
class framebuffer
{
public:
framebuffer();
virtual ~framebuffer();
bool initialized();
void initialize(int width, int height);
int width();
int height();
sprite *getframe();
void begin();
void end();
protected:
GLubyte *pixels;
int w, h;
GLuint id, tex;
sprite frame;
};
}
framebuffer.cpp:
#include "framebuffer.hpp"
#include <iostream>
#include <cmath>
namespace core
{
framebuffer::framebuffer()
: pixels(NULL), w(0), h(0)
{
// empty
}
framebuffer::~framebuffer()
{
glDeleteFramebuffers(1, &id);
if (pixels)
{
// TODO: convert to smart pointer once everything works ok
delete [] pixels;
pixels = NULL;
}
}
bool framebuffer::initialized()
{
return w != 0 && h != 0;
}
void framebuffer::initialize(int width, int height)
{
w = width;
h = height;
//std::cout << "framebuffer.initialize: w = " << w << "\nh = " << h << std::endl;
}
int framebuffer::width()
{
return w;
}
int framebuffer::height()
{
return h;
}
sprite *framebuffer::getframe()
{
GLuint oldtex;
glBindFramebuffer(GL_FRAMEBUFFER, id);
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glGetIntegerv(GL_TEXTURE_RECTANGLE_ARB, reinterpret_cast<GLint *>(&oldtex)); // store old bound texture
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex); // bind framebuffer texture
glGetTexImage(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); // get framebuffer pixels
frame.fromtexture(tex, GL_RGBA, w, h, GL_RGBA, pixels); // create a sprite object from raw pixels
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, oldtex); // re-bind the old texture
glDisable(GL_TEXTURE_RECTANGLE_ARB);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return &frame;
}
void framebuffer::begin() // call this to start drawing to the framebuffer
{
if (!pixels)
{
GLenum result;
// initialize the frame buffer if it isn't already
glGenTextures(1, &tex); // generate the texture that will store the frame
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
// reserve video memory for the framebuffer image
pixels = new GLubyte[w * h * 4];
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glGenFramebuffers(1, &id); // create frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, id);
// assign the texture to the framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 0);
result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (result != GL_FRAMEBUFFER_COMPLETE)
{
if (result == GL_FRAMEBUFFER_UNSUPPORTED)
{
// todo: handle errors as exceptions and cleanup everything gracefully
std::cout << "framebuffer.begin: your video card doesn't seem to support framebuffers" << std::endl;
exit(0);
}
std::cout << "framebuffer.begin: failed to initialize framebuffer | error " << result << std::endl;
exit(0);
}
glDisable(GL_TEXTURE_RECTANGLE_ARB);
}
glBindFramebuffer(GL_FRAMEBUFFER, id); // bind the framebuffer
glPushAttrib(GL_VIEWPORT_BIT | GL_ENABLE_BIT);
glViewport(0, 0, w, h); // adjust viewport to the fbo's size
glLoadIdentity(); // reset modelview matrix
glMatrixMode(GL_PROJECTION);
glPushMatrix(); // store the non-flipped matrix
glLoadIdentity();
glOrtho(0, w, 0, h, -1, 1); // stupid fbo's flipping stuff upside down
glMatrixMode(GL_MODELVIEW);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void framebuffer::end() // call this to stop drawing to the framebuffer
{
glPopAttrib();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glMatrixMode(GL_PROJECTION);
glPopMatrix(); // restore old matrix
glMatrixMode(GL_MODELVIEW);
}
}
From the official documentation:
"The return value is GL_FRAMEBUFFER_COMPLETE if the framebuffer bound to target is complete. Otherwise, the return value is determined as follows:
...
Additionally, if an error occurs, zero is returned."
So there you have it, an error occured. Maybe glGetError has additional information.
If framebuffers are not supported and you wanted to use it for render-to-texture you could see if pbuffers are supported, otherwise glReadPixels?