Browse Source

Added 'VertexBuffer' abstraction. SpriteBatches are now always supported.

Also improved memory usage in SpriteBatch. (No need to retain full copy of
sprites in memory. Can read that data back later to save data from OpenGL
context destruction).
rude 14 years ago
parent
commit
480f936815

+ 2 - 0
platform/msvc2010/love.vcxproj

@@ -104,6 +104,7 @@
     <ClCompile Include="..\..\src\modules\graphics\opengl\PixelEffect.cpp" />
     <ClCompile Include="..\..\src\modules\graphics\opengl\Quad.cpp" />
     <ClCompile Include="..\..\src\modules\graphics\opengl\SpriteBatch.cpp" />
+    <ClCompile Include="..\..\src\modules\graphics\opengl\VertexBuffer.cpp" />
     <ClCompile Include="..\..\src\modules\graphics\opengl\wrap_Font.cpp" />
     <ClCompile Include="..\..\src\modules\graphics\opengl\wrap_Framebuffer.cpp" />
     <ClCompile Include="..\..\src\modules\graphics\opengl\wrap_Graphics.cpp" />
@@ -270,6 +271,7 @@
     <ClInclude Include="..\..\src\modules\graphics\opengl\PixelEffect.h" />
     <ClInclude Include="..\..\src\modules\graphics\opengl\Quad.h" />
     <ClInclude Include="..\..\src\modules\graphics\opengl\SpriteBatch.h" />
+    <ClInclude Include="..\..\src\modules\graphics\opengl\VertexBuffer.h" />
     <ClInclude Include="..\..\src\modules\graphics\opengl\wrap_Font.h" />
     <ClInclude Include="..\..\src\modules\graphics\opengl\wrap_Framebuffer.h" />
     <ClInclude Include="..\..\src\modules\graphics\opengl\wrap_Graphics.h" />

+ 6 - 0
platform/msvc2010/love.vcxproj.filters

@@ -595,6 +595,9 @@
     <ClCompile Include="..\..\src\modules\graphics\opengl\wrap_PixelEffect.cpp">
       <Filter>modules\graphics\opengl</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\modules\graphics\opengl\VertexBuffer.cpp">
+      <Filter>modules\graphics\opengl</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\src\modules\audio\Audio.h">
@@ -966,6 +969,9 @@
     <ClInclude Include="..\..\src\modules\graphics\opengl\wrap_PixelEffect.h">
       <Filter>modules\graphics\opengl</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\src\modules\graphics\opengl\VertexBuffer.h">
+      <Filter>modules\graphics\opengl</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="app.rc" />

+ 0 - 1
src/modules/graphics/Graphics.cpp

@@ -153,7 +153,6 @@ namespace graphics
 	{
 		{ "framebuffers", Graphics::SUPPORT_FRAMEBUFFERS },
 		{ "pixeleffects", Graphics::SUPPORT_PIXELEFFECTS },
-		{ "spritebatches", Graphics::SUPPORT_SPRITEBATCHES }
 	};
 
 	StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));

+ 0 - 1
src/modules/graphics/Graphics.h

@@ -82,7 +82,6 @@ namespace graphics
 		{
 			SUPPORT_FRAMEBUFFERS = 1,
 			SUPPORT_PIXELEFFECTS,
-			SUPPORT_SPRITEBATCHES,
 			SUPPORT_MAX_ENUM
 		};
 

+ 81 - 113
src/modules/graphics/opengl/SpriteBatch.cpp

@@ -26,6 +26,7 @@
 // LOVE
 #include "Image.h"
 #include "Quad.h"
+#include "VertexBuffer.h"
 
 namespace love
 {
@@ -34,80 +35,68 @@ namespace graphics
 namespace opengl
 {
 	SpriteBatch::SpriteBatch(Image * image, int size, int usage)
-		: image(image), size(size), next(0), usage(usage), lockp(0), color(0)
+		: image(image)
+		, size(size)
+		, next(0)
+		, color(0)
+		, array_buf(0)
+		, element_buf(0)
 	{
-		if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5))
-			throw love::Exception("Your OpenGL version does not support SpriteBatches. Go upgrade!");
-
 		image->retain();
 
-		vertices = new vertex[size*4];
-		indices = new GLushort[size*6];
+		GLenum gl_usage;
 
-		for(int i = 0; i<size; i++)
+		switch (usage)
 		{
-			indices[i*6+0] = 0+(i*4);
-			indices[i*6+1] = 1+(i*4);
-			indices[i*6+2] = 2+(i*4);
-
-			indices[i*6+3] = 0+(i*4);
-			indices[i*6+4] = 2+(i*4);
-			indices[i*6+5] = 3+(i*4);
+		default:
+		case USAGE_DYNAMIC:
+			gl_usage = GL_DYNAMIC_DRAW;
+			break;
+		case USAGE_STATIC:
+			gl_usage = GL_STATIC_DRAW;
+			break;
+		case USAGE_STREAM:
+			gl_usage = GL_STREAM_DRAW;
+			break;
 		}
 
-		loadVolatile();
-	}
-
-	SpriteBatch::~SpriteBatch()
-	{
-		image->release();
-
-		if(vbo[0] != 0 && vbo[1] != 0)
-			glDeleteBuffers(2, vbo);
-
-		delete [] vertices;
-		delete [] indices;
-		delete color;
-	}
+		int vertex_size = sizeof(vertex) * 4 * size;
+		int element_size = sizeof(GLushort) * 6 * size;
 
-	bool SpriteBatch::isSupported()
-	{
-		return (GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5);
-	}
+		array_buf = VertexArray::Create(vertex_size, GL_ARRAY_BUFFER, gl_usage);
+		element_buf = VertexArray::Create(element_size, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW);
 
-	bool SpriteBatch::loadVolatile()
-	{
-		// Find out which OpenGL VBO usage hint to use.
-		gl_usage = GL_STREAM_DRAW_ARB;
-		gl_usage = (usage == USAGE_DYNAMIC) ? GL_DYNAMIC_DRAW_ARB : gl_usage;
-		gl_usage = (usage == USAGE_STATIC) ? GL_STATIC_DRAW_ARB : gl_usage;
-		gl_usage = (usage == USAGE_STREAM) ? GL_STREAM_DRAW_ARB : gl_usage;
+		// Fill element buffer.
+		{
+			VertexBuffer::Bind bind(*element_buf);
 
-		glGenBuffersARB(2, vbo);
+			GLushort *indices = static_cast<GLushort*>(element_buf->map(GL_WRITE_ONLY));
 
-		glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo[0]);
-		glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(vertex)*size*4, vertices, gl_usage);
-		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
+			if (indices)
+			{
+				for (int i = 0; i < size; ++i)
+				{
+					indices[i*6+0] = 0+(i*4);
+					indices[i*6+1] = 1+(i*4);
+					indices[i*6+2] = 2+(i*4);
 
-		glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vbo[1]);
-		glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(GLushort)*size*6, indices, GL_STATIC_DRAW_ARB);
-		glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+					indices[i*6+3] = 0+(i*4);
+					indices[i*6+4] = 2+(i*4);
+					indices[i*6+5] = 3+(i*4);
+				}
+			}
 
-		return true;
+			element_buf->unmap();
+		}
 	}
 
-	void SpriteBatch::unloadVolatile()
+	SpriteBatch::~SpriteBatch()
 	{
-		vertex * v = (vertex *)lock();
-
-		// Copy to system memory.
-		memcpy(vertices, v, sizeof(vertex)*size*4);
-
-		unlock();
+		image->release();
 
-		// Delete the buffers.
-		if(vbo[0] != 0 && vbo[1] != 0)
-			glDeleteBuffersARB(2, vbo);
+		delete color;
+		delete array_buf;
+		delete element_buf;
 	}
 
 	void SpriteBatch::add(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky)
@@ -115,21 +104,18 @@ namespace opengl
 		// Only do this if there's a free slot.
 		if(next < size)
 		{
-			// Get a pointer to the correct insertion position.
-			vertex * v = vertices + next*4;
-
 			// Needed for texture coordinates.
-			memcpy(v, image->getVertices(), sizeof(vertex)*4);
+			memcpy(sprite, image->getVertices(), sizeof(vertex)*4);
 
 			// Transform.
 			Matrix t;
 			t.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
-			t.transform(v, v, 4);
+			t.transform(sprite, sprite, 4);
 
 			if (color)
-				setColorv(v, *color);
+				setColorv(sprite, *color);
 
-			addv(v);
+			addv(sprite);
 
 			// Increment counter.
 			next++;
@@ -141,21 +127,18 @@ namespace opengl
 		// Only do this if there's a free slot.
 		if(next < size)
 		{
-			// Get a pointer to the correct insertion position.
-			vertex * v = vertices + next*4;
-
 			// Needed for colors.
-			memcpy(v, quad->getVertices(), sizeof(vertex)*4);
+			memcpy(sprite, quad->getVertices(), sizeof(vertex)*4);
 
 			// Transform.
 			Matrix t;
 			t.setTransformation(x, y, a, sx, sy, ox, oy, kx, ky);
-			t.transform(v, v, 4);
+			t.transform(sprite, sprite, 4);
 
 			if (color)
-				setColorv(v, *color);
+				setColorv(sprite, *color);
 
-			addv(v);
+			addv(sprite);
 
 			// Increment counter.
 			next++;
@@ -170,22 +153,16 @@ namespace opengl
 
 	void * SpriteBatch::lock()
 	{
-		// If already locked, prevent from locking again.
-		if(lockp != 0)
-			return lockp;
-
-		glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo[0]);
-		lockp = (vertex *)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB);
-		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
-		return lockp;
+		VertexArray::Bind bind(*array_buf);
+
+		return array_buf->map(GL_READ_WRITE);
 	}
 
 	void SpriteBatch::unlock()
 	{
-		glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo[0]);
-		glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
-		lockp = 0;
-		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
+		VertexArray::Bind bind(*array_buf);
+
+		array_buf->unmap();
 	}
 
 	void SpriteBatch::setImage(Image * newimage)
@@ -211,6 +188,10 @@ namespace opengl
 
 	void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
 	{
+		const int color_offset = 0;
+		const int vertex_offset = sizeof(unsigned char) * 4;
+		const int texel_offset = sizeof(unsigned char) * 4 + sizeof(float) * 2;
+
 		static Matrix t;
 
 		glPushMatrix();
@@ -220,51 +201,38 @@ namespace opengl
 
 		image->bind();
 
-		// Enable vertex arrays.
-		glEnableClientState(GL_VERTEX_ARRAY);
-		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-
-		// Bind the VBO buffer.
-		glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo[0]);
-		glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vbo[1]);
+		VertexBuffer::Bind array_bind(*array_buf);
+		VertexBuffer::Bind element_bind(*element_buf);
 
 		// Apply per-sprite color, if a color is set.
 		if (color)
 		{
 			glEnableClientState(GL_COLOR_ARRAY);
-			glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), 0);
+			glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), array_buf->getPointer(color_offset));
 		}
 
-		glVertexPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)(sizeof(unsigned char)*4));
-		glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), (GLvoid*)(sizeof(unsigned char)*4+sizeof(float)*2));
-		
-		glDrawElements(GL_TRIANGLES, next*6, GL_UNSIGNED_SHORT, 0);
+		glEnableClientState(GL_VERTEX_ARRAY);
+		glVertexPointer(2, GL_FLOAT, sizeof(vertex), array_buf->getPointer(vertex_offset));
 
-		// Disable vertex arrays.
-		glDisableClientState(GL_COLOR_ARRAY);
-		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-		glDisableClientState(GL_VERTEX_ARRAY);
+		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+		glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), array_buf->getPointer(texel_offset));
 
-		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
-		glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+		glDrawElements(GL_TRIANGLES, next*6, GL_UNSIGNED_SHORT, element_buf->getPointer(0));
+
+		glDisableClientState(GL_VERTEX_ARRAY);
+		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+		glDisableClientState(GL_COLOR_ARRAY);
 
 		glPopMatrix();
 	}
 
 	void SpriteBatch::addv(const vertex * v)
 	{
-		if(lockp != 0)
-		{
-			// Copy into mapped memory if buffer is locked.
-			memcpy(lockp + (next*4), v,  sizeof(vertex)*4);
-		}
-		else
-		{
-			// ... use glBufferSubData otherwise.
-			glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo[0]);
-			glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, (next*4)*sizeof(vertex), sizeof(vertex)*4, v);
-			glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
-		}
+		int sprite_size = sizeof(vertex) * 4;
+
+		VertexArray::Bind bind(*array_buf);
+
+		array_buf->fill(next * sprite_size, sprite_size, v);
 	}
 
 	void SpriteBatch::setColorv(vertex * v, const Color & color)

+ 6 - 21
src/modules/graphics/opengl/SpriteBatch.h

@@ -46,8 +46,9 @@ namespace opengl
 	// Forward declarations.
 	class Image;
 	class Quad;
+	class VertexBuffer;
 
-	class SpriteBatch : public Drawable, public Volatile
+	class SpriteBatch : public Drawable
 	{
 	private:
 
@@ -59,25 +60,15 @@ namespace opengl
 		// The next free element.
 		int next;
 
-		GLuint vbo[2];
-
-		// Vertex Buffer.
-		vertex * vertices;
-
-		// Index buffer.
-		GLushort * indices;
-
-		// The uage hint for the vertex buffer.
-		int usage;
-		int gl_usage;
-
-		// If the buffer is locked, this pointer is nonzero.
-		vertex * lockp;
+		vertex sprite[4];
 
 		// Current color. This color, if present, will be applied to the next
 		// added quad.
 		Color * color;
 
+		VertexBuffer *array_buf;
+		VertexBuffer *element_buf;
+
 	public:
 
 		enum UsageHint
@@ -90,12 +81,6 @@ namespace opengl
 		SpriteBatch(Image * image, int size, int usage);
 		virtual ~SpriteBatch();
 
-		static bool isSupported();
-
-		// Implements Volatile.
-		bool loadVolatile();
-		void unloadVolatile();
-
 		void add(float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky);
 		void addq(Quad * quad, float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky);
 		void clear();

+ 237 - 0
src/modules/graphics/opengl/VertexBuffer.cpp

@@ -0,0 +1,237 @@
+/**
+* Copyright (c) 2006-2011 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 "VertexBuffer.h"
+
+#include "common/Exception.h"
+
+#include <cstdlib>
+
+namespace love
+{
+namespace graphics
+{
+namespace opengl
+{
+	// VertexBuffer
+
+	VertexBuffer *VertexBuffer::Create(int size, GLenum target, GLenum usage)
+	{
+		try
+		{
+			// Try to create a VBO.
+			return new VBO(size, target, usage);
+
+		} catch (const love::Exception &e)
+		{
+			(void)e;
+
+			// VBO not supported ... create regular array.
+			return new VertexArray(size, target, usage);
+		}
+	}
+
+	VertexBuffer::VertexBuffer(int size, GLenum target, GLenum usage)
+		: size(size)
+		, target(target)
+		, usage(usage)
+	{
+	}
+
+	VertexBuffer::~VertexBuffer()
+	{
+	}
+
+	// VertexBuffer::Bind
+
+	VertexBuffer::Bind::Bind(VertexBuffer &buf)
+		: buf(buf)
+	{
+		buf.bind();
+	}
+
+	VertexBuffer::Bind::~Bind()
+	{
+		buf.unbind();
+	}
+
+	// VertexArray
+
+	VertexArray::VertexArray(int size, GLenum target, GLenum usage)
+		: VertexBuffer(size, target, usage)
+		, buf(new char[size])
+	{
+	}
+
+	VertexArray::~VertexArray()
+	{
+		delete [] buf;
+	}
+
+	void *VertexArray::map(GLenum access)
+	{
+		return buf;
+	}
+
+	void VertexArray::unmap()
+	{
+	}
+
+	void VertexArray::bind()
+	{
+	}
+
+	void VertexArray::unbind()
+	{
+	}
+
+	void VertexArray::fill(int offset, int size, const void *data)
+	{
+		memcpy(buf + offset, data, size);
+	}
+
+	const void *VertexArray::getPointer(int offset) const
+	{
+		return buf + offset;
+	}
+
+	// VBO
+
+	VBO::VBO(int size, GLenum target, GLenum usage)
+		: VertexBuffer(size, target, usage)
+		, vbo(0)
+		, buffer_copy(0)
+		, mapped(0)
+	{
+		if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5))
+			throw love::Exception("Not supported");
+
+		bool ok = load(false);
+
+		if (!ok)
+			throw love::Exception("Could not load VBO.");
+	}
+
+	VBO::~VBO()
+	{
+		if (vbo != 0)
+			unload(false);
+	}
+
+	void *VBO::map(GLenum access)
+	{
+		// Don't map twice.
+		if (mapped)
+			return mapped;
+
+		mapped = glMapBufferARB(getTarget(), access);
+
+		return mapped;
+	}
+
+	void VBO::unmap()
+	{
+		glUnmapBufferARB(getTarget());
+		mapped = 0;
+	}
+
+	void VBO::bind()
+	{
+		glBindBufferARB(getTarget(), vbo);
+	}
+
+	void VBO::unbind()
+	{
+		glBindBufferARB(getTarget(), 0);
+	}
+
+	void VBO::fill(int offset, int size, const void *data)
+	{
+		if (mapped)
+			memcpy(static_cast<char*>(mapped) + offset, data, size);
+		else
+			glBufferSubDataARB(getTarget(), offset, size, data);
+	}
+
+	const void *VBO::getPointer(int offset) const
+	{
+		return reinterpret_cast<const void*>(offset);
+	}
+
+	bool VBO::loadVolatile()
+	{
+		return load(true);
+	}
+
+	void VBO::unloadVolatile()
+	{
+		unload(true);
+	}
+
+	bool VBO::load(bool restore)
+	{
+		glGenBuffersARB(1, &vbo);
+
+		VertexBuffer::Bind bind(*this);
+
+		// Copy the old buffer only if 'restore' was requested.
+		const GLvoid *src = restore ? buffer_copy : 0;
+		
+		// Note that if 'src' is '0', no data will be copied.
+		glBufferDataARB(getTarget(), getSize(), src, getUsage());
+
+		// Clean up buffer_copy, if it exists.
+		delete[] buffer_copy;
+		buffer_copy = 0;
+
+		return true;
+	}
+
+	void VBO::unload(bool save)
+	{
+		// Clean up buffer_copy, if it exists.
+		delete[] buffer_copy;
+		buffer_copy = 0;
+
+		// Save data before unloading.
+		if (save)
+		{
+			VertexBuffer::Bind bind(*this);
+
+			GLint size;
+			glGetBufferParameterivARB(getTarget(), GL_BUFFER_SIZE, &size);
+
+			const char *src = static_cast<char *>(map(GL_READ_ONLY));
+
+			if (src)
+			{
+				buffer_copy = new char[size];
+				memcpy(buffer_copy, src, size);
+				unmap();
+			}
+		}
+
+		glDeleteBuffers(1, &vbo);
+		vbo = 0;
+	}
+
+} // opengl
+} // graphics
+} // love

+ 287 - 0
src/modules/graphics/opengl/VertexBuffer.h

@@ -0,0 +1,287 @@
+/**
+* Copyright (c) 2006-2011 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_VERTEX_BUFFER_H
+#define LOVE_GRAPHICS_OPENGL_VERTEX_BUFFER_H
+
+// LOVE
+#include <graphics/Volatile.h>
+
+// OpenGL
+#include "GLee.h"
+#include <SDL/SDL_opengl.h>
+
+namespace love
+{
+namespace graphics
+{
+namespace opengl
+{
+	/**
+	 * VertexBuffer is an abstraction over VBOs (Vertex Buffer Objecys), which
+	 * falls back to regular vertex arrays if VBOs are not supported.
+	 *
+	 * This allows code to take advantage of VBOs where available, but still
+	 * work on older systems where it's *not* available. Everyone's happy.
+	 * 
+	 * The class is (for now) meant for internal use.
+	 */
+	class VertexBuffer
+	{
+	public:
+
+		/**
+		 * Create a new VertexBuffer (either a plain vertex array, or a VBO),
+		 * based on what's supported on the system.
+		 *
+		 * If VBOs are not supported, a plain vertex array will automatically
+		 * be created and returned instead.
+		 *
+		 * @param size The size of the VertexBuffer (in bytes).
+		 * @param target GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER.
+		 * @param usage GL_DYNAMIC_DRAW, etc.
+		 * @return A new VertexBuffer.
+		 */
+		static VertexBuffer *Create(int size, GLenum target, GLenum usage);
+
+		/**
+		 * Constructor.
+		 *
+		 * @param size The size of the VertexBuffer in bytes.
+		 * @param target The target VertexBuffer object, e.g. GL_ARRAY_BUFFER.
+		 * @param usage Usage hint, e.g. GL_DYNAMIC_DRAW.
+		 */
+		VertexBuffer(int size, GLenum target, GLenum usage);
+
+		/**
+		 * Destructor. Does nothing, but must be declared virtual.
+		 */
+		virtual ~VertexBuffer();
+
+		/**
+		 * Get the size of the VertexBuffer, in bytes.
+		 *
+		 * @return The size of the VertexBuffer.
+		 */
+		int getSize() const { return size; }
+
+		/**
+		 * Get the target buffer object.
+		 *
+		 * @return The target buffer object, e.g. GL_ARRAY_BUFFER.
+		 */
+		GLenum getTarget() const { return target; }
+
+		/**
+		 * Get the usage hint for this VertexBuffer.
+		 *
+		 * @return The usage hint, e.g. GL_DYNAMIC_DRAW.
+		 */
+		GLenum getUsage() const { return usage; }
+
+		/**
+		 * Map the VertexBuffer to client memory.
+		 *
+		 * This can be faster for large changes to the buffer. For smaller
+		 * changes, see fill().
+		 *
+		 * The VertexBuffer must be bound to use this function.
+		 *
+		 * @param access GL_READ_ONLY, GL_WRITE_ONLY, GL_READ_WRITE.
+		 * @return A pointer to memory which represents the buffer.
+		 */
+		virtual void *map(GLenum access) = 0;
+
+		/**
+		 * Unmap a previously mapped VertexBuffer. The buffer must be unmapped
+		 * when used to draw elements.
+		 *
+		 * The VertexBuffer must be bound to use this function.
+		 */
+		virtual void unmap() = 0;
+
+		/**
+		 * Bind the VertexBuffer to the specified target.
+		 * (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
+		 *
+		 * @param target GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER.
+		 */
+		virtual void bind() = 0;
+
+		/**
+		 * Unbind a prevously bound VertexBuffer.
+		 */
+		virtual void unbind() = 0;
+
+		/**
+		 * Fill a portion of the buffer with data.
+		 *
+		 * The VertexBuffer must be bound to use this function.
+		 *
+		 * @param offset The offset in the VertexBuffer to store the data.
+		 * @param size The size of the incoming data.
+		 * @param data Pointer to memory to copy data from.
+		 */
+		virtual void fill(int offset, int size, const void *data) = 0;
+
+		/**
+		 * Get a pointer which represents the specified byte offset.
+		 *
+		 * @param offset The byte offset. (0 is first byte).
+		 * @return A pointer which represents the offset.
+		 */
+		virtual const void *getPointer(int offset) const = 0;
+
+		/**
+		 * This helper class can bind a VertexArray temporarily, and
+		 * automatically un-bind when it's destroyed.
+		 */
+		class Bind
+		{
+		public:
+
+			/**
+			 * Bind a VertexBuffer.
+			 */
+			Bind(VertexBuffer &buf);
+			
+			/**
+			 * Unbinds a VertexBuffer.
+			 */
+			~Bind();
+
+		private:
+
+			// VertexBuffer to work on.
+			VertexBuffer &buf;
+		};
+
+	private:
+
+		// The size of the buffer, in bytes.
+		int size;
+
+		// The target buffer object. (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER).
+		GLenum target;
+
+		// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
+		GLenum usage;
+	};
+
+	/**
+	 * Implementation of VertexBuffer which uses plain arrays to store the data.
+	 *
+	 * This implementation should be supported everywhere, and acts as a fallback
+	 * on systems which do not support VBOs.
+	 */
+	class VertexArray : public VertexBuffer
+	{
+	public:
+
+		/**
+		 * @copydoc VertexBuffer(int, GLenum, GLenum)
+		 */ 
+		VertexArray(int size, GLenum target, GLenum usage);
+
+		/**
+		 * Frees the data we've allocated.
+		 */
+		virtual ~VertexArray();
+
+		// Implements VertexBuffer.
+		virtual void *map(GLenum access);
+		virtual void unmap();
+		virtual void bind();
+		virtual void unbind();
+		virtual void fill(int offset, int size, const void *data);
+		virtual const void *getPointer(int offset) const ;
+
+	private:
+		// Holds the data.
+		char *buf;
+	};
+
+	/**
+	 * Vertex Buffer Object (VBO) implementation of VertexBuffer.
+	 *
+	 * This will be used on all systems that support it. It's in general
+	 * faster than vertex arrays, but especially in use-cases where there
+	 * is no need to update the data every frame.
+	 */
+	class VBO : public VertexBuffer, public Volatile
+	{
+	public:
+
+		/**
+		 * @copydoc VertexBuffer(int, GLenum, GLenum)
+		 */ 
+		VBO(int size, GLenum target, GLenum usage);
+
+		/**
+		 * Deletes the VBOs from OpenGL.
+		 */
+		virtual ~VBO();
+
+		// Implements VertexBuffer.
+		virtual void *map(GLenum access);
+		virtual void unmap();
+		virtual void bind();
+		virtual void unbind();
+		virtual void fill(int offset, int size, const void *data);
+		virtual const void *getPointer(int offset) const ;
+
+		// Implements Volatile.
+		bool loadVolatile();
+		void unloadVolatile();
+
+	private:
+
+		/**
+		 * Creates the VBO, and optionally restores data we saved earlier.
+		 *
+		 * @param restore True to restore data previously saved with 'unload'.
+		 * @return True on success, false otherwise.
+		 */
+		bool load(bool restore);
+
+		/**
+		 * Optionally save the data in the VBO, then delete it.
+		 *
+		 * @param save True to save the data before deleting.
+		 */
+		void unload(bool save);
+
+		// The VBO identifier. Assigned by OpenGL.
+		GLuint vbo;
+
+		// *May* contain data saved by 'unload'.
+		char *buffer_copy;
+
+		// A pointer to mapped memory. Zero if memory is currently
+		// not mapped.
+		void *mapped;
+	};
+
+} // opengl
+} // graphics
+} // love
+
+#endif // LOVE_GRAPHICS_OPENGL_SPRITE_BATCH_H

+ 0 - 4
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -774,10 +774,6 @@ namespace opengl
 					if (!PixelEffect::isSupported())
 						supported = false;
 					break;
-				case Graphics::SUPPORT_SPRITEBATCHES:
-					if (!SpriteBatch::isSupported())
-						supported = false;
-					break;
 				default:
 					supported = false;
 			}