Browse Source

mesh and spritebatch attributes reuse buffer bindings when possible.

Sasha Szpakowski 11 months ago
parent
commit
0a552ef352

+ 58 - 37
src/modules/graphics/Mesh.cpp

@@ -660,6 +660,58 @@ bool Mesh::getDrawRange(int &start, int &count) const
 	return true;
 }
 
+void Mesh::updateVertexAttributes(Graphics *gfx)
+{
+	VertexAttributes attributes;
+	BufferBindings &buffers = bufferBindings;
+
+	int activebuffers = 0;
+
+	for (const auto &attrib : attachedAttributes)
+	{
+		if (!attrib.enabled)
+			continue;
+
+		Buffer *buffer = attrib.buffer.get();
+		int bindinglocation = attrib.bindingLocation;
+
+		// Query the index from the shader as a fallback to support old code that
+		// hasn't set a binding location.
+		if (bindinglocation < 0 && Shader::current)
+			bindinglocation = Shader::current->getVertexAttributeIndex(attrib.name);
+
+		if (bindinglocation >= 0)
+		{
+			const auto &member = buffer->getDataMember(attrib.indexInBuffer);
+
+			uint16 offset = (uint16)member.offset;
+			uint16 stride = (uint16)buffer->getArrayStride();
+			size_t bufferoffset = (size_t)stride * attrib.startArrayIndex;
+
+			int bufferindex = activebuffers;
+
+			for (int i = 0; i < activebuffers; i++)
+			{
+				if (buffers.info[i].buffer == buffer && buffers.info[i].offset == bufferoffset
+					&& attributes.bufferLayouts[i].stride == stride && attributes.getBufferStep(i) == attrib.step)
+				{
+					bufferindex = i;
+					break;
+				}
+			}
+
+			attributes.set(bindinglocation, member.decl.format, offset, bufferindex);
+			attributes.setBufferLayout(bufferindex, stride, attrib.step);
+
+			buffers.set(bufferindex, buffer, bufferoffset);
+
+			activebuffers = std::max(activebuffers, bufferindex + 1);
+		}
+	}
+
+	attributesID = gfx->registerVertexAttributes(attributes);
+}
+
 void Mesh::draw(Graphics *gfx, const love::Matrix4 &m)
 {
 	drawInternal(gfx, m, 1, nullptr, 0);
@@ -709,53 +761,22 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
 
 	bool attributesIDneedsupdate = !attributesID.isValid();
 
-	VertexAttributes attributes;
-	BufferBindings buffers;
-
-	int activebuffers = 0;
-
 	for (const auto &attrib : attachedAttributes)
 	{
 		if (!attrib.enabled)
 			continue;
 
-		Buffer *buffer = attrib.buffer.get();
-		int bindinglocation = attrib.bindingLocation;
+		if (attrib.mesh.get())
+			attrib.mesh->flush();
 
 		// Query the index from the shader as a fallback to support old code that
 		// hasn't set a binding location.
-		if (bindinglocation < 0 && Shader::current)
-		{
-			bindinglocation = Shader::current->getVertexAttributeIndex(attrib.name);
+		if (attrib.bindingLocation < 0)
 			attributesIDneedsupdate = true;
-		}
-
-		if (bindinglocation >= 0)
-		{
-			if (attrib.mesh.get())
-				attrib.mesh->flush();
-
-			const auto &member = buffer->getDataMember(attrib.indexInBuffer);
-
-			uint16 offset = (uint16) member.offset;
-			uint16 stride = (uint16) buffer->getArrayStride();
-			size_t bufferoffset = (size_t) stride * attrib.startArrayIndex;
-
-			attributes.set(bindinglocation, member.decl.format, offset, activebuffers);
-			attributes.setBufferLayout(activebuffers, stride, attrib.step);
-
-			// TODO: Ideally we want to reuse buffers with the same stride+step.
-			buffers.set(activebuffers, buffer, bufferoffset);
-			activebuffers++;
-		}
 	}
 
-	// Not supported on all platforms or GL versions, I believe.
-	if ((attributes.enableBits & ~(ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR)) == 0)
-		throw love::Exception("Mesh must have an enabled VertexPosition or custom attribute to be drawn.");
-
 	if (attributesIDneedsupdate)
-		attributesID = gfx->registerVertexAttributes(attributes);
+		updateVertexAttributes(gfx);
 
 	Graphics::TempTransform transform(gfx, m);
 
@@ -782,7 +803,7 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
 		if (range.isValid())
 			r.intersect(range);
 
-		Graphics::DrawIndexedCommand cmd(attributesID, &buffers, indexbuffer);
+		Graphics::DrawIndexedCommand cmd(attributesID, &bufferBindings, indexbuffer);
 
 		cmd.primitiveType = primitiveType;
 		cmd.indexType = indexDataType;
@@ -805,7 +826,7 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff
 		if (range.isValid())
 			r.intersect(range);
 
-		Graphics::DrawCommand cmd(attributesID, &buffers);
+		Graphics::DrawCommand cmd(attributesID, &bufferBindings);
 
 		cmd.primitiveType = primitiveType;
 		cmd.vertexStart = (int) r.getOffset();

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

@@ -196,6 +196,8 @@ private:
 	int getAttachedAttributeIndex(int bindingLocation) const;
 	void finalizeAttribute(BufferAttribute &attrib) const;
 
+	void updateVertexAttributes(Graphics *gfx);
+
 	void drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buffer *indirectargs, int argsindex);
 
 	std::vector<Buffer::DataMember> vertexFormat;
@@ -226,6 +228,8 @@ private:
 
 	StrongRef<Texture> texture;
 
+	BufferBindings bufferBindings;
+
 }; // Mesh
 
 } // graphics

+ 56 - 35
src/modules/graphics/SpriteBatch.cpp

@@ -321,6 +321,57 @@ bool SpriteBatch::getDrawRange(int &start, int &count) const
 	return true;
 }
 
+void SpriteBatch::updateVertexAttributes(Graphics *gfx)
+{
+	VertexAttributes attributes;
+	BufferBindings &buffers = bufferBindings;
+
+	buffers.set(0, array_buf, 0);
+	attributes.setCommonFormat(vertex_format, 0);
+
+	int activebuffers = 1;
+
+	for (const auto &it : attached_attributes)
+	{
+		Buffer *buffer = it.second.buffer.get();
+
+		int bindingindex = it.second.bindingIndex;
+
+		// If the attribute is one of the LOVE-defined ones, use the constant
+		// attribute index for it, otherwise query the index from the shader.
+		if (bindingindex < 0 && Shader::current)
+			bindingindex = Shader::current->getVertexAttributeIndex(it.first);
+
+		if (bindingindex >= 0)
+		{
+			const auto &member = buffer->getDataMember(it.second.index);
+
+			uint16 offset = (uint16) buffer->getMemberOffset(it.second.index);
+			uint16 stride = (uint16) buffer->getArrayStride();
+
+			int bufferindex = activebuffers;
+
+			for (int i = 1; i < activebuffers; i++)
+			{
+				if (buffers.info[i].buffer == buffer && attributes.bufferLayouts[i].stride == stride)
+				{
+					bufferindex = i;
+					break;
+				}
+			}
+
+			attributes.set(bindingindex, member.decl.format, offset, bufferindex);
+			attributes.setBufferLayout(bufferindex, stride);
+
+			buffers.set(bufferindex, buffer, 0);
+
+			activebuffers = std::max(activebuffers, bufferindex + 1);
+		}
+	}
+
+	attributesID = gfx->registerVertexAttributes(attributes);
+}
+
 void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 {
 	if (next == 0)
@@ -347,16 +398,6 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 
 	bool attributesIDneedsupdate = !attributesID.isValid();
 
-	VertexAttributes attributes;
-	BufferBindings buffers;
-
-	{
-		buffers.set(0, array_buf, 0);
-		attributes.setCommonFormat(vertex_format, 0);
-	}
-
-	int activebuffers = 1;
-
 	for (const auto &it : attached_attributes)
 	{
 		Buffer *buffer = it.second.buffer.get();
@@ -366,37 +407,17 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 		if (buffer->getArrayLength() < (size_t) next * 4)
 			throw love::Exception("Buffer with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str());
 
-		int bindingindex = it.second.bindingIndex;
-
 		// If the attribute is one of the LOVE-defined ones, use the constant
 		// attribute index for it, otherwise query the index from the shader.
-		if (bindingindex < 0 && Shader::current)
-		{
-			bindingindex = Shader::current->getVertexAttributeIndex(it.first);
+		if (it.second.bindingIndex < 0)
 			attributesIDneedsupdate = true;
-		}
-
-		if (bindingindex >= 0)
-		{
-			if (it.second.mesh.get())
-				it.second.mesh->flush();
 
-			const auto &member = buffer->getDataMember(it.second.index);
-
-			uint16 offset = (uint16) buffer->getMemberOffset(it.second.index);
-			uint16 stride = (uint16) buffer->getArrayStride();
-
-			attributes.set(bindingindex, member.decl.format, offset, activebuffers);
-			attributes.setBufferLayout(activebuffers, stride);
-
-			// TODO: We should reuse buffer bindings with the same buffer+stride+step.
-			buffers.set(activebuffers, buffer, 0);
-			activebuffers++;
-		}
+		if (it.second.mesh.get())
+			it.second.mesh->flush();
 	}
 
 	if (attributesIDneedsupdate)
-		attributesID = gfx->registerVertexAttributes(attributes);
+		updateVertexAttributes(gfx);
 
 	Graphics::TempTransform transform(gfx, m);
 
@@ -411,7 +432,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
 	if (count > 0)
 	{
 		Texture *tex = gfx->getTextureOrDefaultForActiveShader(texture);
-		gfx->drawQuads(start, count, attributesID, buffers, tex);
+		gfx->drawQuads(start, count, attributesID, bufferBindings, tex);
 	}
 }
 

+ 3 - 0
src/modules/graphics/SpriteBatch.h

@@ -108,6 +108,8 @@ public:
 
 private:
 
+	void updateVertexAttributes(Graphics *gfx);
+
 	struct AttachedAttribute
 	{
 		StrongRef<Buffer> buffer;
@@ -138,6 +140,7 @@ private:
 	size_t vertex_stride;
 
 	VertexAttributesID attributesID;
+	BufferBindings bufferBindings;
 
 	StrongRef<love::graphics::Buffer> array_buf;
 	uint8 *vertex_data;