mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
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:
@@ -1542,7 +1542,49 @@ void Graphics::copyBufferToTexture(Buffer *source, Texture *dest, size_t sourceo
|
||||
dest->copyFromBuffer(source, sourceoffset, sourcewidth, size, slice, mipmap, rect);
|
||||
}
|
||||
|
||||
void Graphics::dispatchThreadgroups(Shader* shader, int x, int y, int z)
|
||||
static const char *getIndirectArgsTypeName(Graphics::IndirectArgsType argstype)
|
||||
{
|
||||
switch (argstype)
|
||||
{
|
||||
case Graphics::INDIRECT_ARGS_DISPATCH: return "Compute shader threadgroup argument data";
|
||||
case Graphics::INDIRECT_ARGS_DRAW_VERTICES: return "Draw vertices argument data";
|
||||
case Graphics::INDIRECT_ARGS_DRAW_INDICES: return "Draw indices argument data";
|
||||
}
|
||||
|
||||
return "(Unknown argument data)";
|
||||
}
|
||||
|
||||
void Graphics::validateIndirectArgsBuffer(IndirectArgsType argstype, Buffer *indirectargs, int argsindex)
|
||||
{
|
||||
if (!capabilities.features[FEATURE_INDIRECT_DRAW])
|
||||
throw love::Exception("Indirect draws and compute dispatches are not supported on this system.");
|
||||
|
||||
if ((indirectargs->getUsageFlags() & BUFFERUSAGEFLAG_INDIRECT_ARGUMENTS) == 0)
|
||||
throw love::Exception("The given Buffer must be created with the indirectarguments usage flag set, to be used for indirect arguments.");
|
||||
|
||||
if (argsindex < 0)
|
||||
throw love::Exception("The given indirect argument index cannot be negative.");
|
||||
|
||||
size_t argelements = 0;
|
||||
if (argstype == INDIRECT_ARGS_DISPATCH)
|
||||
argelements = 3;
|
||||
else if (argstype == INDIRECT_ARGS_DRAW_VERTICES)
|
||||
argelements = 4;
|
||||
else if (argstype == INDIRECT_ARGS_DRAW_INDICES)
|
||||
argelements = 5;
|
||||
|
||||
size_t totalmembers = indirectargs->getArrayLength() * indirectargs->getDataMembers().size();
|
||||
|
||||
if (totalmembers % argelements != 0)
|
||||
throw love::Exception("%s requires the given indirect argument Buffer to have a multiple of %ld int or uint values.", getIndirectArgsTypeName(argstype), argelements);
|
||||
|
||||
size_t argsoffset = argsindex * indirectargs->getArrayStride();
|
||||
|
||||
if (indirectargs->getSize() < argsoffset + sizeof(uint32) * argelements)
|
||||
throw love::Exception("The given index into the indirect argument Buffer does not fit within the Buffer's size.");
|
||||
}
|
||||
|
||||
void Graphics::dispatchThreadgroups(Shader *shader, int x, int y, int z)
|
||||
{
|
||||
if (!shader->hasStage(SHADERSTAGE_COMPUTE))
|
||||
throw love::Exception("Only compute shaders can have threads dispatched.");
|
||||
@@ -1562,7 +1604,28 @@ void Graphics::dispatchThreadgroups(Shader* shader, int x, int y, int z)
|
||||
auto prevshader = Shader::current;
|
||||
shader->attach();
|
||||
|
||||
bool success = dispatch(x, y, z);
|
||||
bool success = dispatch(shader, x, y, z);
|
||||
|
||||
if (prevshader != nullptr)
|
||||
prevshader->attach();
|
||||
|
||||
if (!success)
|
||||
throw love::Exception("Compute shader must have resources bound to all writable texture and buffer variables.");
|
||||
}
|
||||
|
||||
void Graphics::dispatchIndirect(Shader *shader, Buffer *indirectargs, int argsindex)
|
||||
{
|
||||
if (!shader->hasStage(SHADERSTAGE_COMPUTE))
|
||||
throw love::Exception("Only compute shaders can have threads dispatched.");
|
||||
|
||||
validateIndirectArgsBuffer(INDIRECT_ARGS_DISPATCH, indirectargs, argsindex);
|
||||
|
||||
flushBatchedDraws();
|
||||
|
||||
auto prevshader = Shader::current;
|
||||
shader->attach();
|
||||
|
||||
bool success = dispatch(shader, indirectargs, argsindex * indirectargs->getArrayStride());
|
||||
|
||||
if (prevshader != nullptr)
|
||||
prevshader->attach();
|
||||
@@ -1819,6 +1882,11 @@ void Graphics::drawInstanced(Mesh *mesh, const Matrix4 &m, int instancecount)
|
||||
mesh->drawInstanced(this, m, instancecount);
|
||||
}
|
||||
|
||||
void Graphics::drawIndirect(Mesh *mesh, const Matrix4 &m, Buffer *indirectargs, int argsindex)
|
||||
{
|
||||
mesh->drawIndirect(this, m, indirectargs, argsindex);
|
||||
}
|
||||
|
||||
void Graphics::drawFromShader(PrimitiveType primtype, int vertexcount, int instancecount, Texture *maintexture)
|
||||
{
|
||||
if (primtype == PRIMITIVE_TRIANGLE_FAN && vertexcount > LOVE_UINT16_MAX)
|
||||
@@ -1899,6 +1967,61 @@ void Graphics::drawFromShader(Buffer *indexbuffer, int indexcount, int instancec
|
||||
draw(cmd);
|
||||
}
|
||||
|
||||
void Graphics::drawFromShaderIndirect(PrimitiveType primtype, Buffer *indirectargs, int argsindex, Texture *maintexture)
|
||||
{
|
||||
flushBatchedDraws();
|
||||
|
||||
if (primtype == PRIMITIVE_TRIANGLE_FAN)
|
||||
throw love::Exception("The fan draw mode is not supported in indirect draws.");
|
||||
|
||||
if (Shader::isDefaultActive() || !Shader::current)
|
||||
throw love::Exception("drawFromShaderIndirect can only be used with a custom shader.");
|
||||
|
||||
validateIndirectArgsBuffer(INDIRECT_ARGS_DRAW_VERTICES, indirectargs, argsindex);
|
||||
|
||||
Shader::current->validateDrawState(primtype, maintexture);
|
||||
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
DrawCommand cmd(&attributes, &buffers);
|
||||
|
||||
cmd.primitiveType = primtype;
|
||||
cmd.indirectBuffer = indirectargs;
|
||||
cmd.indirectBufferOffset = argsindex * indirectargs->getArrayStride();
|
||||
cmd.texture = maintexture;
|
||||
|
||||
draw(cmd);
|
||||
}
|
||||
|
||||
void Graphics::drawFromShaderIndirect(Buffer *indexbuffer, Buffer *indirectargs, int argsindex, Texture *maintexture)
|
||||
{
|
||||
flushBatchedDraws();
|
||||
|
||||
if (!(indexbuffer->getUsageFlags() & BUFFERUSAGEFLAG_INDEX))
|
||||
throw love::Exception("The buffer passed to the indexed variant of drawFromShaderIndirect must be an index buffer.");
|
||||
|
||||
if (Shader::isDefaultActive() || !Shader::current)
|
||||
throw love::Exception("drawFromShaderIndirect can only be used with a custom shader.");
|
||||
|
||||
validateIndirectArgsBuffer(INDIRECT_ARGS_DRAW_INDICES, indirectargs, argsindex);
|
||||
|
||||
Shader::current->validateDrawState(PRIMITIVE_TRIANGLES, maintexture);
|
||||
|
||||
VertexAttributes attributes;
|
||||
BufferBindings buffers;
|
||||
|
||||
DrawIndexedCommand cmd(&attributes, &buffers, indexbuffer);
|
||||
|
||||
cmd.primitiveType = PRIMITIVE_TRIANGLES;
|
||||
cmd.indexType = getIndexDataType(indexbuffer->getDataMember(0).decl.format);
|
||||
cmd.indirectBuffer = indirectargs;
|
||||
cmd.indexBufferOffset = argsindex * indirectargs->getArrayStride();
|
||||
cmd.texture = maintexture;
|
||||
|
||||
draw(cmd);
|
||||
}
|
||||
|
||||
void Graphics::print(const std::vector<love::font::ColoredString> &str, const Matrix4 &m)
|
||||
{
|
||||
checkSetDefaultFont();
|
||||
@@ -2533,6 +2656,7 @@ STRINGMAP_CLASS_BEGIN(Graphics, Graphics::Feature, Graphics::FEATURE_MAX_ENUM, f
|
||||
{ "copytexturetobuffer", Graphics::FEATURE_COPY_TEXTURE_TO_BUFFER },
|
||||
{ "copyrendertargettobuffer", Graphics::FEATURE_COPY_RENDER_TARGET_TO_BUFFER },
|
||||
{ "mipmaprange", Graphics::FEATURE_MIPMAP_RANGE },
|
||||
{ "indirectdraw", Graphics::FEATURE_INDIRECT_DRAW },
|
||||
}
|
||||
STRINGMAP_CLASS_END(Graphics, Graphics::Feature, Graphics::FEATURE_MAX_ENUM, feature)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user