mirror of
https://github.com/love2d/love.git
synced 2026-08-17 19:23:38 +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:
@@ -78,8 +78,10 @@ Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const st
|
||||
mapUsage = BUFFERUSAGE_VERTEX;
|
||||
else if (usageFlags & BUFFERUSAGEFLAG_INDEX)
|
||||
mapUsage = BUFFERUSAGE_INDEX;
|
||||
else if (usageFlags & BUFFERUSAGEFLAG_SHADER_STORAGE)
|
||||
else if (usageFlags & BUFFERUSAGEFLAG_SHADER_STORAGE)
|
||||
mapUsage = BUFFERUSAGE_SHADER_STORAGE;
|
||||
else if (usageFlags & BUFFERUSAGEFLAG_INDIRECT_ARGUMENTS)
|
||||
mapUsage = BUFFERUSAGE_INDIRECT_ARGUMENTS;
|
||||
|
||||
target = OpenGL::getGLBufferType(mapUsage);
|
||||
|
||||
|
||||
@@ -505,7 +505,6 @@ void Graphics::setActive(bool enable)
|
||||
|
||||
static bool computeDispatchBarriers(Shader *shader, GLbitfield &preDispatchBarriers, GLbitfield &postDispatchBarriers)
|
||||
{
|
||||
// TODO: handle indirect argument buffer types, when those are added.
|
||||
for (auto buffer : shader->getActiveWritableStorageBuffers())
|
||||
{
|
||||
if (buffer == nullptr)
|
||||
@@ -521,6 +520,10 @@ static bool computeDispatchBarriers(Shader *shader, GLbitfield &preDispatchBarri
|
||||
postDispatchBarriers |= GL_SHADER_STORAGE_BARRIER_BIT;
|
||||
}
|
||||
|
||||
// TODO: does this need a pre dispatch barrier too?
|
||||
if (usage & BUFFERUSAGEFLAG_INDIRECT_ARGUMENTS)
|
||||
postDispatchBarriers |= GL_COMMAND_BARRIER_BIT;
|
||||
|
||||
if (usage & BUFFERUSAGEFLAG_TEXEL)
|
||||
postDispatchBarriers |= GL_TEXTURE_FETCH_BARRIER_BIT;
|
||||
|
||||
@@ -554,10 +557,9 @@ static bool computeDispatchBarriers(Shader *shader, GLbitfield &preDispatchBarri
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Graphics::dispatch(int x, int y, int z)
|
||||
bool Graphics::dispatch(love::graphics::Shader *s, int x, int y, int z)
|
||||
{
|
||||
// Set by higher level code before calling dispatch(x, y, z).
|
||||
auto shader = (Shader *) Shader::current;
|
||||
auto shader = (Shader *) s;
|
||||
|
||||
GLbitfield preDispatchBarriers = 0;
|
||||
GLbitfield postDispatchBarriers = 0;
|
||||
@@ -584,6 +586,33 @@ bool Graphics::dispatch(int x, int y, int z)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Graphics::dispatch(love::graphics::Shader *s, love::graphics::Buffer *indirectargs, size_t argsoffset)
|
||||
{
|
||||
auto shader = (Shader *) s;
|
||||
|
||||
GLbitfield preDispatchBarriers = 0;
|
||||
GLbitfield postDispatchBarriers = 0;
|
||||
|
||||
if (!computeDispatchBarriers(shader, preDispatchBarriers, postDispatchBarriers))
|
||||
return false;
|
||||
|
||||
if (preDispatchBarriers != 0)
|
||||
glMemoryBarrier(preDispatchBarriers);
|
||||
|
||||
// Note: OpenGL has separate bind points for draw versus dispatch indirect
|
||||
// buffers. Our gl.bindBuffer wrapper uses the draw bind point, so we can't
|
||||
// use it here.
|
||||
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, (GLuint)indirectargs->getHandle());
|
||||
glDispatchComputeIndirect(argsoffset);
|
||||
|
||||
// Not as (theoretically) efficient as issuing the barrier right before
|
||||
// they're used later, but much less complicated.
|
||||
if (postDispatchBarriers != 0)
|
||||
glMemoryBarrier(postDispatchBarriers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Graphics::draw(const DrawCommand &cmd)
|
||||
{
|
||||
gl.prepareDraw(this);
|
||||
@@ -593,7 +622,12 @@ void Graphics::draw(const DrawCommand &cmd)
|
||||
|
||||
GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType);
|
||||
|
||||
if (cmd.instanceCount > 1)
|
||||
if (cmd.indirectBuffer != nullptr)
|
||||
{
|
||||
gl.bindBuffer(BUFFERUSAGE_INDIRECT_ARGUMENTS, (GLuint) cmd.indirectBuffer->getHandle());
|
||||
glDrawArraysIndirect(glprimitivetype, BUFFER_OFFSET(cmd.indirectBufferOffset));
|
||||
}
|
||||
else if (cmd.instanceCount > 1)
|
||||
glDrawArraysInstanced(glprimitivetype, cmd.vertexStart, cmd.vertexCount, cmd.instanceCount);
|
||||
else
|
||||
glDrawArrays(glprimitivetype, cmd.vertexStart, cmd.vertexCount);
|
||||
@@ -614,7 +648,14 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
|
||||
|
||||
gl.bindBuffer(BUFFERUSAGE_INDEX, cmd.indexBuffer->getHandle());
|
||||
|
||||
if (cmd.instanceCount > 1)
|
||||
if (cmd.indirectBuffer != nullptr)
|
||||
{
|
||||
// Note: OpenGL doesn't support indirect indexed draws with a non-zero
|
||||
// index buffer offset.
|
||||
gl.bindBuffer(BUFFERUSAGE_INDIRECT_ARGUMENTS, (GLuint) cmd.indirectBuffer->getHandle());
|
||||
glDrawElementsIndirect(glprimitivetype, gldatatype, BUFFER_OFFSET(cmd.indirectBufferOffset));
|
||||
}
|
||||
else if (cmd.instanceCount > 1)
|
||||
glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount);
|
||||
else
|
||||
glDrawElements(glprimitivetype, cmd.indexCount, gldatatype, gloffset);
|
||||
@@ -1662,7 +1703,8 @@ void Graphics::initCapabilities()
|
||||
capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = gl.isCopyTextureToBufferSupported();
|
||||
capabilities.features[FEATURE_COPY_RENDER_TARGET_TO_BUFFER] = gl.isCopyRenderTargetToBufferSupported();
|
||||
capabilities.features[FEATURE_MIPMAP_RANGE] = GLAD_VERSION_1_2 || GLAD_ES_VERSION_3_0;
|
||||
static_assert(FEATURE_MAX_ENUM == 18, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
|
||||
capabilities.features[FEATURE_INDIRECT_DRAW] = capabilities.features[FEATURE_GLSL4];
|
||||
static_assert(FEATURE_MAX_ENUM == 19, "Graphics::initCapabilities must be updated when adding a new graphics feature!");
|
||||
|
||||
capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize();
|
||||
capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize();
|
||||
|
||||
@@ -70,7 +70,8 @@ public:
|
||||
|
||||
void setActive(bool active) override;
|
||||
|
||||
bool dispatch(int x, int y, int z) override;
|
||||
bool dispatch(love::graphics::Shader *shader, int x, int y, int z) override;
|
||||
bool dispatch(love::graphics::Shader *shader, love::graphics::Buffer *indirectargs, size_t argsoffset) override;
|
||||
|
||||
void draw(const DrawCommand &cmd) override;
|
||||
void draw(const DrawIndexedCommand &cmd) override;
|
||||
|
||||
@@ -667,6 +667,7 @@ GLenum OpenGL::getGLBufferType(BufferUsage usage)
|
||||
case BUFFERUSAGE_INDEX: return GL_ELEMENT_ARRAY_BUFFER;
|
||||
case BUFFERUSAGE_TEXEL: return GL_TEXTURE_BUFFER;
|
||||
case BUFFERUSAGE_SHADER_STORAGE: return GL_SHADER_STORAGE_BUFFER;
|
||||
case BUFFERUSAGE_INDIRECT_ARGUMENTS: return GL_DRAW_INDIRECT_BUFFER;
|
||||
case BUFFERUSAGE_MAX_ENUM: return GL_ZERO;
|
||||
}
|
||||
|
||||
@@ -1491,6 +1492,8 @@ bool OpenGL::isBufferUsageSupported(BufferUsage usage) const
|
||||
return GLAD_VERSION_3_1 || GLAD_ES_VERSION_3_2;
|
||||
case BUFFERUSAGE_SHADER_STORAGE:
|
||||
return (GLAD_VERSION_4_3 && isCoreProfile()) || GLAD_ES_VERSION_3_1;
|
||||
case BUFFERUSAGE_INDIRECT_ARGUMENTS:
|
||||
return (GLAD_VERSION_4_0 && isCoreProfile()) || GLAD_ES_VERSION_3_1;
|
||||
case BUFFERUSAGE_MAX_ENUM:
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user