Browse Source

Prevent redundant shader uniform uploads for built-in uniforms when OpenGL ES 2 is used.

As a result, the graphics primitive drawing functions (e.g. love.graphics.rectangle) are now more efficient on mobile devices.
Alex Szpakowski 10 years ago
parent
commit
4c571e8fd7

+ 6 - 22
src/modules/graphics/opengl/OpenGL.cpp

@@ -321,31 +321,15 @@ void OpenGL::prepareDraw()
 {
 	TempDebugGroup debuggroup("Prepare OpenGL draw");
 
-	Shader *shader = Shader::current;
+	// Make sure the active shader's love-provided uniforms are up to date.
+	if (Shader::current != nullptr)
+		Shader::current->checkSetBuiltinUniforms();
 
-	if (shader != nullptr)
-	{
-		// Make sure the active shader has the correct values for its love-
-		// provided uniforms.
-		shader->checkSetScreenParams();
-	}
-
-	const Matrix &curproj = matrices.projection.back();
-	const Matrix &curxform = matrices.transform.back();
-
-	if (GLAD_ES_VERSION_2_0 && shader)
+	if (GLAD_VERSION_1_0)
 	{
-		// Send built-in uniforms to the current shader.
-		shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_MATRIX, 4, curxform.getElements(), 1);
-		shader->sendBuiltinMatrix(Shader::BUILTIN_PROJECTION_MATRIX, 4, curproj.getElements(), 1);
-
-		Matrix tp_matrix(curproj * curxform);
-		shader->sendBuiltinMatrix(Shader::BUILTIN_TRANSFORM_PROJECTION_MATRIX, 4, tp_matrix.getElements(), 1);
+		const Matrix &curproj = matrices.projection.back();
+		const Matrix &curxform = matrices.transform.back();
 
-		shader->checkSetPointSize(state.pointSize);
-	}
-	else if (GLAD_VERSION_1_0)
-	{
 		const Matrix &lastproj = state.lastProjectionMatrix;
 		const Matrix &lastxform = state.lastTransformMatrix;
 

+ 71 - 67
src/modules/graphics/opengl/Shader.cpp

@@ -26,6 +26,7 @@
 
 // C++
 #include <algorithm>
+#include <limits>
 
 namespace love
 {
@@ -50,7 +51,7 @@ namespace
 		~TemporaryAttacher()
 		{
 			if (prevShader != nullptr)
-				prevShader->attach();
+				prevShader->attach(true);
 			else
 				curShader->detach();
 		}
@@ -217,7 +218,12 @@ bool Shader::loadVolatile()
     lastCanvas = (Canvas *) -1;
     lastViewport = OpenGL::Viewport();
 
-	lastPointSize = 0.0f;
+	lastPointSize = -1.0f;
+
+	// Invalidate the cached matrices by setting some elements to NaN.
+	float nan = std::numeric_limits<float>::quiet_NaN();
+	lastProjectionMatrix.setTranslation(nan, nan);
+	lastTransformMatrix.setTranslation(nan, nan);
 
 	// zero out active texture list
 	activeTexUnits.clear();
@@ -299,7 +305,7 @@ bool Shader::loadVolatile()
 		// make sure glUseProgram gets called.
 		current = nullptr;
 		attach();
-        checkSetScreenParams();
+		checkSetBuiltinUniforms();
 	}
 
 	return true;
@@ -629,68 +635,6 @@ bool Shader::hasVertexAttrib(VertexAttribID attrib) const
 	return builtinAttributes[int(attrib)] != -1;
 }
 
-bool Shader::hasBuiltinUniform(BuiltinUniform builtin) const
-{
-	return builtinUniforms[int(builtin)] != -1;
-}
-
-bool Shader::sendBuiltinMatrix(BuiltinUniform builtin, int size, const GLfloat *m, int count)
-{
-	if (!hasBuiltinUniform(builtin))
-		return false;
-
-	GLint location = builtinUniforms[GLint(builtin)];
-
-	TemporaryAttacher attacher(this);
-
-	switch (size)
-	{
-	case 2:
-		glUniformMatrix2fv(location, count, GL_FALSE, m);
-		break;
-	case 3:
-		glUniformMatrix3fv(location, count, GL_FALSE, m);
-		break;
-	case 4:
-		glUniformMatrix4fv(location, count, GL_FALSE, m);
-		break;
-	default:
-		return false;
-	}
-
-	return true;
-}
-
-bool Shader::sendBuiltinFloat(BuiltinUniform builtin, int size, const GLfloat *vec, int count)
-{
-	if (!hasBuiltinUniform(builtin))
-		return false;
-
-	GLint location = builtinUniforms[int(builtin)];
-
-	TemporaryAttacher attacher(this);
-
-	switch (size)
-	{
-	case 1:
-		glUniform1fv(location, count, vec);
-		break;
-	case 2:
-		glUniform2fv(location, count, vec);
-		break;
-	case 3:
-		glUniform3fv(location, count, vec);
-		break;
-	case 4:
-		glUniform4fv(location, count, vec);
-		break;
-	default:
-		return false;
-	}
-
-	return true;
-}
-
 void Shader::checkSetScreenParams()
 {
 	OpenGL::Viewport view = gl.getViewport();
@@ -720,7 +664,13 @@ void Shader::checkSetScreenParams()
 		params[3] = (GLfloat) view.h;
 	}
 
-	sendBuiltinFloat(BUILTIN_SCREEN_SIZE, 4, params, 1);
+	GLint location = builtinUniforms[BUILTIN_SCREEN_SIZE];
+
+	if (location >= 0)
+	{
+		TemporaryAttacher attacher(this);
+		glUniform4fv(location, 1, params);
+	}
 
 	lastCanvas = Canvas::current;
 	lastViewport = view;
@@ -731,11 +681,65 @@ void Shader::checkSetPointSize(float size)
 	if (size == lastPointSize)
 		return;
 
-	sendBuiltinFloat(BUILTIN_POINT_SIZE, 1, &size, 1);
+	GLint location = builtinUniforms[BUILTIN_POINT_SIZE];
+
+	if (location >= 0)
+	{
+		TemporaryAttacher attacher(this);
+		glUniform1f(location, size);
+	}
 
 	lastPointSize = size;
 }
 
+void Shader::checkSetBuiltinUniforms()
+{
+	checkSetScreenParams();
+
+	if (GLAD_ES_VERSION_2_0)
+	{
+		checkSetPointSize(gl.getPointSize());
+
+		const Matrix &curxform = gl.matrices.transform.back();
+		const Matrix &curproj = gl.matrices.projection.back();
+
+		TemporaryAttacher attacher(this);
+
+		bool tpmatrixneedsupdate = false;
+
+		// Only upload the matrices if they've changed.
+		if (memcmp(curxform.getElements(), lastTransformMatrix.getElements(), sizeof(float) * 16) != 0)
+		{
+			GLint location = builtinUniforms[BUILTIN_TRANSFORM_MATRIX];
+			if (location >= 0)
+				glUniformMatrix4fv(location, 1, GL_FALSE, curxform.getElements());
+
+			tpmatrixneedsupdate = true;
+			lastTransformMatrix = curxform;
+		}
+
+		if (memcmp(curproj.getElements(), lastProjectionMatrix.getElements(), sizeof(float) * 16) != 0)
+		{
+			GLint location = builtinUniforms[BUILTIN_PROJECTION_MATRIX];
+			if (location >= 0)
+				glUniformMatrix4fv(location, 1, GL_FALSE, curproj.getElements());
+
+			tpmatrixneedsupdate = true;
+			lastProjectionMatrix = curproj;
+		}
+
+		if (tpmatrixneedsupdate)
+		{
+			GLint location = builtinUniforms[BUILTIN_TRANSFORM_PROJECTION_MATRIX];
+			if (location >= 0)
+			{
+				Matrix tp_matrix(curproj * curxform);
+				glUniformMatrix4fv(location, 1, GL_FALSE, tp_matrix.getElements());
+			}
+		}
+	}
+}
+
 const std::map<std::string, Object *> &Shader::getBoundRetainables() const
 {
 	return boundRetainables;

+ 5 - 3
src/modules/graphics/opengl/Shader.h

@@ -180,11 +180,10 @@ public:
 	 * Internal use only.
 	 **/
 	bool hasVertexAttrib(VertexAttribID attrib) const;
-	bool hasBuiltinUniform(BuiltinUniform builtin) const;
-	bool sendBuiltinMatrix(BuiltinUniform builtin, int size, const GLfloat *m, int count);
-	bool sendBuiltinFloat(BuiltinUniform builtin, int size, const GLfloat *m, int count);
+
 	void checkSetScreenParams();
 	void checkSetPointSize(float size);
+	void checkSetBuiltinUniforms();
 
 	const std::map<std::string, Object *> &getBoundRetainables() const;
 
@@ -260,6 +259,9 @@ private:
 
 	float lastPointSize;
 
+	Matrix lastTransformMatrix;
+	Matrix lastProjectionMatrix;
+
 	// Counts total number of textures bound to each texture unit in all shaders
 	static std::vector<int> textureCounters;