Browse Source

Initial work on unified graphics buffers representing different types of data.

Alex Szpakowski 5 years ago
parent
commit
b0232a4a6b

+ 31 - 4
src/modules/graphics/Buffer.cpp

@@ -19,21 +19,48 @@
  **/
 
 #include "Buffer.h"
+#include "Graphics.h"
 
 namespace love
 {
 namespace graphics
 {
 
-Buffer::Buffer(size_t size, BufferType type, vertex::Usage usage, uint32 mapflags)
+Buffer::Buffer(size_t size, BufferTypeFlags typeflags, vertex::Usage usage, uint32 mapflags)
 	: size(size)
-	, type(type)
+	, typeFlags(typeflags)
 	, usage(usage)
-	, map_flags(mapflags)
-	, is_mapped(false)
+	, mapFlags(mapflags)
+	, mapped(false)
 {
 }
 
+Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMember> &format, size_t arraylength)
+	: Buffer(0, settings.typeFlags, settings.usage, settings.mapFlags)
+{
+	bool uniformbuffer = settings.typeFlags & BUFFERFLAG_UNIFORM;
+	bool indexbuffer = settings.typeFlags & BUFFERFLAG_INDEX;
+	bool vertexbuffer = settings.typeFlags & BUFFERFLAG_VERTEX;
+	bool ssbuffer = settings.typeFlags & BUFFERFLAG_SHADER_STORAGE;
+
+	if (indexbuffer && format.size() > 1)
+		throw love::Exception("test");
+
+	for (const auto &member : format)
+	{
+		if (indexbuffer)
+		{
+			if (member.type != DATA_UINT16 && member.type != DATA_UINT32)
+				throw love::Exception("test");
+		}
+
+		if (uniformbuffer)
+		{
+			
+		}
+	}
+}
+
 Buffer::~Buffer()
 {
 }

+ 90 - 24
src/modules/graphics/Buffer.h

@@ -28,12 +28,16 @@
 
 // C
 #include <stddef.h>
+#include <string>
+#include <vector>
 
 namespace love
 {
 namespace graphics
 {
 
+class Graphics;
+
 /**
  * A block of GPU-owned memory. Currently meant for internal use.
  **/
@@ -47,16 +51,90 @@ public:
 		MAP_READ = (1 << 1),
 	};
 
-	Buffer(size_t size, BufferType type, vertex::Usage usage, uint32 mapflags);
-	virtual ~Buffer();
+	enum DataType
+	{
+		DATA_FLOAT,
+		DATA_FLOAT_VEC2,
+		DATA_FLOAT_VEC3,
+		DATA_FLOAT_VEC4,
 
-	size_t getSize() const { return size; }
+		DATA_FLOAT_MAT2X2,
+		DATA_FLOAT_MAT2X3,
+		DATA_FLOAT_MAT2X4,
 
-	BufferType getType() const { return type; }
+		DATA_FLOAT_MAT3X2,
+		DATA_FLOAT_MAT3X3,
+		DATA_FLOAT_MAT3X4,
 
-	vertex::Usage getUsage() const { return usage; }
+		DATA_FLOAT_MAT4X2,
+		DATA_FLOAT_MAT4X3,
+		DATA_FLOAT_MAT4X4,
+
+		DATA_INT32,
+		DATA_INT32_VEC2,
+		DATA_INT32_VEC3,
+		DATA_INT32_VEC4,
+
+		DATA_UINT32,
+		DATA_UINT32_VEC2,
+		DATA_UINT32_VEC3,
+		DATA_UINT32_VEC4,
+
+		DATA_SNORM8_VEC4,
+
+		DATA_UNORM8_VEC4,
+
+		DATA_INT8_VEC4,
+
+		DATA_UINT8_VEC4,
+
+		DATA_SNORM16,
+		DATA_SNORM16_VEC2,
+		DATA_SNORM16_VEC4,
+
+		DATA_UNORM16,
+		DATA_UNORM16_VEC2,
+		DATA_UNORM16_VEC4,
+
+		DATA_INT16,
+		DATA_INT16_VEC2,
+		DATA_INT16_VEC4,
+
+		DATA_UINT16,
+		DATA_UINT16_VEC2,
+		DATA_UINT16_VEC4,
+
+		DATA_BOOL,
+		DATA_BOOL_VEC2,
+		DATA_BOOL_VEC3,
+		DATA_BOOL_VEC4,
 
-	bool isMapped() const { return is_mapped; }
+		DATA_MAX_ENUM
+	};
+
+	struct DataMember
+	{
+		std::string name;
+		DataType type;
+		int arraySize;
+	};
+
+	struct Settings
+	{
+		BufferTypeFlags typeFlags;
+		MapFlags mapFlags;
+		vertex::Usage usage;
+	};
+
+	Buffer(size_t size, BufferTypeFlags typeflags, vertex::Usage usage, uint32 mapflags);
+	Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMember> &format, size_t arraylength);
+	virtual ~Buffer();
+
+	size_t getSize() const { return size; }
+	BufferTypeFlags getTypeFlags() const { return typeFlags; }
+	vertex::Usage getUsage() const { return usage; }
+	bool isMapped() const { return mapped; }
+	uint32 getMapFlags() const { return mapFlags; }
 
 	/**
 	 * Map the Buffer to client memory.
@@ -92,36 +170,22 @@ public:
 	 **/
 	virtual void copyTo(size_t offset, size_t size, Buffer *other, size_t otheroffset) = 0;
 
-	uint32 getMapFlags() const { return map_flags; }
-
 	class Mapper
 	{
 	public:
 
-		/**
-		 * Memory-maps a Buffer.
-		 */
 		Mapper(Buffer &buffer)
 			: buf(buffer)
 		{
 			elems = buf.map();
 		}
 
-		/**
-		 * unmaps the buffer
-		 */
 		~Mapper()
 		{
 			buf.unmap();
 		}
 
-		/**
-		 * Get pointer to memory mapped region
-		 */
-		void *get()
-		{
-			return elems;
-		}
+		void *get() { return elems; }
 
 	private:
 
@@ -130,20 +194,22 @@ public:
 
 	}; // Mapper
 
+//	static size_t getDataTypeSize(DataType type, bool uniform)
+
 protected:
 
 	// The size of the buffer, in bytes.
 	size_t size;
 
 	// The type of the buffer object.
-	BufferType type;
+	BufferTypeFlags typeFlags;
 
 	// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
 	vertex::Usage usage;
 	
-	uint32 map_flags;
+	uint32 mapFlags;
 
-	bool is_mapped;
+	bool mapped;
 	
 }; // Buffer
 

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

@@ -173,7 +173,7 @@ void Graphics::createQuadIndexBuffer()
 		return;
 
 	size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6;
-	quadIndexBuffer = newBuffer(size, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0);
+	quadIndexBuffer = newBuffer(size, nullptr, BUFFERFLAG_INDEX, vertex::USAGE_STATIC, 0);
 
 	Buffer::Mapper map(*quadIndexBuffer);
 	vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.get());

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

@@ -445,7 +445,10 @@ public:
 	ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source);
 	Shader *newShader(const std::string &vertex, const std::string &pixel);
 
-	virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0;
+	virtual Buffer *newBuffer(size_t size, const void *data, BufferTypeFlags typeflags, vertex::Usage usage, uint32 mapflags) = 0;
+	virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataMember> &format, size_t arraylength) = 0;
+
+//	Buffer *newIndexBuffer(IndexDataType dataType, const void *indices, size_t bytesize, vertex::Usage usage, uint32 mapflags) = 0;
 
 	Mesh *newMesh(const std::vector<Vertex> &vertices, PrimitiveType drawmode, vertex::Usage usage);
 	Mesh *newMesh(int vertexcount, PrimitiveType drawmode, vertex::Usage usage);

+ 4 - 4
src/modules/graphics/Mesh.cpp

@@ -81,7 +81,7 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexforma
 	if (vertexCount == 0)
 		throw love::Exception("Data size is too small for specified vertex attribute formats.");
 
-	vertexBuffer = gfx->newBuffer(datasize, data, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
+	vertexBuffer = gfx->newBuffer(datasize, data, BUFFERFLAG_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
 
 	vertexScratchBuffer = new char[vertexStride];
 }
@@ -107,7 +107,7 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexforma
 
 	size_t buffersize = vertexCount * vertexStride;
 
-	vertexBuffer = gfx->newBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
+	vertexBuffer = gfx->newBuffer(buffersize, nullptr, BUFFERFLAG_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
 
 	// Initialize the buffer's contents to 0.
 	memset(vertexBuffer->map(), 0, buffersize);
@@ -420,7 +420,7 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
 	if (!indexBuffer && size > 0)
 	{
 		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
-		indexBuffer = gfx->newBuffer(size, nullptr, BUFFER_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ);
+		indexBuffer = gfx->newBuffer(size, nullptr, BUFFERFLAG_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ);
 	}
 
 	useIndexBuffer = true;
@@ -457,7 +457,7 @@ void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasiz
 	if (!indexBuffer && datasize > 0)
 	{
 		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
-		indexBuffer = gfx->newBuffer(datasize, nullptr, BUFFER_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ);
+		indexBuffer = gfx->newBuffer(datasize, nullptr, BUFFERFLAG_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ);
 	}
 
 	indexCount = datasize / vertex::getIndexDataSize(datatype);

+ 1 - 1
src/modules/graphics/ParticleSystem.cpp

@@ -191,7 +191,7 @@ void ParticleSystem::createBuffers(size_t 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);
+		buffer = gfx->newBuffer(bytes, nullptr, BUFFERFLAG_VERTEX, vertex::USAGE_STREAM, 0);
 	}
 	catch (std::bad_alloc &)
 	{

+ 2 - 2
src/modules/graphics/SpriteBatch.cpp

@@ -64,7 +64,7 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usag
 	vertex_stride = vertex::getFormatStride(vertex_format);
 
 	size_t vertex_size = vertex_stride * 4 * size;
-	array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
+	array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFERFLAG_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
 }
 
 SpriteBatch::~SpriteBatch()
@@ -222,7 +222,7 @@ void SpriteBatch::setBufferSize(int newsize)
 	try
 	{
 		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());
+		new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getTypeFlags(), array_buf->getUsage(), array_buf->getMapFlags());
 
 		// Copy as much of the old data into the new GLBuffer as can fit.
 		size_t copy_size = vertex_stride * 4 * new_next;

+ 1 - 1
src/modules/graphics/Text.cpp

@@ -60,7 +60,7 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
 			newsize = std::max(size_t(vertex_buffer->getSize() * 1.5), newsize);
 
 		auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
-		Buffer *new_buffer = gfx->newBuffer(newsize, nullptr, BUFFER_VERTEX, vertex::USAGE_DYNAMIC, 0);
+		Buffer *new_buffer = gfx->newBuffer(newsize, nullptr, BUFFERFLAG_VERTEX, vertex::USAGE_DYNAMIC, 0);
 
 		if (vertex_buffer != nullptr)
 			vertex_buffer->copyTo(0, vertex_buffer->getSize(), new_buffer, 0);

+ 55 - 46
src/modules/graphics/opengl/Buffer.cpp

@@ -35,18 +35,27 @@ namespace graphics
 namespace opengl
 {
 
-Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
-	: love::graphics::Buffer(size, type, usage, mapflags)
+Buffer::Buffer(size_t size, const void *data, BufferTypeFlags typeflags, vertex::Usage usage, uint32 mapflags)
+	: love::graphics::Buffer(size, typeflags, usage, mapflags)
 	, vbo(0)
-	, memory_map(nullptr)
-	, modified_offset(0)
-	, modified_size(0)
+	, memoryMap(nullptr)
+	, modifiedOffset(0)
+	, modifiedSize(0)
 {
-	target = OpenGL::getGLBufferType(type);
+	if (typeflags & BUFFERFLAG_VERTEX)
+		mapType = BUFFER_VERTEX;
+	else if (typeflags & BUFFERFLAG_INDEX)
+		mapType = BUFFER_INDEX;
+	else if (mapflags & BUFFERFLAG_UNIFORM)
+		mapType = BUFFER_UNIFORM;
+	else if (mapflags & BUFFERFLAG_SHADER_STORAGE)
+		mapType = BUFFER_SHADER_STORAGE;
+
+	target = OpenGL::getGLBufferType(mapType);
 
 	try
 	{
-		memory_map = new char[size];
+		memoryMap = new char[size];
 	}
 	catch (std::bad_alloc &)
 	{
@@ -54,11 +63,11 @@ Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usa
 	}
 
 	if (data != nullptr)
-		memcpy(memory_map, data, size);
+		memcpy(memoryMap, data, size);
 
 	if (!load(data != nullptr))
 	{
-		delete[] memory_map;
+		delete[] memoryMap;
 		throw love::Exception("Could not load vertex buffer (out of VRAM?)");
 	}
 }
@@ -68,20 +77,20 @@ Buffer::~Buffer()
 	if (vbo != 0)
 		unload();
 
-	delete[] memory_map;
+	delete[] memoryMap;
 }
 
 void *Buffer::map()
 {
-	if (is_mapped)
-		return memory_map;
+	if (mapped)
+		return memoryMap;
 
-	is_mapped = true;
+	mapped = true;
 
-	modified_offset = 0;
-	modified_size = 0;
+	modifiedOffset = 0;
+	modifiedSize = 0;
 
-	return memory_map;
+	return memoryMap;
 }
 
 void Buffer::unmapStatic(size_t offset, size_t size)
@@ -90,8 +99,8 @@ void Buffer::unmapStatic(size_t offset, size_t size)
 		return;
 
 	// Upload the mapped data to the buffer.
-	gl.bindBuffer(type, vbo);
-	glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
+	gl.bindBuffer(mapType, vbo);
+	glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memoryMap + offset);
 }
 
 void Buffer::unmapStream()
@@ -100,40 +109,40 @@ void Buffer::unmapStream()
 
 	// "orphan" current buffer to avoid implicit synchronisation on the GPU:
 	// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
-	gl.bindBuffer(type, vbo);
+	gl.bindBuffer(mapType, vbo);
 	glBufferData(target, (GLsizeiptr) getSize(), nullptr, glusage);
 
 #if LOVE_WINDOWS
 	// TODO: Verify that this codepath is a useful optimization.
 	if (gl.getVendor() == OpenGL::VENDOR_INTEL)
-		glBufferData(target, (GLsizeiptr) getSize(), memory_map, glusage);
+		glBufferData(target, (GLsizeiptr) getSize(), memoryMap, glusage);
 	else
 #endif
-		glBufferSubData(target, 0, (GLsizeiptr) getSize(), memory_map);
+		glBufferSubData(target, 0, (GLsizeiptr) getSize(), memoryMap);
 }
 
 void Buffer::unmap()
 {
-	if (!is_mapped)
+	if (!mapped)
 		return;
 
-	if ((map_flags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
+	if ((mapFlags & MAP_EXPLICIT_RANGE_MODIFY) != 0)
 	{
-		modified_offset = std::min(modified_offset, getSize() - 1);
-		modified_size = std::min(modified_size, getSize() - modified_offset);
+		modifiedOffset = std::min(modifiedOffset, getSize() - 1);
+		modifiedSize = std::min(modifiedSize, getSize() - modifiedOffset);
 	}
 	else
 	{
-		modified_offset = 0;
-		modified_size = getSize();
+		modifiedOffset = 0;
+		modifiedSize = getSize();
 	}
 
-	if (modified_size > 0)
+	if (modifiedSize > 0)
 	{
 		switch (getUsage())
 		{
 		case vertex::USAGE_STATIC:
-			unmapStatic(modified_offset, modified_size);
+			unmapStatic(modifiedOffset, modifiedSize);
 			break;
 		case vertex::USAGE_STREAM:
 			unmapStream();
@@ -142,45 +151,45 @@ void Buffer::unmap()
 		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().
-			if (modified_size >= getSize() / 3)
+			if (modifiedSize >= getSize() / 3)
 				unmapStream();
 			else
-				unmapStatic(modified_offset, modified_size);
+				unmapStatic(modifiedOffset, modifiedSize);
 			break;
 		}
 	}
 
-	modified_offset = 0;
-	modified_size = 0;
+	modifiedOffset = 0;
+	modifiedSize = 0;
 
-	is_mapped = false;
+	mapped = false;
 }
 
 void Buffer::setMappedRangeModified(size_t offset, size_t modifiedsize)
 {
-	if (!is_mapped || !(map_flags & MAP_EXPLICIT_RANGE_MODIFY))
+	if (!mapped || !(mapFlags & MAP_EXPLICIT_RANGE_MODIFY))
 		return;
 
 	// We're being conservative right now by internally marking the whole range
 	// from the start of section a to the end of section b as modified if both
 	// a and b are marked as modified.
 
-	size_t old_range_end = modified_offset + modified_size;
-	modified_offset = std::min(modified_offset, offset);
+	size_t oldrangeend = modifiedOffset + modifiedSize;
+	modifiedOffset = std::min(modifiedOffset, offset);
 
-	size_t new_range_end = std::max(offset + modifiedsize, old_range_end);
-	modified_size = new_range_end - modified_offset;
+	size_t newrangeend = std::max(offset + modifiedsize, oldrangeend);
+	modifiedSize = newrangeend - modifiedOffset;
 }
 
 void Buffer::fill(size_t offset, size_t size, const void *data)
 {
-	memcpy(memory_map + offset, data, size);
+	memcpy(memoryMap + offset, data, size);
 
-	if (is_mapped)
+	if (mapped)
 		setMappedRangeModified(offset, size);
 	else
 	{
-		gl.bindBuffer(type, vbo);
+		gl.bindBuffer(mapType, vbo);
 		glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data);
 	}
 }
@@ -192,7 +201,7 @@ ptrdiff_t Buffer::getHandle() const
 
 void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset)
 {
-	other->fill(otheroffset, size, memory_map + offset);
+	other->fill(otheroffset, size, memoryMap + offset);
 }
 
 bool Buffer::loadVolatile()
@@ -208,13 +217,13 @@ void Buffer::unloadVolatile()
 bool Buffer::load(bool restore)
 {
 	glGenBuffers(1, &vbo);
-	gl.bindBuffer(type, vbo);
+	gl.bindBuffer(mapType, vbo);
 
 	while (glGetError() != GL_NO_ERROR)
 		/* Clear the error buffer. */;
 
 	// Copy the old buffer only if 'restore' was requested.
-	const GLvoid *src = restore ? memory_map : nullptr;
+	const GLvoid *src = restore ? memoryMap : nullptr;
 
 	// Note that if 'src' is '0', no data will be copied.
 	glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage()));
@@ -224,7 +233,7 @@ bool Buffer::load(bool restore)
 
 void Buffer::unload()
 {
-	is_mapped = false;
+	mapped = false;
 	gl.deleteBuffer(vbo);
 	vbo = 0;
 }

+ 5 - 4
src/modules/graphics/opengl/Buffer.h

@@ -39,7 +39,7 @@ class Buffer final : public love::graphics::Buffer, public Volatile
 {
 public:
 
-	Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags);
+	Buffer(size_t size, const void *data, BufferTypeFlags typeflags, vertex::Usage usage, uint32 mapflags);
 	virtual ~Buffer();
 
 	void *map() override;
@@ -62,16 +62,17 @@ private:
 	void unmapStatic(size_t offset, size_t size);
 	void unmapStream();
 
+	BufferType mapType;
 	GLenum target;
 
 	// The VBO identifier. Assigned by OpenGL.
 	GLuint vbo;
 
 	// A pointer to mapped memory.
-	char *memory_map;
+	char *memoryMap;
 
-	size_t modified_offset;
-	size_t modified_size;
+	size_t modifiedOffset;
+	size_t modifiedSize;
 
 }; // Buffer
 

+ 8 - 2
src/modules/graphics/opengl/Graphics.cpp

@@ -155,9 +155,15 @@ love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage
 	return new Shader(vertex, pixel);
 }
 
-love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags)
+love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferTypeFlags typeflags, vertex::Usage usage, uint32 mapflags)
 {
-	return new Buffer(size, data, type, usage, mapflags);
+	return new Buffer(size, data, typeflags, usage, mapflags);
+}
+
+love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataMember> &format, size_t arraylength)
+{
+	// TODO
+	return nullptr;
 }
 
 void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)

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

@@ -63,7 +63,8 @@ public:
 	love::graphics::Image *newImage(const Image::Slices &data, const Image::Settings &settings) override;
 	love::graphics::Image *newImage(TextureType textype, PixelFormat format, int width, int height, int slices, const Image::Settings &settings) override;
 	love::graphics::Canvas *newCanvas(const Canvas::Settings &settings) override;
-	love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
+	love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferTypeFlags typeflags, vertex::Usage usage, uint32 mapflags) override;
+	love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataMember> &format, size_t arraylength) override;
 
 	void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
 	bool setMode(int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) override;

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

@@ -588,6 +588,10 @@ GLenum OpenGL::getGLBufferType(BufferType type)
 		return GL_ARRAY_BUFFER;
 	case BUFFER_INDEX:
 		return GL_ELEMENT_ARRAY_BUFFER;
+	case BUFFER_UNIFORM:
+		return GL_UNIFORM_BUFFER;
+	case BUFFER_SHADER_STORAGE:
+		return GL_SHADER_STORAGE_BUFFER;
 	case BUFFER_MAX_ENUM:
 		return GL_ZERO;
 	}

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

@@ -46,7 +46,7 @@ enum BuiltinVertexAttribute
 	ATTRIB_MAX_ENUM
 };
 
-enum BuiltinVertexAttributeFlag
+enum BuiltinVertexAttributeFlags
 {
 	ATTRIBFLAG_POS = 1 << ATTRIB_POS,
 	ATTRIBFLAG_TEXCOORD = 1 << ATTRIB_TEXCOORD,
@@ -57,9 +57,19 @@ enum BufferType
 {
 	BUFFER_VERTEX = 0,
 	BUFFER_INDEX,
+	BUFFER_UNIFORM,
+	BUFFER_SHADER_STORAGE,
 	BUFFER_MAX_ENUM
 };
 
+enum BufferTypeFlags
+{
+	BUFFERFLAG_VERTEX = 1 << BUFFER_VERTEX,
+	BUFFERFLAG_INDEX = 1 << BUFFER_INDEX,
+	BUFFERFLAG_UNIFORM = 1 << BUFFER_UNIFORM,
+	BUFFERFLAG_SHADER_STORAGE = 1 << BUFFER_SHADER_STORAGE,
+};
+
 enum IndexDataType
 {
 	INDEX_UINT16,