From 0a552ef3520eefcaa32786eab528a21c3cce5919 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Wed, 11 Sep 2024 18:46:21 -0300 Subject: [PATCH] mesh and spritebatch attributes reuse buffer bindings when possible. --- src/modules/graphics/Mesh.cpp | 95 +++++++++++++++++----------- src/modules/graphics/Mesh.h | 4 ++ src/modules/graphics/SpriteBatch.cpp | 91 ++++++++++++++++---------- src/modules/graphics/SpriteBatch.h | 3 + 4 files changed, 121 insertions(+), 72 deletions(-) diff --git a/src/modules/graphics/Mesh.cpp b/src/modules/graphics/Mesh.cpp index 0d205ac0c..ef1156982 100644 --- a/src/modules/graphics/Mesh.cpp +++ b/src/modules/graphics/Mesh.cpp @@ -660,6 +660,58 @@ bool Mesh::getDrawRange(int &start, int &count) const return true; } +void Mesh::updateVertexAttributes(Graphics *gfx) +{ + VertexAttributes attributes; + BufferBindings &buffers = bufferBindings; + + int activebuffers = 0; + + for (const auto &attrib : attachedAttributes) + { + if (!attrib.enabled) + continue; + + Buffer *buffer = attrib.buffer.get(); + int bindinglocation = attrib.bindingLocation; + + // Query the index from the shader as a fallback to support old code that + // hasn't set a binding location. + if (bindinglocation < 0 && Shader::current) + bindinglocation = Shader::current->getVertexAttributeIndex(attrib.name); + + if (bindinglocation >= 0) + { + const auto &member = buffer->getDataMember(attrib.indexInBuffer); + + uint16 offset = (uint16)member.offset; + uint16 stride = (uint16)buffer->getArrayStride(); + size_t bufferoffset = (size_t)stride * attrib.startArrayIndex; + + int bufferindex = activebuffers; + + for (int i = 0; i < activebuffers; i++) + { + if (buffers.info[i].buffer == buffer && buffers.info[i].offset == bufferoffset + && attributes.bufferLayouts[i].stride == stride && attributes.getBufferStep(i) == attrib.step) + { + bufferindex = i; + break; + } + } + + attributes.set(bindinglocation, member.decl.format, offset, bufferindex); + attributes.setBufferLayout(bufferindex, stride, attrib.step); + + buffers.set(bufferindex, buffer, bufferoffset); + + activebuffers = std::max(activebuffers, bufferindex + 1); + } + } + + attributesID = gfx->registerVertexAttributes(attributes); +} + void Mesh::draw(Graphics *gfx, const love::Matrix4 &m) { drawInternal(gfx, m, 1, nullptr, 0); @@ -709,53 +761,22 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff bool attributesIDneedsupdate = !attributesID.isValid(); - VertexAttributes attributes; - BufferBindings buffers; - - int activebuffers = 0; - for (const auto &attrib : attachedAttributes) { if (!attrib.enabled) continue; - Buffer *buffer = attrib.buffer.get(); - int bindinglocation = attrib.bindingLocation; + if (attrib.mesh.get()) + attrib.mesh->flush(); // Query the index from the shader as a fallback to support old code that // hasn't set a binding location. - if (bindinglocation < 0 && Shader::current) - { - bindinglocation = Shader::current->getVertexAttributeIndex(attrib.name); + if (attrib.bindingLocation < 0) attributesIDneedsupdate = true; - } - - if (bindinglocation >= 0) - { - if (attrib.mesh.get()) - attrib.mesh->flush(); - - const auto &member = buffer->getDataMember(attrib.indexInBuffer); - - uint16 offset = (uint16) member.offset; - uint16 stride = (uint16) buffer->getArrayStride(); - size_t bufferoffset = (size_t) stride * attrib.startArrayIndex; - - attributes.set(bindinglocation, member.decl.format, offset, activebuffers); - attributes.setBufferLayout(activebuffers, stride, attrib.step); - - // TODO: Ideally we want to reuse buffers with the same stride+step. - buffers.set(activebuffers, buffer, bufferoffset); - activebuffers++; - } } - // Not supported on all platforms or GL versions, I believe. - if ((attributes.enableBits & ~(ATTRIBFLAG_TEXCOORD | ATTRIBFLAG_COLOR)) == 0) - throw love::Exception("Mesh must have an enabled VertexPosition or custom attribute to be drawn."); - if (attributesIDneedsupdate) - attributesID = gfx->registerVertexAttributes(attributes); + updateVertexAttributes(gfx); Graphics::TempTransform transform(gfx, m); @@ -782,7 +803,7 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff if (range.isValid()) r.intersect(range); - Graphics::DrawIndexedCommand cmd(attributesID, &buffers, indexbuffer); + Graphics::DrawIndexedCommand cmd(attributesID, &bufferBindings, indexbuffer); cmd.primitiveType = primitiveType; cmd.indexType = indexDataType; @@ -805,7 +826,7 @@ void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buff if (range.isValid()) r.intersect(range); - Graphics::DrawCommand cmd(attributesID, &buffers); + Graphics::DrawCommand cmd(attributesID, &bufferBindings); cmd.primitiveType = primitiveType; cmd.vertexStart = (int) r.getOffset(); diff --git a/src/modules/graphics/Mesh.h b/src/modules/graphics/Mesh.h index 1276a5aad..6552ca393 100644 --- a/src/modules/graphics/Mesh.h +++ b/src/modules/graphics/Mesh.h @@ -196,6 +196,8 @@ private: int getAttachedAttributeIndex(int bindingLocation) const; void finalizeAttribute(BufferAttribute &attrib) const; + void updateVertexAttributes(Graphics *gfx); + void drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buffer *indirectargs, int argsindex); std::vector vertexFormat; @@ -226,6 +228,8 @@ private: StrongRef texture; + BufferBindings bufferBindings; + }; // Mesh } // graphics diff --git a/src/modules/graphics/SpriteBatch.cpp b/src/modules/graphics/SpriteBatch.cpp index c42cf38d5..5af6492fa 100644 --- a/src/modules/graphics/SpriteBatch.cpp +++ b/src/modules/graphics/SpriteBatch.cpp @@ -321,6 +321,57 @@ bool SpriteBatch::getDrawRange(int &start, int &count) const return true; } +void SpriteBatch::updateVertexAttributes(Graphics *gfx) +{ + VertexAttributes attributes; + BufferBindings &buffers = bufferBindings; + + buffers.set(0, array_buf, 0); + attributes.setCommonFormat(vertex_format, 0); + + int activebuffers = 1; + + for (const auto &it : attached_attributes) + { + Buffer *buffer = it.second.buffer.get(); + + int bindingindex = it.second.bindingIndex; + + // If the attribute is one of the LOVE-defined ones, use the constant + // attribute index for it, otherwise query the index from the shader. + if (bindingindex < 0 && Shader::current) + bindingindex = Shader::current->getVertexAttributeIndex(it.first); + + if (bindingindex >= 0) + { + const auto &member = buffer->getDataMember(it.second.index); + + uint16 offset = (uint16) buffer->getMemberOffset(it.second.index); + uint16 stride = (uint16) buffer->getArrayStride(); + + int bufferindex = activebuffers; + + for (int i = 1; i < activebuffers; i++) + { + if (buffers.info[i].buffer == buffer && attributes.bufferLayouts[i].stride == stride) + { + bufferindex = i; + break; + } + } + + attributes.set(bindingindex, member.decl.format, offset, bufferindex); + attributes.setBufferLayout(bufferindex, stride); + + buffers.set(bufferindex, buffer, 0); + + activebuffers = std::max(activebuffers, bufferindex + 1); + } + } + + attributesID = gfx->registerVertexAttributes(attributes); +} + void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) { if (next == 0) @@ -347,16 +398,6 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) bool attributesIDneedsupdate = !attributesID.isValid(); - VertexAttributes attributes; - BufferBindings buffers; - - { - buffers.set(0, array_buf, 0); - attributes.setCommonFormat(vertex_format, 0); - } - - int activebuffers = 1; - for (const auto &it : attached_attributes) { Buffer *buffer = it.second.buffer.get(); @@ -366,37 +407,17 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) if (buffer->getArrayLength() < (size_t) next * 4) throw love::Exception("Buffer with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str()); - int bindingindex = it.second.bindingIndex; - // If the attribute is one of the LOVE-defined ones, use the constant // attribute index for it, otherwise query the index from the shader. - if (bindingindex < 0 && Shader::current) - { - bindingindex = Shader::current->getVertexAttributeIndex(it.first); + if (it.second.bindingIndex < 0) attributesIDneedsupdate = true; - } - if (bindingindex >= 0) - { - if (it.second.mesh.get()) - it.second.mesh->flush(); - - const auto &member = buffer->getDataMember(it.second.index); - - uint16 offset = (uint16) buffer->getMemberOffset(it.second.index); - uint16 stride = (uint16) buffer->getArrayStride(); - - attributes.set(bindingindex, member.decl.format, offset, activebuffers); - attributes.setBufferLayout(activebuffers, stride); - - // TODO: We should reuse buffer bindings with the same buffer+stride+step. - buffers.set(activebuffers, buffer, 0); - activebuffers++; - } + if (it.second.mesh.get()) + it.second.mesh->flush(); } if (attributesIDneedsupdate) - attributesID = gfx->registerVertexAttributes(attributes); + updateVertexAttributes(gfx); Graphics::TempTransform transform(gfx, m); @@ -411,7 +432,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) if (count > 0) { Texture *tex = gfx->getTextureOrDefaultForActiveShader(texture); - gfx->drawQuads(start, count, attributesID, buffers, tex); + gfx->drawQuads(start, count, attributesID, bufferBindings, tex); } } diff --git a/src/modules/graphics/SpriteBatch.h b/src/modules/graphics/SpriteBatch.h index 978664450..d9ec35934 100644 --- a/src/modules/graphics/SpriteBatch.h +++ b/src/modules/graphics/SpriteBatch.h @@ -108,6 +108,8 @@ public: private: + void updateVertexAttributes(Graphics *gfx); + struct AttachedAttribute { StrongRef buffer; @@ -138,6 +140,7 @@ private: size_t vertex_stride; VertexAttributesID attributesID; + BufferBindings bufferBindings; StrongRef array_buf; uint8 *vertex_data;