From 3435a6bcbf6ae8c70688f05e428f7aa3e1dba39a Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Mon, 18 Nov 2019 21:13:54 -0400 Subject: [PATCH] Change the internal constant color from a (per-draw) vertex attribute to a uniform. Pack most built-in uniforms into a single vec4 array and update them all with a single glUniform call. --HG-- branch : minor --- src/modules/graphics/Shader.cpp | 18 ++- src/modules/graphics/Shader.h | 16 ++- src/modules/graphics/opengl/Graphics.cpp | 8 +- src/modules/graphics/opengl/OpenGL.cpp | 26 +--- src/modules/graphics/opengl/OpenGL.h | 12 +- src/modules/graphics/opengl/Shader.cpp | 136 ++++++------------- src/modules/graphics/opengl/Shader.h | 9 +- src/modules/graphics/vertex.cpp | 1 - src/modules/graphics/vertex.h | 2 - src/modules/graphics/wrap_GraphicsShader.lua | 42 ++++-- 10 files changed, 98 insertions(+), 172 deletions(-) diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index cd131b29a..c44c2ba7d 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -34,6 +34,8 @@ namespace love namespace graphics { +static_assert(sizeof(Shader::BuiltinUniformData) == sizeof(float) * 4 * 13, "Update the array in wrap_GraphicsShader.lua if this changes."); + love::Type Shader::type("Shader", &Object::type); Shader *Shader::current = nullptr; @@ -187,16 +189,12 @@ StringMap Shader::languages(Shader: StringMap::Entry Shader::builtinNameEntries[] = { - { "MainTex", BUILTIN_TEXTURE_MAIN }, - { "love_VideoYChannel", BUILTIN_TEXTURE_VIDEO_Y }, - { "love_VideoCbChannel", BUILTIN_TEXTURE_VIDEO_CB }, - { "love_VideoCrChannel", BUILTIN_TEXTURE_VIDEO_CR }, - { "ViewSpaceFromLocal", BUILTIN_MATRIX_VIEW_FROM_LOCAL }, - { "ClipSpaceFromView", BUILTIN_MATRIX_CLIP_FROM_VIEW }, - { "ClipSpaceFromLocal", BUILTIN_MATRIX_CLIP_FROM_LOCAL }, - { "ViewNormalFromLocal", BUILTIN_MATRIX_VIEW_NORMAL_FROM_LOCAL }, - { "love_PointSize", BUILTIN_POINT_SIZE }, - { "love_ScreenSize", BUILTIN_SCREEN_SIZE }, + { "MainTex", BUILTIN_TEXTURE_MAIN }, + { "love_VideoYChannel", BUILTIN_TEXTURE_VIDEO_Y }, + { "love_VideoCbChannel", BUILTIN_TEXTURE_VIDEO_CB }, + { "love_VideoCrChannel", BUILTIN_TEXTURE_VIDEO_CR }, + { "love_UniformsPerDraw", BUILTIN_UNIFORMS_PER_DRAW }, + { "love_PointSize", BUILTIN_POINT_SIZE }, }; StringMap Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries)); diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index 14bff1e7b..0f9e5a2d8 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -68,12 +68,8 @@ public: BUILTIN_TEXTURE_VIDEO_Y, BUILTIN_TEXTURE_VIDEO_CB, BUILTIN_TEXTURE_VIDEO_CR, - BUILTIN_MATRIX_VIEW_FROM_LOCAL, - BUILTIN_MATRIX_CLIP_FROM_VIEW, - BUILTIN_MATRIX_CLIP_FROM_LOCAL, - BUILTIN_MATRIX_VIEW_NORMAL_FROM_LOCAL, + BUILTIN_UNIFORMS_PER_DRAW, BUILTIN_POINT_SIZE, - BUILTIN_SCREEN_SIZE, BUILTIN_MAX_ENUM }; @@ -133,6 +129,16 @@ public: Texture **textures; }; + // The members in here must respect uniform buffer alignment/padding rules. + struct BuiltinUniformData + { + Matrix4 transformMatrix; + Matrix4 projectionMatrix; + Vector4 normalMatrix[3]; // 3x3 matrix padded to an array of 3 vector4s. + Vector4 screenSizeParams; + Colorf constantColor; + }; + // Pointer to currently active Shader. static Shader *current; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 3961138dd..b3d2c79b7 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -309,7 +309,7 @@ void Graphics::setActive(bool enable) void Graphics::draw(const DrawCommand &cmd) { - gl.prepareDraw(); + gl.prepareDraw(this); gl.setVertexAttributes(*cmd.attributes, *cmd.buffers); gl.bindTextureToUnit(cmd.texture, 0, false); gl.setCullMode(cmd.cullMode); @@ -326,7 +326,7 @@ void Graphics::draw(const DrawCommand &cmd) void Graphics::draw(const DrawIndexedCommand &cmd) { - gl.prepareDraw(); + gl.prepareDraw(this); gl.setVertexAttributes(*cmd.attributes, *cmd.buffers); gl.bindTextureToUnit(cmd.texture, 0, false); gl.setCullMode(cmd.cullMode); @@ -373,7 +373,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX; const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4; - gl.prepareDraw(); + gl.prepareDraw(this); gl.bindTextureToUnit(texture, 0, false); gl.setCullMode(CULL_NONE); @@ -1223,8 +1223,6 @@ void Graphics::setColor(Colorf c) c.b = std::min(std::max(c.b, 0.0f), 1.0f); c.a = std::min(std::max(c.a, 0.0f), 1.0f); - gl.setConstantColor(c); - states.back().color = c; } diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index f2fcbb01a..d81eea521 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -110,10 +110,6 @@ OpenGL::OpenGL() , vendor(VENDOR_UNKNOWN) , state() { - state.constantColor = Colorf(1.0f, 1.0f, 1.0f, 1.0f); - - float nan = std::numeric_limits::quiet_NaN(); - state.lastConstantColor = Colorf(nan, nan, nan, nan); } bool OpenGL::initContext() @@ -183,7 +179,6 @@ void OpenGL::setupContext() GLfloat glcolor[4] = {1.0f, 1.0f, 1.0f, 1.0f}; glVertexAttrib4fv(ATTRIB_COLOR, glcolor); - glVertexAttrib4fv(ATTRIB_CONSTANTCOLOR, glcolor); GLint maxvertexattribs = 1; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxvertexattribs); @@ -553,20 +548,15 @@ void OpenGL::createDefaultTexture() } } -void OpenGL::prepareDraw() +void OpenGL::prepareDraw(love::graphics::Graphics *gfx) { TempDebugGroup debuggroup("Prepare OpenGL draw"); // Make sure the active shader's love-provided uniforms are up to date. if (Shader::current != nullptr) - ((Shader *)Shader::current)->updateBuiltinUniforms(); - - if (state.constantColor != state.lastConstantColor) { - state.lastConstantColor = state.constantColor; - Colorf c = state.constantColor; - gammaCorrectColor(c); - glVertexAttrib4f(ATTRIB_CONSTANTCOLOR, c.r, c.g, c.b, c.a); + Rect viewport = getViewport(); + ((Shader *)Shader::current)->updateBuiltinUniforms(gfx, viewport.w, viewport.h); } } @@ -801,16 +791,6 @@ void OpenGL::setScissor(const Rect &v, bool canvasActive) state.scissor = v; } -void OpenGL::setConstantColor(const Colorf &color) -{ - state.constantColor = color; -} - -const Colorf &OpenGL::getConstantColor() const -{ - return state.constantColor; -} - void OpenGL::setPointSize(float size) { if (GLAD_VERSION_1_0) diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 91fca4094..7b99bc99b 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -220,7 +220,7 @@ public: * Set up necessary state (LOVE-provided shader uniforms, etc.) for drawing. * This *MUST* be called directly before OpenGL drawing functions. **/ - void prepareDraw(); + void prepareDraw(love::graphics::Graphics *gfx); /** * State-tracked glBindBuffer. @@ -262,13 +262,6 @@ public: **/ void setScissor(const Rect &v, bool canvasActive); - /** - * Sets the constant color (vertex attribute). This may be applied - * internally at draw-time. This gets gamma-corrected internally as well. - **/ - void setConstantColor(const Colorf &color); - const Colorf &getConstantColor() const; - /** * Sets the global point size. **/ @@ -473,9 +466,6 @@ private: uint32 enabledAttribArrays; uint32 instancedAttribArrays; - Colorf constantColor; - Colorf lastConstantColor; - Rect viewport; Rect scissor; diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 853f98ede..eac6fadba 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -42,8 +42,6 @@ Shader::Shader(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage , builtinUniforms() , builtinUniformInfo() , builtinAttributes() - , canvasWasActive(false) - , lastViewport() , lastPointSize(0.0f) { // load shader source and create program object @@ -297,17 +295,8 @@ bool Shader::loadVolatile() { OpenGL::TempDebugGroup debuggroup("Shader load"); - // Recreating the shader program will invalidate uniforms that rely on these. - canvasWasActive = false; - lastViewport = Rect(); - lastPointSize = -1.0f; - // Invalidate the cached matrices by setting some elements to NaN. - float nan = std::numeric_limits::quiet_NaN(); - lastProjectionMatrix.setTranslation(nan, nan); - lastTransformMatrix.setTranslation(nan, nan); - // zero out active texture list textureUnits.clear(); textureUnits.push_back(TextureUnit()); @@ -367,7 +356,6 @@ bool Shader::loadVolatile() // make sure glUseProgram gets called. current = nullptr; attach(); - updateBuiltinUniforms(); } return true; @@ -701,46 +689,6 @@ void Shader::setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *cr } } -void Shader::updateScreenParams() -{ - Rect view = gl.getViewport(); - - auto gfx = Module::getInstance(Module::M_GRAPHICS); - bool canvasActive = gfx->isCanvasActive(); - - if ((view == lastViewport && canvasWasActive == canvasActive) || current != this) - return; - - // In the shader, we do pixcoord.y = gl_FragCoord.y * params.z + params.w. - // This lets us flip pixcoord.y when needed, to be consistent (drawing with - // no Canvas active makes the y-values for pixel coordinates flipped.) - GLfloat params[] = { - (GLfloat) view.w, (GLfloat) view.h, - 0.0f, 0.0f, - }; - - if (canvasActive) - { - // No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0. - params[2] = 1.0f; - params[3] = 0.0f; - } - else - { - // gl_FragCoord.y is flipped when drawing to the screen, so we un-flip: - // pixcoord.y = gl_FragCoord.y * -1.0 + height. - params[2] = -1.0f; - params[3] = (GLfloat) view.h; - } - - GLint location = builtinUniforms[BUILTIN_SCREEN_SIZE]; - if (location >= 0) - glUniform4fv(location, 1, params); - - canvasWasActive = canvasActive; - lastViewport = view; -} - void Shader::updatePointSize(float size) { if (size == lastPointSize || current != this) @@ -753,63 +701,59 @@ void Shader::updatePointSize(float size) lastPointSize = size; } -void Shader::updateBuiltinUniforms() +void Shader::updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW, int viewportH) { if (current != this) return; - updateScreenParams(); - if (GLAD_ES_VERSION_2_0) updatePointSize(gl.getPointSize()); - auto gfx = Module::getInstance(Module::M_GRAPHICS); + BuiltinUniformData data; - const Matrix4 &curproj = gfx->getProjection(); - const Matrix4 &curxform = gfx->getTransform(); + data.transformMatrix = gfx->getTransform(); + data.projectionMatrix = gfx->getProjection(); - bool tpmatrixneedsupdate = false; - - // Only upload the matrices if they've changed. - if (memcmp(curxform.getElements(), lastTransformMatrix.getElements(), sizeof(float) * 16) != 0) + // The normal matrix is the transpose of the inverse of the rotation portion + // (top-left 3x3) of the transform matrix. { - GLint location = builtinUniforms[BUILTIN_MATRIX_VIEW_FROM_LOCAL]; - if (location >= 0) - glUniformMatrix4fv(location, 1, GL_FALSE, curxform.getElements()); - - // Also upload the re-calculated normal matrix, if possible. The normal - // matrix is the transpose of the inverse of the rotation portion - // (top-left 3x3) of the transform matrix. - location = builtinUniforms[BUILTIN_MATRIX_VIEW_NORMAL_FROM_LOCAL]; - if (location >= 0) + Matrix3 normalmatrix = Matrix3(data.transformMatrix).transposedInverse(); + const float *e = normalmatrix.getElements(); + for (int i = 0; i < 3; i++) { - Matrix3 normalmatrix = Matrix3(curxform).transposedInverse(); - glUniformMatrix3fv(location, 1, GL_FALSE, normalmatrix.getElements()); - } - - tpmatrixneedsupdate = true; - lastTransformMatrix = curxform; - } - - if (memcmp(curproj.getElements(), lastProjectionMatrix.getElements(), sizeof(float) * 16) != 0) - { - GLint location = builtinUniforms[BUILTIN_MATRIX_CLIP_FROM_VIEW]; - if (location >= 0) - glUniformMatrix4fv(location, 1, GL_FALSE, curproj.getElements()); - - tpmatrixneedsupdate = true; - lastProjectionMatrix = curproj; - } - - if (tpmatrixneedsupdate) - { - GLint location = builtinUniforms[BUILTIN_MATRIX_CLIP_FROM_LOCAL]; - if (location >= 0) - { - Matrix4 tp_matrix(curproj, curxform); - glUniformMatrix4fv(location, 1, GL_FALSE, tp_matrix.getElements()); + data.normalMatrix[i].x = e[i * 3 + 0]; + data.normalMatrix[i].y = e[i * 3 + 1]; + data.normalMatrix[i].z = e[i * 3 + 2]; + data.normalMatrix[i].w = 0.0f; } } + + data.screenSizeParams.x = viewportW; + data.screenSizeParams.y = viewportH; + + // The shader does pixcoord.y = gl_FragCoord.y * params.z + params.w. + // This lets us flip pixcoord.y when needed, to be consistent (drawing + // with no Canvas active makes the pixel coordinates y-flipped.) + if (gfx->isCanvasActive()) + { + // No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0. + data.screenSizeParams.z = 1.0f; + data.screenSizeParams.w = 0.0f; + } + else + { + // gl_FragCoord.y is flipped when drawing to the screen, so we + // un-flip: pixcoord.y = gl_FragCoord.y * -1.0 + height. + data.screenSizeParams.z = -1.0f; + data.screenSizeParams.w = viewportH; + } + + data.constantColor = gfx->getColor(); + gammaCorrectColor(data.constantColor); + + GLint location = builtinUniforms[BUILTIN_UNIFORMS_PER_DRAW]; + if (location >= 0) + glUniform4fv(location, 13, (const GLfloat *) &data); } std::string Shader::getGLSLVersion() diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index b20ca9e1e..b1bc402c0 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -66,9 +66,8 @@ public: ptrdiff_t getHandle() const override; void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) override; - void updateScreenParams(); void updatePointSize(float size); - void updateBuiltinUniforms(); + void updateBuiltinUniforms(love::graphics::Graphics *gfx, int viewportW, int viewportH); static std::string getGLSLVersion(); static bool isSupported(); @@ -119,14 +118,8 @@ private: std::vector> pendingUniformUpdates; - bool canvasWasActive; - Rect lastViewport; - float lastPointSize; - Matrix4 lastTransformMatrix; - Matrix4 lastProjectionMatrix; - }; // Shader } // opengl diff --git a/src/modules/graphics/vertex.cpp b/src/modules/graphics/vertex.cpp index 7e1d7a719..2740bc381 100644 --- a/src/modules/graphics/vertex.cpp +++ b/src/modules/graphics/vertex.cpp @@ -282,7 +282,6 @@ static StringMap::Entry attribNameEntri { "VertexPosition", ATTRIB_POS }, { "VertexTexCoord", ATTRIB_TEXCOORD }, { "VertexColor", ATTRIB_COLOR }, - { "ConstantColor", ATTRIB_CONSTANTCOLOR }, }; static StringMap attribNames(attribNameEntries, sizeof(attribNameEntries)); diff --git a/src/modules/graphics/vertex.h b/src/modules/graphics/vertex.h index abc4f0b0f..0f2778475 100644 --- a/src/modules/graphics/vertex.h +++ b/src/modules/graphics/vertex.h @@ -43,7 +43,6 @@ enum BuiltinVertexAttribute ATTRIB_POS = 0, ATTRIB_TEXCOORD, ATTRIB_COLOR, - ATTRIB_CONSTANTCOLOR, ATTRIB_MAX_ENUM }; @@ -52,7 +51,6 @@ enum BuiltinVertexAttributeFlag ATTRIBFLAG_POS = 1 << ATTRIB_POS, ATTRIBFLAG_TEXCOORD = 1 << ATTRIB_TEXCOORD, ATTRIBFLAG_COLOR = 1 << ATTRIB_COLOR, - ATTRIBFLAG_CONSTANTCOLOR = 1 << ATTRIB_CONSTANTCOLOR }; enum BufferType diff --git a/src/modules/graphics/wrap_GraphicsShader.lua b/src/modules/graphics/wrap_GraphicsShader.lua index 391623a3c..48b6fb69f 100644 --- a/src/modules/graphics/wrap_GraphicsShader.lua +++ b/src/modules/graphics/wrap_GraphicsShader.lua @@ -71,17 +71,38 @@ GLSL.UNIFORMS = [[ // According to the GLSL ES 1.0 spec, uniform precision must match between stages, // but we can't guarantee that highp is always supported in fragment shaders... // We *really* don't want to use mediump for these in vertex shaders though. -uniform LOVE_HIGHP_OR_MEDIUMP mat4 ViewSpaceFromLocal; -uniform LOVE_HIGHP_OR_MEDIUMP mat4 ClipSpaceFromView; -uniform LOVE_HIGHP_OR_MEDIUMP mat4 ClipSpaceFromLocal; -uniform LOVE_HIGHP_OR_MEDIUMP mat3 ViewNormalFromLocal; -uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize; +uniform LOVE_HIGHP_OR_MEDIUMP vec4 love_UniformsPerDraw[13]; -// Compatibility -#define TransformMatrix ViewSpaceFromLocal -#define ProjectionMatrix ClipSpaceFromView -#define TransformProjectionMatrix ClipSpaceFromLocal -#define NormalMatrix ViewNormalFromLocal +LOVE_HIGHP_OR_MEDIUMP mat4 TransformMatrix = mat4( + love_UniformsPerDraw[0], + love_UniformsPerDraw[1], + love_UniformsPerDraw[2], + love_UniformsPerDraw[3] +); + +LOVE_HIGHP_OR_MEDIUMP mat4 ProjectionMatrix = mat4( + love_UniformsPerDraw[4], + love_UniformsPerDraw[5], + love_UniformsPerDraw[6], + love_UniformsPerDraw[7] +); + +LOVE_HIGHP_OR_MEDIUMP mat3 NormalMatrix = mat3( + love_UniformsPerDraw[8].xyz, + love_UniformsPerDraw[9].xyz, + love_UniformsPerDraw[10].xyz +); + +LOVE_HIGHP_OR_MEDIUMP vec4 love_ScreenSize = love_UniformsPerDraw[11]; +LOVE_HIGHP_OR_MEDIUMP vec4 ConstantColor = love_UniformsPerDraw[12]; + +#define TransformProjectionMatrix (ProjectionMatrix * TransformMatrix) + +// Alternate names +#define ViewSpaceFromLocal TransformMatrix +#define ClipSpaceFromView ProjectionMatrix +#define ClipSpaceFromLocal TransformProjectionMatrix +#define ViewNormalFromLocal NormalMatrix ]] GLSL.FUNCTIONS = [[ @@ -218,7 +239,6 @@ void setPointSize() { attribute vec4 VertexPosition; attribute vec4 VertexTexCoord; attribute vec4 VertexColor; -attribute vec4 ConstantColor; varying vec4 VaryingTexCoord; varying vec4 VaryingColor;