Skip to content

Commit

Permalink
mesh and spritebatch attributes reuse buffer bindings when possible.
Browse files Browse the repository at this point in the history
  • Loading branch information
slime73 committed Sep 11, 2024
1 parent 2c28871 commit 0a552ef
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 72 deletions.
95 changes: 58 additions & 37 deletions src/modules/graphics/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand All @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions src/modules/graphics/Mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ class Mesh : public Drawable
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;
Expand Down Expand Up @@ -226,6 +228,8 @@ class Mesh : public Drawable

StrongRef<Texture> texture;

BufferBindings bufferBindings;

}; // Mesh

} // graphics
Expand Down
91 changes: 56 additions & 35 deletions src/modules/graphics/SpriteBatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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();
Expand All @@ -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);

Expand All @@ -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);
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/modules/graphics/SpriteBatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ class SpriteBatch : public Drawable

private:

void updateVertexAttributes(Graphics *gfx);

struct AttachedAttribute
{
StrongRef<Buffer> buffer;
Expand Down Expand Up @@ -138,6 +140,7 @@ class SpriteBatch : public Drawable
size_t vertex_stride;

VertexAttributesID attributesID;
BufferBindings bufferBindings;

StrongRef<love::graphics::Buffer> array_buf;
uint8 *vertex_data;
Expand Down

0 comments on commit 0a552ef

Please sign in to comment.