Browse Source

Make internal vertex attribute code less OpenGL-specific.

--HG--
branch : minor
Alex Szpakowski 7 years ago
parent
commit
15cc540e4c

+ 1 - 0
CMakeLists.txt

@@ -527,6 +527,7 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT
 	src/modules/graphics/Polyline.h
 	src/modules/graphics/Quad.cpp
 	src/modules/graphics/Quad.h
+	src/modules/graphics/Resource.h
 	src/modules/graphics/Shader.cpp
 	src/modules/graphics/Shader.h
 	src/modules/graphics/ShaderStage.cpp

+ 2 - 0
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -1730,6 +1730,7 @@
 		FA0B7CCC1A95902C000E1D17 /* wrap_Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Window.h; sourceTree = "<group>"; };
 		FA0B7EF01A959D2C000E1D17 /* ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ios.h; sourceTree = "<group>"; };
 		FA0B7EF11A959D2C000E1D17 /* ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ios.mm; sourceTree = "<group>"; };
+		FA10DD7B1F9EC24E00E1FE3D /* Resource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Resource.h; sourceTree = "<group>"; };
 		FA1557BF1CE90A2C00AFF582 /* tinyexr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tinyexr.h; sourceTree = "<group>"; };
 		FA1557C11CE90BD200AFF582 /* EXRHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EXRHandler.cpp; sourceTree = "<group>"; };
 		FA1557C21CE90BD200AFF582 /* EXRHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXRHandler.h; sourceTree = "<group>"; };
@@ -2725,6 +2726,7 @@
 				FA0B7B9C1A95902C000E1D17 /* Polyline.h */,
 				FA0B7BBC1A95902C000E1D17 /* Quad.cpp */,
 				FA0B7BBD1A95902C000E1D17 /* Quad.h */,
+				FA10DD7B1F9EC24E00E1FE3D /* Resource.h */,
 				FA1BA0AF1E16FD0800AA2803 /* Shader.cpp */,
 				FA1BA0B01E16FD0800AA2803 /* Shader.h */,
 				FA3C5E401F8C368C0003C579 /* ShaderStage.cpp */,

+ 2 - 6
src/modules/graphics/Buffer.h

@@ -24,6 +24,7 @@
 #include "common/config.h"
 #include "common/int.h"
 #include "vertex.h"
+#include "Resource.h"
 
 // C
 #include <stddef.h>
@@ -38,7 +39,7 @@ class Graphics;
 /**
  * A block of GPU-owned memory. Currently meant for internal use.
  **/
-class Buffer
+class Buffer : public Resource
 {
 public:
 
@@ -91,11 +92,6 @@ public:
 	 **/
 	virtual void setMappedRangeModified(size_t offset, size_t size) = 0;
 
-	/**
-	 * Gets the backend-specific handle for this Buffer.
-	 **/
-	virtual ptrdiff_t getHandle() const = 0;
-
 	/**
 	 * Fill a portion of the buffer with data and marks the range as modified.
 	 *

+ 31 - 11
src/modules/graphics/Mesh.cpp

@@ -323,6 +323,8 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::strin
 	auto it = attachedAttributes.find(name);
 	if (it != attachedAttributes.end())
 		oldattrib = it->second;
+	else if (attachedAttributes.size() + 1 > vertex::Attributes::MAX)
+		throw love::Exception("A maximum of %d attributes can be attached at once.", vertex::Attributes::MAX);
 
 	newattrib.mesh = mesh;
 	newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true;
@@ -585,30 +587,48 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
 	if (Shader::current && texture.get())
 		Shader::current->checkMainTexture(texture);
 
-	uint32 enabledattribs = 0;
-	uint32 instancedattribs = 0;
+	vertex::Attributes attributes;
+	vertex::Buffers buffers;
+
+	int activebuffers = 0;
 
 	for (const auto &attrib : attachedAttributes)
 	{
 		if (!attrib.second.enabled)
 			continue;
 
-		love::graphics::Mesh *mesh = attrib.second.mesh;
-		int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first);
+		Mesh *mesh = attrib.second.mesh;
+		int attributeindex = -1;
+
+		// If the attribute is one of the LOVE-defined ones, use the constant
+		// attribute index for it, otherwise query the index from the shader.
+		VertexAttribID builtinattrib;
+		if (vertex::getConstant(attrib.first.c_str(), builtinattrib))
+			attributeindex = (int) builtinattrib;
+		else if (Shader::current)
+			attributeindex = Shader::current->getVertexAttributeIndex(attrib.first);
 
-		if (location >= 0)
+		if (attributeindex >= 0)
 		{
-			uint32 bit = 1u << (uint32) location;
+			// Make sure the buffer isn't mapped (sends data to GPU if needed.)
+			mesh->vbo->unmap();
+
+			const auto &formats = mesh->getVertexFormat();
+			const auto &format = formats[attrib.second.index];
+
+			uint16 offset = (uint16) mesh->getAttributeOffset(attrib.second.index);
+			uint16 stride = (uint16) mesh->getVertexStride();
 
-			enabledattribs |= bit;
+			attributes.set(attributeindex, format.type, format.components, offset, stride, activebuffers, attrib.second.step);
 
-			if (attrib.second.step == STEP_PER_INSTANCE)
-				instancedattribs |= bit;
+			// TODO: Ideally we want to reuse buffers with the same stride+step.
+			buffers.set(activebuffers, mesh->vbo, 0);
+			activebuffers++;
 		}
 	}
 
 	// Not supported on all platforms or GL versions, I believe.
-	if (!(enabledattribs & ATTRIBFLAG_POS))
+	if (!attributes.isEnabled(ATTRIB_POS))
 		throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
 
 	bool useindexbuffer = useIndexBuffer && ibo != nullptr && indexCount > 0;
@@ -643,7 +663,7 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
 	Graphics::TempTransform transform(gfx, m);
 
 	if (count > 0)
-		drawInternal(start, count, instancecount, useindexbuffer, enabledattribs, instancedattribs);
+		drawInternal(start, count, instancecount, useindexbuffer, attributes, buffers);
 }
 
 } // graphics

+ 3 - 3
src/modules/graphics/Mesh.h

@@ -167,8 +167,6 @@ public:
 	void setDrawRange();
 	bool getDrawRange(int &start, int &count) const;
 
-	virtual int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) = 0;
-
 	// Implements Drawable.
 	void draw(Graphics *gfx, const Matrix4 &m) override;
 
@@ -178,6 +176,8 @@ public:
 
 protected:
 
+	friend class SpriteBatch;
+
 	struct AttachedAttribute
 	{
 		Mesh *mesh;
@@ -190,7 +190,7 @@ protected:
 	void calculateAttributeSizes();
 	size_t getAttributeOffset(size_t attribindex) const;
 
-	virtual void drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const = 0;
+	virtual void drawInternal(int start, int count, int instancecount, bool useindexbuffer, const vertex::Attributes &attributes, const vertex::Buffers &buffers) const = 0;
 
 	std::vector<AttribFormat> vertexFormat;
 	std::vector<size_t> attributeSizes;

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

@@ -93,6 +93,7 @@ ParticleSystem::ParticleSystem(Graphics *gfx, Texture *texture, uint32 size)
 	, offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f)
 	, defaultOffset(true)
 	, relativeRotation(false)
+	, vertexAttributes(vertex::CommonFormat::XYf_STf_RGBAub, 0)
 	, buffer(nullptr)
 	, quadIndices(gfx, size)
 {
@@ -154,6 +155,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
 	, colors(p.colors)
 	, quads(p.quads)
 	, relativeRotation(p.relativeRotation)
+	, vertexAttributes(p.vertexAttributes)
 	, buffer(nullptr)
 	, quadIndices(p.quadIndices)
 {
@@ -1089,8 +1091,11 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
 
 	buffer->unmap();
 
+	vertex::Buffers vertexbuffers;
+	vertexbuffers.set(0, buffer, 0);
+
 	Graphics::TempTransform transform(gfx, m);
-	drawInternal();
+	drawInternal(vertexAttributes, vertexbuffers);
 }
 
 bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)

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

@@ -584,7 +584,7 @@ protected:
 		int quadIndex;
 	};
 
-	virtual void drawInternal() const = 0;
+	virtual void drawInternal(const vertex::Attributes &attributes, const vertex::Buffers &buffers) const = 0;
 
 	// Pointer to the beginning of the allocated memory.
 	Particle *pMem;
@@ -687,6 +687,7 @@ protected:
 
 	bool relativeRotation;
 
+	const vertex::Attributes vertexAttributes;
 	Buffer *buffer;
 
 	// Vertex index buffer.

+ 40 - 0
src/modules/graphics/Resource.h

@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2006-2017 LOVE Development Team
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ **/
+
+#pragma once
+
+#include <stddef.h>
+
+namespace love
+{
+namespace graphics
+{
+
+class Resource
+{
+public:
+
+	virtual ~Resource() {}
+	virtual ptrdiff_t getHandle() const = 0;
+
+}; // Resource
+
+} // graphics
+} // love

+ 2 - 3
src/modules/graphics/Shader.h

@@ -25,6 +25,7 @@
 #include "common/StringMap.h"
 #include "Texture.h"
 #include "ShaderStage.h"
+#include "Resource.h"
 
 // STL
 #include <string>
@@ -45,7 +46,7 @@ namespace graphics
 class Graphics;
 
 // A GLSL shader
-class Shader : public Object
+class Shader : public Object, public Resource
 {
 public:
 
@@ -185,8 +186,6 @@ public:
 	void checkMainTextureType(TextureType textype, bool isDepthSampler) const;
 	void checkMainTexture(Texture *texture) const;
 
-	virtual ptrdiff_t getHandle() const = 0;
-
 	static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err);
 
 	static bool initialize();

+ 2 - 3
src/modules/graphics/ShaderStage.h

@@ -23,6 +23,7 @@
 #include "common/Object.h"
 #include "common/StringMap.h"
 #include "Volatile.h"
+#include "Resource.h"
 
 #include <stddef.h>
 #include <string>
@@ -39,7 +40,7 @@ namespace graphics
 
 class Graphics;
 
-class ShaderStage : public love::Object, public Volatile
+class ShaderStage : public love::Object, public Volatile, public Resource
 {
 public:
 
@@ -57,8 +58,6 @@ public:
 	const std::string &getSource() const { return source; }
 	const std::string &getWarnings() const { return warnings; }
 	glslang::TShader *getGLSLangShader() const { return glslangShader; }
-	
-	virtual ptrdiff_t getHandle() const = 0;
 
 	static bool getConstant(const char *in, StageType &out);
 	static bool getConstant(StageType in, const char *&out);

+ 48 - 7
src/modules/graphics/SpriteBatch.cpp

@@ -334,14 +334,55 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 	// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
 	array_buf->unmap();
 
-	CommonFormat format = vertex_format;
+	Attributes attributes;
+	Buffers buffers;
 
-	if (!color_active)
 	{
-		if (format == CommonFormat::XYf_STPf_RGBAub)
-			format = CommonFormat::XYf_STPf;
-		else
-			format = CommonFormat::XYf_STf;
+		buffers.set(0, array_buf, 0);
+		attributes.setCommonFormat(vertex_format, 0);
+
+		if (!color_active)
+			attributes.disable(ATTRIB_COLOR);
+	}
+
+	int activebuffers = 1;
+
+	for (const auto &it : attached_attributes)
+	{
+		Mesh *mesh = it.second.mesh.get();
+
+		// We have to do this check here as wll because setBufferSize can be
+		// called after attachAttribute.
+		if (mesh->getVertexCount() < (size_t) next * 4)
+			throw love::Exception("Mesh with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
+
+		int attributeindex = -1;
+
+		// If the attribute is one of the LOVE-defined ones, use the constant
+		// attribute index for it, otherwise query the index from the shader.
+		VertexAttribID builtinattrib;
+		if (vertex::getConstant(it.first.c_str(), builtinattrib))
+			attributeindex = (int) builtinattrib;
+		else if (Shader::current)
+			attributeindex = Shader::current->getVertexAttributeIndex(it.first);
+
+		if (attributeindex >= 0)
+		{
+			// Make sure the buffer isn't mapped (sends data to GPU if needed.)
+			mesh->vbo->unmap();
+
+			const auto &formats = mesh->getVertexFormat();
+			const auto &format = formats[it.second.index];
+
+			uint16 offset = (uint16) mesh->getAttributeOffset(it.second.index);
+			uint16 stride = (uint16) mesh->getVertexStride();
+
+			attributes.set(attributeindex, format.type, format.components, offset, stride, activebuffers);
+
+			// TODO: Ideally we want to reuse buffers with the same stride+step.
+			buffers.set(activebuffers, mesh->vbo, 0);
+			activebuffers++;
+		}
 	}
 
 	int start = std::min(std::max(0, range_start), next - 1);
@@ -358,7 +399,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 	Graphics::TempTransform transform(gfx, m);
 
 	if (count > 0)
-		drawInternal(format, indexbytestart, indexcount);
+		drawInternal(indexbytestart, indexcount, attributes, buffers);
 }
 
 } // graphics

+ 1 - 1
src/modules/graphics/SpriteBatch.h

@@ -123,7 +123,7 @@ protected:
 	 **/
 	void setBufferSize(int newsize);
 
-	virtual void drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount) = 0;
+	virtual void drawInternal(size_t indexbytestart, size_t indexcount, const vertex::Attributes &attributes, const vertex::Buffers &buffers) = 0;
 
 	StrongRef<Texture> texture;
 

+ 2 - 3
src/modules/graphics/StreamBuffer.h

@@ -23,6 +23,7 @@
 // LOVE
 #include "common/int.h"
 #include "vertex.h"
+#include "Resource.h"
 
 // C
 #include <cstddef>
@@ -32,7 +33,7 @@ namespace love
 namespace graphics
 {
 
-class StreamBuffer
+class StreamBuffer : public Resource
 {
 public:
 
@@ -61,8 +62,6 @@ public:
 
 	virtual void nextFrame() {}
 
-	virtual ptrdiff_t getHandle() const = 0;
-
 protected:
 
 	StreamBuffer(BufferType mode, size_t size);

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

@@ -32,6 +32,7 @@ love::Type Text::type("Text", &Drawable::type);
 
 Text::Text(Graphics *gfx, Font *font, const std::vector<Font::ColoredString> &text)
 	: font(font)
+	, vertexAttributes(Font::vertexFormat, 0)
 	, vbo(nullptr)
 	, quadIndices(gfx, 20)
 	, vert_offset(0)
@@ -68,6 +69,8 @@ void Text::uploadVertices(const std::vector<Font::GlyphVertex> &vertices, size_t
 
 		delete vbo;
 		vbo = new_vbo;
+
+		vertexBuffers.set(0, vbo, 0);
 	}
 
 	if (vbo != nullptr && datasize > 0)

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

@@ -86,6 +86,10 @@ protected:
 	virtual void drawInternal(const std::vector<Font::DrawCommand> &commands) const = 0;
 
 	StrongRef<Font> font;
+
+	vertex::Attributes vertexAttributes;
+	vertex::Buffers vertexBuffers;
+
 	Buffer *vbo;
 	QuadIndices quadIndices;
 

+ 2 - 3
src/modules/graphics/Texture.h

@@ -32,6 +32,7 @@
 #include "Quad.h"
 #include "vertex.h"
 #include "depthstencil.h"
+#include "Resource.h"
 
 // C
 #include <stddef.h>
@@ -56,7 +57,7 @@ enum TextureType
  * Base class for 2D textures. All textures can be drawn with Quads, have a
  * width and height, and have filter and wrap modes.
  **/
-class Texture : public Drawable
+class Texture : public Drawable, public Resource
 {
 public:
 
@@ -145,8 +146,6 @@ public:
 
 	Quad *getQuad() const;
 
-	virtual ptrdiff_t getHandle() const = 0;
-
 	static bool validateFilter(const Filter &f, bool mipmapsAllowed);
 
 	static int getMipmapCount(int w, int h);

+ 13 - 15
src/modules/graphics/opengl/Graphics.cpp

@@ -353,7 +353,9 @@ void Graphics::flushStreamDraws()
 
 	OpenGL::TempDebugGroup debuggroup("Stream vertices flush and draw");
 
-	uint32 attribs = 0;
+	Attributes attributes;
+	Buffers buffers;
+
 	size_t usedsizes[3] = {0, 0, 0};
 
 	for (int i = 0; i < 2; i++)
@@ -361,26 +363,20 @@ void Graphics::flushStreamDraws()
 		if (sbstate.formats[i] == CommonFormat::NONE)
 			continue;
 
-		usedsizes[i] = getFormatStride(sbstate.formats[i]) * sbstate.vertexCount;
-
-		love::graphics::StreamBuffer *buffer = sbstate.vb[i];
+		attributes.setCommonFormat(sbstate.formats[i], (uint8) i);
 
-		gl.bindBuffer(BUFFER_VERTEX, (GLuint) buffer->getHandle());
-		size_t offset = buffer->unmap(usedsizes[i]);
+		usedsizes[i] = getFormatStride(sbstate.formats[i]) * sbstate.vertexCount;
 
+		size_t offset = sbstate.vb[i]->unmap(usedsizes[i]);
+		buffers.set(i, sbstate.vb[i], offset);
 		sbstate.vbMap[i] = StreamBuffer::MapInfo();
-
-		gl.setVertexPointers(sbstate.formats[i], offset);
-		attribs |= getFormatFlags(sbstate.formats[i]);
 	}
 
-	if (attribs == 0)
+	if (attributes.enablebits == 0)
 		return;
 
-	GLenum glprimitivetype = OpenGL::getGLPrimitiveType(sbstate.primitiveMode);
-
 	Colorf nc = gl.getConstantColor();
-	if (attribs & ATTRIBFLAG_COLOR)
+	if (attributes.isEnabled(ATTRIB_COLOR))
 		gl.setConstantColor(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
 
 	pushIdentityTransform();
@@ -388,7 +384,9 @@ void Graphics::flushStreamDraws()
 	gl.prepareDraw();
 	gl.bindTextureToUnit(sbstate.texture, 0, false);
 
-	gl.useVertexAttribArrays(attribs);
+	gl.setVertexAttributes(attributes, buffers);
+
+	GLenum glprimitivetype = OpenGL::getGLPrimitiveType(sbstate.primitiveMode);
 
 	if (sbstate.indexCount > 0)
 	{
@@ -415,7 +413,7 @@ void Graphics::flushStreamDraws()
 
 	popTransform();
 
-	if (attribs & ATTRIB_CONSTANTCOLOR)
+	if (attributes.isEnabled(ATTRIB_COLOR))
 		gl.setConstantColor(nc);
 
 	streamBufferState.vertexCount = 0;

+ 2 - 35
src/modules/graphics/opengl/Mesh.cpp

@@ -49,44 +49,11 @@ Mesh::~Mesh()
 {
 }
 
-int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inputname)
-{
-	const AttribFormat &format = vertexFormat[attributeindex];
-
-	GLint attriblocation = -1;
-
-	// If the attribute is one of the LOVE-defined ones, use the constant
-	// attribute index for it, otherwise query the index from the shader.
-	VertexAttribID builtinattrib;
-	if (vertex::getConstant(inputname.c_str(), builtinattrib))
-		attriblocation = (GLint) builtinattrib;
-	else if (Shader::current)
-		attriblocation = Shader::current->getVertexAttributeIndex(inputname);
-
-	// The active shader might not use this vertex attribute name.
-	if (attriblocation < 0)
-		return attriblocation;
-
-	// Make sure the buffer isn't mapped (sends data to GPU if needed.)
-	vbo->unmap();
-
-	gl.bindBuffer(BUFFER_VERTEX, (GLuint) vbo->getHandle());
-
-	GLboolean normalized = GL_FALSE;
-	GLenum datatype = OpenGL::getGLVertexDataType(format.type, normalized);
-
-	const void *gloffset = BUFFER_OFFSET(getAttributeOffset(attributeindex));
-
-	glVertexAttribPointer(attriblocation, format.components, datatype, normalized, (GLsizei) vertexStride, gloffset);
-
-	return attriblocation;
-}
-
-void Mesh::drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const
+void Mesh::drawInternal(int start, int count, int instancecount, bool useindexbuffer, const vertex::Attributes &attributes, const vertex::Buffers &buffers) const
 {
 	OpenGL::TempDebugGroup debuggroup("Mesh draw");
 
-	gl.useVertexAttribArrays(attribflags, instancedattribflags);
+	gl.setVertexAttributes(attributes, buffers);
 	gl.bindTextureToUnit(texture, 0, false);
 	gl.prepareDraw();
 

+ 1 - 4
src/modules/graphics/opengl/Mesh.h

@@ -37,14 +37,11 @@ public:
 
 	Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage);
 	Mesh(graphics::Graphics *gfx, const std::vector<AttribFormat> &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage);
-
 	virtual ~Mesh();
 
-	int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) override;
-
 protected:
 
-	void drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const override;
+	void drawInternal(int start, int count, int instancecount, bool useindexbuffer, const vertex::Attributes &attributes, const vertex::Buffers &buffers) const override;
 
 }; // Mesh
 

+ 25 - 84
src/modules/graphics/opengl/OpenGL.cpp

@@ -172,7 +172,7 @@ void OpenGL::setupContext()
 	else
 		state.instancedAttribArrays = 0;
 
-	useVertexAttribArrays(0, 0);
+	setVertexAttributes(vertex::Attributes(), vertex::Buffers());
 
 	// Get the current viewport.
 	glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
@@ -647,111 +647,52 @@ void OpenGL::drawElements(GLenum mode, GLsizei count, GLenum type, const void *i
 	++stats.drawCalls;
 }
 
-void OpenGL::useVertexAttribArrays(uint32 arraybits, uint32 instancedbits)
+void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const vertex::Buffers &buffers)
 {
-	uint32 diff = arraybits ^ state.enabledAttribArrays;
-	uint32 instancediff = instancedbits ^ state.instancedAttribArrays;
+	uint32 enablediff = attributes.enablebits ^ state.enabledAttribArrays;
+	uint32 instancediff = attributes.instancebits ^ state.instancedAttribArrays;
 
-	if (diff == 0 && instancediff == 0)
-		return;
-
-	// Max 32 attributes. As of when this was written, no GL driver exposes more
-	// than 32. Lets hope that doesn't change...
-	for (uint32 i = 0; i < 32; i++)
+	for (uint32 i = 0; i < vertex::Attributes::MAX; i++)
 	{
 		uint32 bit = 1u << i;
 
-		if (diff & bit)
+		if (enablediff & bit)
 		{
-			if (arraybits & bit)
+			if (attributes.enablebits & bit)
 				glEnableVertexAttribArray(i);
 			else
 				glDisableVertexAttribArray(i);
 		}
 
 		if (instancediff & bit)
-			glVertexAttribDivisor(i, (instancedbits & bit) != 0 ? 1 : 0);
+			glVertexAttribDivisor(i, (attributes.instancebits & bit) != 0 ? 1 : 0);
+
+		if (attributes.enablebits & bit)
+		{
+			const auto &attrib = attributes.attribs[i];
+			const auto &bufferinfo = buffers.info[attrib.bufferindex];
+
+			GLboolean normalized = GL_FALSE;
+			GLenum gltype = getGLVertexDataType(attrib.type, normalized);
+
+			const void *offsetpointer = BUFFER_OFFSET(bufferinfo.offset + attrib.offsetfromvertex);
+
+			bindBuffer(BUFFER_VERTEX, (GLuint) bufferinfo.buffer->getHandle());
+			glVertexAttribPointer(i, attrib.components, gltype, normalized, attrib.stride, offsetpointer);
+		}
 	}
 
-	state.enabledAttribArrays = arraybits;
-	state.instancedAttribArrays = instancedbits;
+	state.enabledAttribArrays = attributes.enablebits;
+	state.instancedAttribArrays = attributes.instancebits;
 
 	// glDisableVertexAttribArray will make the constant value for a vertex
 	// attribute undefined. We rely on the per-vertex color attribute being
 	// white when no per-vertex color is used, so we set it here.
 	// FIXME: Is there a better place to do this?
-	if ((diff & ATTRIBFLAG_COLOR) && !(arraybits & ATTRIBFLAG_COLOR))
+	if ((enablediff & ATTRIBFLAG_COLOR) && !(attributes.enablebits & ATTRIBFLAG_COLOR))
 		glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
 }
 
-void OpenGL::setVertexPointers(vertex::CommonFormat format, size_t stride, size_t offset)
-{
-	using namespace vertex;
-
-	switch (format)
-	{
-	case CommonFormat::NONE:
-		break;
-	case CommonFormat::XYf:
-		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset));
-		break;
-	case CommonFormat::XYZf:
-		glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset));
-		break;
-	case CommonFormat::RGBAub:
-		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset));
-		break;
-	case CommonFormat::STf_RGBAub:
-		glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(STf_RGBAub, s)));
-		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(STf_RGBAub, color.r)));
-		break;
-	case CommonFormat::STPf_RGBAub:
-		glVertexAttribPointer(ATTRIB_TEXCOORD, 3, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(STPf_RGBAub, s)));
-		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(STPf_RGBAub, color.r)));
-		break;
-	case CommonFormat::XYf_STf:
-		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf, x)));
-		glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf, s)));
-		break;
-	case CommonFormat::XYf_STPf:
-		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STPf, x)));
-		glVertexAttribPointer(ATTRIB_TEXCOORD, 3, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STPf, s)));
-		break;
-	case CommonFormat::XYf_STf_RGBAub:
-		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf_RGBAub, x)));
-		glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf_RGBAub, s)));
-		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf_RGBAub, color.r)));
-		break;
-	case CommonFormat::XYf_STus_RGBAub:
-		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STus_RGBAub, x)));
-		glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STus_RGBAub, s)));
-		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STus_RGBAub, color.r)));
-		break;
-	case CommonFormat::XYf_STPf_RGBAub:
-		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STPf_RGBAub, x)));
-		glVertexAttribPointer(ATTRIB_TEXCOORD, 3, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STPf_RGBAub, s)));
-		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STPf_RGBAub, color.r)));
-		break;
-	}
-}
-
-void OpenGL::setVertexPointers(vertex::CommonFormat format, size_t offset)
-{
-	setVertexPointers(format, getFormatStride(format), offset);
-}
-
-void OpenGL::setVertexPointers(vertex::CommonFormat format, love::graphics::Buffer *buffer, size_t offset)
-{
-	bindBuffer(BUFFER_VERTEX, (GLuint) buffer->getHandle());
-	setVertexPointers(format, offset);
-}
-
-void OpenGL::setVertexPointers(vertex::CommonFormat format, love::graphics::Buffer *buffer, size_t stride, size_t offset)
-{
-	bindBuffer(BUFFER_VERTEX, (GLuint) buffer->getHandle());
-	setVertexPointers(format, stride, offset);
-}
-
 void OpenGL::clearDepth(double value)
 {
 	if (GLAD_ES_VERSION_2_0)

+ 2 - 17
src/modules/graphics/opengl/OpenGL.h

@@ -46,6 +46,7 @@ namespace love
 namespace graphics
 {
 
+class Resource;
 class Buffer;
 
 namespace opengl
@@ -206,23 +207,7 @@ public:
 	void drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instancecount = 1);
 	void drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount = 1);
 
-	/**
-	 * Sets the enabled vertex attribute arrays based on the specified attribute
-	 * bits. Each bit in the uint32 represents an enabled attribute array index.
-	 * For example, useVertexAttribArrays(1 << 0) will enable attribute index 0.
-	 * See the VertexAttribFlags enum for the standard vertex attributes.
-	 * This function *must* be used instead of glEnable/DisableVertexAttribArray.
-	 **/
-	void useVertexAttribArrays(uint32 arraybits, uint32 instancedbits = 0);
-
-	/**
-	 * Calls glVertexAttribPointer appropriately for each attribute used in the
-	 * specified format.
-	 **/
-	void setVertexPointers(vertex::CommonFormat format, size_t offset);
-	void setVertexPointers(vertex::CommonFormat format, size_t stride, size_t offset);
-	void setVertexPointers(vertex::CommonFormat format, love::graphics::Buffer *buffer, size_t offset);
-	void setVertexPointers(vertex::CommonFormat format, love::graphics::Buffer *buffer, size_t stride, size_t offset);
+	void setVertexAttributes(const vertex::Attributes &attributes, const vertex::Buffers &buffers);
 
 	/**
 	 * Wrapper for glClearDepth and glClearDepthf.

+ 2 - 5
src/modules/graphics/opengl/ParticleSystem.cpp

@@ -50,17 +50,14 @@ ParticleSystem *ParticleSystem::clone()
 	return new ParticleSystem(*this);
 }
 
-void ParticleSystem::drawInternal() const
+void ParticleSystem::drawInternal(const vertex::Attributes &attributes, const vertex::Buffers &buffers) const
 {
-	using namespace vertex;
-
 	OpenGL::TempDebugGroup debuggroup("ParticleSystem draw");
 
 	gl.bindTextureToUnit(texture, 0, false);
 	gl.prepareDraw();
 
-	gl.useVertexAttribArrays(getFormatFlags(CommonFormat::XYf_STf_RGBAub));
-	gl.setVertexPointers(CommonFormat::XYf_STf_RGBAub, buffer, 0);
+	gl.setVertexAttributes(attributes, buffers);
 
 	GLsizei count = (GLsizei) quadIndices.getIndexCount(getCount());
 	GLenum gltype = OpenGL::getGLIndexDataType(quadIndices.getType());

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

@@ -47,7 +47,7 @@ public:
 
 private:
 
-	void drawInternal() const override;
+	void drawInternal(const vertex::Attributes &attributes, const vertex::Buffers &buffers) const override;
 
 }; // ParticleSystem
 

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

@@ -51,32 +51,11 @@ SpriteBatch::~SpriteBatch()
 {
 }
 
-void SpriteBatch::drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount)
+void SpriteBatch::drawInternal(size_t indexbytestart, size_t indexcount, const vertex::Attributes &attributes, const vertex::Buffers &buffers)
 {
 	OpenGL::TempDebugGroup debuggroup("SpriteBatch draw");
 
-	uint32 enabledattribs = getFormatFlags(format);
-
-	// We want attached attributes to override local attributes, so we should
-	// call this before binding attached attributes.
-	gl.setVertexPointers(format, array_buf, vertex_stride, 0);
-
-	for (const auto &it : attached_attributes)
-	{
-		Mesh *mesh = it.second.mesh.get();
-
-		// We have to do this check here as wll because setBufferSize can be
-		// called after attachAttribute.
-		if (mesh->getVertexCount() < (size_t) next * 4)
-			throw love::Exception("Mesh with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
-
-		int location = mesh->bindAttributeToShaderInput(it.second.index, it.first);
-
-		if (location >= 0)
-			enabledattribs |= 1u << (uint32) location;
-	}
-
-	gl.useVertexAttribArrays(enabledattribs);
+	gl.setVertexAttributes(attributes, buffers);
 	gl.bindTextureToUnit(texture, 0, false);
 
 	gl.prepareDraw();

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

@@ -39,7 +39,7 @@ public:
 
 protected:
 
-	void drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount) override;
+	void drawInternal(size_t indexbytestart, size_t indexcount, const vertex::Attributes &attributes, const vertex::Buffers &buffers) override;
 
 }; // SpriteBatch
 

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

@@ -45,9 +45,7 @@ void Text::drawInternal(const std::vector<Font::DrawCommand> &commands) const
 	OpenGL::TempDebugGroup debuggroup("Text object draw");
 
 	gl.prepareDraw();
-
-	gl.setVertexPointers(Font::vertexFormat, vbo, 0);
-	gl.useVertexAttribArrays(vertex::getFormatFlags(Font::vertexFormat));
+	gl.setVertexAttributes(vertexAttributes, vertexBuffers);
 
 	const GLenum gltype = OpenGL::getGLIndexDataType(quadIndices.getType());
 	const size_t elemsize = quadIndices.getElementSize();

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

@@ -37,6 +37,8 @@ static_assert(sizeof(XYf_STf_RGBAub) == sizeof(float)*2 + sizeof(float)*2 + size
 static_assert(sizeof(XYf_STus_RGBAub) == sizeof(float)*2 + sizeof(uint16)*2 + sizeof(Color), "sizeof(XYf_STus_RGBAub) incorrect!");
 static_assert(sizeof(XYf_STPf_RGBAub) == sizeof(float)*2 + sizeof(float)*3 + sizeof(Color), "sizeof(XYf_STPf_RGBAub) incorrect!");
 
+static_assert(sizeof(AttributeInfo) == sizeof(uint64), "sizeof(AttributeInfo) incorrect!");
+
 size_t getFormatStride(CommonFormat format)
 {
 	switch (format)
@@ -222,6 +224,57 @@ void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount,
 	fillIndicesT(mode, vertexStart, vertexCount, indices);
 }
 
+void Attributes::setCommonFormat(CommonFormat format, uint8 bufferindex)
+{
+	uint16 stride = (uint16) getFormatStride(format);
+
+	switch (format)
+	{
+	case CommonFormat::NONE:
+		break;
+	case CommonFormat::XYf:
+		set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex);
+		break;
+	case CommonFormat::XYZf:
+		set(ATTRIB_POS, DATA_FLOAT, 3, 0, stride, bufferindex);
+		break;
+	case CommonFormat::RGBAub:
+		set(ATTRIB_COLOR, DATA_UNORM8, 4, 0, stride, bufferindex);
+		break;
+	case CommonFormat::STf_RGBAub:
+		set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, 0, stride, bufferindex);
+		set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2), stride, bufferindex);
+		break;
+	case CommonFormat::STPf_RGBAub:
+		set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, 0, stride, bufferindex);
+		set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 3), stride, bufferindex);
+		break;
+	case CommonFormat::XYf_STf:
+		set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex);
+		set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), stride, bufferindex);
+		break;
+	case CommonFormat::XYf_STPf:
+		set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex);
+		set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), stride, bufferindex);
+		break;
+	case CommonFormat::XYf_STf_RGBAub:
+		set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex);
+		set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), stride, bufferindex);
+		set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 4), stride, bufferindex);
+		break;
+	case CommonFormat::XYf_STus_RGBAub:
+		set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex);
+		set(ATTRIB_TEXCOORD, DATA_UNORM16, 2, uint16(sizeof(float) * 2), stride, bufferindex);
+		set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2 + sizeof(uint16) * 2), stride, bufferindex);
+		break;
+	case CommonFormat::XYf_STPf_RGBAub:
+		set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex);
+		set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), stride, bufferindex);
+		set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 5), stride, bufferindex);
+		break;
+	}
+}
+
 static StringMap<VertexAttribID, ATTRIB_MAX_ENUM>::Entry attribNameEntries[] =
 {
 	{ "VertexPosition", ATTRIB_POS           },

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

@@ -34,6 +34,8 @@ namespace love
 namespace graphics
 {
 
+class Resource;
+
 // Vertex attribute indices used in shaders by LOVE. The values map to OpenGL
 // generic vertex attribute indices.
 enum VertexAttribID
@@ -172,6 +174,119 @@ struct XYf_STPf_RGBAub
 	Color color;
 };
 
+struct Buffers
+{
+	static const unsigned int MAX = 32;
+
+	uint32 usebits = 0;
+
+	struct
+	{
+		Resource *buffer;
+		size_t offset;
+	} info[MAX];
+
+	void set(unsigned int index, Resource *r, size_t offset)
+	{
+		usebits |= (1u << index);
+		info[index] = {r, offset};
+	}
+
+	void disable(unsigned int index) { usebits &= (1u << index); }
+	void clear() { usebits = 0; }
+};
+
+struct AttributeInfo
+{
+	uint8 bufferindex;
+	DataType type : 8;
+	uint16 components;
+	uint16 offsetfromvertex;
+	uint16 stride;
+};
+
+struct Attributes
+{
+	static const unsigned int MAX = 32;
+
+	uint32 enablebits = 0;
+	uint32 instancebits = 0;
+
+	union
+	{
+		AttributeInfo attribs[MAX];
+		uint64 data[MAX];
+	};
+
+	Attributes() {}
+	Attributes(CommonFormat format, uint8 bufferindex)
+	{
+		setCommonFormat(format, bufferindex);
+	}
+
+	void set(unsigned int index, DataType type, uint16 components, uint16 offsetfromvertex, uint16 stride, uint8 bufferindex, AttributeStep step = STEP_PER_VERTEX)
+	{
+		uint32 bit = (1u << index);
+
+		enablebits |= bit;
+
+		if (step == STEP_PER_INSTANCE)
+			instancebits |= bit;
+		else
+			instancebits &= ~bit;
+
+		attribs[index].bufferindex = bufferindex;
+		attribs[index].type = type;
+		attribs[index].components = components;
+		attribs[index].offsetfromvertex = offsetfromvertex;
+		attribs[index].stride = stride;
+	}
+
+	void disable(unsigned int index)
+	{
+		enablebits &= ~(1u << index);
+	}
+
+	void clear()
+	{
+		enablebits = 0;
+	}
+
+	bool isEnabled(unsigned int index) const
+	{
+		return (enablebits & (1u << index)) != 0;
+	}
+
+	AttributeStep getStep(unsigned int index) const
+	{
+		return (instancebits & (1u << index)) != 0 ? STEP_PER_INSTANCE : STEP_PER_VERTEX;
+	}
+
+	void setCommonFormat(CommonFormat format, uint8 bufferindex);
+
+	bool operator == (const Attributes &other) const
+	{
+		if (enablebits != other.enablebits)
+			return false;
+
+		uint32 instancediff = instancebits ^ other.instancebits;
+
+		for (unsigned int i = 0; i < MAX; i++)
+		{
+			if (isEnabled(i))
+			{
+				if (instancediff & (1u << i))
+					return false;
+
+				if (data[i] != other.data[i])
+					return false;
+			}
+		}
+
+		return true;
+	}
+};
+
 size_t getFormatStride(CommonFormat format);
 
 uint32 getFormatFlags(CommonFormat format);