mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
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.
This commit is contained in:
@@ -1,6 +1,170 @@
|
||||
#include "Framebuffer.h"
|
||||
#include <common/Matrix.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
// functions to get opengl capabilities at runtime
|
||||
vector<string> tokenize(const string& str)
|
||||
{
|
||||
vector<string> tokens;
|
||||
|
||||
istringstream iss( str );
|
||||
copy(istream_iterator<string>(iss), istream_iterator<string>(),
|
||||
back_inserter< vector<string> >(tokens));
|
||||
return tokens;
|
||||
}
|
||||
|
||||
float getOpenGLVersionNumber()
|
||||
{
|
||||
vector<string> tokens = tokenize( (const char*)glGetString(GL_VERSION) );
|
||||
stringstream toNumber( tokens.at(0) );
|
||||
|
||||
double version;
|
||||
toNumber >> version;
|
||||
return version;
|
||||
}
|
||||
|
||||
bool hasFramebufferExtension()
|
||||
{
|
||||
vector<string> 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user