Commits

Alex Szpakowski committed b01df32

Moved OpenGL wrapper functions to a new Context class which internally tracks GL state

Comments (0)

Files changed (20)

platform/macosx/love.xcodeproj/project.pbxproj

 		A95684F7125D3555001B276B /* b64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A95684F5125D3555001B276B /* b64.cpp */; };
 		A958F911142D364C007F320F /* wrap_Fixture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A958F910142D3627007F320F /* wrap_Fixture.cpp */; };
 		A958F917142D6B3A007F320F /* Body.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A958F916142D6B37007F320F /* Body.cpp */; };
-		A9642D7514D1A66000CE0B02 /* OpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9642D7314D1A66000CE0B02 /* OpenGL.cpp */; };
+		A9642D7514D1A66000CE0B02 /* Context.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9642D7314D1A66000CE0B02 /* Context.cpp */; };
 		A968F0CF1083A9A900A895AA /* Event.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A968F0CE1083A9A900A895AA /* Event.cpp */; };
 		A968F0D11083A9B900A895AA /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A968F0D01083A9B900A895AA /* File.cpp */; };
 		A968F0D31083A9D400A895AA /* Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A968F0D21083A9D400A895AA /* Graphics.cpp */; };
 		A958F912142D3687007F320F /* wrap_Fixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Fixture.h; sourceTree = "<group>"; };
 		A958F916142D6B37007F320F /* Body.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Body.cpp; sourceTree = "<group>"; };
 		A958F918142D6B60007F320F /* Body.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Body.h; sourceTree = "<group>"; };
-		A9642D7314D1A66000CE0B02 /* OpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OpenGL.cpp; sourceTree = "<group>"; };
-		A9642D7414D1A66000CE0B02 /* OpenGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenGL.h; sourceTree = "<group>"; };
+		A9642D7314D1A66000CE0B02 /* Context.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Context.cpp; sourceTree = "<group>"; };
+		A9642D7414D1A66000CE0B02 /* Context.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Context.h; sourceTree = "<group>"; };
 		A968F0C61083A07B00A895AA /* EnumMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EnumMap.h; sourceTree = "<group>"; };
 		A968F0C71083A07C00A895AA /* StringMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringMap.h; sourceTree = "<group>"; };
 		A968F0CE1083A9A900A895AA /* Event.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Event.cpp; sourceTree = "<group>"; };
 				A93E6A8E10420AC2007D418B /* Graphics.h */,
 				A93E6A8F10420AC2007D418B /* Image.cpp */,
 				A93E6A9010420AC2007D418B /* Image.h */,
-				A9642D7314D1A66000CE0B02 /* OpenGL.cpp */,
-				A9642D7414D1A66000CE0B02 /* OpenGL.h */,
+				A9642D7314D1A66000CE0B02 /* Context.cpp */,
+				A9642D7414D1A66000CE0B02 /* Context.h */,
 				A9B4BA9A1045937F001DBC80 /* ParticleSystem.cpp */,
 				A9B4BA981045937F001DBC80 /* ParticleSystem.h */,
 				A93E6A9310420AC2007D418B /* Quad.cpp */,
 				A9A5BE48149F0ACB00D9931C /* Window.cpp in Sources */,
 				A9A5BE4B149F0B0200D9931C /* Variant.cpp in Sources */,
 				A9F2D09114BA85EC0035D2A5 /* Audio.cpp in Sources */,
-				A9642D7514D1A66000CE0B02 /* OpenGL.cpp in Sources */,
+				A9642D7514D1A66000CE0B02 /* Context.cpp in Sources */,
 				A9F6E6AE15A1080D00C86200 /* love.cpp in Sources */,
 				A9F6E6B115A1099C00C86200 /* GmeDecoder.cpp in Sources */,
 				A911D2DB15DFECC8005B7EB8 /* Module.cpp in Sources */,

src/modules/graphics/opengl/Canvas.cpp

 {
 	virtual GLenum createFBO(GLuint &framebuffer, GLuint &depth_stencil,  GLuint &img, int width, int height, Canvas::TextureType texture_type)
 	{
+		Context *ctx = getContext();
+
 		// get currently bound fbo to reset to it later
 		GLint current_fbo;
 		glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
 		}
 
 		glGenTextures(1, &img);
-		bindTexture(img);
+		ctx->bindTexture(img);
 
-		setTextureFilter(Image::getDefaultFilter());
+		ctx->setTextureFilter(Image::getDefaultFilter());
 
 		glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
 			0, GL_RGBA, format, NULL);
-		bindTexture(0);
+		ctx->bindTexture(0);
 		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
 			GL_TEXTURE_2D, img, 0);
 
 	}
 	virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil,  GLuint img)
 	{
-		deleteTexture(img);
+		getContext()->deleteTexture(img);
 		glDeleteRenderbuffers(1, &depth_stencil);
 		glDeleteFramebuffers(1, &framebuffer);
 	}
 {
 	virtual GLenum createFBO(GLuint &framebuffer, GLuint &depth_stencil, GLuint &img, int width, int height, Canvas::TextureType texture_type)
 	{
+		Context *ctx = getContext();
+
 		GLint current_fbo;
 		glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &current_fbo);
 
 		}
 
 		glGenTextures(1, &img);
-		bindTexture(img);
+		ctx->bindTexture(img);
 
-		setTextureFilter(Image::getDefaultFilter());
+		ctx->setTextureFilter(Image::getDefaultFilter());
 
 		glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
 			0, GL_RGBA, format, NULL);
-		bindTexture(0);
+		ctx->bindTexture(0);
 		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
 			GL_TEXTURE_2D, img, 0);
 
 
 	virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
 	{
-		deleteTexture(img);
+		getContext()->deleteTexture(img);
 		glDeleteRenderbuffersEXT(1, &depth_stencil);
 		glDeleteFramebuffersEXT(1, &framebuffer);
 	}
 {
 	virtual GLenum createFBO(GLuint &framebuffer, GLuint &stencil, GLuint &img, int width, int height, Canvas::TextureType texture_type)
 	{
+		Context *ctx = getContext();
+
 		GLint current_fbo;
 		glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, &current_fbo);
 
 		}
 
 		glGenTextures(1, &img);
-		bindTexture(img);
+		ctx->bindTexture(img);
 
-		setTextureFilter(Image::getDefaultFilter());
+		ctx->setTextureFilter(Image::getDefaultFilter());
 
 		glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
 			0, GL_RGBA, format, NULL);
-		bindTexture(0);
+		ctx->bindTexture(0);
 		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
 			GL_TEXTURE_2D, img, 0);
 
 
 void Canvas::setFilter(const Image::Filter &f)
 {
-	bindTexture(img);
-	setTextureFilter(f);
+	Context *ctx = getContext();
+	ctx->bindTexture(img);
+	ctx->setTextureFilter(f);
 }
 
 Image::Filter Canvas::getFilter() const
 {
-	bindTexture(img);
-	return getTextureFilter();
+	Context *ctx = getContext();
+	ctx->bindTexture(img);
+	return ctx->getTextureFilter();
 }
 
 void Canvas::setWrap(const Image::Wrap &w)
 {
-	bindTexture(img);
-	setTextureWrap(w);
+	Context *ctx = getContext();
+	ctx->bindTexture(img);
+	ctx->setTextureWrap(w);
 }
 
 Image::Wrap Canvas::getWrap() const
 {
-	bindTexture(img);
-	return getTextureWrap();
+	Context *ctx = getContext();
+	ctx->bindTexture(img);
+	return ctx->getTextureWrap();
 }
 
 bool Canvas::loadVolatile()
 
 	glMultMatrixf((const GLfloat *)t.getElements());
 
-	bindTexture(img);
+	getContext()->bindTexture(img);
 
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

src/modules/graphics/opengl/Canvas.h

 #include "image/ImageData.h"
 #include "common/math.h"
 #include "common/Matrix.h"
-#include "OpenGL.h"
+#include "Context.h"
 
 namespace love
 {

src/modules/graphics/opengl/Context.cpp

+/**
+ * Copyright (c) 2006-2012 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#include "common/config.h"
+#include "common/Exception.h"
+
+#include "Context.h"
+
+#include <algorithm>
+
+namespace love
+{
+namespace graphics
+{
+namespace opengl
+{
+
+Context *Context::current = NULL;
+
+Context *getContext()
+{
+	if (Context::current == NULL)
+		Context::current = new Context;
+
+	return Context::current;
+}
+
+Context *resetContext()
+{
+	if (Context::current != NULL)
+	{
+		delete Context::current;
+		Context::current = NULL;
+	}
+
+	return getContext();
+}
+
+Context::Context()
+{
+	initialize();
+}
+
+Context::~Context()
+{
+	uninitialize();
+}
+
+void Context::initialize()
+{
+	textureUnits.clear();
+
+	// initialize multiple texture unit support, if available
+	if (GLEE_VERSION_1_3 || GLEE_ARB_multitexture)
+	{
+		GLint maxtextureunits;
+		glGetIntegerv(GL_MAX_TEXTURE_UNITS, &maxtextureunits);
+
+		// shaders/GL2.0 added "Texture Image Units." Total max texture units is the greater of the two
+		if (GLEE_VERSION_2_0 || GLEE_ARB_vertex_shader)
+		{
+			GLint maxtextureimageunits;
+			glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureimageunits);
+			maxtextureunits = std::max(maxtextureunits, maxtextureimageunits);
+		}
+
+		textureUnits.resize(maxtextureunits, 0);
+
+		GLenum curgltextureunit;
+		glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *)&curgltextureunit);
+
+		curTextureUnit = curgltextureunit - GL_TEXTURE0;
+
+		// retrieve currently bound textures for each texture unit
+		for (size_t i = 0; i < textureUnits.size(); ++i)
+		{
+			if (GLEE_VERSION_1_3)
+				glActiveTexture(GL_TEXTURE0 + i);
+			else
+				glActiveTextureARB(GL_TEXTURE0 + i);
+
+			glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &textureUnits[i]);
+		}
+
+		if (GLEE_VERSION_1_3)
+			glActiveTexture(curgltextureunit);
+		else
+			glActiveTextureARB(curgltextureunit);
+	}
+	else
+	{
+		// multitexturing not supported, so we only have 1 texture unit
+		textureUnits.resize(1, 0);
+		curTextureUnit = 0;
+
+		glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &textureUnits[0]);
+	}
+
+	// Set the 'default' texture (id 0) as a repeating white pixel.
+	// Otherwise, texture2D inside a shader would return black when drawing graphics primitives,
+	// which would create the need to use different "passthrough" shaders for untextured primitives vs images.
+
+	GLuint curtexture = textureUnits[curTextureUnit];
+	bindTexture(0);
+
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+	GLubyte pixel = 255;
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, &pixel);
+
+	bindTexture(curtexture);
+}
+
+void Context::uninitialize()
+{
+	// ?
+}
+
+void Context::setActiveTextureUnit(int textureunit)
+{
+	if (textureunit < 0 || (size_t) textureunit >= textureUnits.size())
+		throw love::Exception("Invalid texture unit index (%d).", textureunit);
+
+	if (textureunit != curTextureUnit)
+	{
+		if (GLEE_VERSION_1_3)
+			glActiveTexture(GL_TEXTURE0 + textureunit);
+		else if (GLEE_ARB_multitexture)
+			glActiveTextureARB(GL_TEXTURE0 + textureunit);
+		else
+			throw love::Exception("Multitexturing not supported.");
+	}
+
+	curTextureUnit = textureunit;
+}
+
+void Context::bindTexture(GLuint texture)
+{
+	if (texture != textureUnits[curTextureUnit])
+	{
+		textureUnits[curTextureUnit] = texture;
+		glBindTexture(GL_TEXTURE_2D, texture);
+	}
+}
+
+void Context::bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev)
+{
+	if (textureunit < 0 || (size_t) textureunit >= textureUnits.size())
+		throw love::Exception("Invalid texture unit index.");
+
+	if (texture != textureUnits[textureunit])
+	{
+		int oldtextureunit = curTextureUnit;
+		setActiveTextureUnit(textureunit);
+
+		textureUnits[textureunit] = texture;
+		glBindTexture(GL_TEXTURE_2D, texture);
+
+		if (restoreprev)
+			setActiveTextureUnit(oldtextureunit);
+	}
+}
+
+void Context::deleteTexture(GLuint texture)
+{
+	// glDeleteTextures binds texture 0 to all texture units the deleted texture was bound to
+	std::vector<GLuint>::iterator it;
+	for (it = textureUnits.begin(); it != textureUnits.end(); ++it)
+	{
+		if (*it == texture)
+			*it = 0;
+	}
+
+	glDeleteTextures(1, &texture);
+}
+
+void Context::setTextureFilter(const graphics::Image::Filter &f)
+{
+	GLint gmin, gmag;
+
+	if (f.mipmap == Image::FILTER_NONE)
+	{
+		if (f.min == Image::FILTER_NEAREST)
+			gmin = GL_NEAREST;
+		else // f.min == Image::FILTER_LINEAR
+			gmin = GL_LINEAR;
+	}
+	else
+	{
+		if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_NEAREST)
+			gmin = GL_NEAREST_MIPMAP_NEAREST;
+		else if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_LINEAR)
+			gmin = GL_NEAREST_MIPMAP_LINEAR;
+		else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_NEAREST)
+			gmin = GL_LINEAR_MIPMAP_NEAREST;
+		else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_LINEAR)
+			gmin = GL_LINEAR_MIPMAP_LINEAR;
+		else
+			gmin = GL_LINEAR;
+	}
+
+
+	switch (f.mag)
+	{
+	case Image::FILTER_NEAREST:
+		gmag = GL_NEAREST;
+		break;
+	case Image::FILTER_LINEAR:
+	default:
+		gmag = GL_LINEAR;
+		break;
+	}
+
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
+}
+
+graphics::Image::Filter Context::getTextureFilter()
+{
+	GLint gmin, gmag;
+	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin);
+	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
+
+	Image::Filter f;
+
+	switch (gmin)
+	{
+	case GL_NEAREST:
+		f.min = Image::FILTER_NEAREST;
+		f.mipmap = Image::FILTER_NONE;
+		break;
+	case GL_NEAREST_MIPMAP_NEAREST:
+		f.min = f.mipmap = Image::FILTER_NEAREST;
+		break;
+	case GL_NEAREST_MIPMAP_LINEAR:
+		f.min = Image::FILTER_NEAREST;
+		f.mipmap = Image::FILTER_LINEAR;
+		break;
+	case GL_LINEAR_MIPMAP_NEAREST:
+		f.min = Image::FILTER_LINEAR;
+		f.mipmap = Image::FILTER_NEAREST;
+		break;
+	case GL_LINEAR_MIPMAP_LINEAR:
+		f.min = f.mipmap = Image::FILTER_LINEAR;
+		break;
+	case GL_LINEAR:
+	default:
+		f.min = Image::FILTER_LINEAR;
+		f.mipmap = Image::FILTER_NONE;
+		break;
+	}
+
+	switch (gmag)
+	{
+	case GL_NEAREST:
+		f.mag = Image::FILTER_NEAREST;
+		break;
+	case GL_LINEAR:
+	default:
+		f.mag = Image::FILTER_LINEAR;
+		break;
+	}
+
+	return f;
+}
+
+void Context::setTextureWrap(const graphics::Image::Wrap &w)
+{
+	GLint gs, gt;
+
+	switch (w.s)
+	{
+	case Image::WRAP_CLAMP:
+		gs = GL_CLAMP_TO_EDGE;
+		break;
+	case Image::WRAP_REPEAT:
+	default:
+		gs = GL_REPEAT;
+		break;
+	}
+
+	switch (w.t)
+	{
+	case Image::WRAP_CLAMP:
+		gt = GL_CLAMP_TO_EDGE;
+		break;
+	case Image::WRAP_REPEAT:
+	default:
+		gt = GL_REPEAT;
+		break;
+	}
+
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gs);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gt);
+}
+
+graphics::Image::Wrap Context::getTextureWrap()
+{
+	GLint gs, gt;
+
+	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &gs);
+	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &gt);
+
+	Image::Wrap w;
+
+	switch (gs)
+	{
+	case GL_CLAMP_TO_EDGE:
+		w.s = Image::WRAP_CLAMP;
+		break;
+	case GL_REPEAT:
+	default:
+		w.s = Image::WRAP_REPEAT;
+		break;
+	}
+
+	switch (gt)
+	{
+	case GL_CLAMP_TO_EDGE:
+		w.t = Image::WRAP_CLAMP;
+		break;
+	case GL_REPEAT:
+	default:
+		w.t = Image::WRAP_REPEAT;
+		break;
+	}
+
+	return w;
+}
+
+} // opengl
+} // graphics
+} // love

src/modules/graphics/opengl/Context.h

+/**
+ * Copyright (c) 2006-2012 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#ifndef LOVE_GRAPHICS_OPENGL_CONTEXT_H
+#define LOVE_GRAPHICS_OPENGL_CONTEXT_H
+
+#include "GLee.h"
+#include "graphics/Image.h"
+
+#include <vector>
+
+namespace love
+{
+namespace graphics
+{
+namespace opengl
+{
+
+/**
+ * Thin layer between OpenGL and the rest of the program.
+ * Tracks OpenGL context state internally for more efficiency.
+ **/
+class Context
+{
+	friend Context *getContext();
+	friend Context *resetContext();
+
+public:
+
+	Context();
+	~Context();
+
+	void initialize();
+
+	void uninitialize();
+
+	/**
+	 * Sets the active texture unit.
+	 *
+	 * @param textureunit Index in the range of [0, maxtextureunits-1]
+	 **/
+	void setActiveTextureUnit(int textureunit);
+
+	/**
+	 * Binds an OpenGL texture to the active texture unit.
+	 * Makes sure we aren't redundantly binding textures.
+	 **/
+	void bindTexture(GLuint texture);
+
+	/**
+	 * Binds a texture to a specific texture unit.
+	 *
+	 * @param textureunit Index in the range of [0, maxtextureunits-1]
+	 * @param resoreprev Restore previously bound texture unit when done.
+	 **/
+	void bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev);
+
+	/**
+	 * Deletes an OpenGL texture.
+	 * Cleans up if the texture is currently bound.
+	 **/
+	void deleteTexture(GLuint texture);
+
+	/**
+	 * Sets the image filter mode for the currently bound texture.
+	 */
+	void setTextureFilter(const graphics::Image::Filter &f);
+
+	/**
+	 * Returns the image filter mode for the currently bound texture.
+	 */
+	graphics::Image::Filter getTextureFilter();
+
+	/**
+	 * Sets the image wrap mode for the currently bound texture.
+	 */
+	void setTextureWrap(const graphics::Image::Wrap &w);
+
+	/**
+	 * Returns the image wrap mode for the currently bound texture.
+	 */
+	graphics::Image::Wrap getTextureWrap();
+
+private:
+
+	int curTextureUnit;
+	std::vector<GLuint> textureUnits;
+
+	// Pointer to the currently active context
+	static Context *current;
+};
+
+Context *getContext();
+
+Context *resetContext();
+
+} // opengl
+} // graphics
+} // love
+
+#endif

src/modules/graphics/opengl/Font.cpp

 	glGenTextures(1, &t);
 	textures.push_back(t);
 
-	bindTexture(t);
+	Context *ctx = getContext();
+
+	ctx->bindTexture(t);
 
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 	if (!initialized)
 	{
 		// cleanup before throwing
-		deleteTexture(t);
-		bindTexture(0);
+		ctx->deleteTexture(t);
+		ctx->bindTexture(0);
 		textures.pop_back();
 
 		throw love::Exception("Could not create font texture!");
 	{
 		const GLuint t = textures.back();
 
-		bindTexture(t);
+		getContext()->bindTexture(t);
 		glTexSubImage2D(GL_TEXTURE_2D,
 						0,
 						texture_x,
 		std::vector<GlyphArrayDrawInfo>::const_iterator it;
 		for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
 		{
-			bindTexture(it->texture);
+			getContext()->bindTexture(it->texture);
 
 			int startvertex = it->startquad * 4;
 			int numvertices = it->numquads * 4;
 {
 	filter = f;
 
+	Context *ctx = getContext();
+
 	std::vector<GLuint>::const_iterator it;
 	for (it = textures.begin(); it != textures.end(); ++it)
 	{
-		bindTexture(*it);
+		ctx->bindTexture(*it);
 		checkMipmapsCreated();
-		setTextureFilter(f);
+		ctx->setTextureFilter(f);
 	}
 }
 
 	// LOD bias has the range (-maxbias, maxbias)
 	mipmapsharpness = std::min(std::max(sharpness, -maxmipmapsharpness + 0.01f), maxmipmapsharpness - 0.01f);
 
+	Context *ctx = getContext();
+
 	std::vector<GLuint>::const_iterator it;
 	for (it = textures.begin(); it != textures.end(); ++it)
 	{
-		bindTexture(*it);
+		ctx->bindTexture(*it);
 		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapsharpness); // negative bias is sharper
 	}
 }
 
 void Font::unloadVolatile()
 {
+	Context *ctx = getContext();
+
 	// nuke everything from orbit
 	std::map<unsigned int, Glyph *>::iterator it = glyphs.begin();
 	Glyph *g;
 	std::vector<GLuint>::iterator iter = textures.begin();
 	while (iter != textures.end())
 	{
-		deleteTexture(*iter);
+		ctx->deleteTexture(*iter);
 		iter++;
 	}
 	textures.clear();

src/modules/graphics/opengl/Font.h

 #include "font/Rasterizer.h"
 #include "graphics/Image.h"
 
-#include "OpenGL.h"
+#include "Context.h"
 
 namespace love
 {

src/modules/graphics/opengl/Graphics.cpp

 	// the display mode change.
 	Volatile::unloadAll();
 	
-	uninitializeContext();
-
 	bool success = currentWindow->setWindow(width, height, fullscreen, vsync, fsaa);
 	// Regardless of failure, we'll have to set up OpenGL once again.
 
 
 	// Okay, setup OpenGL.
 
-	initializeContext();
+	Context *ctx = resetContext();
 
 	// Make sure antialiasing works when set elsewhere
 	glEnable(GL_MULTISAMPLE);
 
 	// Enable textures
 	glEnable(GL_TEXTURE_2D);
-	setActiveTextureUnit(0);
+	ctx->setActiveTextureUnit(0);
 
 	// Set the viewport to top-left corner
 	glViewport(0, 0, width, height);
 
 void Graphics::point(float x, float y)
 {
-	bindTexture(0);
+	getContext()->bindTexture(0);
 	glBegin(GL_POINTS);
 	glVertex2f(x, y);
 	glEnd();
 	// end get line vertex boundaries
 
 	// draw the core line
-	bindTexture(0);
+	getContext()->bindTexture(0);
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)vertices);
 	glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
 	}
 	else
 	{
-		bindTexture(0);
+		getContext()->bindTexture(0);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *) coords);
 		glDrawArrays(GL_TRIANGLE_FAN, 0, points + 2);
 	}
 	else
 	{
-		bindTexture(0);
+		getContext()->bindTexture(0);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)coords);
 		glDrawArrays(GL_POLYGON, 0, count/2-1); // opengl will close the polygon for us

src/modules/graphics/opengl/Graphics.h

 #include <cmath>
 
 // OpenGL
-#include "OpenGL.h"
+#include "Context.h"
 
 // LOVE
 #include "graphics/Graphics.h"

src/modules/graphics/opengl/Image.cpp

 
 	bind();
 	checkMipmapsCreated();
-	setTextureFilter(f);
+	getContext()->setTextureFilter(f);
 }
 
 const Image::Filter &Image::getFilter() const
 	wrap = w;
 
 	bind();
-	setTextureWrap(w);
+	getContext()->setTextureWrap(w);
 }
 
 const Image::Wrap &Image::getWrap() const
 	if (texture == 0)
 		return;
 
-	bindTexture(texture);
+	getContext()->bindTexture(texture);
 }
 
 bool Image::load()
 bool Image::loadVolatilePOT()
 {
 	glGenTextures(1,(GLuint *)&texture);
-	bindTexture(texture);
+	getContext()->bindTexture(texture);
 
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 bool Image::loadVolatileNPOT()
 {
 	glGenTextures(1,(GLuint *)&texture);
-	bindTexture(texture);
+	getContext()->bindTexture(texture);
 
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 	// Delete the hardware texture.
 	if (texture != 0)
 	{
-		deleteTexture(texture);
+		getContext()->deleteTexture(texture);
 		texture = 0;
 	}
 }

src/modules/graphics/opengl/Image.h

 #include "graphics/Image.h"
 
 // OpenGL
-#include "OpenGL.h"
+#include "Context.h"
 
 namespace love
 {

src/modules/graphics/opengl/OpenGL.cpp

-/**
- * Copyright (c) 2006-2012 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty.  In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- *    claim that you wrote the original software. If you use this software
- *    in a product, an acknowledgment in the product documentation would be
- *    appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- *    misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
-
-#include "common/config.h"
-#include "common/Exception.h"
-
-#include "OpenGL.h"
-
-#include <vector>
-#include <algorithm>
-
-namespace love
-{
-namespace graphics
-{
-namespace opengl
-{
-
-static bool contextInitialized = false;
-
-static int curTextureUnit = 0;
-static std::vector<GLuint> textureUnits;
-
-void initializeContext()
-{
-	if (contextInitialized)
-		return;
-
-	contextInitialized = true;
-
-	textureUnits.clear();
-
-	// initialize multiple texture unit support, if available
-	if (GLEE_VERSION_1_3 || GLEE_ARB_multitexture)
-	{
-		GLint maxtextureunits;
-		glGetIntegerv(GL_MAX_TEXTURE_UNITS, &maxtextureunits);
-
-		// shaders/GL2.0 added "Texture Image Units." Total max texture units is the greater of the two
-		if (GLEE_VERSION_2_0 || GLEE_ARB_vertex_shader)
-		{
-			GLint maxtextureimageunits;
-			glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtextureimageunits);
-			maxtextureunits = std::max(maxtextureunits, maxtextureimageunits);
-		}
-
-		textureUnits.resize(maxtextureunits, 0);
-
-		GLenum curgltextureunit;
-		glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *)&curgltextureunit);
-
-		curTextureUnit = curgltextureunit - GL_TEXTURE0;
-
-		// retrieve currently bound textures for each texture unit
-		for (size_t i = 0; i < textureUnits.size(); ++i)
-		{
-			if (GLEE_VERSION_1_3)
-				glActiveTexture(GL_TEXTURE0 + i);
-			else
-				glActiveTextureARB(GL_TEXTURE0 + i);
-
-			glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &textureUnits[i]);
-		}
-
-		if (GLEE_VERSION_1_3)
-			glActiveTexture(curgltextureunit);
-		else
-			glActiveTextureARB(curgltextureunit);
-	}
-	else
-	{
-		// multitexturing not supported, so we only have 1 texture unit
-		textureUnits.resize(1, 0);
-		curTextureUnit = 0;
-
-		glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &textureUnits[0]);
-	}
-
-	// Set the 'default' texture (id 0) as a repeating white pixel.
-	// Otherwise, texture2D inside a shader would return black when drawing graphics primitives,
-	// which would create the need to use different "passthrough" shaders for untextured primitives vs images.
-
-	GLuint curtexture = textureUnits[curTextureUnit];
-	bindTexture(0);
-
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-
-	GLubyte pixel = 255;
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, 1, 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, &pixel);
-
-	bindTexture(curtexture);
-}
-
-void uninitializeContext()
-{
-	contextInitialized = false;
-}
-
-void setActiveTextureUnit(int textureunit)
-{
-	initializeContext();
-
-	if (textureunit < 0 || (size_t) textureunit >= textureUnits.size())
-		throw love::Exception("Invalid texture unit index (%d).", textureunit);
-
-	if (textureunit != curTextureUnit)
-	{
-		if (GLEE_VERSION_1_3)
-			glActiveTexture(GL_TEXTURE0 + textureunit);
-		else if (GLEE_ARB_multitexture)
-			glActiveTextureARB(GL_TEXTURE0 + textureunit);
-		else
-			throw love::Exception("Multitexturing not supported.");
-	}
-
-	curTextureUnit = textureunit;
-}
-
-void bindTexture(GLuint texture)
-{
-	initializeContext();
-
-	if (texture != textureUnits[curTextureUnit])
-	{
-		textureUnits[curTextureUnit] = texture;
-		glBindTexture(GL_TEXTURE_2D, texture);
-	}
-}
-
-void bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev)
-{
-	initializeContext();
-
-	if (textureunit < 0 || (size_t) textureunit >= textureUnits.size())
-		throw love::Exception("Invalid texture unit index.");
-
-	if (texture != textureUnits[textureunit])
-	{
-		int oldtextureunit = curTextureUnit;
-		setActiveTextureUnit(textureunit);
-
-		textureUnits[textureunit] = texture;
-		glBindTexture(GL_TEXTURE_2D, texture);
-
-		if (restoreprev)
-			setActiveTextureUnit(oldtextureunit);
-	}
-}
-
-void deleteTexture(GLuint texture)
-{
-	initializeContext();
-
-	// glDeleteTextures binds texture 0 to all texture units the deleted texture was bound to
-	std::vector<GLuint>::iterator it;
-	for (it = textureUnits.begin(); it != textureUnits.end(); ++it)
-	{
-		if (*it == texture)
-			*it = 0;
-	}
-
-	glDeleteTextures(1, &texture);
-}
-
-void setTextureFilter(const graphics::Image::Filter &f)
-{
-	initializeContext();
-
-	GLint gmin, gmag;
-
-	if (f.mipmap == Image::FILTER_NONE)
-	{
-		if (f.min == Image::FILTER_NEAREST)
-			gmin = GL_NEAREST;
-		else // f.min == Image::FILTER_LINEAR
-			gmin = GL_LINEAR;
-	}
-	else
-	{
-		if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_NEAREST)
-			gmin = GL_NEAREST_MIPMAP_NEAREST;
-		else if (f.min == Image::FILTER_NEAREST && f.mipmap == Image::FILTER_LINEAR)
-			gmin = GL_NEAREST_MIPMAP_LINEAR;
-		else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_NEAREST)
-			gmin = GL_LINEAR_MIPMAP_NEAREST;
-		else if (f.min == Image::FILTER_LINEAR && f.mipmap == Image::FILTER_LINEAR)
-			gmin = GL_LINEAR_MIPMAP_LINEAR;
-		else
-			gmin = GL_LINEAR;
-	}
-
-
-	switch (f.mag)
-	{
-	case Image::FILTER_NEAREST:
-		gmag = GL_NEAREST;
-		break;
-	case Image::FILTER_LINEAR:
-	default:
-		gmag = GL_LINEAR;
-		break;
-	}
-
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gmin);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gmag);
-}
-
-graphics::Image::Filter getTextureFilter()
-{
-	initializeContext();
-
-	GLint gmin, gmag;
-	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &gmin);
-	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &gmag);
-
-	Image::Filter f;
-
-	switch (gmin)
-	{
-	case GL_NEAREST:
-		f.min = Image::FILTER_NEAREST;
-		f.mipmap = Image::FILTER_NONE;
-		break;
-	case GL_NEAREST_MIPMAP_NEAREST:
-		f.min = f.mipmap = Image::FILTER_NEAREST;
-		break;
-	case GL_NEAREST_MIPMAP_LINEAR:
-		f.min = Image::FILTER_NEAREST;
-		f.mipmap = Image::FILTER_LINEAR;
-		break;
-	case GL_LINEAR_MIPMAP_NEAREST:
-		f.min = Image::FILTER_LINEAR;
-		f.mipmap = Image::FILTER_NEAREST;
-		break;
-	case GL_LINEAR_MIPMAP_LINEAR:
-		f.min = f.mipmap = Image::FILTER_LINEAR;
-		break;
-	case GL_LINEAR:
-	default:
-		f.min = Image::FILTER_LINEAR;
-		f.mipmap = Image::FILTER_NONE;
-		break;
-	}
-
-	switch (gmag)
-	{
-	case GL_NEAREST:
-		f.mag = Image::FILTER_NEAREST;
-		break;
-	case GL_LINEAR:
-	default:
-		f.mag = Image::FILTER_LINEAR;
-		break;
-	}
-
-	return f;
-}
-
-void setTextureWrap(const graphics::Image::Wrap &w)
-{
-	initializeContext();
-
-	GLint gs, gt;
-
-	switch (w.s)
-	{
-	case Image::WRAP_CLAMP:
-		gs = GL_CLAMP_TO_EDGE;
-		break;
-	case Image::WRAP_REPEAT:
-	default:
-		gs = GL_REPEAT;
-		break;
-	}
-
-	switch (w.t)
-	{
-	case Image::WRAP_CLAMP:
-		gt = GL_CLAMP_TO_EDGE;
-		break;
-	case Image::WRAP_REPEAT:
-	default:
-		gt = GL_REPEAT;
-		break;
-	}
-
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gs);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gt);
-}
-
-graphics::Image::Wrap getTextureWrap()
-{
-	initializeContext();
-
-	GLint gs, gt;
-
-	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &gs);
-	glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &gt);
-
-	Image::Wrap w;
-
-	switch (gs)
-	{
-	case GL_CLAMP_TO_EDGE:
-		w.s = Image::WRAP_CLAMP;
-		break;
-	case GL_REPEAT:
-	default:
-		w.s = Image::WRAP_REPEAT;
-		break;
-	}
-
-	switch (gt)
-	{
-	case GL_CLAMP_TO_EDGE:
-		w.t = Image::WRAP_CLAMP;
-		break;
-	case GL_REPEAT:
-	default:
-		w.t = Image::WRAP_REPEAT;
-		break;
-	}
-
-	return w;
-}
-
-} // opengl
-} // graphics
-} // love

src/modules/graphics/opengl/OpenGL.h

-/**
- * Copyright (c) 2006-2012 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty.  In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- *    claim that you wrote the original software. If you use this software
- *    in a product, an acknowledgment in the product documentation would be
- *    appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- *    misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
-
-#ifndef LOVE_GRAPHICS_OPENGL_OPENGL_H
-#define LOVE_GRAPHICS_OPENGL_OPENGL_H
-
-#include "GLee.h"
-#include "graphics/Image.h"
-
-namespace love
-{
-namespace graphics
-{
-namespace opengl
-{
-
-/**
- * Initializes some required context state,
- * based on current and default OpenGL state.
- **/
-void initializeContext();
-
-/**
- * Marks current context state as invalid.
- **/
-void uninitializeContext();
-
-/**
- * Helper for setting the active texture unit.
- *
- * @param textureunit Index in the range of [0, maxtextureunits-1]
- **/
-void setActiveTextureUnit(int textureunit);
-
-/**
- * Helper for binding an OpenGL texture.
- * Makes sure we aren't redundantly binding textures.
- **/
-void bindTexture(GLuint texture);
-
-/**
- * Helper for binding a texture to a specific texture unit.
- *
- * @param textureunit Index in the range of [0, maxtextureunits-1]
- * @param resoreprev Restore previously bound texture unit when done.
- **/
-void bindTextureToUnit(GLuint texture, int textureunit, bool restoreprev);
-
-/**
- * Helper for deleting an OpenGL texture.
- * Cleans up if the texture is currently bound.
- **/
-void deleteTexture(GLuint texture);
-
-/**
- * Sets the image filter mode for the currently bound texture.
- */
-void setTextureFilter(const graphics::Image::Filter &f);
-
-/**
- * Returns the image filter mode for the currently bound texture.
- */
-graphics::Image::Filter getTextureFilter();
-
-/**
- * Sets the image wrap mode for the currently bound texture.
- */
-void setTextureWrap(const graphics::Image::Wrap &w);
-
-/**
- * Returns the image wrap mode for the currently bound texture.
- */
-graphics::Image::Wrap getTextureWrap();
-
-} // opengl
-} // graphics
-} // love
-
-#endif

src/modules/graphics/opengl/ParticleSystem.cpp

 
 #include "common/math.h"
 
-#include "OpenGL.h"
+#include "Context.h"
 #include <cmath>
 #include <cstdlib>
 

src/modules/graphics/opengl/Quad.cpp

 #include "common/Matrix.h"
 
 // OpenGL
-#include "OpenGL.h"
+#include "Context.h"
 
 // STD
 #include <cstring> // For memcpy

src/modules/graphics/opengl/Shader.cpp

 
 	current = this;
 
+	Context *ctx = getContext();
+
 	if (!temporary)
 	{
 		// make sure all sent textures are properly bound to their respective texture units
 		for (size_t i = 0; i < activeTextureUnits.size(); ++i)
 		{
 			if (activeTextureUnits[i] > 0)
-				bindTextureToUnit(activeTextureUnits[i], i + 1, false);
+				ctx->bindTextureToUnit(activeTextureUnits[i], i + 1, false);
 		}
-		setActiveTextureUnit(0);
+		ctx->setActiveTextureUnit(0);
 	}
 }
 
 	GLint location = getUniformLocation(name);
 	int textureunit = getTextureUnit(name);
 
+	Context *ctx = getContext();
+
 	// bind texture to assigned texture unit and send uniform to shader program
-	bindTextureToUnit(texture, textureunit, false);
+	ctx->bindTextureToUnit(texture, textureunit, false);
 	glUniform1i(location, textureunit);
 
 	// reset texture unit
-	setActiveTextureUnit(0);
+	ctx->setActiveTextureUnit(0);
 
 	// throw error if needed
 	checkSetUniformError();

src/modules/graphics/opengl/Shader.h

 #include <string>
 #include <map>
 #include <vector>
-#include "OpenGL.h"
+#include "Context.h"
 #include "Image.h"
 #include "Canvas.h"
 

src/modules/graphics/opengl/SpriteBatch.cpp

 #include <iostream>
 
 // OpenGL
-#include "OpenGL.h"
+#include "Context.h"
 
 // LOVE
 #include "Image.h"

src/modules/graphics/opengl/VertexBuffer.h

 #include "graphics/Volatile.h"
 
 // OpenGL
-#include "OpenGL.h"
+#include "Context.h"
 
 namespace love
 {

src/modules/graphics/opengl/wrap_Graphics.cpp

  **/
 
 #include "wrap_Graphics.h"
-#include "OpenGL.h"
+#include "Context.h"
 #include "graphics/DrawQable.h"
 #include "image/ImageData.h"
 #include "font/Rasterizer.h"