Browse Source

Meshes expose GraphicsBuffers, part 2

Alex Szpakowski 5 years ago
parent
commit
2572f0c7a1

+ 49 - 24
src/modules/graphics/Buffer.cpp

@@ -28,56 +28,62 @@ namespace graphics
 
 
 love::Type Buffer::type("GraphicsBuffer", &Object::type);
 love::Type Buffer::type("GraphicsBuffer", &Object::type);
 
 
-Buffer::Buffer(size_t size, BufferTypeFlags typeflags, BufferUsage usage, uint32 mapflags)
+Buffer::Buffer(const Settings &settings, const void */*data*/, size_t size)
 	: arrayLength(0)
 	: arrayLength(0)
 	, arrayStride(0)
 	, arrayStride(0)
 	, size(size)
 	, size(size)
-	, typeFlags(typeflags)
-	, usage(usage)
-	, mapFlags(mapflags)
+	, typeFlags(settings.typeFlags)
+	, usage(settings.usage)
+	, mapFlags(settings.mapFlags)
 	, mapped(false)
 	, mapped(false)
 {
 {
 }
 }
 
 
-Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMember> &dataMembers, size_t arraylength)
-	: Buffer(0, settings.typeFlags, settings.usage, settings.mapFlags)
+Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &bufferformat, const void *data, size_t size, size_t arraylength)
+	: Buffer(settings, data, size)
 {
 {
-	if (dataMembers.size() == 0)
-		throw love::Exception("Data format must contain values.");
+	if (size == 0 && arraylength == 0)
+		throw love::Exception("Size or array length must be specified.");
 
 
-	this->dataMembers = dataMembers;
+	if (bufferformat.size() == 0)
+		throw love::Exception("Data format must contain values.");
 
 
 	bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
 	bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
 
 
-	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 && dataMembers.size() > 1)
-		throw love::Exception("test");
+	bool uniformbuffer = settings.typeFlags & TYPEFLAG_UNIFORM;
+	bool indexbuffer = settings.typeFlags & TYPEFLAG_INDEX;
+	bool vertexbuffer = settings.typeFlags & TYPEFLAG_VERTEX;
+	bool ssbuffer = settings.typeFlags & TYPEFLAG_SHADER_STORAGE;
 
 
 	size_t offset = 0;
 	size_t offset = 0;
 	size_t stride = 0;
 	size_t stride = 0;
 
 
-	for (const auto &member : dataMembers)
+	for (const auto &decl : bufferformat)
 	{
 	{
-		DataFormat format = member.format;
-		const DataFormatInfo &info = getDataFormatInfo(format);
+		DataMember member(decl);
+
+		DataFormat format = member.decl.format;
+		const DataFormatInfo &info = member.info;
 
 
 		if (indexbuffer)
 		if (indexbuffer)
 		{
 		{
 			if (format != DATAFORMAT_UINT16 && format != DATAFORMAT_UINT32)
 			if (format != DATAFORMAT_UINT16 && format != DATAFORMAT_UINT32)
 				throw love::Exception("Index buffers only support uint16 and uint32 data types.");
 				throw love::Exception("Index buffers only support uint16 and uint32 data types.");
+
+			if (bufferformat.size() > 1)
+				throw love::Exception("Index buffers only support a single value per element.");
 		}
 		}
 
 
 		if (vertexbuffer)
 		if (vertexbuffer)
 		{
 		{
+			if (decl.arraySize > 0)
+				throw love::Exception("Arrays are not supported in vertex buffers.");
+
 			if (info.isMatrix)
 			if (info.isMatrix)
-				throw love::Exception("matrix types are not supported in vertex buffers.");
+				throw love::Exception("Matrix types are not supported in vertex buffers.");
 
 
 			if (info.baseType == DATA_BASETYPE_BOOL)
 			if (info.baseType == DATA_BASETYPE_BOOL)
-				throw love::Exception("bool types are not supported in vertex buffers.");
+				throw love::Exception("Bool types are not supported in vertex buffers.");
 
 
 			if ((info.baseType == DATA_BASETYPE_INT || info.baseType == DATA_BASETYPE_UINT) && !supportsGLSL3)
 			if ((info.baseType == DATA_BASETYPE_INT || info.baseType == DATA_BASETYPE_UINT) && !supportsGLSL3)
 				throw love::Exception("Integer vertex attribute data types require GLSL 3 support.");
 				throw love::Exception("Integer vertex attribute data types require GLSL 3 support.");
@@ -88,11 +94,30 @@ Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMe
 			if (info.componentSize != 4)
 			if (info.componentSize != 4)
 				throw love::Exception("");
 				throw love::Exception("");
 		}
 		}
+
+
+		// TODO: alignment
+		member.offset = offset;
+		offset += member.info.size;
+
+		dataMembers.push_back(member);
+	}
+
+	if (size != 0)
+	{
+		size_t remainder = size % stride;
+		if (remainder > 0)
+			size += stride - remainder;
+		arraylength = size / stride;
+	}
+	else
+	{
+		size = arraylength * stride;
 	}
 	}
 
 
-	this->arrayLength = arraylength;
 	this->arrayStride = stride;
 	this->arrayStride = stride;
-	this->size = stride * arraylength;
+	this->arrayLength = arraylength;
+	this->size = size;
 }
 }
 
 
 Buffer::~Buffer()
 Buffer::~Buffer()
@@ -103,7 +128,7 @@ int Buffer::getDataMemberIndex(const std::string &name) const
 {
 {
 	for (size_t i = 0; i < dataMembers.size(); i++)
 	for (size_t i = 0; i < dataMembers.size(); i++)
 	{
 	{
-		if (dataMembers[i].name == name)
+		if (dataMembers[i].decl.name == name)
 			return (int) i;
 			return (int) i;
 	}
 	}
 
 

+ 43 - 9
src/modules/graphics/Buffer.h

@@ -40,7 +40,7 @@ namespace graphics
 class Graphics;
 class Graphics;
 
 
 /**
 /**
- * A block of GPU-owned memory. Currently meant for internal use.
+ * A block of GPU-owned memory.
  **/
  **/
 class Buffer : public love::Object, public Resource
 class Buffer : public love::Object, public Resource
 {
 {
@@ -50,30 +50,65 @@ public:
 
 
 	enum MapFlags
 	enum MapFlags
 	{
 	{
+		MAP_NONE = 0,
 		MAP_EXPLICIT_RANGE_MODIFY = (1 << 0), // see setMappedRangeModified.
 		MAP_EXPLICIT_RANGE_MODIFY = (1 << 0), // see setMappedRangeModified.
 		MAP_READ = (1 << 1),
 		MAP_READ = (1 << 1),
 	};
 	};
 
 
-	struct DataMember
+	enum TypeFlags
+	{
+		TYPEFLAG_NONE = 0,
+		TYPEFLAG_VERTEX = 1 << BUFFERTYPE_VERTEX,
+		TYPEFLAG_INDEX = 1 << BUFFERTYPE_INDEX,
+		TYPEFLAG_UNIFORM = 1 << BUFFERTYPE_UNIFORM,
+		TYPEFLAG_SHADER_STORAGE = 1 << BUFFERTYPE_SHADER_STORAGE,
+	};
+
+	struct DataDeclaration
 	{
 	{
 		std::string name;
 		std::string name;
 		DataFormat format;
 		DataFormat format;
 		int arraySize;
 		int arraySize;
+
+		DataDeclaration(const std::string &name, DataFormat format, int arraySize = 0)
+			: name(name)
+			, format(format)
+			, arraySize(arraySize)
+		{}
+	};
+
+	struct DataMember
+	{
+		DataDeclaration decl;
+		DataFormatInfo info;
+		size_t offset;
+
+		DataMember(const DataDeclaration &decl)
+			: decl(decl)
+			, info(getDataFormatInfo(decl.format))
+			, offset(0)
+		{}
 	};
 	};
 
 
 	struct Settings
 	struct Settings
 	{
 	{
-		BufferTypeFlags typeFlags;
+		TypeFlags typeFlags;
 		MapFlags mapFlags;
 		MapFlags mapFlags;
 		BufferUsage usage;
 		BufferUsage usage;
+
+		Settings(uint32 typeflags, uint32 mapflags, BufferUsage usage)
+			: typeFlags((TypeFlags)typeflags)
+			, mapFlags((MapFlags)mapflags)
+			, usage(usage)
+		{}
 	};
 	};
 
 
-	Buffer(size_t size, BufferTypeFlags typeflags, BufferUsage usage, uint32 mapflags);
-	Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataMember> &members, size_t arraylength);
+	Buffer(const Settings &settings, const void *data, size_t size);
+	Buffer(Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength);
 	virtual ~Buffer();
 	virtual ~Buffer();
 
 
 	size_t getSize() const { return size; }
 	size_t getSize() const { return size; }
-	BufferTypeFlags getTypeFlags() const { return typeFlags; }
+	TypeFlags getTypeFlags() const { return typeFlags; }
 	BufferUsage getUsage() const { return usage; }
 	BufferUsage getUsage() const { return usage; }
 	bool isMapped() const { return mapped; }
 	bool isMapped() const { return mapped; }
 	uint32 getMapFlags() const { return mapFlags; }
 	uint32 getMapFlags() const { return mapFlags; }
@@ -82,7 +117,7 @@ public:
 	size_t getArrayStride() const { return arrayStride; }
 	size_t getArrayStride() const { return arrayStride; }
 	const std::vector<DataMember> &getDataMembers() const { return dataMembers; }
 	const std::vector<DataMember> &getDataMembers() const { return dataMembers; }
 	const DataMember &getDataMember(int index) const { return dataMembers[index]; }
 	const DataMember &getDataMember(int index) const { return dataMembers[index]; }
-	size_t getMemberOffset(int index) const { return memberOffsets[index]; }
+	size_t getMemberOffset(int index) const { return dataMembers[index].offset; }
 	int getDataMemberIndex(const std::string &name) const;
 	int getDataMemberIndex(const std::string &name) const;
 
 
 	/**
 	/**
@@ -146,7 +181,6 @@ public:
 protected:
 protected:
 
 
 	std::vector<DataMember> dataMembers;
 	std::vector<DataMember> dataMembers;
-	std::vector<size_t> memberOffsets;
 	size_t arrayLength;
 	size_t arrayLength;
 	size_t arrayStride;
 	size_t arrayStride;
 
 
@@ -154,7 +188,7 @@ protected:
 	size_t size;
 	size_t size;
 
 
 	// The type of the buffer object.
 	// The type of the buffer object.
-	BufferTypeFlags typeFlags;
+	TypeFlags typeFlags;
 
 
 	// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
 	// Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW.
 	BufferUsage usage;
 	BufferUsage usage;

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

@@ -642,7 +642,7 @@ void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector<D
 	{
 	{
 		Graphics::StreamDrawCommand streamcmd;
 		Graphics::StreamDrawCommand streamcmd;
 		streamcmd.formats[0] = vertexFormat;
 		streamcmd.formats[0] = vertexFormat;
-		streamcmd.indexMode = TriangleIndexMode::QUADS;
+		streamcmd.indexMode = TRIANGLEINDEX_QUADS;
 		streamcmd.vertexCount = cmd.vertexcount;
 		streamcmd.vertexCount = cmd.vertexcount;
 		streamcmd.texture = cmd.texture;
 		streamcmd.texture = cmd.texture;
 
 

+ 22 - 11
src/modules/graphics/Graphics.cpp

@@ -173,10 +173,10 @@ void Graphics::createQuadIndexBuffer()
 		return;
 		return;
 
 
 	size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6;
 	size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6;
-	quadIndexBuffer = newBuffer(size, nullptr, BUFFERFLAG_INDEX, BUFFERUSAGE_STATIC, 0);
+	quadIndexBuffer = newIndexBuffer(INDEX_UINT16, nullptr, size, BUFFERUSAGE_STATIC, 0);
 
 
 	Buffer::Mapper map(*quadIndexBuffer);
 	Buffer::Mapper map(*quadIndexBuffer);
-	fillIndices(TriangleIndexMode::QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.get());
+	fillIndices(TRIANGLEINDEX_QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.get());
 }
 }
 
 
 Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh)
 Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh)
@@ -260,6 +260,17 @@ Shader *Graphics::newShader(const std::string &vertex, const std::string &pixel)
 	return newShaderInternal(vertexstage.get(), pixelstage.get());
 	return newShaderInternal(vertexstage.get(), pixelstage.get());
 }
 }
 
 
+Buffer *Graphics::newIndexBuffer(IndexDataType dataType, const void *indices, size_t size, BufferUsage usage, uint32 mapflags)
+{
+	Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, mapflags, usage);
+
+	std::vector<Buffer::DataDeclaration> format = {
+		{ "index", getIndexDataFormat(dataType), 0 }
+	};
+
+	return newBuffer(settings, format, indices, size, 0);
+}
+
 Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, PrimitiveType drawmode, BufferUsage usage)
 Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, PrimitiveType drawmode, BufferUsage usage)
 {
 {
 	return newMesh(Mesh::getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage);
 	return newMesh(Mesh::getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage);
@@ -270,12 +281,12 @@ Mesh *Graphics::newMesh(int vertexcount, PrimitiveType drawmode, BufferUsage usa
 	return newMesh(Mesh::getDefaultVertexFormat(), vertexcount, drawmode, usage);
 	return newMesh(Mesh::getDefaultVertexFormat(), vertexcount, drawmode, usage);
 }
 }
 
 
-love::graphics::Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage)
+love::graphics::Mesh *Graphics::newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage)
 {
 {
 	return new Mesh(this, vertexformat, vertexcount, drawmode, usage);
 	return new Mesh(this, vertexformat, vertexcount, drawmode, usage);
 }
 }
 
 
-love::graphics::Mesh *Graphics::newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage)
+love::graphics::Mesh *Graphics::newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage)
 {
 {
 	return new Mesh(this, vertexformat, data, datasize, drawmode, usage);
 	return new Mesh(this, vertexformat, data, datasize, drawmode, usage);
 }
 }
@@ -1003,7 +1014,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawCommand &
 
 
 	if (cmd.primitiveMode != state.primitiveMode
 	if (cmd.primitiveMode != state.primitiveMode
 		|| cmd.formats[0] != state.formats[0] || cmd.formats[1] != state.formats[1]
 		|| cmd.formats[0] != state.formats[0] || cmd.formats[1] != state.formats[1]
-		|| ((cmd.indexMode != TriangleIndexMode::NONE) != (state.indexCount > 0))
+		|| ((cmd.indexMode != TRIANGLEINDEX_NONE) != (state.indexCount > 0))
 		|| cmd.texture != state.texture
 		|| cmd.texture != state.texture
 		|| cmd.standardShaderType != state.standardShaderType)
 		|| cmd.standardShaderType != state.standardShaderType)
 	{
 	{
@@ -1013,7 +1024,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawCommand &
 	int totalvertices = state.vertexCount + cmd.vertexCount;
 	int totalvertices = state.vertexCount + cmd.vertexCount;
 
 
 	// We only support uint16 index buffers for now.
 	// We only support uint16 index buffers for now.
-	if (totalvertices > LOVE_UINT16_MAX && cmd.indexMode != TriangleIndexMode::NONE)
+	if (totalvertices > LOVE_UINT16_MAX && cmd.indexMode != TRIANGLEINDEX_NONE)
 		shouldflush = true;
 		shouldflush = true;
 
 
 	int reqIndexCount = getIndexCount(cmd.indexMode, cmd.vertexCount);
 	int reqIndexCount = getIndexCount(cmd.indexMode, cmd.vertexCount);
@@ -1042,7 +1053,7 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawCommand &
 		newdatasizes[i] = stride * cmd.vertexCount;
 		newdatasizes[i] = stride * cmd.vertexCount;
 	}
 	}
 
 
-	if (cmd.indexMode != TriangleIndexMode::NONE)
+	if (cmd.indexMode != TRIANGLEINDEX_NONE)
 	{
 	{
 		size_t datasize = (state.indexCount + reqIndexCount) * sizeof(uint16);
 		size_t datasize = (state.indexCount + reqIndexCount) * sizeof(uint16);
 
 
@@ -1080,18 +1091,18 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawCommand &
 			if (state.vb[i]->getSize() < buffersizes[i])
 			if (state.vb[i]->getSize() < buffersizes[i])
 			{
 			{
 				delete state.vb[i];
 				delete state.vb[i];
-				state.vb[i] = newStreamBuffer(BUFFER_VERTEX, buffersizes[i]);
+				state.vb[i] = newStreamBuffer(BUFFERTYPE_VERTEX, buffersizes[i]);
 			}
 			}
 		}
 		}
 
 
 		if (state.indexBuffer->getSize() < buffersizes[2])
 		if (state.indexBuffer->getSize() < buffersizes[2])
 		{
 		{
 			delete state.indexBuffer;
 			delete state.indexBuffer;
-			state.indexBuffer = newStreamBuffer(BUFFER_INDEX, buffersizes[2]);
+			state.indexBuffer = newStreamBuffer(BUFFERTYPE_INDEX, buffersizes[2]);
 		}
 		}
 	}
 	}
 
 
-	if (cmd.indexMode != TriangleIndexMode::NONE)
+	if (cmd.indexMode != TRIANGLEINDEX_NONE)
 	{
 	{
 		if (state.indexBufferMap.data == nullptr)
 		if (state.indexBufferMap.data == nullptr)
 			state.indexBufferMap = state.indexBuffer->map(reqIndexSize);
 			state.indexBufferMap = state.indexBuffer->map(reqIndexSize);
@@ -1573,7 +1584,7 @@ void Graphics::polygon(DrawMode mode, const Vector2 *coords, size_t count, bool
 		StreamDrawCommand cmd;
 		StreamDrawCommand cmd;
 		cmd.formats[0] = getSinglePositionFormat(is2D);
 		cmd.formats[0] = getSinglePositionFormat(is2D);
 		cmd.formats[1] = CommonFormat::RGBAub;
 		cmd.formats[1] = CommonFormat::RGBAub;
-		cmd.indexMode = TriangleIndexMode::FAN;
+		cmd.indexMode = TRIANGLEINDEX_FAN;
 		cmd.vertexCount = (int)count - (skipLastFilledVertex ? 1 : 0);
 		cmd.vertexCount = (int)count - (skipLastFilledVertex ? 1 : 0);
 
 
 		StreamVertexData data = requestStreamDraw(cmd);
 		StreamVertexData data = requestStreamDraw(cmd);

+ 6 - 6
src/modules/graphics/Graphics.h

@@ -260,7 +260,7 @@ public:
 	{
 	{
 		PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES;
 		PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES;
 		CommonFormat formats[2];
 		CommonFormat formats[2];
-		TriangleIndexMode indexMode = TriangleIndexMode::NONE;
+		TriangleIndexMode indexMode = TRIANGLEINDEX_NONE;
 		int vertexCount = 0;
 		int vertexCount = 0;
 		Texture *texture = nullptr;
 		Texture *texture = nullptr;
 		Shader::StandardShader standardShaderType = Shader::STANDARD_DEFAULT;
 		Shader::StandardShader standardShaderType = Shader::STANDARD_DEFAULT;
@@ -445,15 +445,15 @@ public:
 	ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source);
 	ShaderStage *newShaderStage(ShaderStage::StageType stage, const std::string &source);
 	Shader *newShader(const std::string &vertex, const std::string &pixel);
 	Shader *newShader(const std::string &vertex, const std::string &pixel);
 
 
-	virtual Buffer *newBuffer(size_t size, const void *data, BufferTypeFlags typeflags, BufferUsage usage, uint32 mapflags) = 0;
-	virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataMember> &format, size_t arraylength) = 0;
+	virtual Buffer *newBuffer(const Buffer::Settings &settings, const void *data, size_t size) = 0;
+	virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) = 0;
 
 
-//	Buffer *newIndexBuffer(IndexDataType dataType, const void *indices, size_t bytesize, vertex::Usage usage, uint32 mapflags) = 0;
+	Buffer *newIndexBuffer(IndexDataType dataType, const void *indices, size_t size, BufferUsage usage, uint32 mapflags);
 
 
 	Mesh *newMesh(const std::vector<Vertex> &vertices, PrimitiveType drawmode, BufferUsage usage);
 	Mesh *newMesh(const std::vector<Vertex> &vertices, PrimitiveType drawmode, BufferUsage usage);
 	Mesh *newMesh(int vertexcount, PrimitiveType drawmode, BufferUsage usage);
 	Mesh *newMesh(int vertexcount, PrimitiveType drawmode, BufferUsage usage);
-	Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage);
-	Mesh *newMesh(const std::vector<Mesh::AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage);
+	Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage);
+	Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage);
 
 
 	Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
 	Text *newText(Font *font, const std::vector<Font::ColoredString> &text = {});
 
 

+ 40 - 85
src/modules/graphics/Mesh.cpp

@@ -45,13 +45,13 @@ static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offs
 static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct");
 static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct");
 static_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct");
 static_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct");
 
 
-std::vector<Mesh::AttribFormat> Mesh::getDefaultVertexFormat()
+std::vector<Buffer::DataDeclaration> Mesh::getDefaultVertexFormat()
 {
 {
 	// Corresponds to the love::Vertex struct.
 	// Corresponds to the love::Vertex struct.
-	std::vector<Mesh::AttribFormat> vertexformat = {
-		{ getBuiltinAttribName(ATTRIB_POS),      DATA_FLOAT,  2 },
-		{ getBuiltinAttribName(ATTRIB_TEXCOORD), DATA_FLOAT,  2 },
-		{ getBuiltinAttribName(ATTRIB_COLOR),    DATA_UNORM8, 4 },
+	std::vector<Buffer::DataDeclaration> vertexformat = {
+		{ getBuiltinAttribName(ATTRIB_POS),      DATAFORMAT_FLOAT_VEC2,  0 },
+		{ getBuiltinAttribName(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2,  0 },
+		{ getBuiltinAttribName(ATTRIB_COLOR),    DATAFORMAT_UNORM8_VEC4, 0 },
 	};
 	};
 
 
 	return vertexformat;
 	return vertexformat;
@@ -59,9 +59,8 @@ std::vector<Mesh::AttribFormat> Mesh::getDefaultVertexFormat()
 
 
 love::Type Mesh::type("Mesh", &Drawable::type);
 love::Type Mesh::type("Mesh", &Drawable::type);
 
 
-Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage)
-	: vertexFormat(vertexformat)
-	, vertexBuffer(nullptr)
+Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage)
+	: vertexBuffer(nullptr)
 	, vertexCount(0)
 	, vertexCount(0)
 	, vertexStride(0)
 	, vertexStride(0)
 	, indexBuffer(nullptr)
 	, indexBuffer(nullptr)
@@ -72,24 +71,22 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexforma
 	, rangeStart(-1)
 	, rangeStart(-1)
 	, rangeCount(-1)
 	, rangeCount(-1)
 {
 {
-	setupAttachedAttributes();
-	calculateAttributeSizes(gfx);
+	Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ, usage);
+	vertexBuffer.set(gfx->newBuffer(settings, vertexformat, data, datasize, 0), Acquire::NORETAIN);
 
 
-	vertexCount = datasize / vertexStride;
-	indexDataType = getIndexDataTypeFromMax(vertexCount);
+	vertexCount = vertexBuffer->getArrayLength();
+	vertexStride = vertexBuffer->getArrayStride();
+	vertexFormat = vertexBuffer->getDataMembers();
 
 
-	if (vertexCount == 0)
-		throw love::Exception("Data size is too small for specified vertex attribute formats.");
+	setupAttachedAttributes();
 
 
-	auto buffer = gfx->newBuffer(datasize, data, BUFFERFLAG_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
-	vertexBuffer.set(buffer, Acquire::NORETAIN);
+	indexDataType = getIndexDataTypeFromMax(vertexCount);
 
 
 	vertexScratchBuffer = new char[vertexStride];
 	vertexScratchBuffer = new char[vertexStride];
 }
 }
 
 
-Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage)
-	: vertexFormat(vertexformat)
-	, vertexBuffer(nullptr)
+Mesh::Mesh(graphics::Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage)
+	: vertexBuffer(nullptr)
 	, vertexCount((size_t) vertexcount)
 	, vertexCount((size_t) vertexcount)
 	, vertexStride(0)
 	, vertexStride(0)
 	, indexBuffer(nullptr)
 	, indexBuffer(nullptr)
@@ -103,16 +100,15 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexforma
 	if (vertexcount <= 0)
 	if (vertexcount <= 0)
 		throw love::Exception("Invalid number of vertices (%d).", vertexcount);
 		throw love::Exception("Invalid number of vertices (%d).", vertexcount);
 
 
-	setupAttachedAttributes();
-	calculateAttributeSizes(gfx);
+	Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ, usage);
+	vertexBuffer.set(gfx->newBuffer(settings, vertexformat, nullptr, 0, vertexcount), Acquire::NORETAIN);
 
 
-	size_t buffersize = vertexCount * vertexStride;
+	vertexStride = vertexBuffer->getArrayStride();
+	vertexFormat = vertexBuffer->getDataMembers();
 
 
-	auto buffer = gfx->newBuffer(buffersize, nullptr, BUFFERFLAG_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ);
-	vertexBuffer.set(buffer, Acquire::NORETAIN);
+	setupAttachedAttributes();
 
 
-	// Initialize the buffer's contents to 0.
-	memset(vertexBuffer->map(), 0, buffersize);
+	memset(vertexBuffer->map(), 0, vertexBuffer->getSize());
 	vertexBuffer->setMappedRangeModified(0, vertexBuffer->getSize());
 	vertexBuffer->setMappedRangeModified(0, vertexBuffer->getSize());
 	vertexBuffer->unmap();
 	vertexBuffer->unmap();
 
 
@@ -134,51 +130,13 @@ void Mesh::setupAttachedAttributes()
 {
 {
 	for (size_t i = 0; i < vertexFormat.size(); i++)
 	for (size_t i = 0; i < vertexFormat.size(); i++)
 	{
 	{
-		const std::string &name = vertexFormat[i].name;
+		const std::string &name = vertexFormat[i].decl.name;
 
 
 		if (attachedAttributes.find(name) != attachedAttributes.end())
 		if (attachedAttributes.find(name) != attachedAttributes.end())
 			throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
 			throw love::Exception("Duplicate vertex attribute name: %s", name.c_str());
 
 
-		attachedAttributes[name] = {nullptr, (int) i, STEP_PER_VERTEX, true};
-	}
-}
-
-void Mesh::calculateAttributeSizes(Graphics *gfx)
-{
-	bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3];
-
-	size_t stride = 0;
-
-	for (const AttribFormat &format : vertexFormat)
-	{
-		size_t size = getDataTypeSize(format.type) * format.components;
-
-		if (format.components <= 0 || format.components > 4)
-			throw love::Exception("Vertex attributes must have between 1 and 4 components.");
-
-		// Hardware really doesn't like attributes that aren't 32 bit-aligned.
-		if (size % 4 != 0)
-			throw love::Exception("Vertex attributes must have enough components to be a multiple of 32 bits.");
-
-		if (isDataTypeInteger(format.type) && !supportsGLSL3)
-			throw love::Exception("Integer vertex attribute data types require GLSL 3 support.");
-
-		// Total size in bytes of each attribute in a single vertex.
-		attributeSizes.push_back(size);
-		stride += size;
+		attachedAttributes[name] = {vertexBuffer, (int) i, STEP_PER_VERTEX, true};
 	}
 	}
-
-	vertexStride = stride;
-}
-
-size_t Mesh::getAttributeOffset(size_t attribindex) const
-{
-	size_t offset = 0;
-
-	for (size_t i = 0; i < attribindex; i++)
-		offset += attributeSizes[i];
-
-	return offset;
 }
 }
 
 
 void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
 void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize)
@@ -223,8 +181,10 @@ void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *dat
 	if (attribindex >= (int) vertexFormat.size())
 	if (attribindex >= (int) vertexFormat.size())
 		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
 		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
 
 
-	size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
-	size_t size = std::min(datasize, attributeSizes[attribindex]);
+	const auto &member = vertexFormat[attribindex];
+
+	size_t offset = vertindex * vertexStride + member.offset;
+	size_t size = std::min(datasize, member.info.size);
 
 
 	uint8 *bufferdata = (uint8 *) vertexBuffer->map();
 	uint8 *bufferdata = (uint8 *) vertexBuffer->map();
 	memcpy(bufferdata + offset, data, size);
 	memcpy(bufferdata + offset, data, size);
@@ -240,8 +200,10 @@ size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, s
 	if (attribindex >= (int) vertexFormat.size())
 	if (attribindex >= (int) vertexFormat.size())
 		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
 		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
 
 
-	size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex);
-	size_t size = std::min(datasize, attributeSizes[attribindex]);
+	const auto &member = vertexFormat[attribindex];
+
+	size_t offset = vertindex * vertexStride + member.offset;
+	size_t size = std::min(datasize, member.info.size);
 
 
 	// We're relying on map() returning read/write data... ew.
 	// We're relying on map() returning read/write data... ew.
 	const uint8 *bufferdata = (const uint8 *) vertexBuffer->map();
 	const uint8 *bufferdata = (const uint8 *) vertexBuffer->map();
@@ -265,25 +227,16 @@ Buffer *Mesh::getVertexBuffer() const
 	return vertexBuffer;
 	return vertexBuffer;
 }
 }
 
 
-const std::vector<Mesh::AttribFormat> &Mesh::getVertexFormat() const
+const std::vector<Buffer::DataMember> &Mesh::getVertexFormat() const
 {
 {
 	return vertexFormat;
 	return vertexFormat;
 }
 }
 
 
-DataType Mesh::getAttributeInfo(int attribindex, int &components) const
-{
-	if (attribindex < 0 || attribindex >= (int) vertexFormat.size())
-		throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1);
-
-	components = vertexFormat[attribindex].components;
-	return vertexFormat[attribindex].type;
-}
-
 int Mesh::getAttributeIndex(const std::string &name) const
 int Mesh::getAttributeIndex(const std::string &name) const
 {
 {
 	for (int i = 0; i < (int) vertexFormat.size(); i++)
 	for (int i = 0; i < (int) vertexFormat.size(); i++)
 	{
 	{
-		if (vertexFormat[i].name == name)
+		if (vertexFormat[i].decl.name == name)
 			return i;
 			return i;
 	}
 	}
 
 
@@ -312,7 +265,7 @@ bool Mesh::isAttributeEnabled(const std::string &name) const
 
 
 void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step)
 void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step)
 {
 {
-	if ((buffer->getTypeFlags() & BUFFER_VERTEX) == 0)
+	if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0)
 		throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a Mesh vertex attribute.");
 		throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a Mesh vertex attribute.");
 
 
 	auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
 	auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
@@ -410,7 +363,8 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
 	if (indexBuffer.get() == nullptr || size > indexBuffer->getSize())
 	if (indexBuffer.get() == nullptr || size > indexBuffer->getSize())
 	{
 	{
 		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
 		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
-		indexBuffer.set(gfx->newBuffer(size, nullptr, BUFFERFLAG_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ), Acquire::NORETAIN);
+		Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, vertexBuffer->getUsage());
+		indexBuffer.set(gfx->newBuffer(settings, nullptr, size), Acquire::NORETAIN);
 	}
 	}
 
 
 	useIndexBuffer = true;
 	useIndexBuffer = true;
@@ -441,7 +395,8 @@ void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasiz
 	if (indexBuffer.get() == nullptr || datasize > indexBuffer->getSize())
 	if (indexBuffer.get() == nullptr || datasize > indexBuffer->getSize())
 	{
 	{
 		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
 		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
-		indexBuffer.set(gfx->newBuffer(datasize, nullptr, BUFFERFLAG_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ), Acquire::NORETAIN);
+		Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, vertexBuffer->getUsage());
+		indexBuffer.set(gfx->newBuffer(settings, nullptr, datasize), Acquire::NORETAIN);
 	}
 	}
 
 
 	indexCount = datasize / getIndexDataSize(datatype);
 	indexCount = datasize / getIndexDataSize(datatype);
@@ -607,7 +562,7 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
 			uint16 offset = (uint16) buffer->getMemberOffset(attrib.second.index);
 			uint16 offset = (uint16) buffer->getMemberOffset(attrib.second.index);
 			uint16 stride = (uint16) buffer->getArrayStride();
 			uint16 stride = (uint16) buffer->getArrayStride();
 
 
-			attributes.set(attributeindex, member.format, offset, activebuffers);
+			attributes.set(attributeindex, member.decl.format, offset, activebuffers);
 			attributes.setBufferLayout(activebuffers, stride, attrib.second.step);
 			attributes.setBufferLayout(activebuffers, stride, attrib.second.step);
 
 
 			// TODO: Ideally we want to reuse buffers with the same stride+step.
 			// TODO: Ideally we want to reuse buffers with the same stride+step.

+ 5 - 16
src/modules/graphics/Mesh.h

@@ -52,15 +52,8 @@ public:
 
 
 	static love::Type type;
 	static love::Type type;
 
 
-	struct AttribFormat
-	{
-		std::string name;
-		DataType type;
-		int components; // max 4
-	};
-
-	Mesh(Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage);
-	Mesh(Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage);
+	Mesh(Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage);
+	Mesh(Graphics *gfx, const std::vector<Buffer::DataDeclaration> &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage);
 
 
 	virtual ~Mesh();
 	virtual ~Mesh();
 
 
@@ -97,8 +90,7 @@ public:
 	/**
 	/**
 	 * Gets the format of each vertex attribute stored in the Mesh.
 	 * Gets the format of each vertex attribute stored in the Mesh.
 	 **/
 	 **/
-	const std::vector<AttribFormat> &getVertexFormat() const;
-	DataType getAttributeInfo(int attribindex, int &components) const;
+	const std::vector<Buffer::DataMember> &getVertexFormat() const;
 	int getAttributeIndex(const std::string &name) const;
 	int getAttributeIndex(const std::string &name) const;
 
 
 	/**
 	/**
@@ -174,7 +166,7 @@ public:
 
 
 	void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount);
 	void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount);
 
 
-	static std::vector<AttribFormat> getDefaultVertexFormat();
+	static std::vector<Buffer::DataDeclaration> getDefaultVertexFormat();
 
 
 private:
 private:
 
 
@@ -189,11 +181,8 @@ private:
 	};
 	};
 
 
 	void setupAttachedAttributes();
 	void setupAttachedAttributes();
-	void calculateAttributeSizes(Graphics *gfx);
-	size_t getAttributeOffset(size_t attribindex) const;
 
 
-	std::vector<AttribFormat> vertexFormat;
-	std::vector<size_t> attributeSizes;
+	std::vector<Buffer::DataMember> vertexFormat;
 
 
 	std::unordered_map<std::string, AttachedAttribute> attachedAttributes;
 	std::unordered_map<std::string, AttachedAttribute> attachedAttributes;
 
 

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

@@ -191,7 +191,8 @@ void ParticleSystem::createBuffers(size_t size)
 		auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
 		auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
 
 
 		size_t bytes = sizeof(Vertex) * size * 4;
 		size_t bytes = sizeof(Vertex) * size * 4;
-		buffer = gfx->newBuffer(bytes, nullptr, BUFFERFLAG_VERTEX, BUFFERUSAGE_STREAM, 0);
+		Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_STREAM);
+		buffer = gfx->newBuffer(settings, nullptr, bytes);
 	}
 	}
 	catch (std::bad_alloc &)
 	catch (std::bad_alloc &)
 	{
 	{
@@ -203,7 +204,8 @@ void ParticleSystem::createBuffers(size_t size)
 void ParticleSystem::deleteBuffers()
 void ParticleSystem::deleteBuffers()
 {
 {
 	delete[] pMem;
 	delete[] pMem;
-	buffer->release();
+	if (buffer)
+		buffer->release();
 
 
 	pMem = nullptr;
 	pMem = nullptr;
 	buffer = nullptr;
 	buffer = nullptr;

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

@@ -82,7 +82,7 @@ void Polyline::render(const Vector2 *coords, size_t count, size_t size_hint, flo
 		// extra degenerate triangle in between the core line and the overdraw
 		// extra degenerate triangle in between the core line and the overdraw
 		// line in order to break up the strip into two. This will let us draw
 		// line in order to break up the strip into two. This will let us draw
 		// everything in one draw call.
 		// everything in one draw call.
-		if (triangle_mode == TriangleIndexMode::STRIP)
+		if (triangle_mode == TRIANGLEINDEX_STRIP)
 			extra_vertices = 2;
 			extra_vertices = 2;
 	}
 	}
 
 
@@ -383,7 +383,7 @@ void Polyline::draw(love::graphics::Graphics *gfx)
 	int maxvertices = LOVE_UINT16_MAX - 3;
 	int maxvertices = LOVE_UINT16_MAX - 3;
 
 
 	int advance = maxvertices;
 	int advance = maxvertices;
-	if (triangle_mode == TriangleIndexMode::STRIP)
+	if (triangle_mode == TRIANGLEINDEX_STRIP)
 		advance -= 2;
 		advance -= 2;
 
 
 	for (int vertex_start = 0; vertex_start < total_vertex_count; vertex_start += advance)
 	for (int vertex_start = 0; vertex_start < total_vertex_count; vertex_start += advance)

+ 2 - 2
src/modules/graphics/Polyline.h

@@ -44,7 +44,7 @@ class Polyline
 {
 {
 public:
 public:
 
 
-	Polyline(TriangleIndexMode mode = TriangleIndexMode::STRIP)
+	Polyline(TriangleIndexMode mode = TRIANGLEINDEX_STRIP)
 		: vertices(nullptr)
 		: vertices(nullptr)
 		, overdraw(nullptr)
 		, overdraw(nullptr)
 		, vertex_count(0)
 		, vertex_count(0)
@@ -109,7 +109,7 @@ class NoneJoinPolyline : public Polyline
 public:
 public:
 
 
 	NoneJoinPolyline()
 	NoneJoinPolyline()
-		: Polyline(TriangleIndexMode::QUADS)
+		: Polyline(TRIANGLEINDEX_QUADS)
 	{}
 	{}
 
 
 	void render(const Vector2 *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
 	void render(const Vector2 *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)

+ 6 - 4
src/modules/graphics/SpriteBatch.cpp

@@ -64,7 +64,8 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage
 	vertex_stride = getFormatStride(vertex_format);
 	vertex_stride = getFormatStride(vertex_format);
 
 
 	size_t vertex_size = vertex_stride * 4 * size;
 	size_t vertex_size = vertex_stride * 4 * size;
-	array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFERFLAG_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY);
+	Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, usage);
+	array_buf = gfx->newBuffer(settings, nullptr, vertex_size);
 }
 }
 
 
 SpriteBatch::~SpriteBatch()
 SpriteBatch::~SpriteBatch()
@@ -218,7 +219,8 @@ void SpriteBatch::setBufferSize(int newsize)
 	try
 	try
 	{
 	{
 		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
 		auto gfx = Module::getInstance<graphics::Graphics>(Module::M_GRAPHICS);
-		new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getTypeFlags(), array_buf->getUsage(), array_buf->getMapFlags());
+		Buffer::Settings settings(array_buf->getTypeFlags(), array_buf->getMapFlags(), array_buf->getUsage());
+		new_array_buf = gfx->newBuffer(settings, nullptr, vertex_size);
 
 
 		// Copy as much of the old data into the new GLBuffer as can fit.
 		// Copy as much of the old data into the new GLBuffer as can fit.
 		size_t copy_size = vertex_stride * 4 * new_next;
 		size_t copy_size = vertex_stride * 4 * new_next;
@@ -246,7 +248,7 @@ int SpriteBatch::getBufferSize() const
 
 
 void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer)
 void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer)
 {
 {
-	if ((buffer->getTypeFlags() & BUFFER_VERTEX) == 0)
+	if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0)
 		throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a SpriteBatch vertex attribute.");
 		throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a SpriteBatch vertex attribute.");
 
 
 	AttachedAttribute oldattrib = {};
 	AttachedAttribute oldattrib = {};
@@ -357,7 +359,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 			uint16 offset = (uint16) buffer->getMemberOffset(it.second.index);
 			uint16 offset = (uint16) buffer->getMemberOffset(it.second.index);
 			uint16 stride = (uint16) buffer->getArrayStride();
 			uint16 stride = (uint16) buffer->getArrayStride();
 
 
-			attributes.set(attributeindex, member.format, offset, activebuffers);
+			attributes.set(attributeindex, member.decl.format, offset, activebuffers);
 			attributes.setBufferLayout(activebuffers, stride);
 			attributes.setBufferLayout(activebuffers, stride);
 
 
 			// TODO: We should reuse buffer bindings with the same buffer+stride+step.
 			// TODO: We should reuse buffer bindings with the same buffer+stride+step.

+ 5 - 3
src/modules/graphics/Text.cpp

@@ -42,7 +42,8 @@ Text::Text(Font *font, const std::vector<Font::ColoredString> &text)
 
 
 Text::~Text()
 Text::~Text()
 {
 {
-	delete vertex_buffer;
+	if (vertex_buffer)
+		vertex_buffer->release();
 }
 }
 
 
 void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
 void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t vertoffset)
@@ -60,12 +61,13 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
 			newsize = std::max(size_t(vertex_buffer->getSize() * 1.5), newsize);
 			newsize = std::max(size_t(vertex_buffer->getSize() * 1.5), newsize);
 
 
 		auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
 		auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
-		Buffer *new_buffer = gfx->newBuffer(newsize, nullptr, BUFFERFLAG_VERTEX, BUFFERUSAGE_DYNAMIC, 0);
+		Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_DYNAMIC);
+		Buffer *new_buffer = gfx->newBuffer(settings, nullptr, newsize);
 
 
 		if (vertex_buffer != nullptr)
 		if (vertex_buffer != nullptr)
 			vertex_buffer->copyTo(0, vertex_buffer->getSize(), new_buffer, 0);
 			vertex_buffer->copyTo(0, vertex_buffer->getSize(), new_buffer, 0);
 
 
-		delete vertex_buffer;
+		vertex_buffer->release();
 		vertex_buffer = new_buffer;
 		vertex_buffer = new_buffer;
 
 
 		vertexBuffers.set(0, vertex_buffer, 0);
 		vertexBuffers.set(0, vertex_buffer, 0);

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

@@ -132,7 +132,7 @@ void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform)
 	Graphics::StreamDrawCommand cmd;
 	Graphics::StreamDrawCommand cmd;
 	cmd.formats[0] = getSinglePositionFormat(is2D);
 	cmd.formats[0] = getSinglePositionFormat(is2D);
 	cmd.formats[1] = CommonFormat::STf_RGBAub;
 	cmd.formats[1] = CommonFormat::STf_RGBAub;
-	cmd.indexMode = TriangleIndexMode::QUADS;
+	cmd.indexMode = TRIANGLEINDEX_QUADS;
 	cmd.vertexCount = 4;
 	cmd.vertexCount = 4;
 	cmd.texture = this;
 	cmd.texture = this;
 
 
@@ -184,7 +184,7 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
 	Graphics::StreamDrawCommand cmd;
 	Graphics::StreamDrawCommand cmd;
 	cmd.formats[0] = getSinglePositionFormat(is2D);
 	cmd.formats[0] = getSinglePositionFormat(is2D);
 	cmd.formats[1] = CommonFormat::STPf_RGBAub;
 	cmd.formats[1] = CommonFormat::STPf_RGBAub;
-	cmd.indexMode = TriangleIndexMode::QUADS;
+	cmd.indexMode = TRIANGLEINDEX_QUADS;
 	cmd.vertexCount = 4;
 	cmd.vertexCount = 4;
 	cmd.texture = this;
 	cmd.texture = this;
 	cmd.standardShaderType = Shader::STANDARD_ARRAY;
 	cmd.standardShaderType = Shader::STANDARD_ARRAY;

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

@@ -117,7 +117,7 @@ void Video::draw(Graphics *gfx, const Matrix4 &m)
 	Graphics::StreamDrawCommand cmd;
 	Graphics::StreamDrawCommand cmd;
 	cmd.formats[0] = getSinglePositionFormat(is2D);
 	cmd.formats[0] = getSinglePositionFormat(is2D);
 	cmd.formats[1] = CommonFormat::STf_RGBAub;
 	cmd.formats[1] = CommonFormat::STf_RGBAub;
-	cmd.indexMode = TriangleIndexMode::QUADS;
+	cmd.indexMode = TRIANGLEINDEX_QUADS;
 	cmd.vertexCount = 4;
 	cmd.vertexCount = 4;
 	cmd.standardShaderType = Shader::STANDARD_VIDEO;
 	cmd.standardShaderType = Shader::STANDARD_VIDEO;
 
 

+ 53 - 52
src/modules/graphics/opengl/Buffer.cpp

@@ -35,21 +35,34 @@ namespace graphics
 namespace opengl
 namespace opengl
 {
 {
 
 
-Buffer::Buffer(size_t size, const void *data, BufferTypeFlags typeflags, BufferUsage usage, uint32 mapflags)
-	: love::graphics::Buffer(size, typeflags, usage, mapflags)
-	, vbo(0)
-	, memoryMap(nullptr)
-	, modifiedOffset(0)
-	, modifiedSize(0)
-{
-	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;
+Buffer::Buffer(const Settings &settings, const void *data, size_t size)
+	: love::graphics::Buffer(settings, data, size)
+{
+	initialize(data);
+}
+
+Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
+	: love::graphics::Buffer(gfx, settings, format, data, size, arraylength)
+{
+	initialize(data);
+}
+
+Buffer::~Buffer()
+{
+	unloadVolatile();
+	delete[] memoryMap;
+}
+
+void Buffer::initialize(const void *data)
+{
+	if (typeFlags & TYPEFLAG_VERTEX)
+		mapType = BUFFERTYPE_VERTEX;
+	else if (typeFlags & TYPEFLAG_INDEX)
+		mapType = BUFFERTYPE_INDEX;
+	else if (typeFlags & TYPEFLAG_UNIFORM)
+		mapType = BUFFERTYPE_UNIFORM;
+	else if (typeFlags & TYPEFLAG_SHADER_STORAGE)
+		mapType = BUFFERTYPE_SHADER_STORAGE;
 
 
 	target = OpenGL::getGLBufferType(mapType);
 	target = OpenGL::getGLBufferType(mapType);
 
 
@@ -72,12 +85,34 @@ Buffer::Buffer(size_t size, const void *data, BufferTypeFlags typeflags, BufferU
 	}
 	}
 }
 }
 
 
-Buffer::~Buffer()
+bool Buffer::loadVolatile()
 {
 {
+	return load(true);
+}
+
+void Buffer::unloadVolatile()
+{
+	mapped = false;
 	if (vbo != 0)
 	if (vbo != 0)
-		unload();
+		gl.deleteBuffer(vbo);
+	vbo = 0;
+}
 
 
-	delete[] memoryMap;
+bool Buffer::load(bool restore)
+{
+	glGenBuffers(1, &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 ? memoryMap : nullptr;
+
+	// Note that if 'src' is '0', no data will be copied.
+	glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage()));
+
+	return (glGetError() == GL_NO_ERROR);
 }
 }
 
 
 void *Buffer::map()
 void *Buffer::map()
@@ -204,40 +239,6 @@ void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, s
 	other->fill(otheroffset, size, memoryMap + offset);
 	other->fill(otheroffset, size, memoryMap + offset);
 }
 }
 
 
-bool Buffer::loadVolatile()
-{
-	return load(true);
-}
-
-void Buffer::unloadVolatile()
-{
-	unload();
-}
-
-bool Buffer::load(bool restore)
-{
-	glGenBuffers(1, &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 ? memoryMap : nullptr;
-
-	// Note that if 'src' is '0', no data will be copied.
-	glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage()));
-
-	return (glGetError() == GL_NO_ERROR);
-}
-
-void Buffer::unload()
-{
-	mapped = false;
-	gl.deleteBuffer(vbo);
-	vbo = 0;
-}
-
 } // opengl
 } // opengl
 } // graphics
 } // graphics
 } // love
 } // love

+ 17 - 12
src/modules/graphics/opengl/Buffer.h

@@ -32,6 +32,9 @@ namespace love
 {
 {
 namespace graphics
 namespace graphics
 {
 {
+
+class Graphics;
+
 namespace opengl
 namespace opengl
 {
 {
 
 
@@ -39,9 +42,14 @@ class Buffer final : public love::graphics::Buffer, public Volatile
 {
 {
 public:
 public:
 
 
-	Buffer(size_t size, const void *data, BufferTypeFlags typeflags, BufferUsage usage, uint32 mapflags);
+	Buffer(const Settings &settings, const void *data, size_t size);
+	Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector<DataDeclaration> &format, const void *data, size_t size, size_t arraylength);
 	virtual ~Buffer();
 	virtual ~Buffer();
 
 
+	// Implements Volatile.
+	bool loadVolatile() override;
+	void unloadVolatile() override;
+
 	void *map() override;
 	void *map() override;
 	void unmap() override;
 	void unmap() override;
 	void setMappedRangeModified(size_t offset, size_t size) override;
 	void setMappedRangeModified(size_t offset, size_t size) override;
@@ -50,29 +58,26 @@ public:
 
 
 	void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) 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:
 private:
 
 
+	void initialize(const void *data);
+
 	bool load(bool restore);
 	bool load(bool restore);
-	void unload();
 
 
 	void unmapStatic(size_t offset, size_t size);
 	void unmapStatic(size_t offset, size_t size);
 	void unmapStream();
 	void unmapStream();
 
 
-	BufferType mapType;
-	GLenum target;
+	BufferType mapType = BUFFERTYPE_VERTEX;
+	GLenum target = 0;
 
 
 	// The VBO identifier. Assigned by OpenGL.
 	// The VBO identifier. Assigned by OpenGL.
-	GLuint vbo;
+	GLuint vbo = 0;
 
 
 	// A pointer to mapped memory.
 	// A pointer to mapped memory.
-	char *memoryMap;
+	char *memoryMap = nullptr;
 
 
-	size_t modifiedOffset;
-	size_t modifiedSize;
+	size_t modifiedOffset = 0;
+	size_t modifiedSize = 0;
 
 
 }; // Buffer
 }; // Buffer
 
 

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

@@ -155,15 +155,14 @@ love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage
 	return new Shader(vertex, pixel);
 	return new Shader(vertex, pixel);
 }
 }
 
 
-love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferTypeFlags typeflags, BufferUsage usage, uint32 mapflags)
+love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const void *data, size_t size)
 {
 {
-	return new Buffer(size, data, typeflags, usage, mapflags);
+	return new Buffer(settings, data, size);
 }
 }
 
 
-love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataMember> &format, size_t arraylength)
+love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength)
 {
 {
-	// TODO
-	return nullptr;
+	return new Buffer(this, settings, format, data, size, arraylength);
 }
 }
 
 
 void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)
 void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight)
@@ -254,9 +253,9 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
 	{
 	{
 		// Initial sizes that should be good enough for most cases. It will
 		// Initial sizes that should be good enough for most cases. It will
 		// resize to fit if needed, later.
 		// resize to fit if needed, later.
-		streamBufferState.vb[0] = CreateStreamBuffer(BUFFER_VERTEX, 1024 * 1024 * 1);
-		streamBufferState.vb[1] = CreateStreamBuffer(BUFFER_VERTEX, 256  * 1024 * 1);
-		streamBufferState.indexBuffer = CreateStreamBuffer(BUFFER_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
+		streamBufferState.vb[0] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 1024 * 1024 * 1);
+		streamBufferState.vb[1] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 256  * 1024 * 1);
+		streamBufferState.indexBuffer = CreateStreamBuffer(BUFFERTYPE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX);
 	}
 	}
 
 
 	// Reload all volatile objects.
 	// Reload all volatile objects.
@@ -375,7 +374,7 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
 	GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType);
 	GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType);
 	GLenum gldatatype = OpenGL::getGLIndexDataType(cmd.indexType);
 	GLenum gldatatype = OpenGL::getGLIndexDataType(cmd.indexType);
 
 
-	gl.bindBuffer(BUFFER_INDEX, cmd.indexBuffer->getHandle());
+	gl.bindBuffer(BUFFERTYPE_INDEX, cmd.indexBuffer->getHandle());
 
 
 	if (cmd.instanceCount > 1)
 	if (cmd.instanceCount > 1)
 		glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount);
 		glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount);
@@ -417,7 +416,7 @@ void Graphics::drawQuads(int start, int count, const VertexAttributes &attribute
 	gl.bindTextureToUnit(texture, 0, false);
 	gl.bindTextureToUnit(texture, 0, false);
 	gl.setCullMode(CULL_NONE);
 	gl.setCullMode(CULL_NONE);
 
 
-	gl.bindBuffer(BUFFER_INDEX, quadIndexBuffer->getHandle());
+	gl.bindBuffer(BUFFERTYPE_INDEX, quadIndexBuffer->getHandle());
 
 
 	if (gl.isBaseVertexSupported())
 	if (gl.isBaseVertexSupported())
 	{
 	{

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

@@ -63,8 +63,8 @@ public:
 	love::graphics::Image *newImage(const Image::Slices &data, const Image::Settings &settings) override;
 	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::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::Canvas *newCanvas(const Canvas::Settings &settings) override;
-	love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferTypeFlags typeflags, BufferUsage usage, uint32 mapflags) override;
-	love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataMember> &format, size_t arraylength) override;
+	love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const void *data, size_t size) override;
+	love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector<Buffer::DataDeclaration> &format, const void *data, size_t size, size_t arraylength) override;
 
 
 	void setViewportSize(int width, int height, int pixelwidth, int pixelheight) 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;
 	bool setMode(int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) override;

+ 8 - 20
src/modules/graphics/opengl/OpenGL.cpp

@@ -223,7 +223,7 @@ void OpenGL::setupContext()
 	glGetIntegerv(GL_CULL_FACE_MODE, &faceCull);
 	glGetIntegerv(GL_CULL_FACE_MODE, &faceCull);
 	state.faceCullMode = faceCull;
 	state.faceCullMode = faceCull;
 
 
-	for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
+	for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++)
 	{
 	{
 		state.boundBuffers[i] = 0;
 		state.boundBuffers[i] = 0;
 		glBindBuffer(getGLBufferType((BufferType) i), 0);
 		glBindBuffer(getGLBufferType((BufferType) i), 0);
@@ -584,15 +584,15 @@ GLenum OpenGL::getGLBufferType(BufferType type)
 {
 {
 	switch (type)
 	switch (type)
 	{
 	{
-	case BUFFER_VERTEX:
+	case BUFFERTYPE_VERTEX:
 		return GL_ARRAY_BUFFER;
 		return GL_ARRAY_BUFFER;
-	case BUFFER_INDEX:
+	case BUFFERTYPE_INDEX:
 		return GL_ELEMENT_ARRAY_BUFFER;
 		return GL_ELEMENT_ARRAY_BUFFER;
-	case BUFFER_UNIFORM:
+	case BUFFERTYPE_UNIFORM:
 		return GL_UNIFORM_BUFFER;
 		return GL_UNIFORM_BUFFER;
-	case BUFFER_SHADER_STORAGE:
+	case BUFFERTYPE_SHADER_STORAGE:
 		return GL_SHADER_STORAGE_BUFFER;
 		return GL_SHADER_STORAGE_BUFFER;
-	case BUFFER_MAX_ENUM:
+	case BUFFERTYPE_MAX_ENUM:
 		return GL_ZERO;
 		return GL_ZERO;
 	}
 	}
 
 
@@ -717,10 +717,6 @@ GLenum OpenGL::getGLVertexDataType(DataFormat format, int &components, GLboolean
 		intformat = true;
 		intformat = true;
 		return GL_UNSIGNED_BYTE;
 		return GL_UNSIGNED_BYTE;
 
 
-	case DATAFORMAT_SNORM16:
-		components = 1;
-		normalized = GL_TRUE;
-		return GL_SHORT;
 	case DATAFORMAT_SNORM16_VEC2:
 	case DATAFORMAT_SNORM16_VEC2:
 		components = 2;
 		components = 2;
 		normalized = GL_TRUE;
 		normalized = GL_TRUE;
@@ -730,10 +726,6 @@ GLenum OpenGL::getGLVertexDataType(DataFormat format, int &components, GLboolean
 		normalized = GL_TRUE;
 		normalized = GL_TRUE;
 		return GL_BYTE;
 		return GL_BYTE;
 
 
-	case DATAFORMAT_UNORM16:
-		components = 1;
-		normalized = GL_TRUE;
-		return GL_UNSIGNED_SHORT;
 	case DATAFORMAT_UNORM16_VEC2:
 	case DATAFORMAT_UNORM16_VEC2:
 		components = 2;
 		components = 2;
 		normalized = GL_TRUE;
 		normalized = GL_TRUE;
@@ -743,10 +735,6 @@ GLenum OpenGL::getGLVertexDataType(DataFormat format, int &components, GLboolean
 		normalized = GL_TRUE;
 		normalized = GL_TRUE;
 		return GL_UNSIGNED_SHORT;
 		return GL_UNSIGNED_SHORT;
 
 
-	case DATAFORMAT_INT16:
-		components = 1;
-		intformat = true;
-		return GL_SHORT;
 	case DATAFORMAT_INT16_VEC2:
 	case DATAFORMAT_INT16_VEC2:
 		components = 2;
 		components = 2;
 		intformat = true;
 		intformat = true;
@@ -810,7 +798,7 @@ void OpenGL::deleteBuffer(GLuint buffer)
 {
 {
 	glDeleteBuffers(1, &buffer);
 	glDeleteBuffers(1, &buffer);
 
 
-	for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++)
+	for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++)
 	{
 	{
 		if (state.boundBuffers[i] == buffer)
 		if (state.boundBuffers[i] == buffer)
 			state.boundBuffers[i] = 0;
 			state.boundBuffers[i] = 0;
@@ -857,7 +845,7 @@ void OpenGL::setVertexAttributes(const VertexAttributes &attributes, const Buffe
 
 
 			const void *offsetpointer = reinterpret_cast<void*>(bufferinfo.offset + attrib.offsetFromVertex);
 			const void *offsetpointer = reinterpret_cast<void*>(bufferinfo.offset + attrib.offsetFromVertex);
 
 
-			bindBuffer(BUFFER_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
+			bindBuffer(BUFFERTYPE_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
 
 
 			if (intformat)
 			if (intformat)
 				glVertexAttribIPointer(i, components, gltype, layout.stride, offsetpointer);
 				glVertexAttribIPointer(i, components, gltype, layout.stride, offsetpointer);

+ 1 - 1
src/modules/graphics/opengl/OpenGL.h

@@ -453,7 +453,7 @@ private:
 	// Tracked OpenGL state.
 	// Tracked OpenGL state.
 	struct
 	struct
 	{
 	{
-		GLuint boundBuffers[BUFFER_MAX_ENUM];
+		GLuint boundBuffers[BUFFERTYPE_MAX_ENUM];
 
 
 		// Texture unit state (currently bound texture for each texture unit.)
 		// Texture unit state (currently bound texture for each texture unit.)
 		std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM];
 		std::vector<GLuint> boundTextures[TEXTURE_MAX_ENUM];

+ 24 - 74
src/modules/graphics/vertex.cpp

@@ -137,21 +137,18 @@ static const DataFormatInfo dataFormatInfo[]
 	{ DATA_BASETYPE_INT,   false, 4, 0, 0, 1, 1, 4 }, // DATAFORMAT_INT8_VEC4
 	{ DATA_BASETYPE_INT,   false, 4, 0, 0, 1, 1, 4 }, // DATAFORMAT_INT8_VEC4
 	{ DATA_BASETYPE_UINT,  false, 4, 0, 0, 1, 1, 4 }, // DATAFORMAT_UINT8_VEC4
 	{ DATA_BASETYPE_UINT,  false, 4, 0, 0, 1, 1, 4 }, // DATAFORMAT_UINT8_VEC4
 
 
-	{ DATA_BASETYPE_SNORM, false, 1, 0, 0, 2, 2, 2 }, // DATAFORMAT_SNORM16
 	{ DATA_BASETYPE_SNORM, false, 2, 0, 0, 2, 2, 4 }, // DATAFORMAT_SNORM16_VEC2
 	{ DATA_BASETYPE_SNORM, false, 2, 0, 0, 2, 2, 4 }, // DATAFORMAT_SNORM16_VEC2
 	{ DATA_BASETYPE_SNORM, false, 4, 0, 0, 2, 2, 8 }, // DATAFORMAT_SNORM16_VEC4
 	{ DATA_BASETYPE_SNORM, false, 4, 0, 0, 2, 2, 8 }, // DATAFORMAT_SNORM16_VEC4
 
 
-	{ DATA_BASETYPE_UNORM, false, 1, 0, 0, 2, 2, 2 }, // DATAFORMAT_SNORM16
-	{ DATA_BASETYPE_UNORM, false, 2, 0, 0, 2, 2, 4 }, // DATAFORMAT_SNORM16_VEC2
-	{ DATA_BASETYPE_UNORM, false, 4, 0, 0, 2, 2, 8 }, // DATAFORMAT_SNORM16_VEC4
+	{ DATA_BASETYPE_UNORM, false, 2, 0, 0, 2, 2, 4 }, // DATAFORMAT_UNORM16_VEC2
+	{ DATA_BASETYPE_UNORM, false, 4, 0, 0, 2, 2, 8 }, // DATAFORMAT_UNORM16_VEC4
 
 
-	{ DATA_BASETYPE_INT, false, 1, 0, 0, 2, 2, 2 }, // DATAFORMAT_SNORM16
-	{ DATA_BASETYPE_INT, false, 2, 0, 0, 2, 2, 4 }, // DATAFORMAT_SNORM16_VEC2
-	{ DATA_BASETYPE_INT, false, 4, 0, 0, 2, 2, 8 }, // DATAFORMAT_SNORM16_VEC4
+	{ DATA_BASETYPE_INT, false, 2, 0, 0, 2, 2, 4 }, // DATAFORMAT_INT16_VEC2
+	{ DATA_BASETYPE_INT, false, 4, 0, 0, 2, 2, 8 }, // DATAFORMAT_INT16_VEC4
 
 
-	{ DATA_BASETYPE_UINT, false, 1, 0, 0, 2, 2, 2 }, // DATAFORMAT_SNORM16
-	{ DATA_BASETYPE_UINT, false, 2, 0, 0, 2, 2, 4 }, // DATAFORMAT_SNORM16_VEC2
-	{ DATA_BASETYPE_UINT, false, 4, 0, 0, 2, 2, 8 }, // DATAFORMAT_SNORM16_VEC4
+	{ DATA_BASETYPE_UINT, false, 1, 0, 0, 2, 2, 2 }, // DATAFORMAT_UINT16
+	{ DATA_BASETYPE_UINT, false, 2, 0, 0, 2, 2, 4 }, // DATAFORMAT_UINT16_VEC2
+	{ DATA_BASETYPE_UINT, false, 4, 0, 0, 2, 2, 8 }, // DATAFORMAT_UINT16_VEC4
 
 
 	{ DATA_BASETYPE_BOOL, false, 1, 0, 0, 4, 4, 4  }, // DATAFORMAT_BOOL
 	{ DATA_BASETYPE_BOOL, false, 1, 0, 0, 4, 4, 4  }, // DATAFORMAT_BOOL
 	{ DATA_BASETYPE_BOOL, false, 2, 0, 0, 4, 4, 8  }, // DATAFORMAT_BOOL_VEC2
 	{ DATA_BASETYPE_BOOL, false, 2, 0, 0, 4, 4, 8  }, // DATAFORMAT_BOOL_VEC2
@@ -176,62 +173,26 @@ size_t getIndexDataSize(IndexDataType type)
 	}
 	}
 }
 }
 
 
-size_t getDataTypeSize(DataType datatype)
-{
-	switch (datatype)
-	{
-	case DATA_SNORM8:
-	case DATA_UNORM8:
-	case DATA_INT8:
-	case DATA_UINT8:
-		return sizeof(uint8);
-	case DATA_SNORM16:
-	case DATA_UNORM16:
-	case DATA_INT16:
-	case DATA_UINT16:
-		return sizeof(uint16);
-	case DATA_INT32:
-	case DATA_UINT32:
-		return sizeof(uint32);
-	case DATA_FLOAT:
-		return sizeof(float);
-	case DATA_MAX_ENUM:
-		return 0;
-	}
-	return 0;
-}
-
-bool isDataTypeInteger(DataType datatype)
+IndexDataType getIndexDataTypeFromMax(size_t maxvalue)
 {
 {
-	switch (datatype)
-	{
-	case DATA_INT8:
-	case DATA_UINT8:
-	case DATA_INT16:
-	case DATA_UINT16:
-	case DATA_INT32:
-	case DATA_UINT32:
-		return true;
-	default:
-		return false;
-	}
+	return maxvalue > LOVE_UINT16_MAX ? INDEX_UINT32 : INDEX_UINT16;
 }
 }
 
 
-IndexDataType getIndexDataTypeFromMax(size_t maxvalue)
+DataFormat getIndexDataFormat(IndexDataType type)
 {
 {
-	return maxvalue > LOVE_UINT16_MAX ? INDEX_UINT32 : INDEX_UINT16;
+	return type == INDEX_UINT32 ? DATAFORMAT_UINT32 : DATAFORMAT_UINT16;
 }
 }
 
 
 int getIndexCount(TriangleIndexMode mode, int vertexCount)
 int getIndexCount(TriangleIndexMode mode, int vertexCount)
 {
 {
 	switch (mode)
 	switch (mode)
 	{
 	{
-	case TriangleIndexMode::NONE:
+	case TRIANGLEINDEX_NONE:
 		return 0;
 		return 0;
-	case TriangleIndexMode::STRIP:
-	case TriangleIndexMode::FAN:
+	case TRIANGLEINDEX_STRIP:
+	case TRIANGLEINDEX_FAN:
 		return 3 * (vertexCount - 2);
 		return 3 * (vertexCount - 2);
-	case TriangleIndexMode::QUADS:
+	case TRIANGLEINDEX_QUADS:
 		return vertexCount * 6 / 4;
 		return vertexCount * 6 / 4;
 	}
 	}
 	return 0;
 	return 0;
@@ -242,9 +203,9 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T
 {
 {
 	switch (mode)
 	switch (mode)
 	{
 	{
-	case TriangleIndexMode::NONE:
+	case TRIANGLEINDEX_NONE:
 		break;
 		break;
-	case TriangleIndexMode::STRIP:
+	case TRIANGLEINDEX_STRIP:
 		{
 		{
 			int i = 0;
 			int i = 0;
 			for (T index = 0; index < vertexCount - 2; index++)
 			for (T index = 0; index < vertexCount - 2; index++)
@@ -255,7 +216,7 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T
 			}
 			}
 		}
 		}
 		break;
 		break;
-	case TriangleIndexMode::FAN:
+	case TRIANGLEINDEX_FAN:
 		{
 		{
 			int i = 0;
 			int i = 0;
 			for (T index = 2; index < vertexCount; index++)
 			for (T index = 2; index < vertexCount; index++)
@@ -266,7 +227,7 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T
 			}
 			}
 		}
 		}
 		break;
 		break;
-	case TriangleIndexMode::QUADS:
+	case TRIANGLEINDEX_QUADS:
 		{
 		{
 			// 0---2
 			// 0---2
 			// | / |
 			// | / |
@@ -390,21 +351,13 @@ DEFINE_STRINGMAP_BEGIN(AttributeStep, STEP_MAX_ENUM, attributeStep)
 }
 }
 DEFINE_STRINGMAP_END(AttributeStep, STEP_MAX_ENUM, attributeStep)
 DEFINE_STRINGMAP_END(AttributeStep, STEP_MAX_ENUM, attributeStep)
 
 
-DEFINE_STRINGMAP_BEGIN(DataType, DATA_MAX_ENUM, dataType)
+DEFINE_STRINGMAP_BEGIN(DataTypeDeprecated, DATADEPRECATED_MAX_ENUM, dataType)
 {
 {
-	{ "snorm8",  DATA_SNORM8  },
-	{ "unorm8",  DATA_UNORM8  },
-	{ "int8",    DATA_INT8    },
-	{ "uint8",   DATA_UINT8   },
-	{ "snorm16", DATA_SNORM16 },
-	{ "unorm16", DATA_UNORM16 },
-	{ "int16",   DATA_INT16   },
-	{ "uint16",  DATA_UINT16  },
-	{ "int32",   DATA_INT32   },
-	{ "uint32",  DATA_UINT32  },
-	{ "float",   DATA_FLOAT   },
+	{ "unorm8",  DATADEPRECATED_UNORM8  },
+	{ "unorm16", DATADEPRECATED_UNORM16 },
+	{ "float",   DATADEPRECATED_FLOAT   },
 }
 }
-DEFINE_STRINGMAP_END(DataType, DATA_MAX_ENUM, dataType)
+DEFINE_STRINGMAP_END(DataTypeDeprecated, DATADEPRECATED_MAX_ENUM, dataType)
 
 
 DEFINE_STRINGMAP_BEGIN(DataFormat, DATAFORMAT_MAX_ENUM, dataFormat)
 DEFINE_STRINGMAP_BEGIN(DataFormat, DATAFORMAT_MAX_ENUM, dataFormat)
 {
 {
@@ -440,15 +393,12 @@ DEFINE_STRINGMAP_BEGIN(DataFormat, DATAFORMAT_MAX_ENUM, dataFormat)
 	{ "int8vec4",   DATAFORMAT_INT8_VEC4   },
 	{ "int8vec4",   DATAFORMAT_INT8_VEC4   },
 	{ "uint8vec4",  DATAFORMAT_UINT8_VEC4  },
 	{ "uint8vec4",  DATAFORMAT_UINT8_VEC4  },
 
 
-	{ "snorm16",     DATAFORMAT_SNORM16      },
 	{ "snorm16vec2", DATAFORMAT_SNORM16_VEC2 },
 	{ "snorm16vec2", DATAFORMAT_SNORM16_VEC2 },
 	{ "snorm16vec4", DATAFORMAT_SNORM16_VEC4 },
 	{ "snorm16vec4", DATAFORMAT_SNORM16_VEC4 },
 
 
-	{ "unorm16",     DATAFORMAT_UNORM16      },
 	{ "unorm16vec2", DATAFORMAT_UNORM16_VEC2 },
 	{ "unorm16vec2", DATAFORMAT_UNORM16_VEC2 },
 	{ "unorm16vec4", DATAFORMAT_UNORM16_VEC4 },
 	{ "unorm16vec4", DATAFORMAT_UNORM16_VEC4 },
 
 
-	{ "int16",     DATAFORMAT_INT16      },
 	{ "int16vec2", DATAFORMAT_INT16_VEC2 },
 	{ "int16vec2", DATAFORMAT_INT16_VEC2 },
 	{ "int16vec4", DATAFORMAT_INT16_VEC4 },
 	{ "int16vec4", DATAFORMAT_INT16_VEC4 },
 
 

+ 17 - 45
src/modules/graphics/vertex.h

@@ -56,19 +56,11 @@ enum BuiltinVertexAttributeFlags
 
 
 enum BufferType
 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,
+	BUFFERTYPE_VERTEX = 0,
+	BUFFERTYPE_INDEX,
+	BUFFERTYPE_UNIFORM,
+	BUFFERTYPE_SHADER_STORAGE,
+	BUFFERTYPE_MAX_ENUM
 };
 };
 
 
 enum IndexDataType
 enum IndexDataType
@@ -112,24 +104,12 @@ enum BufferUsage
 	BUFFERUSAGE_MAX_ENUM
 	BUFFERUSAGE_MAX_ENUM
 };
 };
 
 
-enum DataType
+enum DataTypeDeprecated
 {
 {
-	DATA_SNORM8,
-	DATA_UNORM8,
-	DATA_INT8,
-	DATA_UINT8,
-
-	DATA_SNORM16,
-	DATA_UNORM16,
-	DATA_INT16,
-	DATA_UINT16,
-
-	DATA_INT32,
-	DATA_UINT32,
-
-	DATA_FLOAT,
-
-	DATA_MAX_ENUM
+	DATADEPRECATED_UNORM8,
+	DATADEPRECATED_UNORM16,
+	DATADEPRECATED_FLOAT,
+	DATADEPRECATED_MAX_ENUM
 };
 };
 
 
 // Value types used when interfacing with the GPU (vertex and shader data).
 // Value types used when interfacing with the GPU (vertex and shader data).
@@ -164,22 +144,16 @@ enum DataFormat
 	DATAFORMAT_UINT32_VEC4,
 	DATAFORMAT_UINT32_VEC4,
 
 
 	DATAFORMAT_SNORM8_VEC4,
 	DATAFORMAT_SNORM8_VEC4,
-
 	DATAFORMAT_UNORM8_VEC4,
 	DATAFORMAT_UNORM8_VEC4,
-
 	DATAFORMAT_INT8_VEC4,
 	DATAFORMAT_INT8_VEC4,
-
 	DATAFORMAT_UINT8_VEC4,
 	DATAFORMAT_UINT8_VEC4,
 
 
-	DATAFORMAT_SNORM16,
 	DATAFORMAT_SNORM16_VEC2,
 	DATAFORMAT_SNORM16_VEC2,
 	DATAFORMAT_SNORM16_VEC4,
 	DATAFORMAT_SNORM16_VEC4,
 
 
-	DATAFORMAT_UNORM16,
 	DATAFORMAT_UNORM16_VEC2,
 	DATAFORMAT_UNORM16_VEC2,
 	DATAFORMAT_UNORM16_VEC4,
 	DATAFORMAT_UNORM16_VEC4,
 
 
-	DATAFORMAT_INT16,
 	DATAFORMAT_INT16_VEC2,
 	DATAFORMAT_INT16_VEC2,
 	DATAFORMAT_INT16_VEC4,
 	DATAFORMAT_INT16_VEC4,
 
 
@@ -213,12 +187,12 @@ enum Winding
 	WINDING_MAX_ENUM
 	WINDING_MAX_ENUM
 };
 };
 
 
-enum class TriangleIndexMode
+enum TriangleIndexMode
 {
 {
-	NONE,
-	STRIP,
-	FAN,
-	QUADS,
+	TRIANGLEINDEX_NONE,
+	TRIANGLEINDEX_STRIP,
+	TRIANGLEINDEX_FAN,
+	TRIANGLEINDEX_QUADS,
 };
 };
 
 
 enum class CommonFormat
 enum class CommonFormat
@@ -403,10 +377,8 @@ inline CommonFormat getSinglePositionFormat(bool is2D)
 const DataFormatInfo &getDataFormatInfo(DataFormat format);
 const DataFormatInfo &getDataFormatInfo(DataFormat format);
 
 
 size_t getIndexDataSize(IndexDataType type);
 size_t getIndexDataSize(IndexDataType type);
-size_t getDataTypeSize(DataType datatype);
-bool isDataTypeInteger(DataType datatype);
-
 IndexDataType getIndexDataTypeFromMax(size_t maxvalue);
 IndexDataType getIndexDataTypeFromMax(size_t maxvalue);
+DataFormat getIndexDataFormat(IndexDataType type);
 
 
 int getIndexCount(TriangleIndexMode mode, int vertexCount);
 int getIndexCount(TriangleIndexMode mode, int vertexCount);
 
 
@@ -418,7 +390,7 @@ DECLARE_STRINGMAP(IndexDataType);
 DECLARE_STRINGMAP(BufferUsage);
 DECLARE_STRINGMAP(BufferUsage);
 DECLARE_STRINGMAP(PrimitiveType);
 DECLARE_STRINGMAP(PrimitiveType);
 DECLARE_STRINGMAP(AttributeStep);
 DECLARE_STRINGMAP(AttributeStep);
-DECLARE_STRINGMAP(DataType);
+DECLARE_STRINGMAP(DataTypeDeprecated);
 DECLARE_STRINGMAP(DataFormat);
 DECLARE_STRINGMAP(DataFormat);
 DECLARE_STRINGMAP(DataBaseType);
 DECLARE_STRINGMAP(DataBaseType);
 DECLARE_STRINGMAP(CullMode);
 DECLARE_STRINGMAP(CullMode);

+ 26 - 21
src/modules/graphics/wrap_Graphics.cpp

@@ -1503,7 +1503,7 @@ static Mesh *newCustomMesh(lua_State *L)
 
 
 	// First argument is the vertex format, second is a table of vertices or
 	// First argument is the vertex format, second is a table of vertices or
 	// the number of vertices.
 	// the number of vertices.
-	std::vector<Mesh::AttribFormat> vertexformat;
+	std::vector<Buffer::DataDeclaration> vertexformat;
 
 
 	PrimitiveType drawmode = luax_optmeshdrawmode(L, 3, PRIMITIVE_TRIANGLE_FAN);
 	PrimitiveType drawmode = luax_optmeshdrawmode(L, 3, PRIMITIVE_TRIANGLE_FAN);
 	BufferUsage usage = luax_optmeshusage(L, 4, BUFFERUSAGE_DYNAMIC);
 	BufferUsage usage = luax_optmeshusage(L, 4, BUFFERUSAGE_DYNAMIC);
@@ -1525,27 +1525,35 @@ static Mesh *newCustomMesh(lua_State *L)
 		for (int j = 1; j <= 3; j++)
 		for (int j = 1; j <= 3; j++)
 			lua_rawgeti(L, -j, j);
 			lua_rawgeti(L, -j, j);
 
 
-		Mesh::AttribFormat format;
-		format.name = luaL_checkstring(L, -3);
+		const char *name = luaL_checkstring(L, -3);
 
 
+		DataFormat format = DATAFORMAT_FLOAT;
 		const char *tname = luaL_checkstring(L, -2);
 		const char *tname = luaL_checkstring(L, -2);
-		if (strcmp(tname, "byte") == 0) // Legacy name.
-			format.type = DATA_UNORM8;
-		else if (!getConstant(tname, format.type))
-		{
-			luax_enumerror(L, "Mesh vertex data type name", getConstants(format.type), tname);
-			return nullptr;
-		}
 
 
-		format.components = (int) luaL_checkinteger(L, -1);
-		if (format.components <= 0 || format.components > 4)
+		if (!getConstant(tname, format))
 		{
 		{
-			luaL_error(L, "Number of vertex attribute components must be between 1 and 4 (got %d)", format.components);
-			return nullptr;
+			DataTypeDeprecated legacyType = DATADEPRECATED_FLOAT;
+
+			if (strcmp(tname, "byte") == 0) // Legacy name.
+				legacyType = DATADEPRECATED_UNORM8;
+			else if (!getConstant(tname, legacyType))
+			{
+				luax_enumerror(L, "Mesh vertex data format name", getConstants(format), tname);
+				return nullptr;
+			}
+
+			int components = (int) luaL_checkinteger(L, -1);
+			if (components <= 0 || components > 4)
+			{
+				luaL_error(L, "Number of vertex attribute components must be between 1 and 4 (got %d)", components);
+				return nullptr;
+			}
+
+			// TODO: convert legacy type+components to new format enum.
 		}
 		}
 
 
 		lua_pop(L, 4);
 		lua_pop(L, 4);
-		vertexformat.push_back(format);
+		vertexformat.emplace_back(name, format);
 	}
 	}
 
 
 	if (lua_isnumber(L, 2))
 	if (lua_isnumber(L, 2))
@@ -1570,10 +1578,6 @@ static Mesh *newCustomMesh(lua_State *L)
 		}
 		}
 		lua_pop(L, 1);
 		lua_pop(L, 1);
 
 
-		int vertexcomponents = 0;
-		for (const Mesh::AttribFormat &format : vertexformat)
-			vertexcomponents += format.components;
-
 		size_t numvertices = luax_objlen(L, 2);
 		size_t numvertices = luax_objlen(L, 2);
 
 
 		luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, numvertices, drawmode, usage); });
 		luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, numvertices, drawmode, usage); });
@@ -1590,7 +1594,8 @@ static Mesh *newCustomMesh(lua_State *L)
 			int n = 0;
 			int n = 0;
 			for (size_t i = 0; i < vertexformat.size(); i++)
 			for (size_t i = 0; i < vertexformat.size(); i++)
 			{
 			{
-				int components = vertexformat[i].components;
+				const auto &info = getDataFormatInfo(vertexformat[i].format);
+				int components = info.components;
 
 
 				// get vertices[vertindex][n]
 				// get vertices[vertindex][n]
 				for (int c = 0; c < components; c++)
 				for (int c = 0; c < components; c++)
@@ -1600,7 +1605,7 @@ static Mesh *newCustomMesh(lua_State *L)
 				}
 				}
 
 
 				// Fetch the values from Lua and store them in data buffer.
 				// Fetch the values from Lua and store them in data buffer.
-				luax_writeAttributeData(L, -components, vertexformat[i].type, components, data);
+				luax_writeAttributeData(L, -components, vertexformat[i].format, components, data);
 
 
 				lua_pop(L, components);
 				lua_pop(L, components);
 
 

+ 136 - 83
src/modules/graphics/wrap_Mesh.cpp

@@ -54,7 +54,7 @@ template <typename T>
 static inline size_t writeSNormData(lua_State *L, int startidx, int components, char *data)
 static inline size_t writeSNormData(lua_State *L, int startidx, int components, char *data)
 {
 {
 	auto componentdata = (T *) data;
 	auto componentdata = (T *) data;
-	auto maxval = std::numeric_limits<T>::max();
+	const auto maxval = std::numeric_limits<T>::max();
 
 
 	for (int i = 0; i < components; i++)
 	for (int i = 0; i < components; i++)
 		componentdata[i] = (T) (luax_optnumberclamped(L, startidx + i, -1.0, 1.0, defaultComponents[i]) * maxval);
 		componentdata[i] = (T) (luax_optnumberclamped(L, startidx + i, -1.0, 1.0, defaultComponents[i]) * maxval);
@@ -66,7 +66,7 @@ template <typename T>
 static inline size_t writeUNormData(lua_State *L, int startidx, int components, char *data)
 static inline size_t writeUNormData(lua_State *L, int startidx, int components, char *data)
 {
 {
 	auto componentdata = (T *) data;
 	auto componentdata = (T *) data;
-	auto maxval = std::numeric_limits<T>::max();
+	const auto maxval = std::numeric_limits<T>::max();
 
 
 	for (int i = 0; i < components; i++)
 	for (int i = 0; i < components; i++)
 		componentdata[i] = (T) (luax_optnumberclamped01(L, startidx + i, 1.0) * maxval);
 		componentdata[i] = (T) (luax_optnumberclamped01(L, startidx + i, 1.0) * maxval);
@@ -74,32 +74,54 @@ static inline size_t writeUNormData(lua_State *L, int startidx, int components,
 	return sizeof(T) * components;
 	return sizeof(T) * components;
 }
 }
 
 
-char *luax_writeAttributeData(lua_State *L, int startidx, DataType type, int components, char *data)
+char *luax_writeAttributeData(lua_State *L, int startidx, DataFormat format, int components, char *data)
 {
 {
-	switch (type)
+	switch (format)
 	{
 	{
-	case DATA_SNORM8:
-		return data + writeSNormData<int8>(L, startidx, components, data);
-	case DATA_UNORM8:
-		return data + writeUNormData<uint8>(L, startidx, components, data);
-	case DATA_INT8:
-		return data + writeData<int8>(L, startidx, components, data);
-	case DATA_UINT8:
-		return data + writeData<uint8>(L, startidx, components, data);
-	case DATA_SNORM16:
+	case DATAFORMAT_FLOAT:
+	case DATAFORMAT_FLOAT_VEC2:
+	case DATAFORMAT_FLOAT_VEC3:
+	case DATAFORMAT_FLOAT_VEC4:
+		return data + writeData<float>(L, startidx, components, data);
+
+	case DATAFORMAT_INT32:
+	case DATAFORMAT_INT32_VEC2:
+	case DATAFORMAT_INT32_VEC3:
+	case DATAFORMAT_INT32_VEC4:
+		return data + writeData<int32>(L, startidx, components, data);
+
+	case DATAFORMAT_UINT32:
+	case DATAFORMAT_UINT32_VEC2:
+	case DATAFORMAT_UINT32_VEC3:
+	case DATAFORMAT_UINT32_VEC4:
+		return data + writeData<uint32>(L, startidx, components, data);
+
+	case DATAFORMAT_SNORM8_VEC4:
+		return data + writeSNormData<int8>(L, startidx, 4, data);
+	case DATAFORMAT_UNORM8_VEC4:
+		return data + writeUNormData<uint8>(L, startidx, 4, data);
+	case DATAFORMAT_INT8_VEC4:
+		return data + writeData<int8>(L, startidx, 4, data);
+	case DATAFORMAT_UINT8_VEC4:
+		return data + writeData<uint8>(L, startidx, 4, data);
+
+	case DATAFORMAT_SNORM16_VEC2:
+	case DATAFORMAT_SNORM16_VEC4:
 		return data + writeSNormData<int16>(L, startidx, components, data);
 		return data + writeSNormData<int16>(L, startidx, components, data);
-	case DATA_UNORM16:
+
+	case DATAFORMAT_UNORM16_VEC2:
+	case DATAFORMAT_UNORM16_VEC4:
 		return data + writeUNormData<uint16>(L, startidx, components, data);
 		return data + writeUNormData<uint16>(L, startidx, components, data);
-	case DATA_INT16:
+
+	case DATAFORMAT_INT16_VEC2:
+	case DATAFORMAT_INT16_VEC4:
 		return data + writeData<int16>(L, startidx, components, data);
 		return data + writeData<int16>(L, startidx, components, data);
-	case DATA_UINT16:
+
+	case DATAFORMAT_UINT16:
+	case DATAFORMAT_UINT16_VEC2:
+	case DATAFORMAT_UINT16_VEC4:
 		return data + writeData<uint16>(L, startidx, components, data);
 		return data + writeData<uint16>(L, startidx, components, data);
-	case DATA_INT32:
-		return data + writeData<int32>(L, startidx, components, data);
-	case DATA_UINT32:
-		return data + writeData<uint32>(L, startidx, components, data);
-	case DATA_FLOAT:
-		return data + writeData<float>(L, startidx, components, data);
+
 	default:
 	default:
 		return data;
 		return data;
 	}
 	}
@@ -108,7 +130,7 @@ char *luax_writeAttributeData(lua_State *L, int startidx, DataType type, int com
 template <typename T>
 template <typename T>
 static inline size_t readData(lua_State *L, int components, const char *data)
 static inline size_t readData(lua_State *L, int components, const char *data)
 {
 {
-	auto componentdata = (const T *) data;
+	const auto componentdata = (const T *) data;
 
 
 	for (int i = 0; i < components; i++)
 	for (int i = 0; i < components; i++)
 		lua_pushnumber(L, (lua_Number) componentdata[i]);
 		lua_pushnumber(L, (lua_Number) componentdata[i]);
@@ -119,8 +141,8 @@ static inline size_t readData(lua_State *L, int components, const char *data)
 template <typename T>
 template <typename T>
 static inline size_t readSNormData(lua_State *L, int components, const char *data)
 static inline size_t readSNormData(lua_State *L, int components, const char *data)
 {
 {
-	auto componentdata = (const T *) data;
-	auto maxval = std::numeric_limits<T>::max();
+	const auto componentdata = (const T *) data;
+	const auto maxval = std::numeric_limits<T>::max();
 
 
 	for (int i = 0; i < components; i++)
 	for (int i = 0; i < components; i++)
 		lua_pushnumber(L, std::max(-1.0, (lua_Number) componentdata[i] / (lua_Number)maxval));
 		lua_pushnumber(L, std::max(-1.0, (lua_Number) componentdata[i] / (lua_Number)maxval));
@@ -131,8 +153,8 @@ static inline size_t readSNormData(lua_State *L, int components, const char *dat
 template <typename T>
 template <typename T>
 static inline size_t readUNormData(lua_State *L, int components, const char *data)
 static inline size_t readUNormData(lua_State *L, int components, const char *data)
 {
 {
-	auto componentdata = (const T *) data;
-	auto maxval = std::numeric_limits<T>::max();
+	const auto componentdata = (const T *) data;
+	const auto maxval = std::numeric_limits<T>::max();
 
 
 	for (int i = 0; i < components; i++)
 	for (int i = 0; i < components; i++)
 		lua_pushnumber(L, (lua_Number) componentdata[i] / (lua_Number)maxval);
 		lua_pushnumber(L, (lua_Number) componentdata[i] / (lua_Number)maxval);
@@ -140,32 +162,54 @@ static inline size_t readUNormData(lua_State *L, int components, const char *dat
 	return sizeof(T) * components;
 	return sizeof(T) * components;
 }
 }
 
 
-const char *luax_readAttributeData(lua_State *L, DataType type, int components, const char *data)
+const char *luax_readAttributeData(lua_State *L, DataFormat format, int components, const char *data)
 {
 {
-	switch (type)
+	switch (format)
 	{
 	{
-	case DATA_SNORM8:
-		return data + readSNormData<int8>(L, components, data);
-	case DATA_UNORM8:
-		return data + readUNormData<uint8>(L, components, data);
-	case DATA_INT8:
-		return data + readData<int8>(L, components, data);
-	case DATA_UINT8:
-		return data + readData<uint8>(L, components, data);
-	case DATA_SNORM16:
+	case DATAFORMAT_FLOAT:
+	case DATAFORMAT_FLOAT_VEC2:
+	case DATAFORMAT_FLOAT_VEC3:
+	case DATAFORMAT_FLOAT_VEC4:
+		return data + readData<float>(L, components, data);
+
+	case DATAFORMAT_INT32:
+	case DATAFORMAT_INT32_VEC2:
+	case DATAFORMAT_INT32_VEC3:
+	case DATAFORMAT_INT32_VEC4:
+		return data + readData<int32>(L, components, data);
+
+	case DATAFORMAT_UINT32:
+	case DATAFORMAT_UINT32_VEC2:
+	case DATAFORMAT_UINT32_VEC3:
+	case DATAFORMAT_UINT32_VEC4:
+		return data + readData<uint32>(L, components, data);
+
+	case DATAFORMAT_SNORM8_VEC4:
+		return data + readSNormData<int8>(L, 4, data);
+	case DATAFORMAT_UNORM8_VEC4:
+		return data + readUNormData<uint8>(L, 4, data);
+	case DATAFORMAT_INT8_VEC4:
+		return data + readData<int8>(L, 4, data);
+	case DATAFORMAT_UINT8_VEC4:
+		return data + readData<uint8>(L, 4, data);
+
+	case DATAFORMAT_SNORM16_VEC2:
+	case DATAFORMAT_SNORM16_VEC4:
 		return data + readSNormData<int16>(L, components, data);
 		return data + readSNormData<int16>(L, components, data);
-	case DATA_UNORM16:
+
+	case DATAFORMAT_UNORM16_VEC2:
+	case DATAFORMAT_UNORM16_VEC4:
 		return data + readUNormData<uint16>(L, components, data);
 		return data + readUNormData<uint16>(L, components, data);
-	case DATA_INT16:
+
+	case DATAFORMAT_INT16_VEC2:
+	case DATAFORMAT_INT16_VEC4:
 		return data + readData<int16>(L, components, data);
 		return data + readData<int16>(L, components, data);
-	case DATA_UINT16:
+
+	case DATAFORMAT_UINT16:
+	case DATAFORMAT_UINT16_VEC2:
+	case DATAFORMAT_UINT16_VEC4:
 		return data + readData<uint16>(L, components, data);
 		return data + readData<uint16>(L, components, data);
-	case DATA_INT32:
-		return data + readData<int32>(L, components, data);
-	case DATA_UINT32:
-		return data + readData<uint32>(L, components, data);
-	case DATA_FLOAT:
-		return data + readData<float>(L, components, data);
+
 	default:
 	default:
 		return data;
 		return data;
 	}
 	}
@@ -216,11 +260,11 @@ int w_Mesh_setVertices(lua_State *L)
 	if (vertstart + vertcount > totalverts)
 	if (vertstart + vertcount > totalverts)
 		return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount);
 		return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount);
 
 
-	const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
+	const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
 
 
 	int ncomponents = 0;
 	int ncomponents = 0;
-	for (const Mesh::AttribFormat &format : vertexformat)
-		ncomponents += format.components;
+	for (const Buffer::DataMember &member : vertexformat)
+		ncomponents += member.info.components;
 
 
 	char *data = (char *) t->mapVertexData() + byteoffset;
 	char *data = (char *) t->mapVertexData() + byteoffset;
 
 
@@ -236,11 +280,11 @@ int w_Mesh_setVertices(lua_State *L)
 
 
 		int idx = -ncomponents;
 		int idx = -ncomponents;
 
 
-		for (const Mesh::AttribFormat &format : vertexformat)
+		for (const Buffer::DataMember &member : vertexformat)
 		{
 		{
 			// Fetch the values from Lua and store them in data buffer.
 			// Fetch the values from Lua and store them in data buffer.
-			data = luax_writeAttributeData(L, idx, format.type, format.components, data);
-			idx += format.components;
+			data = luax_writeAttributeData(L, idx, member.decl.format, member.info.components, data);
+			idx += member.info.components;
 		}
 		}
 
 
 		lua_pop(L, ncomponents + 1);
 		lua_pop(L, ncomponents + 1);
@@ -257,7 +301,7 @@ int w_Mesh_setVertex(lua_State *L)
 
 
 	bool istable = lua_istable(L, 3);
 	bool istable = lua_istable(L, 3);
 
 
-	const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
+	const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
 
 
 	char *data = (char *) t->getVertexScratchBuffer();
 	char *data = (char *) t->getVertexScratchBuffer();
 	char *writtendata = data;
 	char *writtendata = data;
@@ -266,25 +310,28 @@ int w_Mesh_setVertex(lua_State *L)
 
 
 	if (istable)
 	if (istable)
 	{
 	{
-		for (const Mesh::AttribFormat &format : vertexformat)
+		for (const Buffer::DataMember &member : vertexformat)
 		{
 		{
-			for (int i = idx; i < idx + format.components; i++)
+			int components = member.info.components;
+
+			for (int i = idx; i < idx + components; i++)
 				lua_rawgeti(L, 3, i);
 				lua_rawgeti(L, 3, i);
 
 
 			// Fetch the values from Lua and store them in data buffer.
 			// Fetch the values from Lua and store them in data buffer.
-			writtendata = luax_writeAttributeData(L, -format.components, format.type, format.components, writtendata);
+			writtendata = luax_writeAttributeData(L, -components, member.decl.format, components, writtendata);
 
 
-			idx += format.components;
-			lua_pop(L, format.components);
+			idx += components;
+			lua_pop(L, components);
 		}
 		}
 	}
 	}
 	else
 	else
 	{
 	{
-		for (const Mesh::AttribFormat &format : vertexformat)
+		for (const Buffer::DataMember &member : vertexformat)
 		{
 		{
 			// Fetch the values from Lua and store them in data buffer.
 			// Fetch the values from Lua and store them in data buffer.
-			writtendata = luax_writeAttributeData(L, idx, format.type, format.components, writtendata);
-			idx += format.components;
+			int components = member.info.components;
+			writtendata = luax_writeAttributeData(L, idx, member.decl.format, components, writtendata);
+			idx += components;
 		}
 		}
 	}
 	}
 
 
@@ -297,7 +344,7 @@ int w_Mesh_getVertex(lua_State *L)
 	Mesh *t = luax_checkmesh(L, 1);
 	Mesh *t = luax_checkmesh(L, 1);
 	size_t index = (size_t) luaL_checkinteger(L, 2) - 1;
 	size_t index = (size_t) luaL_checkinteger(L, 2) - 1;
 
 
-	const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
+	const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
 
 
 	char *data = (char *) t->getVertexScratchBuffer();
 	char *data = (char *) t->getVertexScratchBuffer();
 	const char *readdata = data;
 	const char *readdata = data;
@@ -306,10 +353,11 @@ int w_Mesh_getVertex(lua_State *L)
 
 
 	int n = 0;
 	int n = 0;
 
 
-	for (const Mesh::AttribFormat &format : vertexformat)
+	for (const Buffer::DataMember &member : vertexformat)
 	{
 	{
-		readdata = luax_readAttributeData(L, format.type, format.components, readdata);
-		n += format.components;
+		int components = member.info.components;
+		readdata = luax_readAttributeData(L, member.decl.format, components, readdata);
+		n += components;
 	}
 	}
 
 
 	return n;
 	return n;
@@ -321,15 +369,18 @@ int w_Mesh_setVertexAttribute(lua_State *L)
 	size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
 	size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
 	int attribindex = (int) luaL_checkinteger(L, 3) - 1;
 	int attribindex = (int) luaL_checkinteger(L, 3) - 1;
 
 
-	DataType type;
-	int components;
-	luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); });
+	const auto &vertexformat = t->getVertexFormat();
+
+	if (attribindex < 0 || attribindex >= (int) vertexformat.size())
+		return luaL_error(L, "Invalid vertex attribute index: %d", attribindex + 1);
+
+	const Buffer::DataMember &member = vertexformat[attribindex];
 
 
 	// Maximum possible size for a single vertex attribute.
 	// Maximum possible size for a single vertex attribute.
 	char data[sizeof(float) * 4];
 	char data[sizeof(float) * 4];
 
 
 	// Fetch the values from Lua and store them in the data buffer.
 	// Fetch the values from Lua and store them in the data buffer.
-	luax_writeAttributeData(L, 4, type, components, data);
+	luax_writeAttributeData(L, 4, member.decl.format, member.info.components, data);
 
 
 	luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
 	luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
 	return 0;
 	return 0;
@@ -341,17 +392,20 @@ int w_Mesh_getVertexAttribute(lua_State *L)
 	size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
 	size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1;
 	int attribindex = (int) luaL_checkinteger(L, 3) - 1;
 	int attribindex = (int) luaL_checkinteger(L, 3) - 1;
 
 
-	DataType type;
-	int components;
-	luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); });
+	const auto &vertexformat = t->getVertexFormat();
+
+	if (attribindex < 0 || attribindex >= (int) vertexformat.size())
+		return luaL_error(L, "Invalid vertex attribute index: %d", attribindex + 1);
+
+	const Buffer::DataMember &member = vertexformat[attribindex];
 
 
 	// Maximum possible size for a single vertex attribute.
 	// Maximum possible size for a single vertex attribute.
 	char data[sizeof(float) * 4];
 	char data[sizeof(float) * 4];
 
 
 	luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
 	luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); });
 
 
-	luax_readAttributeData(L, type, components, data);
-	return components;
+	luax_readAttributeData(L, member.decl.format, member.info.components, data);
+	return member.info.components;
 }
 }
 
 
 int w_Mesh_getVertexCount(lua_State *L)
 int w_Mesh_getVertexCount(lua_State *L)
@@ -365,28 +419,27 @@ int w_Mesh_getVertexFormat(lua_State *L)
 {
 {
 	Mesh *t = luax_checkmesh(L, 1);
 	Mesh *t = luax_checkmesh(L, 1);
 
 
-	const std::vector<Mesh::AttribFormat> &vertexformat = t->getVertexFormat();
+	const std::vector<Buffer::DataMember> &vertexformat = t->getVertexFormat();
 	lua_createtable(L, (int) vertexformat.size(), 0);
 	lua_createtable(L, (int) vertexformat.size(), 0);
 
 
 	const char *tname = nullptr;
 	const char *tname = nullptr;
 
 
 	for (size_t i = 0; i < vertexformat.size(); i++)
 	for (size_t i = 0; i < vertexformat.size(); i++)
 	{
 	{
-		if (!getConstant(vertexformat[i].type, tname))
-			return luax_enumerror(L, "vertex attribute data type", getConstants(vertexformat[i].type), tname);
+		const auto &decl = vertexformat[i].decl;
+
+		if (!getConstant(decl.format, tname))
+			return luax_enumerror(L, "vertex attribute data type", getConstants(decl.format), tname);
 
 
 		lua_createtable(L, 3, 0);
 		lua_createtable(L, 3, 0);
 
 
-		lua_pushstring(L, vertexformat[i].name.c_str());
+		lua_pushstring(L, decl.name.c_str());
 		lua_rawseti(L, -2, 1);
 		lua_rawseti(L, -2, 1);
 
 
 		lua_pushstring(L, tname);
 		lua_pushstring(L, tname);
 		lua_rawseti(L, -2, 2);
 		lua_rawseti(L, -2, 2);
 
 
-		lua_pushinteger(L, vertexformat[i].components);
-		lua_rawseti(L, -2, 3);
-
-		// format[i] = {name, type, components}
+		// format[i] = {name, type}
 		lua_rawseti(L, -2, (int) i + 1);
 		lua_rawseti(L, -2, (int) i + 1);
 	}
 	}
 
 

+ 2 - 2
src/modules/graphics/wrap_Mesh.h

@@ -30,8 +30,8 @@ namespace love
 namespace graphics
 namespace graphics
 {
 {
 
 
-char *luax_writeAttributeData(lua_State *L, int startidx, DataType type, int components, char *data);
-const char *luax_readAttributeData(lua_State *L, DataType type, int components, const char *data);
+char *luax_writeAttributeData(lua_State *L, int startidx, DataFormat format, int components, char *data);
+const char *luax_readAttributeData(lua_State *L, DataFormat format, int components, const char *data);
 
 
 Mesh *luax_checkmesh(lua_State *L, int idx);
 Mesh *luax_checkmesh(lua_State *L, int idx);
 extern "C" int luaopen_mesh(lua_State *L);
 extern "C" int luaopen_mesh(lua_State *L);