#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#include "system-gl.h"
#include "fbo.h"
fbo_t * fbo_new()
{
fbo_t * fbo = malloc (sizeof(fbo_t));
fbo->fbo_id = 0;
fbo->old_fbo_id = 0;
fbo->renderbuf_id = 0;
fbo->depthbuf_id = 0;
return fbo;
}
bool use_ext()
{
// do we need to use the EXT or ARB version?
return (!glewIsSupported("GL_ARB_framebuffer_object") &&
glewIsSupported("GL_EXT_framebuffer_object"));
}
bool check_fbo_status()
{
/* This code is based on user V-man code from
http://www.opengl.org/wiki/GL_EXT_framebuffer_multisample
See also: http://www.songho.ca/opengl/gl_fbo.html */
GLenum status;
bool result = false;
if (use_ext()) {
status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
}
else {
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
}
if (report_glerror("checking framebuffer status")) return false;
if (status == GL_FRAMEBUFFER_COMPLETE) {
result = true;
}
else if (status == GL_FRAMEBUFFER_UNSUPPORTED) {
fprintf (stderr, "GL_FRAMEBUFFER_UNSUPPORTED\n");
}
else if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) {
fprintf (stderr, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT\n");
}
else if (status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
fprintf (stderr, "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT\n");
}
else if (status == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT) {
fprintf (stderr, "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT\n");
}
else if (status == GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT) {
fprintf (stderr, "GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT\n");
}
else if (status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT) {
fprintf (stderr, "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT\n");
}
else if (status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT) {
fprintf (stderr, "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT\n");
}
else if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT) {
fprintf (stderr, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT\n");
}
else {
fprintf (stderr, "Unknown Code: glCheckFramebufferStatusEXT returned: %d\n",
status);
}
return result;
}
bool fbo_ext_init(fbo_t *fbo, size_t width, size_t height)
{
// Generate and bind FBO
glGenFramebuffersEXT(1, &fbo->fbo_id);
if (report_glerror("glGenFramebuffersEXT")) return false;
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->fbo_id);
if (report_glerror("glBindFramebufferEXT")) return false;
// Generate depth and render buffers
glGenRenderbuffersEXT(1, &fbo->depthbuf_id);
glGenRenderbuffersEXT(1, &fbo->renderbuf_id);
// Create buffers with correct size
if (!fbo_resize(fbo, width, height)) return false;
// Attach render and depth buffers
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_RENDERBUFFER_EXT, fbo->renderbuf_id);
if (report_glerror("specifying color render buffer EXT")) return false;
if (!check_fbo_status()) {
fprintf (stderr, "Problem with OpenGL EXT framebuffer"
" after specifying color render buffer.\n");
return false;
}
if (glewIsSupported("GL_EXT_packed_depth_stencil")) {
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, fbo->depthbuf_id);
if (report_glerror("specifying depth render buffer EXT")) return false;
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, fbo->depthbuf_id);
if (report_glerror("specifying stencil render buffer EXT")) return false;
if (!check_fbo_status()) {
fprintf (stderr,
"Problem with OpenGL EXT framebuffer after"
" specifying depth render buffer.\n");
return false;
}
}
else {
fprintf (stderr,
"Warning: Cannot create stencil buffer "
"(GL_EXT_packed_depth_stencil not supported)\n");
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, fbo->depthbuf_id);
if (report_glerror("specifying depth render buffer EXT")) return false;
if (!check_fbo_status()) {
fprintf (stderr,
"Problem with OpenGL EXT framebuffer "
"after specifying depth stencil render buffer.\n");
return false;
}
}
return true;
}
bool fbo_arb_init(fbo_t *fbo, size_t width, size_t height)
{
// Generate and bind FBO
glGenFramebuffers(1, &fbo->fbo_id);
if (report_glerror("glGenFramebuffers")) return false;
glBindFramebuffer(GL_FRAMEBUFFER, fbo->fbo_id);
if (report_glerror("glBindFramebuffer")) return false;
// Generate depth and render buffers
glGenRenderbuffers(1, &fbo->depthbuf_id);
glGenRenderbuffers(1, &fbo->renderbuf_id);
// Create buffers with correct size
if (!fbo_resize(fbo, width, height)) return false;
// Attach render and depth buffers
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, fbo->renderbuf_id);
if (report_glerror("specifying color render buffer")) return false;
if (!check_fbo_status()) {
fprintf (stderr, "Problem with OpenGL framebuffer after "
"specifying color render buffer.\n");
return false;
}
//glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
// to prevent Mesa's software renderer from crashing, do this in two stages.
// ie. instead of using GL_DEPTH_STENCIL_ATTACHMENT, do DEPTH then STENCIL.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, fbo->depthbuf_id);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, fbo->depthbuf_id);
if (report_glerror("specifying depth stencil render buffer")) return false;
if (!check_fbo_status()) {
fprintf (stderr, "Problem with OpenGL framebuffer after "
"specifying depth render buffer.\n");
return false;
}
return true;
}
bool fbo_init(fbo_t *fbo, size_t width, size_t height)
{
/*
Some OpenGL drivers include the framebuffer functions but not with
core or ARB names, only with the EXT name. This has been worked-around
by deciding at runtime, using GLEW, which version needs to be used. See also:
http://www.opengl.org/wiki/Framebuffer_Object
http://stackoverflow.com/questions/6912988/glgenframebuffers-or-glgenframebuffersex
http://www.devmaster.net/forums/showthread.php?t=10967
*/
bool result = false;
if (glewIsSupported("GL_ARB_framebuffer_object")) {
result = fbo_arb_init(fbo, width, height);
}
else if (use_ext()) {
result = fbo_ext_init(fbo, width, height);
}
else {
fprintf (stderr, "Framebuffer Object extension not found by GLEW\n");
}
return result;
}
bool fbo_resize(fbo_t *fbo, size_t width, size_t height)
{
if (use_ext()) {
glBindRenderbufferEXT(GL_RENDERBUFFER, fbo->depthbuf_id);
if (glewIsSupported("GL_EXT_packed_depth_stencil")) {
glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
if (report_glerror("creating EXT depth stencil render buffer")) return false;
}
else {
glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
if (report_glerror("creating EXT depth render buffer")) return false;
}
glBindRenderbufferEXT(GL_RENDERBUFFER, fbo->renderbuf_id);
glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA8, width, height);
if (report_glerror("creating EXT color render buffer")) return false;
} else {
glBindRenderbuffer(GL_RENDERBUFFER, fbo->renderbuf_id);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
if (report_glerror("creating color render buffer")) return false;
glBindRenderbuffer(GL_RENDERBUFFER, fbo->depthbuf_id);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
if (report_glerror("creating depth stencil render buffer")) return false;
}
return true;
}
void fbo_delete(fbo_t *fbo)
{
free (fbo);
}
GLuint fbo_bind(fbo_t *fbo)
{
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)(&fbo->old_fbo_id));
if (use_ext()) {
glBindFramebufferEXT(GL_FRAMEBUFFER, fbo->fbo_id);
}
else {
glBindFramebuffer(GL_FRAMEBUFFER, fbo->fbo_id);
}
return fbo->old_fbo_id;
}
void fbo_unbind(fbo_t *fbo)
{
if (use_ext()) {
glBindFramebufferEXT(GL_FRAMEBUFFER, fbo->old_fbo_id);
}
else {
glBindFramebuffer(GL_FRAMEBUFFER, fbo->old_fbo_id);
}
}
bool report_glerror(const char * function)
{
GLenum tGLErr = glGetError();
if (tGLErr != GL_NO_ERROR) {
fprintf (stderr, "OpenGL error %04x after %s\n", tGLErr, function);
return true;
}
return false;
}