فهرست منبع

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.
vrld 15 سال پیش
والد
کامیت
f9f7e83f0e
1فایلهای تغییر یافته به همراه182 افزوده شده و 32 حذف شده
  1. 182 32
      src/modules/graphics/opengl/Framebuffer.cpp

+ 182 - 32
src/modules/graphics/opengl/Framebuffer.cpp

@@ -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;
 	}