From a31b500b033b1efa00ed9c69e7b86e0acd59bd04 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sun, 24 Dec 2017 22:05:10 -0400 Subject: [PATCH] Simplify quad drawing code, and use glDrawElementsBaseVertex when available. --- src/modules/graphics/Buffer.cpp | 103 ----------------------- src/modules/graphics/Buffer.h | 55 ------------ src/modules/graphics/Graphics.cpp | 15 ++++ src/modules/graphics/Graphics.h | 5 ++ src/modules/graphics/ParticleSystem.cpp | 6 +- src/modules/graphics/ParticleSystem.h | 3 - src/modules/graphics/SpriteBatch.cpp | 3 +- src/modules/graphics/SpriteBatch.h | 3 +- src/modules/graphics/Text.cpp | 3 +- src/modules/graphics/Text.h | 1 - src/modules/graphics/opengl/Graphics.cpp | 83 +++++++++++++++--- src/modules/graphics/opengl/Graphics.h | 2 +- src/modules/graphics/opengl/OpenGL.cpp | 35 ++++++++ src/modules/graphics/opengl/OpenGL.h | 3 + 14 files changed, 135 insertions(+), 185 deletions(-) diff --git a/src/modules/graphics/Buffer.cpp b/src/modules/graphics/Buffer.cpp index 2b73dd0ec..285fc6463 100644 --- a/src/modules/graphics/Buffer.cpp +++ b/src/modules/graphics/Buffer.cpp @@ -18,12 +18,7 @@ * 3. This notice may not be removed or altered from any source distribution. **/ -#include "common/config.h" #include "Buffer.h" -#include "Graphics.h" -#include "common/Exception.h" - -#include // std::min namespace love { @@ -43,103 +38,5 @@ Buffer::~Buffer() { } -// QuadIndices - -static const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX; -static const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4; - -size_t QuadIndices::objectCount = 0; - -Buffer *QuadIndices::indexBuffer = nullptr; - -QuadIndices::QuadIndices(Graphics *gfx) -{ - if (indexBuffer == nullptr) - { - size_t buffersize = sizeof(uint16) * MAX_QUADS_PER_DRAW * 6; - - indexBuffer = gfx->newBuffer(buffersize, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0); - - Buffer::Mapper map(*indexBuffer); - vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, MAX_VERTICES_PER_DRAW, (uint16 *) map.get()); - } - - objectCount++; -} - -QuadIndices::QuadIndices(const QuadIndices &) -{ - objectCount++; -} - -QuadIndices &QuadIndices::operator = (const QuadIndices &) -{ - return *this; -} - -QuadIndices::~QuadIndices() -{ - --objectCount; - - // Delete the buffer if we were the last living QuadIndices object. - if (objectCount <= 0) - { - delete indexBuffer; - indexBuffer = nullptr; - } -} - -static inline void advanceVertexOffsets(const vertex::Attributes &attributes, vertex::Buffers &buffers, int vertexcount) -{ - // TODO: Figure out a better way to avoid touching the same buffer multiple - // times, if multiple attributes share the buffer. - uint32 touchedbuffers = 0; - - for (unsigned int i = 0; i < vertex::Attributes::MAX; i++) - { - if (!attributes.isEnabled(i)) - continue; - - auto &attrib = attributes.attribs[i]; - - uint32 bufferbit = 1u << attrib.bufferindex; - if ((touchedbuffers & bufferbit) == 0) - { - touchedbuffers |= bufferbit; - buffers.info[attrib.bufferindex].offset += attrib.stride * vertexcount; - } - } -} - -void QuadIndices::draw(Graphics *gfx, int quadstart, int quadcount, const vertex::Attributes &attributes, vertex::Buffers buffers, Texture *texture) -{ - Graphics::DrawIndexedCommand cmd(&attributes, &buffers, indexBuffer); - cmd.primitiveType = PRIMITIVE_TRIANGLES; - cmd.indexBufferOffset = 0; - cmd.indexType = INDEX_UINT16; - cmd.texture = texture; - - // TODO: We can use glDrawElementsBaseVertex when supported, instead of - // advancing the vertex offset. - if (quadstart > 0) - advanceVertexOffsets(attributes, buffers, quadstart * 4); - - for (int quadindex = 0; quadindex < quadcount; quadindex += MAX_QUADS_PER_DRAW) - { - int quaddrawcount = std::min(MAX_QUADS_PER_DRAW, quadcount - quadindex); - - if (quadindex > 0) - advanceVertexOffsets(attributes, buffers, quaddrawcount * 4); - - cmd.indexCount = quaddrawcount * 6; - gfx->draw(cmd); - } -} - -Buffer *QuadIndices::getBuffer() const -{ - return indexBuffer; -} - } // graphics } // love diff --git a/src/modules/graphics/Buffer.h b/src/modules/graphics/Buffer.h index 8d26b65cc..4dfe277bb 100644 --- a/src/modules/graphics/Buffer.h +++ b/src/modules/graphics/Buffer.h @@ -34,9 +34,6 @@ namespace love namespace graphics { -class Graphics; -class Texture; - /** * A block of GPU-owned memory. Currently meant for internal use. **/ @@ -150,57 +147,5 @@ protected: }; // Buffer -/** - * QuadIndices manages one shared Buffer that stores the indices for an element - * array. Vertex arrays using the vertex structure (or anything else that can - * use the pattern below) can request a size and use it for the indexed draw - * call. - * - * indices[i*6 + 0] = i*4 + 0; - * indices[i*6 + 1] = i*4 + 1; - * indices[i*6 + 2] = i*4 + 2; - * - * indices[i*6 + 3] = i*4 + 2; - * indices[i*6 + 4] = i*4 + 1; - * indices[i*6 + 5] = i*4 + 3; - * - * There will always be a large enough Buffer around until all QuadIndices - * instances have been deleted. - * - * Q: Why have something like QuadIndices? - * A: The indices for the SpriteBatch do not change, only the array size varies. - * Using one Buffer for all element arrays removes this duplicated data and - * saves some memory. - */ -class QuadIndices -{ -public: - - /** - * Adds an entry to the list of sizes and resizes the Buffer - * if needed. A size of 1 allocates a group of 6 indices for 4 vertices - * creating 1 face. - */ - QuadIndices(Graphics *gfx); - - QuadIndices(const QuadIndices &other); - QuadIndices &operator = (const QuadIndices &other); - - /** - * Removes an entry from the list of sizes and resizes the GLBuffer - * if needed. - */ - ~QuadIndices(); - - void draw(Graphics *gfx, int quadstart, int quadcount, const vertex::Attributes &attributes, vertex::Buffers buffers, Texture *texture); - Buffer *getBuffer() const; - -private: - - static size_t objectCount; - static Buffer *indexBuffer; - -}; // QuadIndices - } // graphics } // love diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 6e6aca355..835428c5f 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -119,6 +119,7 @@ Graphics::Graphics() , canvasSwitchCount(0) , drawCalls(0) , drawCallsBatched(0) + , quadIndexBuffer(nullptr) , capabilities() , cachedShaderStages() { @@ -134,6 +135,8 @@ Graphics::Graphics() Graphics::~Graphics() { + delete quadIndexBuffer; + // Clean up standard shaders before the active shader. If we do it after, // the active shader may try to activate a standard shader when deactivating // itself, which will cause problems since it calls Graphics methods in the @@ -161,6 +164,18 @@ Graphics::~Graphics() Shader::deinitialize(); } +void Graphics::createQuadIndexBuffer() +{ + if (quadIndexBuffer != nullptr) + return; + + size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6; + quadIndexBuffer = newBuffer(size, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0); + + Buffer::Mapper map(*quadIndexBuffer); + vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.get()); +} + Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh) { return new Quad(v, sw, sh); diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index e6859aa3a..b25f02d4e 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -851,6 +851,7 @@ public: virtual void draw(const DrawCommand &cmd) = 0; virtual void draw(const DrawIndexedCommand &cmd) = 0; + virtual void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::Buffers &buffers, Texture *texture) = 0; void flushStreamDraws(); StreamVertexData requestStreamDraw(const StreamDrawCommand &command); @@ -996,6 +997,8 @@ protected: virtual void initCapabilities() = 0; virtual void getAPIStats(int &shaderswitches) const = 0; + void createQuadIndexBuffer(); + Canvas *getTemporaryCanvas(PixelFormat format, int w, int h, int samples); void restoreState(const DisplayState &s); @@ -1035,6 +1038,8 @@ protected: int drawCalls; int drawCallsBatched; + Buffer *quadIndexBuffer; + Capabilities capabilities; Deprecations deprecations; diff --git a/src/modules/graphics/ParticleSystem.cpp b/src/modules/graphics/ParticleSystem.cpp index 938a2485d..a8ae86fb4 100644 --- a/src/modules/graphics/ParticleSystem.cpp +++ b/src/modules/graphics/ParticleSystem.cpp @@ -95,7 +95,6 @@ ParticleSystem::ParticleSystem(Graphics *gfx, Texture *texture, uint32 size) , relativeRotation(false) , vertexAttributes(vertex::CommonFormat::XYf_STf_RGBAub, 0) , buffer(nullptr) - , quadIndices(gfx) { if (size == 0 || size > MAX_PARTICLES) throw love::Exception("Invalid ParticleSystem size."); @@ -157,7 +156,6 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p) , relativeRotation(p.relativeRotation) , vertexAttributes(p.vertexAttributes) , buffer(nullptr) - , quadIndices(p.quadIndices) { setBufferSize(maxParticles); } @@ -194,8 +192,6 @@ void ParticleSystem::createBuffers(size_t size) size_t bytes = sizeof(Vertex) * size * 4; buffer = gfx->newBuffer(bytes, nullptr, BUFFER_VERTEX, vertex::USAGE_STREAM, 0); - - quadIndices = QuadIndices(gfx); } catch (std::bad_alloc &) { @@ -1101,7 +1097,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m) vertex::Buffers vertexbuffers; vertexbuffers.set(0, buffer, 0); - quadIndices.draw(gfx, 0, pCount, vertexAttributes, vertexbuffers, texture); + gfx->drawQuads(0, pCount, vertexAttributes, vertexbuffers, texture); } bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out) diff --git a/src/modules/graphics/ParticleSystem.h b/src/modules/graphics/ParticleSystem.h index d21a9c327..7f83887ef 100644 --- a/src/modules/graphics/ParticleSystem.h +++ b/src/modules/graphics/ParticleSystem.h @@ -702,9 +702,6 @@ private: const vertex::Attributes vertexAttributes; Buffer *buffer; - // Vertex index buffer. - QuadIndices quadIndices; - static StringMap::Entry distributionsEntries[]; static StringMap distributions; diff --git a/src/modules/graphics/SpriteBatch.cpp b/src/modules/graphics/SpriteBatch.cpp index 3971ccf1d..7f07b373e 100644 --- a/src/modules/graphics/SpriteBatch.cpp +++ b/src/modules/graphics/SpriteBatch.cpp @@ -47,7 +47,6 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usag , color(255, 255, 255, 255) , color_active(false) , array_buf(nullptr) - , quad_indices(gfx) , range_start(-1) , range_count(-1) { @@ -394,7 +393,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) count = std::min(count, next - start); if (count > 0) - quad_indices.draw(gfx, start, count, attributes, buffers, texture); + gfx->drawQuads(start, count, attributes, buffers, texture); } } // graphics diff --git a/src/modules/graphics/SpriteBatch.h b/src/modules/graphics/SpriteBatch.h index 7bd844737..1e3a500ca 100644 --- a/src/modules/graphics/SpriteBatch.h +++ b/src/modules/graphics/SpriteBatch.h @@ -140,8 +140,7 @@ private: size_t vertex_stride; love::graphics::Buffer *array_buf; - QuadIndices quad_indices; - + std::unordered_map attached_attributes; int range_start; diff --git a/src/modules/graphics/Text.cpp b/src/modules/graphics/Text.cpp index 9640cd2d8..4d4a22bde 100644 --- a/src/modules/graphics/Text.cpp +++ b/src/modules/graphics/Text.cpp @@ -34,7 +34,6 @@ Text::Text(Graphics *gfx, Font *font, const std::vector &te : font(font) , vertexAttributes(Font::vertexFormat, 0) , vbo(nullptr) - , quadIndices(gfx) , vert_offset(0) , texture_cache_id((uint32) -1) { @@ -264,7 +263,7 @@ void Text::draw(Graphics *gfx, const Matrix4 &m) Graphics::TempTransform transform(gfx, m); for (const Font::DrawCommand &cmd : draw_commands) - quadIndices.draw(gfx, cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, cmd.texture); + gfx->drawQuads(cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, cmd.texture); } } // graphics diff --git a/src/modules/graphics/Text.h b/src/modules/graphics/Text.h index 8294ff55c..f55593a1f 100644 --- a/src/modules/graphics/Text.h +++ b/src/modules/graphics/Text.h @@ -89,7 +89,6 @@ private: vertex::Buffers vertexBuffers; Buffer *vbo; - QuadIndices quadIndices; std::vector draw_commands; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 72d24dd44..4d8c057f9 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -55,8 +55,7 @@ namespace opengl { Graphics::Graphics() - : quadIndices(nullptr) - , windowHasStencil(false) + : windowHasStencil(false) , mainVAO(0) { gl = OpenGL(); @@ -87,8 +86,6 @@ Graphics::Graphics() Graphics::~Graphics() { - if (quadIndices) - delete quadIndices; } const char *Graphics::getName() const @@ -226,13 +223,7 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b if (!Volatile::loadAll()) ::printf("Could not reload all volatile objects.\n"); - // Create a quad indices object owned by love.graphics, so at least one - // QuadIndices object is alive at all times while love.graphics is alive. - // This makes sure there aren't too many expensive destruction/creations of - // index buffer objects, since the shared index buffer used by QuadIndices - // objects is destroyed when the last object is destroyed. - if (quadIndices == nullptr) - quadIndices = new QuadIndices(this); + createQuadIndexBuffer(); // Restore the graphics state. restoreState(states.back()); @@ -354,6 +345,76 @@ void Graphics::draw(const DrawIndexedCommand &cmd) ++drawCalls; } +static inline void advanceVertexOffsets(const vertex::Attributes &attributes, vertex::Buffers &buffers, int vertexcount) +{ + // TODO: Figure out a better way to avoid touching the same buffer multiple + // times, if multiple attributes share the buffer. + uint32 touchedbuffers = 0; + + for (unsigned int i = 0; i < vertex::Attributes::MAX; i++) + { + if (!attributes.isEnabled(i)) + continue; + + auto &attrib = attributes.attribs[i]; + + uint32 bufferbit = 1u << attrib.bufferindex; + if ((touchedbuffers & bufferbit) == 0) + { + touchedbuffers |= bufferbit; + buffers.info[attrib.bufferindex].offset += attrib.stride * vertexcount; + } + } +} + +void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::Buffers &buffers, love::graphics::Texture *texture) +{ + const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX; + const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4; + + gl.prepareDraw(); + gl.bindTextureToUnit(texture, 0, false); + gl.setCullMode(CULL_NONE); + + gl.bindBuffer(BUFFER_INDEX, quadIndexBuffer->getHandle()); + + if (gl.isBaseVertexSupported()) + { + gl.setVertexAttributes(attributes, buffers); + + int basevertex = start; + + for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) + { + int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex); + + glDrawElementsBaseVertex(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0), basevertex); + ++drawCalls; + + basevertex += quadcount * 4; + } + } + else + { + vertex::Buffers bufferscopy = buffers; + if (start > 0) + advanceVertexOffsets(attributes, bufferscopy, start * 4); + + for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW) + { + gl.setVertexAttributes(attributes, bufferscopy); + + int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex); + + glDrawElements(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0)); + ++drawCalls; + + if (count > MAX_QUADS_PER_DRAW) + advanceVertexOffsets(attributes, bufferscopy, quadcount * 4); + } + } +} + static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/) { // Human-readable strings for the debug info. diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 46f1e67c1..e18ac1681 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -71,6 +71,7 @@ public: void draw(const DrawCommand &cmd) override; void draw(const DrawIndexedCommand &cmd) override; + void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::Buffers &buffers, Texture *texture) override; void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override; void clear(const std::vector &colors, OptionalInt stencil, OptionalDouble depth) override; @@ -127,7 +128,6 @@ private: void setDebug(bool enable); std::unordered_map framebufferObjects; - QuadIndices *quadIndices; bool windowHasStencil; GLuint mainVAO; diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 189195c9f..07020b2f5 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -95,6 +95,7 @@ OpenGL::OpenGL() : stats() , contextInitialized(false) , pixelShaderHighpSupported(false) + , baseVertexSupported(false) , maxAnisotropy(1.0f) , max2DTextureSize(0) , max3DTextureSize(0) @@ -375,6 +376,32 @@ void OpenGL::initOpenGLFunctions() fp_glCompressedTexSubImage3D = fp_glCompressedTexSubImage3DOES; fp_glFramebufferTexture3D = fp_glFramebufferTexture3DOES; } + + if (!GLAD_VERSION_3_2 && !GLAD_ES_VERSION_3_2 && !GLAD_ARB_draw_elements_base_vertex) + { + if (GLAD_OES_draw_elements_base_vertex) + { + fp_glDrawElementsBaseVertex = fp_glDrawElementsBaseVertexOES; + + if (GLAD_ES_VERSION_3_0) + { + fp_glDrawRangeElementsBaseVertex = fp_glDrawRangeElementsBaseVertexOES; + fp_glDrawElementsInstancedBaseVertex = fp_glDrawElementsInstancedBaseVertexOES; + } + + } + else if (GLAD_EXT_draw_elements_base_vertex) + { + fp_glDrawElementsBaseVertex = fp_glDrawElementsBaseVertexEXT; + + if (GLAD_ES_VERSION_3_0) + { + fp_glDrawRangeElementsBaseVertex = fp_glDrawRangeElementsBaseVertexEXT; + fp_glDrawElementsInstancedBaseVertex = fp_glDrawElementsInstancedBaseVertexEXT; + } + + } + } } void OpenGL::initMaxValues() @@ -389,6 +416,9 @@ void OpenGL::initMaxValues() else pixelShaderHighpSupported = true; + baseVertexSupported = GLAD_VERSION_3_2 || GLAD_ES_VERSION_3_2 || GLAD_ARB_draw_elements_base_vertex + || GLAD_OES_draw_elements_base_vertex || GLAD_EXT_draw_elements_base_vertex; + // We'll need this value to clamp anisotropy. if (GLAD_EXT_texture_filter_anisotropic) glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy); @@ -1177,6 +1207,11 @@ bool OpenGL::isSamplerLODBiasSupported() const return GLAD_VERSION_1_4; } +bool OpenGL::isBaseVertexSupported() const +{ + return baseVertexSupported; +} + int OpenGL::getMax2DTextureSize() const { return std::max(max2DTextureSize, 1); diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 998db4032..22b216409 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -334,6 +334,7 @@ public: bool isInstancingSupported() const; bool isDepthCompareSampleSupported() const; bool isSamplerLODBiasSupported() const; + bool isBaseVertexSupported() const; /** * Returns the maximum supported width or height of a texture. @@ -413,6 +414,8 @@ private: bool contextInitialized; bool pixelShaderHighpSupported; + bool baseVertexSupported; + float maxAnisotropy; float maxLODBias; int max2DTextureSize;