From f9f7e83f0ed553f4286dc0e95d71ceebe0a13270 Mon Sep 17 00:00:00 2001 From: vrld Date: Fri, 27 Aug 2010 13:51:11 +0200 Subject: [PATCH] Issue #7: Use GL_EXT_framebuffer_object when OpenGL version < 3.0 Add runtime check of OpenGL version and extensions. If OpenGL version >= 3.0, use core functions. Else check for framebuffer object extension and use EXT functions. If none of the above, do nothing but return GL_FRAMEBUFFER_COMPLETE. --- src/modules/graphics/opengl/Framebuffer.cpp | 214 +++++++++++++++++--- 1 file changed, 182 insertions(+), 32 deletions(-) diff --git a/src/modules/graphics/opengl/Framebuffer.cpp b/src/modules/graphics/opengl/Framebuffer.cpp index 1acc97af8..b62b95a77 100644 --- a/src/modules/graphics/opengl/Framebuffer.cpp +++ b/src/modules/graphics/opengl/Framebuffer.cpp @@ -1,6 +1,170 @@ #include "Framebuffer.h" #include +#include +#include +#include +#include +#include +#include +using namespace std; + +namespace { + + // functions to get opengl capabilities at runtime + vector tokenize(const string& str) + { + vector tokens; + + istringstream iss( str ); + copy(istream_iterator(iss), istream_iterator(), + back_inserter< vector >(tokens)); + return tokens; + } + + float getOpenGLVersionNumber() + { + vector tokens = tokenize( (const char*)glGetString(GL_VERSION) ); + stringstream toNumber( tokens.at(0) ); + + double version; + toNumber >> version; + return version; + } + + bool hasFramebufferExtension() + { + vector ext = tokenize( (const char*)glGetString(GL_EXTENSIONS) ); + return find(ext.begin(), ext.end(), "GL_EXT_framebuffer_object") != ext.end(); + } + + + // strategy for fbo creation, interchangable at runtime: + // none, opengl >= 3.0, extensions + struct FramebufferStrategy { + /// create a new framebuffer, depthbuffer and texture + /** + * @param[out] framebuffer Framebuffer name + * @param[out] depthbuffer Depthbuffer name + * @param[out] img Texture name + * @param[in] width Width of framebuffer + * @param[in] height Height of framebuffer + * @return Creation status + */ + virtual GLenum createFBO(GLuint& framebuffer, GLuint& depthbuffer, GLuint& img, int width, int height) + { return GL_FRAMEBUFFER_UNSUPPORTED; } + /// remove objects + /** + * @param[in] framebuffer Framebuffer name + * @param[in] depthbuffer Depthbuffer name + * @param[in] img Texture name + */ + virtual void deleteFBO(GLuint framebuffer, GLuint depthbuffer, GLuint img) {} + virtual void bindFBO(GLuint framebuffer) {} + }; + +#ifdef GL_VERSION_3_0 + struct FramebufferStrategyGL3 : public FramebufferStrategy { + virtual GLenum createFBO(GLuint& framebuffer, GLuint& depthbuffer, GLuint& img, int width, int height) + { + // generate depth buffer + glGenRenderbuffers(1, &depthbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + // generate texture save target + glGenTextures(1, &img); + glBindTexture(GL_TEXTURE_2D, img); + 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); + glBindTexture(GL_TEXTURE_2D, 0); + + // create framebuffer + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, img, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, depthbuffer); + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + // unbind buffers and texture + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return status; + } + virtual void deleteFBO(GLuint framebuffer, GLuint depthbuffer, GLuint img) + { + glDeleteTextures(1, &framebuffer); + glDeleteRenderbuffers(1, &depthbuffer); + glDeleteFramebuffers(1, &img); + } + + virtual void bindFBO(GLuint framebuffer) + { + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + } + }; +#endif + + struct FramebufferStrategyEXT : public FramebufferStrategy { + + virtual GLenum createFBO(GLuint& framebuffer, GLuint& depthbuffer, GLuint& img, int width, int height) + { + // generate depth buffer + glGenRenderbuffersEXT(1, &depthbuffer); + glBindRenderbuffer(GL_RENDERBUFFER_EXT, depthbuffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, width, height); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + + // generate texture save target + glGenTextures(1, &img); + glBindTexture(GL_TEXTURE_2D, img); + 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); + glBindTexture(GL_TEXTURE_2D, 0); + + // create framebuffer + glGenFramebuffersEXT(1, &framebuffer); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, img, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, depthbuffer); + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + + // unbind buffers and texture + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + return status; + } + + virtual void deleteFBO(GLuint framebuffer, GLuint depthbuffer, GLuint img) + { + glDeleteTextures(1, &framebuffer); + glDeleteRenderbuffersEXT(1, &depthbuffer); + glDeleteFramebuffersEXT(1, &img); + } + + virtual void bindFBO(GLuint framebuffer) + { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer); + } + }; + + FramebufferStrategy* strategy = NULL; + + FramebufferStrategy strategyNone; +#ifdef GL_VERSION_3_0 + FramebufferStrategyGL3 strategyGL3; +#endif + FramebufferStrategyEXT strategyEXT; + +}; + namespace love { namespace graphics @@ -25,32 +189,21 @@ namespace opengl vertices[2].s = 1; vertices[2].t = 0; vertices[3].s = 1; vertices[3].t = 1; - // generate depth buffer - glGenRenderbuffers(1, &depthbuffer); - glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); - glBindRenderbuffer(GL_RENDERBUFFER, 0); + if (!strategy) { +#ifdef GL_VERSION_3_0 + if (getOpenGLVersionNumber() >= 3.0) + strategy = &strategyGL3; + else if (hasFramebufferExtension()) +#else + if (hasFramebufferExtension()) +#endif + strategy = &strategyEXT; + else + strategy = &strategyNone; + + } - // generate texture save target - glGenTextures(1, &img); - glBindTexture(GL_TEXTURE_2D, img); - 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); - glBindTexture(GL_TEXTURE_2D, 0); - - // create framebuffer - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, img, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, depthbuffer); - status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - // unbind buffers and texture - glBindFramebuffer(GL_FRAMEBUFFER, 0); + status = strategy->createFBO(fbo, depthbuffer, img, width, height); } Framebuffer::~Framebuffer() @@ -59,10 +212,7 @@ namespace opengl if (current == this) stopGrab(); - // clear fbo - glDeleteTextures(1, &fbo); - glDeleteRenderbuffers(1, &depthbuffer); - glDeleteFramebuffers(1, &img); + strategy->deleteFBO(fbo, depthbuffer, img); } void Framebuffer::bindDefaultBuffer() @@ -83,12 +233,12 @@ namespace opengl // bind buffer and clear screen glPushAttrib(GL_VIEWPORT_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); + strategy->bindFBO(fbo); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(.0f, .0f, .0f, .0f); glViewport(0, 0, width, height); - // indicate + // indicate we are using this fbo current = this; } @@ -99,7 +249,7 @@ namespace opengl return; // bind default - glBindFramebuffer(GL_FRAMEBUFFER, 0); + strategy->bindFBO( 0 ); glPopAttrib(); current = NULL; }