Add functions for indirect draws and compute dispatches.

Resolves #1879.

- Add an 'indirectdraw' boolean field to the graphics feature support table returned by love.graphics.getSupported. This is almost always supported when compute shaders are supported, except on a few older phones.

- Add an 'indirectarguments' boolean field to the settings table in love.graphics.newBuffer.

- Add love.graphics.dispatchIndirect(shader, argumentsbuffer [, argumentsindex = 1]).
    The compute dispatch's threadgroup width, height, and depth values are fetched from the buffer (as 3 uints) instead of coming from function parameters.

- Add love.graphics.drawIndirect(mesh, argumentsbuffer, argumentsindex, x, y, ....).
    Vertex or index count, instance count, and other related parameters for drawing the mesh are fetched from the buffer (as 4 or 5 uints depending on whether the mesh has an index buffer) instead of coming from function parameters. It's usually a good idea to keep parameters other than the instance count in sync with what the mesh should be using.

- Add love.graphics.drawFromShaderIndirect(drawmode, argumentsbuffer [, argumentsindex = 1] [, maintexture = nil]) and drawFromShaderIndirect(indexbuffer, argumentsbuffer [, argumentsindex] [, maintexture = nil]).
    Vertex or index count, instance count, and other related parameters are fetched from the buffer as 4 or 5 uints, as above.

For the dispatch indirect arguments buffer, it has to have 3 uint32 elements: { uint threadgroupsX, uint threadgroupsY, uint threadgroupsZ }.

For non-indexed draws, the arguments buffer has to have 4 uint32 elements: { uint vertexCount, uint instanceCount, uint baseVertex, uint baseInstance }. Note that baseInstance should always be set to 0 as many drivers don't support non-zero values.

For draws which use an index buffer, the arguments buffer has to have 5 uint32 elements: { uint indexCount, uint instanceCount, uint firstIndex, uint baseVertex, uint baseInstance }. As above, the baseInstance value should always be 0.

A buffer can be created to have an array of those structures, which can be used with the argumentsindex parameter of the Indirect dispatch/draw functions.
This commit is contained in:
Sasha Szpakowski
2023-02-05 15:55:19 -04:00
parent d3747cebb1
commit 2178b634c8
17 changed files with 460 additions and 59 deletions
+31 -4
View File
@@ -525,17 +525,38 @@ bool Mesh::getDrawRange(int &start, int &count) const
void Mesh::draw(Graphics *gfx, const love::Matrix4 &m)
{
drawInstanced(gfx, m, 1);
drawInternal(gfx, m, 1, nullptr, 0);
}
void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
{
if (vertexCount <= 0 || instancecount <= 0)
drawInternal(gfx, m, instancecount, nullptr, 0);
}
void Mesh::drawIndirect(Graphics *gfx, const Matrix4 &m, Buffer *indirectargs, int argsindex)
{
drawInternal(gfx, m, 0, indirectargs, argsindex);
}
void Mesh::drawInternal(Graphics *gfx, const Matrix4 &m, int instancecount, Buffer *indirectargs, int argsindex)
{
if (vertexCount <= 0 || (instancecount <= 0 && indirectargs == nullptr))
return;
if (instancecount > 1 && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING])
throw love::Exception("Instancing is not supported on this system.");
if (indirectargs != nullptr)
{
if (primitiveType == PRIMITIVE_TRIANGLE_FAN)
throw love::Exception("The fan draw mode is not supported in indirect draws.");
if (useIndexBuffer && indexBuffer != nullptr)
gfx->validateIndirectArgsBuffer(Graphics::INDIRECT_ARGS_DRAW_INDICES, indirectargs, argsindex);
else
gfx->validateIndirectArgsBuffer(Graphics::INDIRECT_ARGS_DRAW_VERTICES, indirectargs, argsindex);
}
// Some graphics backends don't natively support triangle fans. So we'd
// have to emulate them with triangles plus an index buffer... which doesn't
// work so well when there's already a custom index buffer.
@@ -616,7 +637,7 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
}
}
if (indexbuffer != nullptr && indexcount > 0)
if (indexbuffer != nullptr && (indexcount > 0 || indirectargs != nullptr))
{
Range r(0, indexcount);
if (range.isValid())
@@ -633,10 +654,13 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
cmd.indexBufferOffset = r.getOffset() * indexbuffer->getArrayStride();
cmd.indexCount = (int) r.getSize();
cmd.indirectBuffer = indirectargs;
cmd.indirectBufferOffset = argsindex * (indirectargs != nullptr ? indirectargs->getArrayStride() : 0);
if (cmd.indexCount > 0)
gfx->draw(cmd);
}
else if (vertexCount > 0)
else if (vertexCount > 0 || indirectargs != nullptr)
{
Range r(0, vertexCount);
if (range.isValid())
@@ -651,6 +675,9 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount)
cmd.texture = texture;
cmd.cullMode = gfx->getMeshCullMode();
cmd.indirectBuffer = indirectargs;
cmd.indirectBufferOffset = argsindex * (indirectargs != nullptr ? indirectargs->getArrayStride() : 0);
if (cmd.vertexCount > 0)
gfx->draw(cmd);
}