Browse Source

love.graphics internal Buffer objects are no longer OpenGL-specific.

--HG--
branch : minor
Alex Szpakowski 8 years ago
parent
commit
d990044288

+ 4 - 2
CMakeLists.txt

@@ -465,6 +465,8 @@ source_group("modules\\font\\freetype" FILES ${LOVE_SRC_MODULE_FONT_FREETYPE})
 #
 
 set(LOVE_SRC_MODULE_GRAPHICS_ROOT
+	src/modules/graphics/Buffer.cpp
+	src/modules/graphics/Buffer.h
 	src/modules/graphics/Canvas.cpp
 	src/modules/graphics/Canvas.h
 	src/modules/graphics/Color.h
@@ -503,14 +505,14 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 )
 
 set(LOVE_SRC_MODULE_GRAPHICS_OPENGL
+	src/modules/graphics/opengl/Buffer.cpp
+	src/modules/graphics/opengl/Buffer.h
 	src/modules/graphics/opengl/BufferSync.cpp
 	src/modules/graphics/opengl/BufferSync.h
 	src/modules/graphics/opengl/Canvas.cpp
 	src/modules/graphics/opengl/Canvas.h
 	src/modules/graphics/opengl/Font.cpp
 	src/modules/graphics/opengl/Font.h
-	src/modules/graphics/opengl/GLBuffer.cpp
-	src/modules/graphics/opengl/GLBuffer.h
 	src/modules/graphics/opengl/Graphics.cpp
 	src/modules/graphics/opengl/Graphics.h
 	src/modules/graphics/opengl/Image.cpp

+ 20 - 10
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -453,9 +453,9 @@
 		FA0B7D511A95902C000E1D17 /* Text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BA11A95902C000E1D17 /* Text.cpp */; };
 		FA0B7D521A95902C000E1D17 /* Text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BA11A95902C000E1D17 /* Text.cpp */; };
 		FA0B7D531A95902C000E1D17 /* Text.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7BA21A95902C000E1D17 /* Text.h */; };
-		FA0B7D551A95902C000E1D17 /* GLBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BA41A95902C000E1D17 /* GLBuffer.cpp */; };
-		FA0B7D561A95902C000E1D17 /* GLBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BA41A95902C000E1D17 /* GLBuffer.cpp */; };
-		FA0B7D571A95902C000E1D17 /* GLBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7BA51A95902C000E1D17 /* GLBuffer.h */; };
+		FA0B7D551A95902C000E1D17 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BA41A95902C000E1D17 /* Buffer.cpp */; };
+		FA0B7D561A95902C000E1D17 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BA41A95902C000E1D17 /* Buffer.cpp */; };
+		FA0B7D571A95902C000E1D17 /* Buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7BA51A95902C000E1D17 /* Buffer.h */; };
 		FA0B7D5E1A95902C000E1D17 /* wrap_Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BAA1A95902C000E1D17 /* wrap_Graphics.cpp */; };
 		FA0B7D5F1A95902C000E1D17 /* wrap_Graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0B7BAA1A95902C000E1D17 /* wrap_Graphics.cpp */; };
 		FA0B7D601A95902C000E1D17 /* wrap_Graphics.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0B7BAB1A95902C000E1D17 /* wrap_Graphics.h */; };
@@ -1011,6 +1011,9 @@
 		FAB2D5AA1AABDD8A008224A4 /* TrueTypeRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAB2D5A81AABDD8A008224A4 /* TrueTypeRasterizer.cpp */; };
 		FAB2D5AB1AABDD8A008224A4 /* TrueTypeRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAB2D5A81AABDD8A008224A4 /* TrueTypeRasterizer.cpp */; };
 		FAB2D5AC1AABDD8A008224A4 /* TrueTypeRasterizer.h in Headers */ = {isa = PBXBuildFile; fileRef = FAB2D5A91AABDD8A008224A4 /* TrueTypeRasterizer.h */; };
+		FADF53F81E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
+		FADF53F91E3C7ACD00012CC0 /* Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */; };
+		FADF53FA1E3C7ACD00012CC0 /* Buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FADF53F71E3C7ACD00012CC0 /* Buffer.h */; };
 		FAE272521C05A15B00A67640 /* ParticleSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAE272501C05A15B00A67640 /* ParticleSystem.cpp */; };
 		FAE272531C05A15B00A67640 /* ParticleSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE272511C05A15B00A67640 /* ParticleSystem.h */; };
 		FAF140531E20934C00F898D2 /* CodeGen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF13FC21E20934C00F898D2 /* CodeGen.cpp */; };
@@ -1450,8 +1453,8 @@
 		FA0B7BA01A95902C000E1D17 /* SpriteBatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpriteBatch.h; sourceTree = "<group>"; };
 		FA0B7BA11A95902C000E1D17 /* Text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Text.cpp; sourceTree = "<group>"; };
 		FA0B7BA21A95902C000E1D17 /* Text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Text.h; sourceTree = "<group>"; };
-		FA0B7BA41A95902C000E1D17 /* GLBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GLBuffer.cpp; sourceTree = "<group>"; };
-		FA0B7BA51A95902C000E1D17 /* GLBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLBuffer.h; sourceTree = "<group>"; };
+		FA0B7BA41A95902C000E1D17 /* Buffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Buffer.cpp; sourceTree = "<group>"; };
+		FA0B7BA51A95902C000E1D17 /* Buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Buffer.h; sourceTree = "<group>"; };
 		FA0B7BAA1A95902C000E1D17 /* wrap_Graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Graphics.cpp; sourceTree = "<group>"; };
 		FA0B7BAB1A95902C000E1D17 /* wrap_Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Graphics.h; sourceTree = "<group>"; };
 		FA0B7BAC1A95902C000E1D17 /* wrap_Image.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Image.cpp; sourceTree = "<group>"; };
@@ -1833,6 +1836,8 @@
 		FAB2D5A91AABDD8A008224A4 /* TrueTypeRasterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrueTypeRasterizer.h; sourceTree = "<group>"; };
 		FAC734C11B2E021A00AB460A /* wrap_SoundData.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_SoundData.lua; sourceTree = "<group>"; };
 		FAC734C21B2E628700AB460A /* wrap_ImageData.lua */ = {isa = PBXFileReference; lastKnownFileType = text; path = wrap_ImageData.lua; sourceTree = "<group>"; };
+		FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Buffer.cpp; sourceTree = "<group>"; };
+		FADF53F71E3C7ACD00012CC0 /* Buffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Buffer.h; sourceTree = "<group>"; };
 		FAE272501C05A15B00A67640 /* ParticleSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParticleSystem.cpp; sourceTree = "<group>"; };
 		FAE272511C05A15B00A67640 /* ParticleSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParticleSystem.h; sourceTree = "<group>"; };
 		FAF13FC21E20934C00F898D2 /* CodeGen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CodeGen.cpp; sourceTree = "<group>"; };
@@ -2615,6 +2620,8 @@
 		FA0B7B871A95902C000E1D17 /* graphics */ = {
 			isa = PBXGroup;
 			children = (
+				FADF53F61E3C7ACD00012CC0 /* Buffer.cpp */,
+				FADF53F71E3C7ACD00012CC0 /* Buffer.h */,
 				FA1BA0A51E16F20600AA2803 /* Canvas.cpp */,
 				FA1BA0A61E16F20600AA2803 /* Canvas.h */,
 				FA0B7B881A95902C000E1D17 /* Color.h */,
@@ -2658,14 +2665,14 @@
 		FA0B7B8C1A95902C000E1D17 /* opengl */ = {
 			isa = PBXGroup;
 			children = (
+				FA0B7BA41A95902C000E1D17 /* Buffer.cpp */,
+				FA0B7BA51A95902C000E1D17 /* Buffer.h */,
 				FA28EBD31E352DB5003446F4 /* BufferSync.cpp */,
 				FA28EBD41E352DB5003446F4 /* BufferSync.h */,
 				FA0B7B8D1A95902C000E1D17 /* Canvas.cpp */,
 				FA0B7B8E1A95902C000E1D17 /* Canvas.h */,
 				FA0B7B8F1A95902C000E1D17 /* Font.cpp */,
 				FA0B7B901A95902C000E1D17 /* Font.h */,
-				FA0B7BA41A95902C000E1D17 /* GLBuffer.cpp */,
-				FA0B7BA51A95902C000E1D17 /* GLBuffer.h */,
 				FA0B7B911A95902C000E1D17 /* Graphics.cpp */,
 				FA0B7B921A95902C000E1D17 /* Graphics.h */,
 				FA0B7B931A95902C000E1D17 /* Image.cpp */,
@@ -3533,7 +3540,7 @@
 				FAF140571E20934C00F898D2 /* arrays.h in Headers */,
 				FA0B7A371A958EA3000E1D17 /* b2Distance.h in Headers */,
 				FA0B7E681A95902C000E1D17 /* wrap_PrismaticJoint.h in Headers */,
-				FA0B7D571A95902C000E1D17 /* GLBuffer.h in Headers */,
+				FA0B7D571A95902C000E1D17 /* Buffer.h in Headers */,
 				FAF140AB1E20934C00F898D2 /* SymbolTable.h in Headers */,
 				FA27B3C71B4985D8008A9DCE /* Video.h in Headers */,
 				FA0B7ED71A95902D000E1D17 /* Timer.h in Headers */,
@@ -3628,6 +3635,7 @@
 				FA0B7E0E1A95902C000E1D17 /* Fixture.h in Headers */,
 				FA0B7A401A958EA3000E1D17 /* b2ChainShape.h in Headers */,
 				217DFBE81D9F6D490055D849 /* inet.h in Headers */,
+				FADF53FA1E3C7ACD00012CC0 /* Buffer.h in Headers */,
 				FA0B7AA61A958EA3000E1D17 /* b2RevoluteJoint.h in Headers */,
 				FA0B7EEA1A95902D000E1D17 /* wrap_Window.h in Headers */,
 				FA1557C01CE90A2C00AFF582 /* tinyexr.h in Headers */,
@@ -4084,6 +4092,7 @@
 				FA0B7D161A95902C000E1D17 /* Font.cpp in Sources */,
 				FA0B7EB61A95902C000E1D17 /* wrap_System.cpp in Sources */,
 				FA0B7DAC1A95902C000E1D17 /* STBHandler.cpp in Sources */,
+				FADF53F91E3C7ACD00012CC0 /* Buffer.cpp in Sources */,
 				FA0B79301A958E3B000E1D17 /* Module.cpp in Sources */,
 				FAF1409B1E20934C00F898D2 /* propagateNoContraction.cpp in Sources */,
 				FA0B7DDA1A95902C000E1D17 /* RandomGenerator.cpp in Sources */,
@@ -4187,7 +4196,7 @@
 				FA0B7DF51A95902C000E1D17 /* wrap_Mouse.cpp in Sources */,
 				FA0B7E861A95902C000E1D17 /* CoreAudioDecoder.cpp in Sources */,
 				FA0B7E761A95902C000E1D17 /* wrap_WeldJoint.cpp in Sources */,
-				FA0B7D561A95902C000E1D17 /* GLBuffer.cpp in Sources */,
+				FA0B7D561A95902C000E1D17 /* Buffer.cpp in Sources */,
 				FA0B7A6C1A958EA3000E1D17 /* b2World.cpp in Sources */,
 				FA1557C51CE90BD900AFF582 /* EXRHandler.cpp in Sources */,
 				FA57FB991AE1993600F2AD6D /* noise1234.cpp in Sources */,
@@ -4437,6 +4446,7 @@
 				FA0B7EB51A95902C000E1D17 /* wrap_System.cpp in Sources */,
 				FA0B7DAB1A95902C000E1D17 /* STBHandler.cpp in Sources */,
 				FA0B7AB81A958EA3000E1D17 /* enet.cpp in Sources */,
+				FADF53F81E3C7ACD00012CC0 /* Buffer.cpp in Sources */,
 				FA0B7DD91A95902C000E1D17 /* RandomGenerator.cpp in Sources */,
 				FAF1409A1E20934C00F898D2 /* propagateNoContraction.cpp in Sources */,
 				FA0B7A9B1A958EA3000E1D17 /* b2MouseJoint.cpp in Sources */,
@@ -4536,7 +4546,7 @@
 				FA0B7E851A95902C000E1D17 /* CoreAudioDecoder.cpp in Sources */,
 				FA0B7E751A95902C000E1D17 /* wrap_WeldJoint.cpp in Sources */,
 				FAF140951E20934C00F898D2 /* PpSymbols.cpp in Sources */,
-				FA0B7D551A95902C000E1D17 /* GLBuffer.cpp in Sources */,
+				FA0B7D551A95902C000E1D17 /* Buffer.cpp in Sources */,
 				FA57FB981AE1993600F2AD6D /* noise1234.cpp in Sources */,
 				FA0B7E211A95902C000E1D17 /* PolygonShape.cpp in Sources */,
 				FA0B7D511A95902C000E1D17 /* Text.cpp in Sources */,

+ 177 - 0
src/modules/graphics/Buffer.cpp

@@ -0,0 +1,177 @@
+/**
+ * Copyright (c) 2006-2017 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 "Buffer.h"
+#include "Graphics.h"
+#include "common/Exception.h"
+
+namespace love
+{
+namespace graphics
+{
+
+Buffer::Buffer(size_t size, BufferType type, vertex::Usage usage, uint32 mapflags)
+	: size(size)
+	, type(type)
+	, usage(usage)
+	, map_flags(mapflags)
+	, is_mapped(false)
+{
+}
+
+Buffer::~Buffer()
+{
+}
+
+// QuadIndices
+
+size_t QuadIndices::maxSize = 0;
+size_t QuadIndices::elementSize = 0;
+size_t QuadIndices::objectCount = 0;
+
+Buffer *QuadIndices::indexBuffer = nullptr;
+char *QuadIndices::indices = nullptr;
+
+QuadIndices::QuadIndices(Graphics *gfx, size_t size)
+: size(size)
+{
+	// The upper limit is the maximum of uint32 divided by six (the number
+	// of indices per size) and divided by the size of uint32. This guarantees
+	// no overflows when calculating the array size in bytes.
+	if (size == 0 || size > ((uint32) -1) / 6 / sizeof(uint32))
+		throw love::Exception("Invalid number of quads.");
+
+	// Create a new / larger buffer if needed.
+	if (indexBuffer == nullptr || size > maxSize)
+	{
+		Buffer *newbuffer = nullptr;
+		char *newindices = nullptr;
+
+		// Depending on the size, a switch to int and more memory is needed.
+		IndexDataType targettype = getType(size);
+		size_t elemsize = vertex::getIndexDataSize(targettype);
+
+		size_t buffersize = elemsize * 6 * size;
+
+		try
+		{
+			newbuffer = gfx->newBuffer(buffersize, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0);
+			newindices = new char[buffersize];
+		}
+		catch (std::bad_alloc &)
+		{
+			delete newbuffer;
+			delete[] newindices;
+			throw love::Exception("Out of memory.");
+		}
+
+		// Allocation of the new Buffer succeeded.
+		// The old Buffer can now be deleted.
+		delete indexBuffer;
+		indexBuffer = newbuffer;
+
+		delete[] indices;
+		indices = newindices;
+
+		maxSize = size;
+		elementSize = elemsize;
+
+		switch (targettype)
+		{
+		case INDEX_UINT16:
+			fill<uint16>();
+			break;
+		case INDEX_UINT32:
+			fill<uint32>();
+			break;
+		case INDEX_MAX_ENUM:
+			break;
+		}
+	}
+
+	objectCount++;
+}
+
+QuadIndices::QuadIndices(const QuadIndices &other)
+: size(other.size)
+{
+	objectCount++;
+}
+
+QuadIndices &QuadIndices::operator = (const QuadIndices &other)
+{
+	size = other.size;
+	return *this;
+}
+
+QuadIndices::~QuadIndices()
+{
+	--objectCount;
+
+	// Delete the buffer if we were the last living QuadIndices object.
+	if (objectCount <= 0)
+	{
+		delete indexBuffer;
+		indexBuffer = nullptr;
+
+		delete[] indices;
+		indices = nullptr;
+	}
+}
+
+size_t QuadIndices::getSize() const
+{
+	return size;
+}
+
+size_t QuadIndices::getIndexCount(size_t elements) const
+{
+	return elements * 6;
+}
+
+IndexDataType QuadIndices::getType(size_t s) const
+{
+	return vertex::getIndexDataTypeFromMax(s);
+}
+
+size_t QuadIndices::getElementSize()
+{
+	return elementSize;
+}
+
+Buffer *QuadIndices::getBuffer() const
+{
+	return indexBuffer;
+}
+
+const void *QuadIndices::getIndices(size_t offset) const
+{
+	return indices + offset;
+}
+
+template <typename T>
+void QuadIndices::fill()
+{
+	vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, maxSize * 4, (T *) indices);
+	indexBuffer->fill(0, indexBuffer->getSize(), indices);
+}
+
+} // graphics
+} // love

+ 59 - 147
src/modules/graphics/opengl/GLBuffer.h → src/modules/graphics/Buffer.h

@@ -18,17 +18,12 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-
-#ifndef LOVE_GRAPHICS_OPENGL_GL_BUFFER_H
-#define LOVE_GRAPHICS_OPENGL_GL_BUFFER_H
+#pragma once
 
 // LOVE
 #include "common/config.h"
-#include "graphics/Volatile.h"
-#include "graphics/vertex.h"
-
-// OpenGL
-#include "OpenGL.h"
+#include "common/int.h"
+#include "vertex.h"
 
 // C
 #include <stddef.h>
@@ -37,57 +32,35 @@ namespace love
 {
 namespace graphics
 {
-namespace opengl
-{
+
+class Graphics;
 
 /**
- * GLBuffer is a thin abstraction over OpenGL's Buffer Objects.
- * The class is meant for internal use.
- */
-class GLBuffer : public Volatile
+ * A block of GPU-owned memory. Currently meant for internal use.
+ **/
+class Buffer
 {
 public:
 
 	enum MapFlags
 	{
-		MAP_EXPLICIT_RANGE_MODIFY = 0x01, // see setMappedRangeModified.
+		MAP_EXPLICIT_RANGE_MODIFY = (1 << 0), // see setMappedRangeModified.
+		MAP_READ = (1 << 1),
 	};
 
-	/**
-	 * Constructor.
-	 *
-	 * @param size The size of the GLBuffer in bytes.
-	 * @param type The type of the buffer object.
-	 * @param usage Usage hint.
-	 */
-	GLBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags = 0);
-
-	/**
-	 * Destructor.
-	 */
-	virtual ~GLBuffer();
+	Buffer(size_t size, BufferType type, vertex::Usage usage, uint32 mapflags);
+	virtual ~Buffer();
 
-	/**
-	 * Get the size of the GLBuffer, in bytes.
-	 *
-	 * @return The size of the GLBuffer.
-	 */
 	size_t getSize() const
 	{
 		return size;
 	}
 
-	/**
-	 * Get the type of the buffer object.
-	 */
 	BufferType getType() const
 	{
 		return type;
 	}
 
-	/**
-	 * Get the usage hint for this GLBuffer.
-	 */
 	vertex::Usage getUsage() const
 	{
 		return usage;
@@ -99,32 +72,29 @@ public:
 	}
 
 	/**
-	 * Map the GLBuffer to client memory.
+	 * Map the Buffer to client memory.
 	 *
 	 * This can be faster for large changes to the buffer. For smaller
 	 * changes, see fill().
-	 *
-	 * @return A pointer to memory which represents the buffer.
 	 */
-	void *map();
+	virtual void *map() = 0;
 
 	/**
-	 * Unmap a previously mapped GLBuffer. The buffer must be unmapped
-	 * when used to draw elements.
+	 * Unmap a previously mapped Buffer. The buffer must be unmapped when used
+	 * to draw.
 	 */
-	void unmap();
+	virtual void unmap() = 0;
 
 	/**
 	 * Marks a range of mapped data as modified.
-	 * NOTE: GLBuffer::fill calls this internally for you.
+	 * NOTE: Buffer::fill calls this internally for you.
 	 **/
-	void setMappedRangeModified(size_t offset, size_t size);
+	virtual void setMappedRangeModified(size_t offset, size_t size) = 0;
 
 	/**
-	 * Bind the GLBuffer to its specified target.
-	 * (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, etc).
-	 */
-	void bind();
+	 * Gets the backend-specific handle for this Buffer.
+	 **/
+	virtual ptrdiff_t getHandle() const = 0;
 
 	/**
 	 * Fill a portion of the buffer with data and marks the range as modified.
@@ -133,34 +103,27 @@ public:
 	 * @param size The size of the incoming data.
 	 * @param data Pointer to memory to copy data from.
 	 */
-	void fill(size_t offset, size_t size, const void *data);
+	virtual void fill(size_t offset, size_t 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.
-	 */
-	const void *getPointer(size_t offset) const;
+	 * Copy the contents of this Buffer to another Buffer object.
+	 **/
+	virtual void copyTo(size_t offset, size_t size, Buffer *other, size_t otheroffset) = 0;
 
 	uint32 getMapFlags() const
 	{
 		return map_flags;
 	}
 
-	// Implements Volatile.
-	bool loadVolatile() override;
-	void unloadVolatile() override;
-
 	class Mapper
 	{
 	public:
 
 		/**
-		 * Memory-maps a GLBuffer.
+		 * Memory-maps a Buffer.
 		 */
-		Mapper(GLBuffer &buffer)
-			: buf(buffer)
+		Mapper(Buffer &buffer)
+		: buf(buffer)
 		{
 			elems = buf.map();
 		}
@@ -183,57 +146,33 @@ public:
 
 	private:
 
-		GLBuffer &buf;
+		Buffer &buf;
 		void *elems;
 
 	}; // Mapper
 
-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);
-	void unload();
-
-	void unmapStatic(size_t offset, size_t size);
-	void unmapStream();
-
-	// Whether the buffer is currently mapped to main memory.
-	bool is_mapped;
+protected:
 
 	// The size of the buffer, in bytes.
 	size_t size;
 
 	// The type of the buffer object.
 	BufferType type;
-	GLenum target;
 
 	// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
 	vertex::Usage usage;
-
-	// The VBO identifier. Assigned by OpenGL.
-	GLuint vbo;
-
-	// A pointer to mapped memory.
-	char *memory_map;
-
-	size_t modified_offset;
-	size_t modified_size;
-
+	
 	uint32 map_flags;
 
-}; // GLBuffer
-
+	bool is_mapped;
+	
+}; // Buffer
 
 /**
- * QuadIndices manages one shared GLBuffer that stores the indices for an
- * element array. Vertex arrays using the vertex structure (or anything else
- * that can use the pattern below) can request a size and use it for the
- * drawElements call.
+ * QuadIndices manages one shared Buffer that stores the indices for an element
+ * array. Vertex arrays using the vertex structure (or anything else that can
+ * use the pattern below) can request a size and use it for the indexed draw
+ * call.
  *
  *  indices[i*6 + 0] = i*4 + 0;
  *  indices[i*6 + 1] = i*4 + 1;
@@ -243,25 +182,23 @@ private:
  *  indices[i*6 + 4] = i*4 + 1;
  *  indices[i*6 + 5] = i*4 + 3;
  *
- * There will always be a large enough GLBuffer around until all
- * QuadIndices instances have been deleted.
+ * There will always be a large enough Buffer around until all QuadIndices
+ * instances have been deleted.
  *
  * Q: Why have something like QuadIndices?
- * A: The indices for the SpriteBatch do not change, only the array size
- * varies. Using one GLBuffer for all element arrays removes this
- * duplicated data and saves some memory.
+ * A: The indices for the SpriteBatch do not change, only the array size varies.
+ * Using one Buffer for all element arrays removes this duplicated data and
+ * saves some memory.
  */
 class QuadIndices
 {
 public:
 	/**
-	 * Adds an entry to the list of sizes and resizes the GLBuffer
+	 * Adds an entry to the list of sizes and resizes the Buffer
 	 * if needed. A size of 1 allocates a group of 6 indices for 4 vertices
 	 * creating 1 face.
-	 *
-	 * @param size The requested size in groups of 6 indices.
 	 */
-	QuadIndices(size_t size);
+	QuadIndices(Graphics *gfx, size_t size);
 
 	QuadIndices(const QuadIndices &other);
 	QuadIndices &operator = (const QuadIndices &other);
@@ -275,8 +212,6 @@ public:
 	/**
 	 * Returns the number of index groups.
 	 * This can be used for getIndexCount to get the full count of indices.
-	 *
-	 * @return The number of index groups.
 	 */
 	size_t getSize() const;
 
@@ -284,9 +219,6 @@ public:
 	 * Returns the number of indices that the passed element count will have.
 	 * Use QuadIndices::getSize to get the full index count for that
 	 * QuadIndices instance.
-	 *
-	 * @param elements The number of elements to calculate the index count for.
-	 * @return The index count.
 	 */
 	size_t getIndexCount(size_t elements) const;
 
@@ -294,12 +226,9 @@ public:
 	 * Returns the integer type of the element array.
 	 * If an optional nonzero size argument is passed, the function returns
 	 * the integer type of the element array of that size.
-	 *
-	 * @param s The size of the array to calculated the integer type of.
-	 * @return The element array integer type.
 	 */
-	GLenum getType(size_t s) const;
-	inline GLenum getType() const
+	IndexDataType getType(size_t s) const;
+	inline IndexDataType getType() const
 	{
 		return getType(maxSize);
 	}
@@ -308,28 +237,16 @@ public:
 	 * Returns the size in bytes of an element in the element array.
 	 * Can be used with getPointer to calculate an offset into the array based
 	 * on a number of elements.
-	 *
-	 * @return The size of an element in bytes.
 	 **/
 	size_t getElementSize();
 
 	/**
-	 * Returns the pointer to the GLBuffer.
-	 * The pointer will change if a new size request or removal causes
-	 * a GLBuffer resize. It is recommended to retrieve the pointer
-	 * value directly before the drawing call.
-	 *
-	 * @return The pointer to the GLBuffer.
+	 * Returns the pointer to the Buffer.
+	 * The pointer will change if a new size request or removal causes a Buffer
+	 * resize. It is recommended to retrieve the pointer value directly before
+	 * the drawing call.
 	 */
-	GLBuffer *getBuffer() const;
-
-	/**
-	 * Returns a pointer which represents the specified byte offset.
-	 *
-	 * @param offset The offset in bytes.
-	 * @return A pointer which represents the offset.
-	 */
-	const void *getPointer(size_t offset) const;
+	Buffer *getBuffer() const;
 
 	/**
 	 * Returns a direct pointer to the index data.
@@ -339,9 +256,6 @@ public:
 	 * the index data came from an Index Buffer.
 	 * So the direct pointer to the index buffer should be used instead of the
 	 * index buffer when rendering using client-side vertex arrays.
-	 *
-	 * @param offset An offset in bytes into the index data.
-	 * @return A direct pointer to the index data at the specified offset.
 	 **/
 	const void *getIndices(size_t offset) const;
 
@@ -360,20 +274,18 @@ private:
 	// The size in bytes of an element in the element array.
 	static size_t elementSize;
 
-	// The current GLBuffer size. 0 means no GLBuffer.
+	// The current GLBuffer size. 0 means no Buffer.
 	static size_t maxSize;
 
 	static size_t objectCount;
 
-	// The GLBuffer for the element array. Can be null.
-	static GLBuffer *indexBuffer;
-
+	// The Buffer for the element array. Can be null.
+	static Buffer *indexBuffer;
+	
 	// The array of indices that will also be stored in the index buffer.
 	static char *indices;
-};
 
-} // opengl
+}; // QuadIndices
+
 } // graphics
 } // love
-
-#endif // LOVE_GRAPHICS_OPENGL_GL_BUFFER_H

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

@@ -20,6 +20,7 @@
 
 // LOVE
 #include "Graphics.h"
+#include "Buffer.h"
 #include "math/MathModule.h"
 #include "Polyline.h"
 #include "font/Font.h"

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

@@ -49,6 +49,8 @@ class Reference;
 namespace graphics
 {
 
+class Buffer;
+
 const int MAX_COLOR_RENDER_TARGETS = 8;
 
 /**
@@ -315,6 +317,8 @@ public:
 
 	virtual Shader *newShader(const Shader::ShaderSource &source) = 0;
 
+	virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
+
 	bool validateShader(bool gles, const Shader::ShaderSource &source, std::string &err);
 
 	/**

+ 66 - 2
src/modules/graphics/ParticleSystem.cpp

@@ -21,6 +21,7 @@
 //LOVE
 #include "common/config.h"
 #include "ParticleSystem.h"
+#include "Graphics.h"
 
 #include "common/math.h"
 #include "modules/math/RandomGenerator.h"
@@ -52,7 +53,7 @@ float calculate_variation(float inner, float outer, float var)
 
 love::Type ParticleSystem::type("ParticleSystem", &Drawable::type);
 
-ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
+ParticleSystem::ParticleSystem(Graphics *gfx, Texture *texture, uint32 size)
 	: pMem(nullptr)
 	, pFree(nullptr)
 	, pHead(nullptr)
@@ -90,12 +91,15 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
 	, offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
 	, defaultOffset(true)
 	, relativeRotation(false)
+	, buffer(nullptr)
+	, quadIndices(gfx, size)
 {
 	if (size == 0 || size > MAX_PARTICLES)
 		throw love::Exception("Invalid ParticleSystem size.");
 
 	sizes.push_back(1.0f);
 	colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
+
 	setBufferSize(size);
 }
 
@@ -143,6 +147,8 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
 	, colors(p.colors)
 	, quads(p.quads)
 	, relativeRotation(p.relativeRotation)
+	, buffer(nullptr)
+	, quadIndices(p.quadIndices)
 {
 	setBufferSize(maxParticles);
 }
@@ -169,6 +175,13 @@ void ParticleSystem::createBuffers(size_t size)
 	{
 		pFree = pMem = new Particle[size];
 		maxParticles = (uint32) size;
+
+		auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
+
+		size_t bytes = sizeof(Vertex) * size * 4;
+		buffer = gfx->newBuffer(bytes, nullptr, BUFFER_VERTEX, vertex::USAGE_STREAM, 0);
+
+		quadIndices = QuadIndices(gfx, size);
 	}
 	catch (std::bad_alloc &)
 	{
@@ -179,10 +192,11 @@ void ParticleSystem::createBuffers(size_t size)
 
 void ParticleSystem::deleteBuffers()
 {
-	// Clean up for great gracefulness!
 	delete[] pMem;
+	delete buffer;
 
 	pMem = nullptr;
+	buffer = nullptr;
 	maxParticles = 0;
 	activeParticles = 0;
 }
@@ -933,6 +947,56 @@ void ParticleSystem::update(float dt)
 	prevPosition = position;
 }
 
+bool ParticleSystem::prepareDraw(Graphics *gfx, const Matrix4 &m)
+{
+	uint32 pCount = getCount();
+
+	if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || buffer == nullptr)
+		return false;
+
+	gfx->flushStreamDraws();
+
+	Graphics::TempTransform transform(gfx, m);
+
+	const Vertex *textureVerts = texture->getVertices();
+	Vertex *pVerts = (Vertex *) buffer->map();
+	Particle *p = pHead;
+
+	bool useQuads = !quads.empty();
+
+	Matrix3 t;
+
+	// set the vertex data for each particle (transformation, texcoords, color)
+	while (p)
+	{
+		if (useQuads)
+			textureVerts = quads[p->quadIndex]->getVertices();
+
+		// particle vertices are image vertices transformed by particle info
+		t.setTransformation(p->position.x, p->position.y, p->angle, p->size, p->size, offset.x, offset.y, 0.0f, 0.0f);
+		t.transform(pVerts, textureVerts, 4);
+
+		// Particle colors are stored as floats (0-1) but vertex colors are
+		// unsigned bytes (0-255).
+		Color c = toColor(p->color);
+
+		// set the texture coordinate and color data for particle vertices
+		for (int v = 0; v < 4; v++)
+		{
+			pVerts[v].s = textureVerts[v].s;
+			pVerts[v].t = textureVerts[v].t;
+			pVerts[v].color = c;
+		}
+
+		pVerts += 4;
+		p = p->next;
+	}
+
+	buffer->unmap();
+
+	return true;
+}
+
 bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
 {
 	return distributions.find(in, out);

+ 11 - 1
src/modules/graphics/ParticleSystem.h

@@ -29,6 +29,7 @@
 #include "Color.h"
 #include "Quad.h"
 #include "Texture.h"
+#include "Buffer.h"
 
 // STL
 #include <vector>
@@ -38,6 +39,8 @@ namespace love
 namespace graphics
 {
 
+class Graphics;
+
 /**
  * A class for creating, moving and drawing particles.
  * A big thanks to bobthebloke.org
@@ -81,7 +84,7 @@ public:
 	/**
 	 * Creates a particle system with the specified buffer size and texture.
 	 **/
-	ParticleSystem(Texture *texture, uint32 buffer);
+	ParticleSystem(Graphics *gfx, Texture *texture, uint32 buffer);
 	ParticleSystem(const ParticleSystem &p);
 
 	/**
@@ -548,6 +551,8 @@ protected:
 		int quadIndex;
 	};
 
+	bool prepareDraw(Graphics *gfx, const Matrix4 &m);
+
 	// Pointer to the beginning of the allocated memory.
 	Particle *pMem;
 
@@ -647,6 +652,11 @@ protected:
 
 	bool relativeRotation;
 
+	Buffer *buffer;
+
+	// Vertex index buffer.
+	QuadIndices quadIndices;
+
 private:
 
 	void resetOffset();

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

@@ -21,6 +21,7 @@
 // LOVE
 #include "Shader.h"
 #include "Graphics.h"
+#include "math/MathModule.h"
 
 // glslang
 #include "libraries/glslang/glslang/Public/ShaderLang.h"

+ 17 - 0
src/modules/graphics/Shader.h

@@ -31,6 +31,11 @@
 #include <vector>
 #include <stddef.h>
 
+namespace glslang
+{
+class TShader;
+}
+
 namespace love
 {
 namespace graphics
@@ -171,6 +176,8 @@ public:
 	 **/
 	virtual void setVideoTextures(ptrdiff_t ytexture, ptrdiff_t cbtexture, ptrdiff_t crtexture) = 0;
 
+	virtual ptrdiff_t getHandle() const = 0;
+
 	static bool validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err);
 
 	static bool initialize();
@@ -187,9 +194,19 @@ public:
 
 protected:
 
+	struct CachedShaderStage
+	{
+		int referenceCount;
+		ShaderStage stage;
+		glslang::TShader *glslangShader;
+		ptrdiff_t handle;
+	};
+
 	// Source code used for this Shader.
 	ShaderSource shaderSource;
 
+	static std::map<std::string, CachedShaderStage> cachedShaders;
+
 private:
 
 	static StringMap<Language, LANGUAGE_MAX_ENUM>::Entry languageEntries[];

+ 26 - 175
src/modules/graphics/opengl/GLBuffer.cpp → src/modules/graphics/opengl/Buffer.cpp

@@ -18,7 +18,7 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#include "GLBuffer.h"
+#include "Buffer.h"
 
 #include "common/Exception.h"
 #include "graphics/vertex.h"
@@ -35,16 +35,12 @@ namespace graphics
 namespace opengl
 {
 
-GLBuffer::GLBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
-	: is_mapped(false)
-	, size(size)
-	, type(type)
-	, usage(usage)
+Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
+	: love::graphics::Buffer(size, type, usage, mapflags)
 	, vbo(0)
 	, memory_map(nullptr)
 	, modified_offset(0)
 	, modified_size(0)
-	, map_flags(mapflags)
 {
 	target = OpenGL::getGLBufferType(type);
 
@@ -67,7 +63,7 @@ GLBuffer::GLBuffer(size_t size, const void *data, BufferType type, vertex::Usage
 	}
 }
 
-GLBuffer::~GLBuffer()
+Buffer::~Buffer()
 {
 	if (vbo != 0)
 		unload();
@@ -75,7 +71,7 @@ GLBuffer::~GLBuffer()
 	delete[] memory_map;
 }
 
-void *GLBuffer::map()
+void *Buffer::map()
 {
 	if (is_mapped)
 		return memory_map;
@@ -88,7 +84,7 @@ void *GLBuffer::map()
 	return memory_map;
 }
 
-void GLBuffer::unmapStatic(size_t offset, size_t size)
+void Buffer::unmapStatic(size_t offset, size_t size)
 {
 	if (size == 0)
 		return;
@@ -98,7 +94,7 @@ void GLBuffer::unmapStatic(size_t offset, size_t size)
 	glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
 }
 
-void GLBuffer::unmapStream()
+void Buffer::unmapStream()
 {
 	GLenum glusage = OpenGL::getGLBufferUsage(getUsage());
 
@@ -109,7 +105,7 @@ void GLBuffer::unmapStream()
 	glBufferData(target, (GLsizeiptr) getSize(), memory_map, glusage);
 }
 
-void GLBuffer::unmap()
+void Buffer::unmap()
 {
 	if (!is_mapped)
 		return;
@@ -127,15 +123,15 @@ void GLBuffer::unmap()
 
 	if (modified_size > 0)
 	{
-		switch (OpenGL::getGLBufferUsage(getUsage()))
+		switch (getUsage())
 		{
-		case GL_STATIC_DRAW:
+		case vertex::USAGE_STATIC:
 			unmapStatic(modified_offset, modified_size);
 			break;
-		case GL_STREAM_DRAW:
+		case vertex::USAGE_STREAM:
 			unmapStream();
 			break;
-		case GL_DYNAMIC_DRAW:
+		case vertex::USAGE_DYNAMIC:
 		default:
 			// It's probably more efficient to treat it like a streaming buffer if
 			// at least a third of its contents have been modified during the map().
@@ -153,7 +149,7 @@ void GLBuffer::unmap()
 	is_mapped = false;
 }
 
-void GLBuffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
+void Buffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
 {
 	if (!is_mapped || !(map_flags & MAP_EXPLICIT_RANGE_MODIFY))
 		return;
@@ -169,12 +165,7 @@ void GLBuffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
 	modified_size = new_range_end - modified_offset;
 }
 
-void GLBuffer::bind()
-{
-	gl.bindBuffer(getType(), vbo);
-}
-
-void GLBuffer::fill(size_t offset, size_t size, const void *data)
+void Buffer::fill(size_t offset, size_t size, const void *data)
 {
 	memcpy(memory_map + offset, data, size);
 
@@ -187,26 +178,30 @@ void GLBuffer::fill(size_t offset, size_t size, const void *data)
 	}
 }
 
-const void *GLBuffer::getPointer(size_t offset) const
+ptrdiff_t Buffer::getHandle() const
 {
-	return BUFFER_OFFSET(offset);
+	return vbo;
 }
 
-bool GLBuffer::loadVolatile()
+void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
+{
+	other->fill(otheroffset, size, memory_map + offset);
+}
+
+bool Buffer::loadVolatile()
 {
 	return load(true);
 }
 
-void GLBuffer::unloadVolatile()
+void Buffer::unloadVolatile()
 {
 	unload();
 }
 
-bool GLBuffer::load(bool restore)
+bool Buffer::load(bool restore)
 {
 	glGenBuffers(1, &vbo);
-
-	bind();
+	gl.bindBuffer(type, vbo);
 
 	while (glGetError() != GL_NO_ERROR)
 		/* Clear the error buffer. */;
@@ -220,157 +215,13 @@ bool GLBuffer::load(bool restore)
 	return (glGetError() == GL_NO_ERROR);
 }
 
-void GLBuffer::unload()
+void Buffer::unload()
 {
 	is_mapped = false;
 	gl.deleteBuffer(vbo);
 	vbo = 0;
 }
 
-
-// QuadIndices
-
-size_t QuadIndices::maxSize = 0;
-size_t QuadIndices::elementSize = 0;
-size_t QuadIndices::objectCount = 0;
-
-GLBuffer *QuadIndices::indexBuffer = nullptr;
-char *QuadIndices::indices = nullptr;
-
-QuadIndices::QuadIndices(size_t size)
-	: size(size)
-{
-	// The upper limit is the maximum of GLuint divided by six (the number
-	// of indices per size) and divided by the size of GLuint. This guarantees
-	// no overflows when calculating the array size in bytes.
-	if (size == 0 || size > ((GLuint) -1) / 6 / sizeof(GLuint))
-		throw love::Exception("Invalid number of quads.");
-
-	// Create a new / larger buffer if needed.
-	if (indexBuffer == nullptr || size > maxSize)
-	{
-		GLBuffer *newbuffer = nullptr;
-		char *newindices = nullptr;
-
-		// Depending on the size, a switch to int and more memory is needed.
-		GLenum targettype = getType(size);
-		size_t elemsize = (targettype == GL_UNSIGNED_SHORT) ? sizeof(GLushort) : sizeof(GLuint);
-
-		size_t buffersize = elemsize * 6 * size;
-
-		// Create may throw out-of-memory exceptions.
-		// QuadIndices will propagate the exception and keep the old GLBuffer.
-		try
-		{
-			newbuffer = new GLBuffer(buffersize, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC);
-			newindices = new char[buffersize];
-		}
-		catch (std::bad_alloc &)
-		{
-			delete newbuffer;
-			delete[] newindices;
-			throw love::Exception("Out of memory.");
-		}
-
-		// Allocation of the new GLBuffer succeeded.
-		// The old GLBuffer can now be deleted.
-		delete indexBuffer;
-		indexBuffer = newbuffer;
-
-		delete[] indices;
-		indices = newindices;
-
-		maxSize = size;
-		elementSize = elemsize;
-
-		switch (targettype)
-		{
-		case GL_UNSIGNED_SHORT:
-			fill<GLushort>();
-			break;
-		case GL_UNSIGNED_INT:
-			fill<GLuint>();
-			break;
-		}
-	}
-
-	objectCount++;
-}
-
-QuadIndices::QuadIndices(const QuadIndices &other)
-	: size(other.size)
-{
-	objectCount++;
-}
-
-QuadIndices &QuadIndices::operator = (const QuadIndices &other)
-{
-	size = other.size;
-	return *this;
-}
-
-QuadIndices::~QuadIndices()
-{
-	--objectCount;
-
-	// Delete the buffer if we were the last living QuadIndices object.
-	if (objectCount <= 0)
-	{
-		delete indexBuffer;
-		indexBuffer = nullptr;
-
-		delete[] indices;
-		indices = nullptr;
-	}
-}
-
-size_t QuadIndices::getSize() const
-{
-	return size;
-}
-
-size_t QuadIndices::getIndexCount(size_t elements) const
-{
-	return elements * 6;
-}
-
-GLenum QuadIndices::getType(size_t s) const
-{
-	// Calculates if unsigned short is big enough to hold all the vertex indices.
-	static const GLenum types[] = {GL_UNSIGNED_SHORT, GL_UNSIGNED_INT};
-	return types[s * 4 > std::numeric_limits<GLushort>::max()];
-	// if buffer-size > max(GLushort) then GL_UNSIGNED_INT else GL_UNSIGNED_SHORT
-}
-
-size_t QuadIndices::getElementSize()
-{
-	return elementSize;
-}
-
-GLBuffer *QuadIndices::getBuffer() const
-{
-	return indexBuffer;
-}
-
-const void *QuadIndices::getPointer(size_t offset) const
-{
-	return indexBuffer->getPointer(offset);
-}
-
-const void *QuadIndices::getIndices(size_t offset) const
-{
-	return indices + offset;
-}
-
-template <typename T>
-void QuadIndices::fill()
-{
-	using namespace love::graphics::vertex;
-
-	fillIndices(TriangleIndexMode::QUADS, 0, maxSize * 4, (T *) indices);
-	indexBuffer->fill(0, indexBuffer->getSize(), indices);
-}
-
 } // opengl
 } // graphics
 } // love

+ 80 - 0
src/modules/graphics/opengl/Buffer.h

@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2006-2017 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.
+ **/
+
+#pragma once
+
+// LOVE
+#include "common/config.h"
+#include "graphics/Buffer.h"
+#include "graphics/Volatile.h"
+
+// OpenGL
+#include "OpenGL.h"
+
+namespace love
+{
+namespace graphics
+{
+namespace opengl
+{
+
+class Buffer final : public love::graphics::Buffer, public Volatile
+{
+public:
+
+	Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags);
+	virtual ~Buffer();
+
+	void *map() override;
+	void unmap() override;
+	void setMappedRangeModified(size_t offset, size_t size) override;
+	void fill(size_t offset, size_t size, const void *data) override;
+	ptrdiff_t getHandle() const override;
+
+	void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override;
+
+	// Implements Volatile.
+	bool loadVolatile() override;
+	void unloadVolatile() override;
+
+private:
+
+	bool load(bool restore);
+	void unload();
+
+	void unmapStatic(size_t offset, size_t size);
+	void unmapStream();
+
+	GLenum target;
+
+	// The VBO identifier. Assigned by OpenGL.
+	GLuint vbo;
+
+	// A pointer to mapped memory.
+	char *memory_map;
+
+	size_t modified_offset;
+	size_t modified_size;
+
+}; // Buffer
+
+} // opengl
+} // graphics
+} // love

+ 16 - 10
src/modules/graphics/opengl/Graphics.cpp

@@ -29,6 +29,7 @@
 #include "StreamBuffer.h"
 #include "math/MathModule.h"
 #include "window/Window.h"
+#include "Buffer.h"
 
 #include "libraries/xxHash/xxhash.h"
 
@@ -117,12 +118,12 @@ graphics::Font *Graphics::newFont(love::font::Rasterizer *r, const Texture::Filt
 
 SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, vertex::Usage usage)
 {
-	return new SpriteBatch(texture, size, usage);
+	return new SpriteBatch(this, texture, size, usage);
 }
 
 ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
 {
-	return new ParticleSystem(texture, size);
+	return new ParticleSystem(this, texture, size);
 }
 
 love::graphics::Canvas *Graphics::newCanvas(int width, int height, const Canvas::Settings &settings)
@@ -159,29 +160,34 @@ love::graphics::Shader *Graphics::newShader(const Shader::ShaderSource &source)
 	return new Shader(source);
 }
 
+love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
+{
+	return new Buffer(size, data, type, usage, mapflags);
+}
+
 Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage)
 {
-	return new Mesh(vertices, drawmode, usage);
+	return new Mesh(this, vertices, drawmode, usage);
 }
 
 Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage)
 {
-	return new Mesh(vertexcount, drawmode, usage);
+	return new Mesh(this, vertexcount, drawmode, usage);
 }
 
 Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage)
 {
-	return new Mesh(vertexformat, vertexcount, drawmode, usage);
+	return new Mesh(this, vertexformat, vertexcount, drawmode, usage);
 }
 
 Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, Mesh::DrawMode drawmode, vertex::Usage usage)
 {
-	return new Mesh(vertexformat, data, datasize, drawmode, usage);
+	return new Mesh(this, vertexformat, data, datasize, drawmode, usage);
 }
 
 Text *Graphics::newText(graphics::Font *font, const std::vector<Font::ColoredString> &text)
 {
-	return new Text(font, text);
+	return new Text(this, font, text);
 }
 
 Video *Graphics::newVideo(love::video::VideoStream *stream, float pixeldensity)
@@ -295,7 +301,7 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
 	// index buffer objects, since the shared index buffer used by QuadIndices
 	// objects is destroyed when the last object is destroyed.
 	if (quadIndices == nullptr)
-		quadIndices = new QuadIndices(20);
+		quadIndices = new QuadIndices(this, 20);
 
 	// Restore the graphics state.
 	restoreState(states.back());
@@ -726,7 +732,7 @@ void Graphics::clear(Colorf c)
 		// This seems to be enough to fix the bug for me. Other methods I've
 		// tried (e.g. dummy draws) don't work in all cases.
 		gl.useProgram(0);
-		gl.useProgram(((Shader *)Shader::current)->getProgram());
+		gl.useProgram((GLuint) Shader::current->getHandle());
 	}
 }
 
@@ -792,7 +798,7 @@ void Graphics::clear(const std::vector<OptionalColorf> &colors)
 		// This seems to be enough to fix the bug for me. Other methods I've
 		// tried (e.g. dummy draws) don't work in all cases.
 		gl.useProgram(0);
-		gl.useProgram(((Shader *)Shader::current)->getProgram());
+		gl.useProgram((GLuint) Shader::current->getHandle());
 	}
 }
 

+ 2 - 0
src/modules/graphics/opengl/Graphics.h

@@ -78,6 +78,8 @@ public:
 
 	love::graphics::Shader *newShader(const Shader::ShaderSource &source) override;
 
+	love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
+
 	Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode drawmode, vertex::Usage usage);
 	Mesh *newMesh(int vertexcount, Mesh::DrawMode drawmode, vertex::Usage usage);
 

+ 27 - 30
src/modules/graphics/opengl/Mesh.cpp

@@ -61,7 +61,7 @@ static std::vector<Mesh::AttribFormat> getDefaultVertexFormat()
 
 love::Type Mesh::type("Mesh", &Drawable::type);
 
-Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage)
+Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage)
 	: vertexFormat(vertexformat)
 	, vbo(nullptr)
 	, vertexCount(0)
@@ -78,17 +78,17 @@ Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, const void *data, size
 	calculateAttributeSizes();
 
 	vertexCount = datasize / vertexStride;
-	elementDataType = getIndexTypeFromMax(vertexCount);
+	elementDataType = vertex::getIndexDataTypeFromMax(vertexCount);
 
 	if (vertexCount == 0)
 		throw love::Exception("Data size is too small for specified vertex attribute formats.");
 
-	vbo = new GLBuffer(datasize, data, BUFFER_VERTEX, usage, GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
+	vbo = gfx->newBuffer(datasize, data, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
 
 	vertexScratchBuffer = new char[vertexStride];
 }
 
-Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage)
+Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage)
 	: vertexFormat(vertexformat)
 	, vbo(nullptr)
 	, vertexCount((size_t) vertexcount)
@@ -96,7 +96,7 @@ Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawM
 	, ibo(nullptr)
 	, useIndexBuffer(false)
 	, elementCount(0)
-	, elementDataType(getIndexTypeFromMax(vertexcount))
+	, elementDataType(vertex::getIndexDataTypeFromMax(vertexcount))
 	, drawMode(drawmode)
 	, rangeStart(-1)
 	, rangeCount(-1)
@@ -109,7 +109,7 @@ Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawM
 
 	size_t buffersize = vertexCount * vertexStride;
 
-	vbo = new GLBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
+	vbo = gfx->newBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
 
 	// Initialize the buffer's contents to 0.
 	memset(vbo->map(), 0, buffersize);
@@ -119,13 +119,13 @@ Mesh::Mesh(const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawM
 	vertexScratchBuffer = new char[vertexStride];
 }
 
-Mesh::Mesh(const std::vector<Vertex> &vertices, DrawMode drawmode, vertex::Usage usage)
-	: Mesh(getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage)
+Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Vertex> &vertices, DrawMode drawmode, vertex::Usage usage)
+	: Mesh(gfx, getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage)
 {
 }
 
-Mesh::Mesh(int vertexcount, DrawMode drawmode, vertex::Usage usage)
-	: Mesh(getDefaultVertexFormat(), vertexcount, drawmode, usage)
+Mesh::Mesh(graphics::Graphics *gfx, int vertexcount, DrawMode drawmode, vertex::Usage usage)
+	: Mesh(gfx, getDefaultVertexFormat(), vertexcount, drawmode, usage)
 {
 }
 
@@ -393,7 +393,7 @@ void Mesh::flush()
  * Copies index data from a vector to a mapped index buffer.
  **/
 template <typename T>
-static void copyToIndexBuffer(const std::vector<uint32> &indices, GLBuffer::Mapper &buffermap, size_t maxval)
+static void copyToIndexBuffer(const std::vector<uint32> &indices, Buffer::Mapper &buffermap, size_t maxval)
 {
 	T *elems = (T *) buffermap.get();
 
@@ -410,7 +410,7 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
 {
 	size_t maxval = getVertexCount();
 
-	IndexDataType datatype = getIndexTypeFromMax(maxval);
+	IndexDataType datatype = vertex::getIndexDataTypeFromMax(maxval);
 
 	// Calculate the size in bytes of the index buffer data.
 	size_t size = map.size() * vertex::getIndexDataSize(datatype);
@@ -422,7 +422,10 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
 	}
 
 	if (!ibo && size > 0)
-		ibo = new GLBuffer(size, nullptr, BUFFER_INDEX, vbo->getUsage());
+	{
+		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
+		ibo = gfx->newBuffer(size, nullptr, BUFFER_INDEX, vbo->getUsage(), Buffer::MAP_READ);
+	}
 
 	useIndexBuffer = true;
 	elementCount = map.size();
@@ -430,7 +433,7 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
 	if (!ibo || elementCount == 0)
 		return;
 
-	GLBuffer::Mapper ibomap(*ibo);
+	Buffer::Mapper ibomap(*ibo);
 
 	// Fill the buffer with the index values from the vector.
 	switch (datatype)
@@ -456,14 +459,17 @@ void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasiz
 	}
 
 	if (!ibo && datasize > 0)
-		ibo = new GLBuffer(datasize, nullptr, BUFFER_INDEX, vbo->getUsage());
+	{
+		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
+		ibo = gfx->newBuffer(datasize, nullptr, BUFFER_INDEX, vbo->getUsage(), Buffer::MAP_READ);
+	}
 
 	elementCount = datasize / vertex::getIndexDataSize(datatype);
 
 	if (!ibo || elementCount == 0)
 		return;
 
-	GLBuffer::Mapper ibomap(*ibo);
+	Buffer::Mapper ibomap(*ibo);
 	memcpy(ibomap.get(), data, datasize);
 
 	useIndexBuffer = true;
@@ -587,13 +593,12 @@ int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inpu
 	if (attriblocation < 0)
 		return attriblocation;
 
-	// Needed for glVertexAttribPointer.
-	vbo->bind();
-
 	// Make sure the buffer isn't mapped (sends data to GPU if needed.)
 	vbo->unmap();
 
-	const void *gloffset = vbo->getPointer(getAttributeOffset(attributeindex));
+	gl.bindBuffer(BUFFER_VERTEX, (GLuint) vbo->getHandle());
+
+	const void *gloffset = BUFFER_OFFSET(getAttributeOffset(attributeindex));
 	GLenum datatype = getGLDataType(format.type);
 	GLboolean normalized = (datatype == GL_UNSIGNED_BYTE);
 
@@ -651,7 +656,7 @@ void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m,
 	if (useIndexBuffer && ibo && elementCount > 0)
 	{
 		// Use the custom vertex map (index buffer) to draw the vertices.
-		ibo->bind();
+		gl.bindBuffer(BUFFER_INDEX, (GLuint) ibo->getHandle());
 
 		// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
 		ibo->unmap();
@@ -665,7 +670,7 @@ void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m,
 		count = std::min(count, (int) elementCount - start);
 
 		size_t elementsize = vertex::getIndexDataSize(elementDataType);
-		const void *indices = ibo->getPointer(start * elementsize);
+		const void *indices = BUFFER_OFFSET(start * elementsize);
 		GLenum type = OpenGL::getGLIndexDataType(elementDataType);
 
 		if (count > 0)
@@ -734,14 +739,6 @@ GLenum Mesh::getGLDataType(DataType type)
 	}
 }
 
-IndexDataType Mesh::getIndexTypeFromMax(size_t maxvalue)
-{
-	if (maxvalue > LOVE_UINT16_MAX)
-		return INDEX_UINT32;
-	else
-		return INDEX_UINT16;
-}
-
 bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
 {
 	return drawModes.find(in, out);

+ 11 - 9
src/modules/graphics/opengl/Mesh.h

@@ -29,7 +29,8 @@
 #include "graphics/Drawable.h"
 #include "graphics/Texture.h"
 #include "graphics/vertex.h"
-#include "GLBuffer.h"
+#include "graphics/Buffer.h"
+#include "OpenGL.h"
 
 // C++
 #include <vector>
@@ -39,6 +40,9 @@ namespace love
 {
 namespace graphics
 {
+
+class Graphics;
+
 namespace opengl
 {
 
@@ -86,11 +90,11 @@ public:
 		int components; // max 4
 	};
 
-	Mesh(const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage);
-	Mesh(const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage);
+	Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage);
+	Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage);
 
-	Mesh(const std::vector<Vertex> &vertices, DrawMode drawmode, vertex::Usage usage);
-	Mesh(int vertexcount, DrawMode drawmode, vertex::Usage usage);
+	Mesh(graphics::Graphics *gfx, const std::vector<Vertex> &vertices, DrawMode drawmode, vertex::Usage usage);
+	Mesh(graphics::Graphics *gfx, int vertexcount, DrawMode drawmode, vertex::Usage usage);
 
 	virtual ~Mesh();
 
@@ -229,8 +233,6 @@ private:
 
 	static size_t getAttribFormatSize(const AttribFormat &format);
 
-	static IndexDataType getIndexTypeFromMax(size_t maxvalue);
-
 	static GLenum getGLDrawMode(DrawMode mode);
 	static GLenum getGLDataType(DataType type);
 
@@ -240,7 +242,7 @@ private:
 	std::unordered_map<std::string, AttachedAttribute> attachedAttributes;
 
 	// Vertex buffer, for the vertex data.
-	GLBuffer *vbo;
+	Buffer *vbo;
 	size_t vertexCount;
 	size_t vertexStride;
 
@@ -249,7 +251,7 @@ private:
 	char *vertexScratchBuffer;
 
 	// Element (vertex index) buffer, for the vertex map.
-	GLBuffer *ibo;
+	Buffer *ibo;
 	bool useIndexBuffer;
 	size_t elementCount;
 	IndexDataType elementDataType;

+ 8 - 78
src/modules/graphics/opengl/ParticleSystem.cpp

@@ -19,17 +19,11 @@
  **/
 
 //LOVE
-#include "common/config.h"
 #include "ParticleSystem.h"
 #include "graphics/Graphics.h"
 
 #include "OpenGL.h"
 
-// STD
-#include <algorithm>
-#include <cmath>
-#include <cstdlib>
-
 namespace love
 {
 namespace graphics
@@ -37,32 +31,18 @@ namespace graphics
 namespace opengl
 {
 
-ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
-	: love::graphics::ParticleSystem(texture, size)
-	, buffer(nullptr)
-	, quadIndices(size)
+ParticleSystem::ParticleSystem(Graphics *gfx, Texture *texture, uint32 size)
+	: love::graphics::ParticleSystem(gfx, texture, size)
 {
-	createVertices(size);
 }
 
 ParticleSystem::ParticleSystem(const ParticleSystem &p)
 	: love::graphics::ParticleSystem(p)
-	, quadIndices(p.quadIndices)
 {
-	createVertices(maxParticles);
 }
 
 ParticleSystem::~ParticleSystem()
 {
-	delete buffer;
-}
-
-void ParticleSystem::createVertices(size_t numparticles)
-{
-	size_t size = sizeof(Vertex) * numparticles * 4;
-	GLBuffer *newbuffer = new GLBuffer(size, nullptr, BUFFER_VERTEX, vertex::USAGE_STREAM, 0);
-	delete buffer;
-	buffer = newbuffer;
 }
 
 ParticleSystem *ParticleSystem::clone()
@@ -70,78 +50,28 @@ ParticleSystem *ParticleSystem::clone()
 	return new ParticleSystem(*this);
 }
 
-void ParticleSystem::setBufferSize(uint32 size)
-{
-	love::graphics::ParticleSystem::setBufferSize(size);
-
-	quadIndices = QuadIndices(size);
-	createVertices(size);
-}
-
 void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
 {
-	uint32 pCount = getCount();
-
-	if (pCount == 0 || texture.get() == nullptr || pMem == nullptr || buffer == nullptr)
+	if (!prepareDraw(gfx, m))
 		return;
 
-	gfx->flushStreamDraws();
-
 	OpenGL::TempDebugGroup debuggroup("ParticleSystem draw");
 
-	Graphics::TempTransform transform(gfx, m);
-
-	const Vertex *textureVerts = texture->getVertices();
-	Vertex *pVerts = (Vertex *) buffer->map();
-	Particle *p = pHead;
-
-	bool useQuads = !quads.empty();
-
-	Matrix3 t;
-
-	// set the vertex data for each particle (transformation, texcoords, color)
-	while (p)
-	{
-		if (useQuads)
-			textureVerts = quads[p->quadIndex]->getVertices();
-
-		// particle vertices are image vertices transformed by particle info
-		t.setTransformation(p->position.x, p->position.y, p->angle, p->size, p->size, offset.x, offset.y, 0.0f, 0.0f);
-		t.transform(pVerts, textureVerts, 4);
-
-		// Particle colors are stored as floats (0-1) but vertex colors are
-		// unsigned bytes (0-255).
-		Color c = toColor(p->color);
-
-		// set the texture coordinate and color data for particle vertices
-		for (int v = 0; v < 4; v++)
-		{
-			pVerts[v].s = textureVerts[v].s;
-			pVerts[v].t = textureVerts[v].t;
-			pVerts[v].color = c;
-		}
-
-		pVerts += 4;
-		p = p->next;
-	}
-
-	buffer->unmap();
-
 	gl.bindTextureToUnit(texture, 0, false);
 	gl.prepareDraw();
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 
-	buffer->bind();
+	gl.bindBuffer(BUFFER_VERTEX, (GLuint) buffer->getHandle());
 	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, color.r)));
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, x)));
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(offsetof(Vertex, s)));
 
-	GLsizei count = (GLsizei) quadIndices.getIndexCount(pCount);
-	GLenum gltype = quadIndices.getType();
+	GLsizei count = (GLsizei) quadIndices.getIndexCount(getCount());
+	GLenum gltype = OpenGL::getGLIndexDataType(quadIndices.getType());
 
-	quadIndices.getBuffer()->bind();
-	gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(0));
+	gl.bindBuffer(BUFFER_INDEX, (GLuint) quadIndices.getBuffer()->getHandle());
+	gl.drawElements(GL_TRIANGLES, count, gltype, BUFFER_OFFSET(0));
 }
 
 } // opengl

+ 6 - 13
src/modules/graphics/opengl/ParticleSystem.h

@@ -23,37 +23,30 @@
 
 // LOVE
 #include "graphics/ParticleSystem.h"
-#include "GLBuffer.h"
 
 namespace love
 {
 namespace graphics
 {
+
+class Graphics;
+
 namespace opengl
 {
 
-class ParticleSystem : public love::graphics::ParticleSystem
+class ParticleSystem final : public love::graphics::ParticleSystem
 {
 public:
 
-	ParticleSystem(Texture *texture, uint32 buffer);
+	ParticleSystem(Graphics *gfx, Texture *texture, uint32 buffer);
 	ParticleSystem(const ParticleSystem &p);
 
 	virtual ~ParticleSystem();
 
 	ParticleSystem *clone() override;
-	void setBufferSize(uint32 size) override;
 	void draw(Graphics *gfx, const Matrix4 &m) override;
 
-private:
-
-	void createVertices(size_t numparticles);
-
-	GLBuffer *buffer;
-
-	// Vertex index buffer.
-	QuadIndices quadIndices;
-};
+}; // ParticleSystem
 
 } // opengl
 } // graphics

+ 5 - 0
src/modules/graphics/opengl/Shader.cpp

@@ -726,6 +726,11 @@ bool Shader::hasUniform(const std::string &name) const
 	return uniforms.find(name) != uniforms.end();
 }
 
+ptrdiff_t Shader::getHandle() const
+{
+	return program;
+}
+
 GLint Shader::getAttribLocation(const std::string &name)
 {
 	auto it = attributes.find(name);

+ 1 - 5
src/modules/graphics/opengl/Shader.h

@@ -62,6 +62,7 @@ public:
 	void updateUniform(const UniformInfo *info, int count, bool internalUpdate = false) override;
 	void sendTextures(const UniformInfo *info, Texture **textures, int count, bool internalUpdate = false) override;
 	bool hasUniform(const std::string &name) const override;
+	ptrdiff_t getHandle() const override;
 	void setVideoTextures(ptrdiff_t ytexture, ptrdiff_t cbtexture, ptrdiff_t crtexture) override;
 
 	GLint getAttribLocation(const std::string &name);
@@ -70,11 +71,6 @@ public:
 	void updatePointSize(float size);
 	void updateBuiltinUniforms();
 
-	GLuint getProgram() const
-	{
-		return program;
-	}
-
 	static std::string getGLSLVersion();
 	static bool isSupported();
 

+ 20 - 20
src/modules/graphics/opengl/SpriteBatch.cpp

@@ -25,7 +25,6 @@
 #include "OpenGL.h"
 
 // LOVE
-#include "GLBuffer.h"
 #include "graphics/Texture.h"
 #include "graphics/Graphics.h"
 
@@ -44,13 +43,13 @@ namespace opengl
 
 love::Type SpriteBatch::type("SpriteBatch", &Drawable::type);
 
-SpriteBatch::SpriteBatch(Texture *texture, int size, vertex::Usage usage)
+SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage)
 	: texture(texture)
 	, size(size)
 	, next(0)
 	, color(0)
 	, array_buf(nullptr)
-	, quad_indices(size)
+	, quad_indices(gfx, size)
 	, range_start(-1)
 	, range_count(-1)
 {
@@ -59,7 +58,7 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, vertex::Usage usage)
 
 	size_t vertex_size = sizeof(Vertex) * 4 * size;
 
-	array_buf = new GLBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, GLBuffer::MAP_EXPLICIT_RANGE_MODIFY);
+	array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
 }
 
 SpriteBatch::~SpriteBatch()
@@ -155,24 +154,21 @@ void SpriteBatch::setBufferSize(int newsize)
 	if (newsize == size)
 		return;
 
-	// Map the old GLBuffer to get a pointer to its data.
-	void *old_data = array_buf->map();
-
 	size_t vertex_size = sizeof(Vertex) * 4 * newsize;
-	GLBuffer *new_array_buf = nullptr;
+	love::graphics::Buffer *new_array_buf = nullptr;
 
 	int new_next = std::min(next, newsize);
 
 	try
 	{
-		new_array_buf = new GLBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
+		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
+		new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags());
 
 		// Copy as much of the old data into the new GLBuffer as can fit.
 		size_t copy_size = sizeof(Vertex) * 4 * new_next;
-		memcpy(new_array_buf->map(), old_data, copy_size);
-		new_array_buf->setMappedRangeModified(0, copy_size);
+		array_buf->copyTo(0, copy_size, new_array_buf, 0);
 
-		quad_indices = QuadIndices(newsize);
+		quad_indices = QuadIndices(gfx, newsize);
 	}
 	catch (love::Exception &)
 	{
@@ -262,17 +258,17 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 	// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
 	array_buf->unmap();
 
-	array_buf->bind();
+	gl.bindBuffer(BUFFER_VERTEX, (GLuint) array_buf->getHandle());
 
 	// Apply per-sprite color, if a color is set.
 	if (color)
 	{
 		enabledattribs |= ATTRIBFLAG_COLOR;
-		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset));
+		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), BUFFER_OFFSET(color_offset));
 	}
 
-	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset));
-	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
+	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(pos_offset));
+	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(texel_offset));
 
 	for (const auto &it : attached_attributes)
 	{
@@ -301,11 +297,15 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 
 	count = std::min(count, next - start);
 
-	quad_indices.getBuffer()->bind();
-	const void *indices = quad_indices.getPointer(start * quad_indices.getElementSize());
-
 	if (count > 0)
-		gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(count), quad_indices.getType(), indices);
+	{
+		gl.bindBuffer(BUFFER_INDEX, (GLuint) quad_indices.getBuffer()->getHandle());
+
+		const void *indices = BUFFER_OFFSET(start * quad_indices.getElementSize());
+		GLenum gltype = OpenGL::getGLIndexDataType(quad_indices.getType());
+
+		gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(count), gltype, indices);
+	}
 }
 
 void SpriteBatch::addv(const Vertex *v, const Matrix4 &m, int index)

+ 4 - 3
src/modules/graphics/opengl/SpriteBatch.h

@@ -34,7 +34,7 @@
 #include "graphics/Volatile.h"
 #include "graphics/Color.h"
 #include "graphics/Quad.h"
-#include "GLBuffer.h"
+#include "graphics/Buffer.h"
 #include "Mesh.h"
 
 namespace love
@@ -44,6 +44,7 @@ namespace graphics
 
 // Forward declarations.
 class Texture;
+class Graphics;
 
 namespace opengl
 {
@@ -54,7 +55,7 @@ public:
 
 	static love::Type type;
 
-	SpriteBatch(Texture *texture, int size, vertex::Usage usage);
+	SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage);
 	virtual ~SpriteBatch();
 
 	int add(const Matrix4 &m, int index = -1);
@@ -138,7 +139,7 @@ private:
 	// added sprite.
 	Color *color;
 
-	GLBuffer *array_buf;
+	love::graphics::Buffer *array_buf;
 	QuadIndices quad_indices;
 
 	std::unordered_map<std::string, AttachedAttribute> attached_attributes;

+ 15 - 23
src/modules/graphics/opengl/Text.cpp

@@ -21,6 +21,7 @@
 #include "Text.h"
 #include "common/Matrix.h"
 #include "graphics/Graphics.h"
+#include "OpenGL.h"
 
 #include <algorithm>
 
@@ -33,10 +34,10 @@ namespace opengl
 
 love::Type Text::type("Text", &Drawable::type);
 
-Text::Text(love::graphics::Font *font, const std::vector<Font::ColoredString> &text)
+Text::Text(love::graphics::Graphics *gfx, love::graphics::Font *font, const std::vector<Font::ColoredString> &text)
 	: font(font)
 	, vbo(nullptr)
-	, quadIndices(20)
+	, quadIndices(gfx, 20)
 	, vert_offset(0)
 	, texture_cache_id((uint32) -1)
 {
@@ -59,25 +60,15 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
 	{
 		// Make it bigger than necessary to reduce potential future allocations.
 		size_t newsize = size_t((offset + datasize) * 1.5);
+
 		if (vbo != nullptr)
 			newsize = std::max(size_t(vbo->getSize() * 1.5), newsize);
 
-		GLBuffer *new_vbo = new GLBuffer(newsize, nullptr, BUFFER_VERTEX, vertex::USAGE_DYNAMIC);
+		auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
+		Buffer *new_vbo = gfx->newBuffer(newsize, nullptr, BUFFER_VERTEX, vertex::USAGE_DYNAMIC, 0);
 
 		if (vbo != nullptr)
-		{
-			try
-			{
-				vbodata = (uint8 *) vbo->map();
-			}
-			catch (love::Exception &)
-			{
-				delete new_vbo;
-				throw;
-			}
-
-			new_vbo->fill(0, vbo->getSize(), vbodata);
-		}
+			vbo->copyTo(0, vbo->getSize(), new_vbo, 0);
 
 		delete vbo;
 		vbo = new_vbo;
@@ -229,7 +220,7 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
 		totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts);
 
 	if ((size_t) totalverts / 4 > quadIndices.getSize())
-		quadIndices = QuadIndices((size_t) totalverts / 4);
+		quadIndices = QuadIndices(gfx, (size_t) totalverts / 4);
 
 	const size_t pos_offset   = offsetof(Font::GlyphVertex, x);
 	const size_t tex_offset   = offsetof(Font::GlyphVertex, s);
@@ -243,16 +234,17 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
 
 	gl.prepareDraw();
 
-	vbo->bind();
 	vbo->unmap(); // Make sure all pending data is flushed to the GPU.
 
-	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(pos_offset));
-	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, vbo->getPointer(tex_offset));
-	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, vbo->getPointer(color_offset));
+	gl.bindBuffer(BUFFER_VERTEX, (GLuint) vbo->getHandle());
+
+	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(pos_offset));
+	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, BUFFER_OFFSET(tex_offset));
+	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(color_offset));
 
 	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 
-	quadIndices.getBuffer()->bind();
+	gl.bindBuffer(BUFFER_INDEX, (GLuint) quadIndices.getBuffer()->getHandle());
 
 	// We need a separate draw call for every section of the text which uses a
 	// different texture than the previous section.
@@ -264,7 +256,7 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
 		// TODO: Use glDrawElementsBaseVertex when supported?
 		gl.bindTextureToUnit((GLuint) cmd.texture, 0, false);
 
-		gl.drawElements(GL_TRIANGLES, count, gltype, quadIndices.getPointer(offset));
+		gl.drawElements(GL_TRIANGLES, count, gltype, BUFFER_OFFSET(offset));
 	}
 }
 

+ 6 - 3
src/modules/graphics/opengl/Text.h

@@ -25,12 +25,15 @@
 #include "common/config.h"
 #include "graphics/Drawable.h"
 #include "graphics/Font.h"
-#include "GLBuffer.h"
+#include "graphics/Buffer.h"
 
 namespace love
 {
 namespace graphics
 {
+
+class Graphics;
+
 namespace opengl
 {
 
@@ -40,7 +43,7 @@ public:
 
 	static love::Type type;
 
-	Text(love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {});
+	Text(love::graphics::Graphics *gfx, love::graphics::Font *font, const std::vector<Font::ColoredString> &text = {});
 	virtual ~Text();
 
 	void set(const std::vector<Font::ColoredString> &text);
@@ -85,7 +88,7 @@ private:
 	void addTextData(const TextData &s);
 
 	StrongRef<love::graphics::Font> font;
-	GLBuffer *vbo;
+	Buffer *vbo;
 	QuadIndices quadIndices;
 
 	std::vector<Font::DrawCommand> draw_commands;

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

@@ -19,7 +19,6 @@
  **/
 
 #include "wrap_Graphics.h"
-#include "OpenGL.h"
 #include "graphics/Texture.h"
 #include "image/ImageData.h"
 #include "image/Image.h"

+ 6 - 0
src/modules/graphics/vertex.cpp

@@ -66,6 +66,12 @@ size_t getIndexDataSize(IndexDataType type)
 	}
 }
 
+IndexDataType getIndexDataTypeFromMax(size_t maxvalue)
+{
+	IndexDataType types[] = {INDEX_UINT16, INDEX_UINT32};
+	return types[maxvalue > LOVE_UINT16_MAX ? 1 : 0];
+}
+
 int getIndexCount(TriangleIndexMode mode, int vertexCount)
 {
 	switch (mode)

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

@@ -123,6 +123,7 @@ struct XYf_STus_RGBAub
 
 size_t getFormatStride(CommonFormat format);
 size_t getIndexDataSize(IndexDataType type);
+IndexDataType getIndexDataTypeFromMax(size_t maxvalue);
 
 int getIndexCount(TriangleIndexMode mode, int vertexCount);