Browse Source

Prevent redundant calls to glEnable/DisableVertexAttribArray. This should hopefully fix a performance regression compared to 0.9.2 as well.

Alex Szpakowski 10 years ago
parent
commit
ed3f419747

+ 3 - 9
src/modules/graphics/opengl/Canvas.cpp

@@ -307,31 +307,25 @@ void Canvas::drawv(const Matrix &t, const Vertex *v)
 
 	gl.bindTexture(texture);
 
-	glEnableVertexAttribArray(ATTRIB_POS);
-	glEnableVertexAttribArray(ATTRIB_TEXCOORD);
+	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
 
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s);
 
 	gl.prepareDraw();
 	gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
-	glDisableVertexAttribArray(ATTRIB_TEXCOORD);
-	glDisableVertexAttribArray(ATTRIB_POS);
 }
 
 void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
 {
-	static Matrix t;
-	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
+	Matrix t(x, y, angle, sx, sy, ox, oy, kx, ky);
 
 	drawv(t, vertices);
 }
 
 void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
 {
-	static Matrix t;
-	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
+	Matrix t(x, y, angle, sx, sy, ox, oy, kx, ky);
 
 	const Vertex *v = quad->getVertices();
 	drawv(t, v);

+ 4 - 19
src/modules/graphics/opengl/Font.cpp

@@ -564,25 +564,12 @@ void Font::printv(const Matrix &t, const std::vector<DrawCommand> &drawcommands,
 	OpenGL::TempTransform transform(gl);
 	transform.get() *= t;
 
-	glEnableVertexAttribArray(ATTRIB_POS);
-	glEnableVertexAttribArray(ATTRIB_TEXCOORD);
+	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
 
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].x);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(GlyphVertex), &vertices[0].s);
 
-	try
-	{
-		drawVertices(drawcommands);
-	}
-	catch (love::Exception &)
-	{
-		glDisableVertexAttribArray(ATTRIB_TEXCOORD);
-		glDisableVertexAttribArray(ATTRIB_POS);
-		throw;
-	}
-
-	glDisableVertexAttribArray(ATTRIB_TEXCOORD);
-	glDisableVertexAttribArray(ATTRIB_POS);
+	drawVertices(drawcommands);
 }
 
 void Font::print(const std::string &text, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
@@ -590,8 +577,7 @@ void Font::print(const std::string &text, float x, float y, float angle, float s
 	std::vector<GlyphVertex> vertices;
 	std::vector<DrawCommand> drawcommands = generateVertices(text, vertices);
 
-	Matrix t;
-	t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
+	Matrix t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
 
 	printv(t, drawcommands, vertices);
 }
@@ -601,8 +587,7 @@ void Font::printf(const std::string &text, float x, float y, float wrap, AlignMo
 	std::vector<GlyphVertex> vertices;
 	std::vector<DrawCommand> drawcommands = generateVerticesFormatted(text, wrap, align, vertices);
 
-	Matrix t;
-	t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
+	Matrix t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
 
 	printv(t, drawcommands, vertices);
 }

+ 3 - 6
src/modules/graphics/opengl/Graphics.cpp

@@ -1088,10 +1088,9 @@ void Graphics::point(float x, float y)
 
 	gl.prepareDraw();
 	gl.bindTexture(gl.getDefaultTexture());
-	glEnableVertexAttribArray(ATTRIB_POS);
+	gl.useVertexAttribArrays(ATTRIBFLAG_POS);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coord);
 	gl.drawArrays(GL_POINTS, 0, 1);
-	glDisableVertexAttribArray(ATTRIB_POS);
 }
 
 void Graphics::polyline(const float *coords, size_t count)
@@ -1247,10 +1246,9 @@ void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1,
 
 		gl.prepareDraw();
 		gl.bindTexture(gl.getDefaultTexture());
-		glEnableVertexAttribArray(ATTRIB_POS);
+		gl.useVertexAttribArrays(ATTRIBFLAG_POS);
 		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coords);
 		gl.drawArrays(GL_TRIANGLE_FAN, 0, points + 2);
-		glDisableVertexAttribArray(ATTRIB_POS);
 	}
 
 	delete[] coords;
@@ -1273,10 +1271,9 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
 
 		gl.prepareDraw();
 		gl.bindTexture(gl.getDefaultTexture());
-		glEnableVertexAttribArray(ATTRIB_POS);
+		gl.useVertexAttribArrays(ATTRIBFLAG_POS);
 		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, coords);
 		gl.drawArrays(GL_TRIANGLE_FAN, 0, (int)count/2-1); // opengl will close the polygon for us
-		glDisableVertexAttribArray(ATTRIB_POS);
 	}
 }
 

+ 3 - 9
src/modules/graphics/opengl/Image.cpp

@@ -360,31 +360,25 @@ void Image::drawv(const Matrix &t, const Vertex *v)
 
 	gl.bindTexture(texture);
 
-	glEnableVertexAttribArray(ATTRIB_POS);
-	glEnableVertexAttribArray(ATTRIB_TEXCOORD);
+	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
 
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].x);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &v[0].s);
 
 	gl.prepareDraw();
 	gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
-	glDisableVertexAttribArray(ATTRIB_TEXCOORD);
-	glDisableVertexAttribArray(ATTRIB_POS);
 }
 
 void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
 {
-	Matrix t;
-	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
+	Matrix t(x, y, angle, sx, sy, ox, oy, kx, ky);
 
 	drawv(t, vertices);
 }
 
 void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
 {
-	Matrix t;
-	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
+	Matrix t(x, y, angle, sx, sy, ox, oy, kx, ky);
 
 	drawv(t, quad->getVertices());
 }

+ 5 - 20
src/modules/graphics/opengl/Mesh.cpp

@@ -546,10 +546,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 {
 	OpenGL::TempDebugGroup debuggroup("Mesh draw");
 
-	std::vector<GLint> attriblocations;
-	attriblocations.reserve(attachedAttributes.size());
-
-	bool hasposattrib = false;
+	uint32 enabledattribs = 0;
 
 	for (const auto &attrib : attachedAttributes)
 	{
@@ -585,24 +582,19 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 		GLenum datatype = getGLDataType(format.type);
 		GLboolean normalized = (datatype == GL_UNSIGNED_BYTE);
 
-		glEnableVertexAttribArray(attriblocation);
 		glVertexAttribPointer(attriblocation, format.components, datatype, normalized, mesh->vertexStride, gloffset);
 
-		attriblocations.push_back(attriblocation);
-
-		if (attriblocation == ATTRIB_POS)
-			hasposattrib = true;
+		enabledattribs |= 1 << uint32(attriblocation);
 	}
 
-	if (!hasposattrib)
+	if (!(enabledattribs & ATTRIBFLAG_POS))
 	{
-		for (GLint attrib : attriblocations)
-			glDisableVertexAttribArray(attrib);
-
 		// Not supported on all platforms or GL versions at least, I believe.
 		throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn.");
 	}
 
+	gl.useVertexAttribArrays(enabledattribs);
+
 	if (texture.get())
 		gl.bindTexture(*(GLuint *) texture->getHandle());
 	else
@@ -649,13 +641,6 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 		// Normal non-indexed drawing (no custom vertex map.)
 		gl.drawArrays(getGLDrawMode(drawMode), min, max - min + 1);
 	}
-
-	for (GLint attrib : attriblocations)
-	{
-		glDisableVertexAttribArray(attrib);
-		if (attrib == ATTRIB_COLOR)
-			glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
-	}
 }
 
 size_t Mesh::getAttribFormatSize(const AttribFormat &format)

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

@@ -90,6 +90,11 @@ void OpenGL::setupContext()
 	glVertexAttrib4fv(ATTRIB_COLOR, glcolor);
 	glVertexAttrib4fv(ATTRIB_CONSTANTCOLOR, glcolor);
 
+	GLint maxvertexattribs = 1;
+	glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxvertexattribs);
+	state.enabledAttribArrays = 1u << uint32(maxvertexattribs - 1);
+	useVertexAttribArrays(0);
+
 	// Get the current viewport.
 	glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x);
 
@@ -375,6 +380,38 @@ void OpenGL::drawElements(GLenum mode, GLsizei count, GLenum type, const void *i
 	++stats.drawCalls;
 }
 
+void OpenGL::useVertexAttribArrays(uint32 arraybits)
+{
+	uint32 diff = arraybits ^ state.enabledAttribArrays;
+
+	if (diff == 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++)
+	{
+		uint32 bit = 1 << i;
+
+		if (diff & bit)
+		{
+			if (arraybits & bit)
+				glEnableVertexAttribArray(i);
+			else
+				glDisableVertexAttribArray(i);
+		}
+	}
+
+	state.enabledAttribArrays = arraybits;
+
+	// glDisableVertexAttribArray will make the constant value for a vertex
+	// attribute undefined. We rely on the per-vertex color attribte 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))
+		glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
+}
+
 void OpenGL::setViewport(const OpenGL::Viewport &v)
 {
 	glViewport(v.x, v.y, v.w, v.h);

+ 20 - 0
src/modules/graphics/opengl/OpenGL.h

@@ -23,6 +23,7 @@
 
 // LOVE
 #include "common/config.h"
+#include "common/int.h"
 #include "graphics/Color.h"
 #include "graphics/Texture.h"
 #include "common/Matrix.h"
@@ -60,6 +61,14 @@ enum VertexAttribID
 	ATTRIB_MAX_ENUM
 };
 
+enum VertexAttribFlags
+{
+	ATTRIBFLAG_POS = 1 << ATTRIB_POS,
+	ATTRIBFLAG_TEXCOORD = 1 << ATTRIB_TEXCOORD,
+	ATTRIBFLAG_COLOR = 1 << ATTRIB_COLOR,
+	ATTRIBFLAG_CONSTANTCOLOR = 1 << ATTRIB_CONSTANTCOLOR
+};
+
 /**
  * Thin layer between OpenGL and the rest of the program.
  * Internally shadows some OpenGL context state for improved efficiency and
@@ -197,6 +206,15 @@ public:
 	void drawArrays(GLenum mode, GLint first, GLsizei count);
 	void drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices);
 
+	/**
+	 * 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);
+
 	/**
 	 * Sets the OpenGL rendering viewport to the specified rectangle.
 	 * The y-coordinate starts at the top.
@@ -353,6 +371,8 @@ private:
 		// Currently active texture unit.
 		int curTextureUnit;
 
+		uint32 enabledAttribArrays;
+
 		Viewport viewport;
 		Viewport scissor;
 

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

@@ -854,8 +854,7 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 
 	OpenGL::TempDebugGroup debuggroup("ParticleSystem draw");
 
-	static Matrix t;
-	t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
+	Matrix t(x, y, angle, sx, sy, ox, oy, kx, ky);
 
 	OpenGL::TempTransform transform(gl);
 	transform.get() *= t;
@@ -896,9 +895,7 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 	gl.bindTexture(*(GLuint *) texture->getHandle());
 	gl.prepareDraw();
 
-	glEnableVertexAttribArray(ATTRIB_COLOR);
-	glEnableVertexAttribArray(ATTRIB_POS);
-	glEnableVertexAttribArray(ATTRIB_TEXCOORD);
+	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR);
 
 	glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), &particleVerts[0].r);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &particleVerts[0].x);
@@ -909,12 +906,6 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 		GLBuffer::Bind ibo_bind(*quadIndices.getBuffer());
 		gl.drawElements(GL_TRIANGLES, count, quadIndices.getType(), quadIndices.getPointer(0));
 	}
-
-	glDisableVertexAttribArray(ATTRIB_TEXCOORD);
-	glDisableVertexAttribArray(ATTRIB_POS);
-	glDisableVertexAttribArray(ATTRIB_COLOR);
-
-	glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
 }
 
 void ParticleSystem::update(float dt)

+ 9 - 11
src/modules/graphics/opengl/Polyline.cpp

@@ -402,21 +402,26 @@ void Polyline::draw()
 	gl.prepareDraw();
 
 	gl.bindTexture(gl.getDefaultTexture());
-	glEnableVertexAttribArray(ATTRIB_POS);
-	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, vertices + vertex_start);
+
+	uint32 enabledattribs = ATTRIBFLAG_POS;
 
 	if (overdraw)
 	{
-		// Prepare colors. Set the core line's colors to white, and the overdraw
+		// Prepare per-vertex colors. Set the core to white, and the overdraw
 		// line's colors to white on one side and transparent on the other.
 		colors = new Color[total_vertex_count];
 		memset(colors, 255, overdraw_vertex_start * sizeof(Color));
 		fill_color_array(colors + overdraw_vertex_start);
 
-		glEnableVertexAttribArray(ATTRIB_COLOR);
 		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
+
+		enabledattribs |= ATTRIBFLAG_COLOR;
 	}
 
+	gl.useVertexAttribArrays(enabledattribs);
+
+	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, vertices + vertex_start);
+
 	// Draw the core line and the overdraw in a single draw call. We can do this
 	// because the vertex array contains both the core line and the overdraw
 	// vertices.
@@ -425,15 +430,8 @@ void Polyline::draw()
 	else
 		gl.drawArrays(draw_mode, 0, (int) total_vertex_count);
 
-	glDisableVertexAttribArray(ATTRIB_POS);
-
 	if (overdraw)
-	{
-		glDisableVertexAttribArray(ATTRIB_COLOR);
-		glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
-
 		delete[] colors;
-	}
 
 	if (indices)
 		delete[] indices;

+ 5 - 13
src/modules/graphics/opengl/SpriteBatch.cpp

@@ -236,30 +236,22 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
 	array_buf->unmap(buffer_used_offset, buffer_used_size);
 	buffer_used_offset = buffer_used_size = 0;
 
+	uint32 enabledattribs = ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD;
+
 	// Apply per-sprite color, if a color is set.
 	if (color)
 	{
-		glEnableVertexAttribArray(ATTRIB_COLOR);
+		enabledattribs |= ATTRIBFLAG_COLOR;
 		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), array_buf->getPointer(color_offset));
 	}
 
-	glEnableVertexAttribArray(ATTRIB_POS);
 	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(pos_offset));
-
-	glEnableVertexAttribArray(ATTRIB_TEXCOORD);
 	glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), array_buf->getPointer(texel_offset));
 
+	gl.useVertexAttribArrays(enabledattribs);
+
 	gl.prepareDraw();
 	gl.drawElements(GL_TRIANGLES, (GLsizei) quad_indices.getIndexCount(next), quad_indices.getType(), quad_indices.getPointer(0));
-
-	glDisableVertexAttribArray(ATTRIB_TEXCOORD);
-	glDisableVertexAttribArray(ATTRIB_POS);
-
-	if (color)
-	{
-		glDisableVertexAttribArray(ATTRIB_COLOR);
-		glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
-	}
 }
 
 void SpriteBatch::addv(const Vertex *v, const Matrix &m, int index)

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

@@ -232,8 +232,7 @@ void Text::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 	const size_t tex_offset = offsetof(Font::GlyphVertex, s);
 	const size_t stride = sizeof(Font::GlyphVertex);
 
-	Matrix t;
-	t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
+	Matrix t(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky);
 
 	OpenGL::TempTransform transform(gl);
 	transform.get() *= t;
@@ -247,22 +246,9 @@ void Text::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 		glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, vbo->getPointer(tex_offset));
 	}
 
-	glEnableVertexAttribArray(ATTRIB_POS);
-	glEnableVertexAttribArray(ATTRIB_TEXCOORD);
+	gl.useVertexAttribArrays(ATTRIBFLAG_POS | ATTRIBFLAG_TEXCOORD);
 
-	try
-	{
-		font->drawVertices(draw_commands);
-	}
-	catch (love::Exception &)
-	{
-		glDisableVertexAttribArray(ATTRIB_TEXCOORD);
-		glDisableVertexAttribArray(ATTRIB_POS);
-		throw;
-	}
-
-	glDisableVertexAttribArray(ATTRIB_TEXCOORD);
-	glDisableVertexAttribArray(ATTRIB_POS);
+	font->drawVertices(draw_commands);
 }
 
 void Text::setFont(Font *f)