From ed3f4197478e123233ec10f6f0cd0d20f0f5e7bd Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Tue, 23 Jun 2015 20:55:23 -0300 Subject: [PATCH] Prevent redundant calls to glEnable/DisableVertexAttribArray. This should hopefully fix a performance regression compared to 0.9.2 as well. --- src/modules/graphics/opengl/Canvas.cpp | 12 ++---- src/modules/graphics/opengl/Font.cpp | 23 ++---------- src/modules/graphics/opengl/Graphics.cpp | 9 ++--- src/modules/graphics/opengl/Image.cpp | 12 ++---- src/modules/graphics/opengl/Mesh.cpp | 25 +++---------- src/modules/graphics/opengl/OpenGL.cpp | 37 +++++++++++++++++++ src/modules/graphics/opengl/OpenGL.h | 20 ++++++++++ .../graphics/opengl/ParticleSystem.cpp | 13 +------ src/modules/graphics/opengl/Polyline.cpp | 20 +++++----- src/modules/graphics/opengl/SpriteBatch.cpp | 18 +++------ src/modules/graphics/opengl/Text.cpp | 20 ++-------- 11 files changed, 94 insertions(+), 115 deletions(-) diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index bb363382c..b0c9d56a0 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/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); diff --git a/src/modules/graphics/opengl/Font.cpp b/src/modules/graphics/opengl/Font.cpp index 8296f9590..d8ab76a47 100644 --- a/src/modules/graphics/opengl/Font.cpp +++ b/src/modules/graphics/opengl/Font.cpp @@ -564,25 +564,12 @@ void Font::printv(const Matrix &t, const std::vector &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 vertices; std::vector 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 vertices; std::vector 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); } diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index edddc9250..a3a5feee6 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/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); } } diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index fb104b466..9b85f93f6 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/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()); } diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index 2e4f5cdb1..24a77c0da 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/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 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) diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 2b9cbf15d..0d2b2550f 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/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); diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 9dcd7c5fb..3e0ffdfa9 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/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; diff --git a/src/modules/graphics/opengl/ParticleSystem.cpp b/src/modules/graphics/opengl/ParticleSystem.cpp index e176acd85..cfb60e974 100644 --- a/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/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) diff --git a/src/modules/graphics/opengl/Polyline.cpp b/src/modules/graphics/opengl/Polyline.cpp index a55284beb..8bfd63aaf 100644 --- a/src/modules/graphics/opengl/Polyline.cpp +++ b/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; diff --git a/src/modules/graphics/opengl/SpriteBatch.cpp b/src/modules/graphics/opengl/SpriteBatch.cpp index 95375a62b..2d53a5d35 100644 --- a/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/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) diff --git a/src/modules/graphics/opengl/Text.cpp b/src/modules/graphics/opengl/Text.cpp index 94cd434ee..99d132894 100644 --- a/src/modules/graphics/opengl/Text.cpp +++ b/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)