mesh and spritebatch attributes reuse buffer bindings when possible.

This commit is contained in:
Sasha Szpakowski
2024-09-11 18:46:21 -03:00
parent 2c288715d4
commit 0a552ef352
4 changed files with 121 additions and 72 deletions
+58 -37
View File
@@ -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();
+4
View File
@@ -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<Buffer::DataMember> vertexFormat;
@@ -226,6 +228,8 @@ private:
StrongRef<Texture> texture;
BufferBindings bufferBindings;
}; // Mesh
} // graphics
+56 -35
View File
@@ -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);
}
}
+3
View File
@@ -108,6 +108,8 @@ public:
private:
void updateVertexAttributes(Graphics *gfx);
struct AttachedAttribute
{
StrongRef<Buffer> buffer;
@@ -138,6 +140,7 @@ private:
size_t vertex_stride;
VertexAttributesID attributesID;
BufferBindings bufferBindings;
StrongRef<love::graphics::Buffer> array_buf;
uint8 *vertex_data;