Browse Source

GL: implement indirect drawing and compute dispatch.

next
Vladimír Vondruš 3 months ago
parent
commit
1c1d3e5ee8
  1. 6
      doc/changelog.dox
  2. 6
      doc/opengl-mapping.dox
  3. 6
      doc/opengl-support.dox
  4. 39
      src/Magnum/GL/AbstractShaderProgram.cpp
  5. 264
      src/Magnum/GL/AbstractShaderProgram.h
  6. 3
      src/Magnum/GL/Buffer.cpp
  7. 16
      src/Magnum/GL/Buffer.h
  8. 4
      src/Magnum/GL/Extensions.h
  9. 8
      src/Magnum/GL/Implementation/BufferState.cpp
  10. 6
      src/Magnum/GL/Implementation/BufferState.h
  11. 66
      src/Magnum/GL/Mesh.cpp
  12. 8
      src/Magnum/GL/Mesh.h
  13. 216
      src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp
  14. 969
      src/Magnum/GL/Test/MeshGLTest.cpp

6
doc/changelog.dox

@ -197,7 +197,11 @@ See also:
- @webgl_extension{WEBGL,multi_draw}
- @webgl_extension{WEBGL,draw_instanced_base_vertex_base_instance}
- @webgl_extension{WEBGL,multi_draw_instanced_base_vertex_base_instance}
- Recognizing the @gl_extension{EXT,multi_draw_indirect} OpenGL ES extension
- Implemented @ref GL::AbstractShaderProgram::drawIndirect() and
@ref GL::AbstractShaderProgram::dispatchComputeIndirect() along with
recognizing the @gl_extension{EXT,multi_draw_indirect} and
@gl_extension{EXT,base_instance} OpenGL ES extensions (see
[mosra/magnum#687](https://github.com/mosra/magnum/discussions/687))
- Recognizing the @gl_extension2{EXT,memory_object,EXT_external_objects},
@gl_extension2{EXT,semaphore,EXT_external_objects},
@gl_extension2{EXT,memory_object_fd,EXT_external_objects_fd},

6
doc/opengl-mapping.dox

@ -140,9 +140,10 @@ OpenGL function | Matching API
@fn_gl{DetachShader} | |
@fn_gl{DispatchCompute} | @ref GL::AbstractShaderProgram::dispatchCompute()
@fn_gl_extension{DispatchComputeGroupSize,ARB,compute_variable_group_size} | |
@fn_gl{DispatchComputeIndirect} | |
@fn_gl{DispatchComputeIndirect} | @ref GL::AbstractShaderProgram::dispatchComputeIndirect()
@fn_gl{DrawArrays}, \n @fn_gl{DrawArraysInstanced}, \n @fn_gl{DrawArraysInstancedBaseInstance}, \n @fn_gl{DrawElements}, \n @fn_gl{DrawRangeElements}, \n @fn_gl{DrawElementsBaseVertex}, \n @fn_gl{DrawRangeElementsBaseVertex}, \n @fn_gl{DrawElementsInstanced}, \n @fn_gl{DrawElementsInstancedBaseInstance}, \n @fn_gl{DrawElementsInstancedBaseVertex}, \n @fn_gl{DrawElementsInstancedBaseVertexBaseInstance} | @ref GL::AbstractShaderProgram::drawTransformFeedback()
@fn_gl{DrawArraysIndirect}, \n @fn_gl{DrawElementsIndirect}, \n @fn_gl{MultiDrawArraysIndirect}, \n @fn_gl{MultiDrawElementsIndirect} | |
@fn_gl{DrawArraysIndirect}, \n @fn_gl{DrawElementsIndirect}, \n @fn_gl{MultiDrawArraysIndirect}, \n @fn_gl{MultiDrawElementsIndirect}, \n
@fn_gl{MultiDrawArraysIndirectCount}, \n @fn_gl{MultiDrawElementsIndirectCount} | @ref GL::AbstractShaderProgram::drawIndirect()
@fn_gl{DrawBuffer}, \n `glNamedFramebufferDrawBuffer()`, \n @fn_gl{DrawBuffers}, \n `glNamedFramebufferDrawBuffers()` | @ref GL::DefaultFramebuffer::mapForDraw(), \n @ref GL::Framebuffer::mapForDraw()
@fn_gl{DrawTransformFeedback}, \n @fn_gl{DrawTransformFeedbackInstanced}, \n @fn_gl{DrawTransformFeedbackStream}, \n @fn_gl{DrawTransformFeedbackStreamInstanced} | @ref GL::AbstractShaderProgram::drawTransformFeedback()
@ -308,7 +309,6 @@ OpenGL function | Matching API
@fn_gl_extension{MemoryObjectParameteriv,EXT,external_objects} | |
@fn_gl{MinSampleShading} | @ref GL::Renderer::setMinSampleShading()
@fn_gl{MultiDrawArrays}, \n @fn_gl{MultiDrawElements}, \n @fn_gl{MultiDrawElementsBaseVertex} | @ref GL::AbstractShaderProgram::draw(const Containers::Iterable<MeshView>&)
@fn_gl{MultiDrawArraysIndirectCount}, \n @fn_gl{MultiDrawElementsIndirectCount} | |
@subsection opengl-mapping-functions-o O

6
doc/opengl-support.dox

@ -143,7 +143,7 @@ GLSL 4.00 | done
@gl_extension{ARB,texture_cube_map_array} | done
@gl_extension{ARB,texture_gather} | missing limit queries
@gl_extension{ARB,texture_query_lod} | done (shading language only)
@gl_extension{ARB,draw_indirect} | |
@gl_extension{ARB,draw_indirect} | done
@gl_extension{ARB,gpu_shader5} | missing limit queries
@gl_extension{ARB,gpu_shader_fp64} | done
@gl_extension{ARB,shader_subroutine} | |
@ -204,7 +204,7 @@ GLSL 4.30 | done
@gl_extension{ARB,framebuffer_no_attachments} | done
@gl_extension{ARB,internalformat_query2} | only compressed texture block queries
@gl_extension{ARB,invalidate_subdata} | done
@gl_extension{ARB,multi_draw_indirect} | |
@gl_extension{ARB,multi_draw_indirect} | done
@gl_extension{ARB,program_interface_query} | |
@gl_extension{ARB,robust_buffer_access_behavior} | done (nothing to do)
@gl_extension{ARB,shader_image_size} | done (shading language only)
@ -262,7 +262,7 @@ GLSL 4.50 | done
Extension | Status
------------------------------------------- | ------
GLSL 4.60 | done
@gl_extension{ARB,indirect_parameters} | |
@gl_extension{ARB,indirect_parameters} | done
@gl_extension{ARB,shader_draw_parameters} | done (shading language only)
@gl_extension{ARB,shader_group_vote} | done (shading language only)
@gl_extension{ARB,pipeline_statistics_query} | done

39
src/Magnum/GL/AbstractShaderProgram.cpp

@ -511,7 +511,38 @@ AbstractShaderProgram& AbstractShaderProgram::draw(const Containers::Iterable<Me
return *this;
}
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
AbstractShaderProgram& AbstractShaderProgram::drawIndirect(Mesh& mesh, Buffer& indirectBuffer, const GLintptr indirectBufferOffset) {
use();
mesh.drawInternal(indirectBuffer, indirectBufferOffset);
return *this;
}
AbstractShaderProgram& AbstractShaderProgram::drawIndirect(Mesh& mesh, Buffer& indirectBuffer, const GLintptr indirectBufferOffset, const GLsizei count, const GLsizei stride) {
/* Nothing to draw, exit without touching any state */
if(!count)
return *this;
use();
mesh.drawInternal(indirectBuffer, indirectBufferOffset, count, stride);
return *this;
}
#endif
#ifndef MAGNUM_TARGET_GLES
AbstractShaderProgram& AbstractShaderProgram::drawIndirect(Mesh& mesh, Buffer& indirectBuffer, const GLintptr indirectBufferOffset, Buffer& countBuffer, GLintptr countBufferOffset, GLsizei maxCount, GLsizei stride) {
/* Nothing to draw, exit without touching any state */
if(!maxCount)
return *this;
use();
mesh.drawInternal(indirectBuffer, indirectBufferOffset, countBuffer, countBufferOffset, maxCount, stride);
return *this;
}
AbstractShaderProgram& AbstractShaderProgram::drawTransformFeedback(Mesh& mesh, TransformFeedback& xfb, UnsignedInt stream) {
/* Nothing to draw, exit without touching any state */
if(!mesh._instanceCount)
@ -543,6 +574,14 @@ AbstractShaderProgram& AbstractShaderProgram::dispatchCompute(const Vector3ui& w
glDispatchCompute(workgroupCount.x(), workgroupCount.y(), workgroupCount.z());
return *this;
}
AbstractShaderProgram& AbstractShaderProgram::dispatchComputeIndirect(Buffer& indirectBuffer, GLintptr indirectBufferOffset) {
use();
indirectBuffer.bindInternal(Buffer::TargetHint::DispatchIndirect);
glDispatchComputeIndirect(indirectBufferOffset);
return *this;
}
#endif
void AbstractShaderProgram::use(const GLuint id) {

264
src/Magnum/GL/AbstractShaderProgram.h

@ -29,7 +29,7 @@
*/
/** @file
* @brief Class @ref Magnum::GL::AbstractShaderProgram, macro @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(), @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION()
* @brief Class @ref Magnum::GL::AbstractShaderProgram, struct @ref Magnum::GL::DrawArraysIndirect, @ref Magnum::GL::DrawElementsIndirect, macro @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(), @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION()
*/
#include "Magnum/Tags.h"
@ -59,6 +59,111 @@ namespace Magnum { namespace GL {
namespace Implementation { struct ShaderProgramState; }
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
/**
@brief Non-indexed draw indirect command
@m_since_latest
Layout for a @ref Buffer containing indirect draw commands for *non-indexed*
meshes drawn with @ref AbstractShaderProgram::drawIndirect(Mesh&, Buffer&, GLintptr),
@ref AbstractShaderProgram::drawIndirect(Mesh&, Buffer&, GLintptr, GLsizei, GLsizei)
and @ref AbstractShaderProgram::drawIndirect(Mesh&, Buffer&, GLintptr, Buffer&, GLintptr, GLsizei, GLsizei).
See the @ref GL-AbstractShaderProgram-rendering-workflow-indirect "AbstractShaderProgram indirect draw documentation"
for more information and usage examples.
@requires_gl43 Extension @gl_extension{ARB,multi_draw_indirect}
@requires_gles31 OpenGL ES 3.1 and @gl_extension{EXT,multi_draw_indirect}
@requires_gles Indirect rendering isn't availabe in WebGL.
*/
struct DrawArraysIndirect {
/**
* @brief Vertex count
*
* Corresponds to the value passed to @ref Mesh::setCount().
*/
UnsignedInt count;
/**
* @brief Instance count
*
* Corresponds to the value passed to @ref Mesh::setInstanceCount().
*/
UnsignedInt instanceCount;
/**
* @brief Vertex offset
*
* Corresponds to the value passed to @ref Mesh::setBaseVertex().
*/
UnsignedInt vertexOffset;
/**
* @brief Instance offset
*
* Corresponds to the value passed to @ref Mesh::setBaseInstance().
* @requires_gl42 Extension @gl_extension{ARB,base_instance}, required to
* be set to `0` otherwise.
* @requires_es_extension Extension @gl_extension{EXT,base_instance},
* required to be set to `0` otherwise.
*/
UnsignedInt instanceOffset;
};
/**
@brief Indexed draw indirect command
@m_since_latest
Layout for a @ref Buffer containing indirect draw commands for *indexed* meshes
drawn with @ref AbstractShaderProgram::drawIndirect(Mesh&, Buffer&, GLintptr),
@ref AbstractShaderProgram::drawIndirect(Mesh&, Buffer&, GLintptr, GLsizei, GLsizei)
and @ref AbstractShaderProgram::drawIndirect(Mesh&, Buffer&, GLintptr, Buffer&, GLintptr, GLsizei, GLsizei).
See the @ref GL-AbstractShaderProgram-rendering-workflow-indirect "AbstractShaderProgram indirect draw documentation"
for more information and usage examples.
@requires_gl43 Extension @gl_extension{ARB,multi_draw_indirect}
@requires_gles31 OpenGL ES 3.1 and @gl_extension{EXT,multi_draw_indirect}
@requires_gles Indirect rendering isn't availabe in WebGL.
*/
struct DrawElementsIndirect {
/**
* @brief Index count
*
* Corresponds to the value passed to @ref Mesh::setCount().
*/
UnsignedInt count;
/**
* @brief Instance count
*
* Corresponds to the value passed to @ref Mesh::setInstanceCount().
*/
UnsignedInt instanceCount;
/**
* @brief Index offset
*
* Corresponds to the value passed to @ref Mesh::setIndexOffset().
*/
UnsignedInt indexOffset;
/**
* @brief Vertex offset
*
* Corresponds to the value passed to @ref Mesh::setBaseVertex().
*/
UnsignedInt vertexOffset;
/**
* @brief Instance offset
*
* Corresponds to the value passed to @ref Mesh::setBaseInstance().
* @requires_gl42 Extension @gl_extension{ARB,base_instance}, required to
* be set to `0` otherwise.
* @requires_es_extension Extension @gl_extension{EXT,base_instance},
* required to be set to `0` otherwise.
*/
UnsignedInt instanceOffset;
};
#endif
/**
@brief Base for shader program implementations
@ -1275,7 +1380,119 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
*/
AbstractShaderProgram& draw(const Containers::Iterable<MeshView>& meshes);
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
/**
* @brief Draw a mesh view indirectly
* @param mesh Mesh to draw
* @param indirectBuffer Buffer to take the indirect command
* from
* @param indirectBufferOffset Four-byte-aligned offset of the
* indirect command in the buffer
* @return Reference to self (for method chaining)
* @m_since_latest
*
* The @p indirectBuffer is assumed to have a @ref DrawArraysIndirect
* structure for a non-indexed @p mesh or a @ref DrawElementsIndirect
* structure for an indexed @p mesh at @p indirectBufferOffset.
* @see @fn_gl{UseProgram}, @fn_gl_keyword{EnableVertexAttribArray},
* @fn_gl{BindBuffer}, @fn_gl_keyword{VertexAttribPointer},
* @fn_gl_keyword{DisableVertexAttribArray} or
* @fn_gl{BindVertexArray}, @fn_gl_keyword{DrawArraysIndirect} or
* @fn_gl_keyword{DrawElementsIndirect}
* @requires_gl40 Extension @gl_extension{ARB,draw_indirect}
* @requires_gl42 Extension @gl_extension{ARB,base_instance} if
* @ref DrawArraysIndirect::instanceOffset or
* @ref DrawElementsIndirect::instanceOffset is not `0`
* @requires_gles31 Indirect rendering isn't available in OpenGL ES
* 3.0 and older.
* @requires_es_extension Extension @gl_extension{EXT,base_instance} if
* @ref DrawArraysIndirect::instanceOffset or
* @ref DrawElementsIndirect::instanceOffset is not `0`
* @requires_gles Indirect rendering isn't availabe in WebGL.
*/
AbstractShaderProgram& drawIndirect(Mesh& mesh, Buffer& indirectBuffer, GLintptr indirectBufferOffset);
/**
* @brief Draw multiple mesh views indirectly
* @param mesh Mesh to draw
* @param indirectBuffer Buffer to take the indirect command
* from
* @param indirectBufferOffset Four-byte-aligned offset of the
* indirect command in the buffer
* @param count Draw count
* @param stride Four-byte-aligned stride between
* commands or @cpp 0 @ce if it's a tightly-packed sequence of
* @ref DrawArraysIndirect structures for a non-indexed @p mesh or
* @ref DrawElementsIndirect structures for an indexed @p mesh.
* @return Reference to self (for method chaining)
* @m_since_latest
*
* The @p indirectBuffer is assumed to have a list of
* @ref DrawArraysIndirect structures for a non-indexed @p mesh or
* @ref DrawElementsIndirect structures for an indexed @p mesh at
* @p indirectBufferOffset with @p count items and a @p stride.
* @see @fn_gl{UseProgram}, @fn_gl_keyword{EnableVertexAttribArray},
* @fn_gl{BindBuffer}, @fn_gl_keyword{VertexAttribPointer},
* @fn_gl_keyword{DisableVertexAttribArray} or
* @fn_gl{BindVertexArray}, @fn_gl_keyword{MultiDrawArraysIndirect} or
* @fn_gl_keyword{MultiDrawElementsIndirect}
* @requires_gl43 Extension @gl_extension{ARB,multi_draw_indirect}
* @requires_gl42 Extension @gl_extension{ARB,base_instance} if
* @ref DrawArraysIndirect::instanceOffset or
* @ref DrawElementsIndirect::instanceOffset is not `0`
* @requires_gles31 OpenGL ES 3.1 and @gl_extension{EXT,multi_draw_indirect}
* @requires_es_extension Extension @gl_extension{EXT,base_instance} if
* @ref DrawArraysIndirect::instanceOffset or
* @ref DrawElementsIndirect::instanceOffset is not `0`
* @requires_gles Indirect rendering isn't availabe in WebGL.
*/
AbstractShaderProgram& drawIndirect(Mesh& mesh, Buffer& indirectBuffer, GLintptr indirectBufferOffset, GLsizei count, GLsizei stride);
#endif
#ifndef MAGNUM_TARGET_GLES
/**
* @brief Draw multiple mesh views indirectly with indirect draw count
* @param mesh Mesh to draw
* @param indirectBuffer Buffer to take the indirect command
* from
* @param indirectBufferOffset Four-byte-aligned offset of the
* indirect command in the buffer
* @param countBuffer Buffer to take the draw count from
* @param countBufferOffset Four-byte-aligned offset of a 32-bit
* draw count value in the buffer
* @param maxCount Max draw count
* @param stride Four-byte-aligned stride between
* commands or @cpp 0 @ce if it's a tightly-packed sequence of
* @ref DrawArraysIndirect structures for a non-indexed @p mesh or
* @ref DrawElementsIndirect structures for an indexed @p mesh.
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Compared to @ref drawIndirect(Mesh&, Buffer&, GLintptr, GLsizei, GLsizei),
* not only the draw commands themselves are taken from a buffer but
* their count as well. The @p indirectBuffer is assumed to have a list
* of @ref DrawArraysIndirect structures for a non-indexed @p mesh or
* @ref DrawElementsIndirect structures for an indexed @p mesh at
* @p indirectBufferOffset with a @p stride. The @p countBuffer is
* assumed to have a 32-bit value describing draw count at
* @p countBufferOffset. If the value in the buffer is larger than
* @p maxCount, only @p maxCount draws is performed.
* @see @ref Buffer::TargetHint::Parameter, @fn_gl{UseProgram},
* @fn_gl_keyword{EnableVertexAttribArray}, @fn_gl{BindBuffer},
* @fn_gl_keyword{VertexAttribPointer},
* @fn_gl_keyword{DisableVertexAttribArray} or
* @fn_gl{BindVertexArray},
* @fn_gl_keyword{MultiDrawArraysIndirectCount} or
* @fn_gl_keyword{MultiDrawElementsIndirectCount}
* @requires_gl46 Extension @gl_extension{ARB,indirect_parameters}
* @requires_gl42 Extension @gl_extension{ARB,base_instance} if
* @ref DrawArraysIndirect::instanceOffset or
* @ref DrawElementsIndirect::instanceOffset is not `0`
* @requires_gl Indirect multidraw with indirect draw count is not
* available in OpenGL ES or WebGL.
*/
AbstractShaderProgram& drawIndirect(Mesh& mesh, Buffer& indirectBuffer, GLintptr indirectBufferOffset, Buffer& countBuffer, GLintptr countBufferOffset, GLsizei maxCount, GLsizei stride);
/**
* @brief Draw a mesh with vertices coming out of transform feedback
* @param mesh Mesh to draw
@ -1348,6 +1565,25 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
* @requires_gles Compute shaders are not available in WebGL.
*/
AbstractShaderProgram& dispatchCompute(const Vector3ui& workgroupCount);
/**
* @brief Dispatch compute indirectly
* @param indirectBuffer Buffer to take the workgroup count from
* @param indirectBufferOffset Four-byte-aligned offset of the
* workgroup count in the buffer
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Valid only on programs with compute shader attached. The
* @p indirectBuffer is assumed to have three 32-bit values describing
* X, Y and Z workgroup count at @p indirectBufferOffset.
* @see @fn_gl{DispatchComputeIndirect}
* @requires_gl43 Extension @gl_extension{ARB,compute_shader}
* @requires_gles31 Compute shaders are not available in OpenGL ES 3.0
* and older.
* @requires_gles Compute shaders are not available in WebGL.
*/
AbstractShaderProgram& dispatchComputeIndirect(Buffer& indirectBuffer, GLintptr indirectBufferOffset);
#endif
/**
@ -2051,6 +2287,9 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
#else
#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES(...)
#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_NOT_GLES(...) \
__VA_ARGS__& drawIndirect(Magnum::GL::Mesh& mesh, Magnum::GL::Buffer& indirectBuffer, GLintptr indirectBufferOffset, Magnum::GL::Buffer& countBuffer, GLintptr countBufferOffset, GLsizei maxCount, GLsizei stride) { \
return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::drawIndirect(mesh, indirectBuffer, indirectBufferOffset, countBuffer, countBufferOffset, maxCount, stride)); \
} \
__VA_ARGS__& drawTransformFeedback(Magnum::GL::Mesh& mesh, Magnum::GL::TransformFeedback& xfb, Magnum::UnsignedInt stream = 0) { \
return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::drawTransformFeedback(mesh, xfb, stream)); \
} \
@ -2065,9 +2304,24 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_XFB
#endif
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
/* The drawIndirect() variant with indirect count is already in
_MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_NOT_GLES */
#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_INDIRECT_IMPLEMENTATION(...) \
__VA_ARGS__& drawIndirect(Magnum::GL::Mesh& mesh, Magnum::GL::Buffer& indirectBuffer, GLintptr indirectBufferOffset) { \
return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::drawIndirect(mesh, indirectBuffer, indirectBufferOffset)); \
} \
__VA_ARGS__& drawIndirect(Magnum::GL::Mesh& mesh, Magnum::GL::Buffer& indirectBuffer, GLintptr indirectBufferOffset, GLsizei count, GLsizei stride) { \
return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::drawIndirect(mesh, indirectBuffer, indirectBufferOffset, count, stride)); \
}
#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_DRAW \
using Magnum::GL::AbstractShaderProgram::draw; \
using Magnum::GL::AbstractShaderProgram::drawIndirect;
#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_COMPUTE \
using Magnum::GL::AbstractShaderProgram::dispatchCompute;
#else
#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_INDIRECT_IMPLEMENTATION(...)
#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_DRAW \
using Magnum::GL::AbstractShaderProgram::draw;
#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_COMPUTE
#endif
#endif
@ -2108,7 +2362,8 @@ accidental calls to
__VA_ARGS__& draw(const Corrade::Containers::Iterable<Magnum::GL::MeshView>& meshes) { \
return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(meshes)); \
} \
_MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_NOT_GLES(__VA_ARGS__)
_MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_NOT_GLES(__VA_ARGS__) \
_MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_INDIRECT_IMPLEMENTATION(__VA_ARGS__)
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
/**
@ -2126,11 +2381,14 @@ accidental calls to @relativeref{Magnum::GL,AbstractShaderProgram::draw()} and
*/
#define MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION(...) \
private: \
using Magnum::GL::AbstractShaderProgram::draw; \
_MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_DRAW \
_MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_XFB \
public: \
__VA_ARGS__& dispatchCompute(const Magnum::Vector3ui& workgroupCount) { \
return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::dispatchCompute(workgroupCount)); \
} \
__VA_ARGS__& dispatchComputeIndirect(Magnum::GL::Buffer& indirectBuffer, GLintptr indirectBufferOffset) { \
return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::dispatchComputeIndirect(indirectBuffer, indirectBufferOffset)); \
}
#endif

3
src/Magnum/GL/Buffer.cpp

@ -725,6 +725,9 @@ Debug& operator<<(Debug& debug, const Buffer::TargetHint value) {
#endif
#endif
_c(ElementArray)
#ifndef MAGNUM_TARGET_GLES
_c(Parameter)
#endif
#ifndef MAGNUM_TARGET_GLES2
_c(PixelPack)
_c(PixelUnpack)

16
src/Magnum/GL/Buffer.h

@ -297,7 +297,8 @@ class MAGNUM_GL_EXPORT Buffer: public AbstractObject {
#ifndef MAGNUM_TARGET_WEBGL
/**
* Indirect compute dispatch commands.
* Indirect compute dispatch commands for
* @ref AbstractShaderProgram::dispatchComputeIndirect()
* @requires_gl43 Extension @gl_extension{ARB,compute_shader}
* @requires_gles31 Compute shaders are not available in OpenGL ES
* 3.0 and older.
@ -306,7 +307,7 @@ class MAGNUM_GL_EXPORT Buffer: public AbstractObject {
DispatchIndirect = GL_DISPATCH_INDIRECT_BUFFER,
/**
* Used for supplying arguments for indirect drawing.
* Indirect draw commands for @ref AbstractShaderProgram::drawIndirect()
* @requires_gl40 Extension @gl_extension{ARB,draw_indirect}
* @requires_gles31 Indirect drawing is not available in OpenGL ES
* 3.0 and older.
@ -319,6 +320,17 @@ class MAGNUM_GL_EXPORT Buffer: public AbstractObject {
/** Used for storing vertex indices. */
ElementArray = GL_ELEMENT_ARRAY_BUFFER,
#ifndef MAGNUM_TARGET_GLES
/**
* Indirect draw count parameter for @ref AbstractShaderProgram::drawIndirect(Mesh&, Buffer&, GLintptr, Buffer&, GLintptr, GLsizei, GLsizei)
* @m_since_latest
* @requires_gl46 Extension @gl_extension{ARB,indirect_parameters}
* @requires_gl Indirect multidraw with indirect draw count is not
* available in OpenGL ES or WebGL.
*/
Parameter = GL_PARAMETER_BUFFER,
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* Target for pixel pack operations.

4
src/Magnum/GL/Extensions.h

@ -547,8 +547,8 @@ namespace ANDROID {
_extension( 85,EXT,semaphore, GLES300, None) // #280
/* These two pairs appear to be exclusive so they share the same indices */
#ifndef CORRADE_TARGET_WINDOWS
_extension( 84,EXT,memory_object_fd, GLES300, None) // #281
_extension( 85,EXT,semaphore_fd, GLES300, None) // #281
_extension( 86,EXT,memory_object_fd, GLES300, None) // #281
_extension( 87,EXT,semaphore_fd, GLES300, None) // #281
#else
_extension( 86,EXT,memory_object_win32, GLES300, None) // #282
_extension( 87,EXT,semaphore_win32, GLES300, None) // #282

8
src/Magnum/GL/Implementation/BufferState.cpp

@ -53,9 +53,12 @@ const Buffer::TargetHint BufferState::targetForIndex[] = {
Buffer::TargetHint::DispatchIndirect,
Buffer::TargetHint::DrawIndirect,
Buffer::TargetHint::ShaderStorage,
Buffer::TargetHint::Texture
Buffer::TargetHint::Texture,
#endif
#endif
#ifndef MAGNUM_TARGET_GLES
Buffer::TargetHint::Parameter
#endif
};
std::size_t BufferState::indexForTarget(Buffer::TargetHint target) {
@ -77,6 +80,9 @@ std::size_t BufferState::indexForTarget(Buffer::TargetHint target) {
case Buffer::TargetHint::Texture: return 13;
#endif
#endif
#ifndef MAGNUM_TARGET_GLES
case Buffer::TargetHint::Parameter: return 14;
#endif
}
CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */

6
src/Magnum/GL/Implementation/BufferState.h

@ -47,8 +47,10 @@ struct BufferState {
TargetCount = 2+1,
#elif defined(MAGNUM_TARGET_WEBGL) /* WebGL 2 */
TargetCount = 8+1,
#else /* ES3 and desktop */
TargetCount = 13+1
#elif defined(MAGNUM_TARGET_GLES) /* ES3 */
TargetCount = 13+1,
#else /* desktop */
TargetCount = 14+1,
#endif
};

66
src/Magnum/GL/Mesh.cpp

@ -1028,6 +1028,72 @@ void Mesh::drawInternal(Int count, Int baseVertex, Int instanceCount, GLintptr i
state.unbindImplementation(*this);
}
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
void Mesh::drawInternal(Buffer& indirectBuffer, const GLintptr indirectBufferOffset) {
const Implementation::MeshState& state = Context::current().state().mesh;
state.bindImplementation(*this);
indirectBuffer.bindInternal(Buffer::TargetHint::DrawIndirect);
/* Non-indexed mesh */
if(!_indexBuffer.id())
glDrawArraysIndirect(GLenum(_primitive), reinterpret_cast<GLvoid*>(indirectBufferOffset));
/* Indexed mesh */
else
glDrawElementsIndirect(GLenum(_primitive), GLenum(_indexType), reinterpret_cast<GLvoid*>(indirectBufferOffset));
state.unbindImplementation(*this);
}
void Mesh::drawInternal(Buffer& indirectBuffer, const GLintptr indirectBufferOffset, GLsizei count, GLsizei stride) {
const Implementation::MeshState& state = Context::current().state().mesh;
state.bindImplementation(*this);
indirectBuffer.bindInternal(Buffer::TargetHint::DrawIndirect);
/* Non-indexed mesh */
if(!_indexBuffer.id())
#ifndef MAGNUM_TARGET_GLES
glMultiDrawArraysIndirect
#else
glMultiDrawArraysIndirectEXT
#endif
(GLenum(_primitive), reinterpret_cast<GLvoid*>(indirectBufferOffset), count, stride);
/* Indexed mesh */
else
#ifndef MAGNUM_TARGET_GLES
glMultiDrawElementsIndirect
#else
glMultiDrawElementsIndirectEXT
#endif
(GLenum(_primitive), GLenum(_indexType), reinterpret_cast<GLvoid*>(indirectBufferOffset), count, stride);
state.unbindImplementation(*this);
}
#endif
#ifndef MAGNUM_TARGET_GLES
void Mesh::drawInternal(Buffer& indirectBuffer, const GLintptr indirectBufferOffset, Buffer& countBuffer, const GLintptr countBufferOffset, const GLsizei maxCount, const GLsizei stride) {
const Implementation::MeshState& state = Context::current().state().mesh;
state.bindImplementation(*this);
indirectBuffer.bindInternal(Buffer::TargetHint::DrawIndirect);
countBuffer.bindInternal(Buffer::TargetHint::Parameter);
/* Non-indexed mesh */
if(!_indexBuffer.id())
glMultiDrawArraysIndirectCount(GLenum(_primitive), reinterpret_cast<GLvoid*>(indirectBufferOffset), countBufferOffset, maxCount, stride);
/* Indexed mesh */
else
glMultiDrawElementsIndirectCount(GLenum(_primitive), GLenum(_indexType), reinterpret_cast<GLvoid*>(indirectBufferOffset), countBufferOffset, maxCount, stride);
state.unbindImplementation(*this);
}
#endif
#ifndef MAGNUM_TARGET_GLES
void Mesh::drawInternal(TransformFeedback& xfb, const UnsignedInt stream, const Int instanceCount) {
const Implementation::MeshState& state = Context::current().state().mesh;

8
src/Magnum/GL/Mesh.h

@ -1325,6 +1325,14 @@ class MAGNUM_GL_EXPORT Mesh: public AbstractObject {
#endif
#endif
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
MAGNUM_GL_LOCAL void drawInternal(Buffer& indirectBuffer, GLintptr indirectBufferOffset);
MAGNUM_GL_LOCAL void drawInternal(Buffer& indirectBuffer, GLintptr indirectBufferOffset, GLsizei count, GLsizei stride);
#endif
#ifndef MAGNUM_TARGET_GLES
MAGNUM_GL_LOCAL void drawInternal(Buffer& indirectBuffer, GLintptr indirectBufferOffset, Buffer& countBuffer, GLintptr countBufferOffset, GLsizei maxCount, GLsizei stride);
#endif
#ifndef MAGNUM_TARGET_GLES
MAGNUM_GL_LOCAL void drawInternal(TransformFeedback& xfb, UnsignedInt stream, Int instanceCount);
#endif

216
src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp

@ -40,6 +40,7 @@
#include "Magnum/GL/AbstractShaderProgram.h"
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Extensions.h"
#include "Magnum/GL/Framebuffer.h"
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
#include "Magnum/GL/ImageFormat.h"
#endif
@ -47,6 +48,8 @@
#include "Magnum/GL/MeshView.h"
#include "Magnum/GL/OpenGLTester.h"
#include "Magnum/GL/PixelFormat.h"
#include "Magnum/GL/Renderbuffer.h"
#include "Magnum/GL/RenderbufferFormat.h"
#include "Magnum/GL/Shader.h"
#include "Magnum/GL/Texture.h"
#include "Magnum/GL/TextureFormat.h"
@ -106,12 +109,15 @@ struct AbstractShaderProgramGLTest: OpenGLTester {
#ifndef MAGNUM_TARGET_WEBGL
void compute();
void computeIndirect();
#endif
#endif
void subclassDraw();
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
void subclassDrawIndirect();
void subclassDispatch();
void subclassDispatchIndirect();
#endif
};
@ -214,12 +220,15 @@ AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() {
#ifndef MAGNUM_TARGET_WEBGL
&AbstractShaderProgramGLTest::compute,
&AbstractShaderProgramGLTest::computeIndirect,
#endif
#endif
&AbstractShaderProgramGLTest::subclassDraw,
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
&AbstractShaderProgramGLTest::subclassDispatch
&AbstractShaderProgramGLTest::subclassDrawIndirect,
&AbstractShaderProgramGLTest::subclassDispatch,
&AbstractShaderProgramGLTest::subclassDispatchIndirect,
#endif
});
}
@ -1311,6 +1320,90 @@ void AbstractShaderProgramGLTest::compute() {
TestSuite::Compare::Container);
#endif
}
void AbstractShaderProgramGLTest::computeIndirect() {
/* Like compute(), just with the workgroup count taken from a buffer */
#ifndef MAGNUM_TARGET_GLES
if(!Context::current().isExtensionSupported<Extensions::ARB::compute_shader>())
CORRADE_SKIP(Extensions::ARB::compute_shader::string() << "is not supported.");
#else
if(!Context::current().isVersionSupported(Version::GLES310))
CORRADE_SKIP("OpenGL ES 3.1 is not supported.");
#endif
struct ComputeShader: AbstractShaderProgram {
explicit ComputeShader() {
Utility::Resource rs("AbstractShaderProgramGLTest");
Shader compute(
#ifndef MAGNUM_TARGET_GLES
Version::GL430,
#else
Version::GLES310,
#endif
Shader::Type::Compute);
compute.addSource(rs.getString("ComputeShader.comp"));
CORRADE_INTERNAL_ASSERT_OUTPUT(compute.compile());
attachShader(compute);
CORRADE_INTERNAL_ASSERT_OUTPUT(link());
}
ComputeShader& setImages(Texture2D& input, Texture2D& output) {
input.bindImage(0, 0, ImageAccess::ReadOnly, ImageFormat::RGBA8UI);
output.bindImage(1, 0, ImageAccess::WriteOnly, ImageFormat::RGBA8UI);
return *this;
}
} shader;
MAGNUM_VERIFY_NO_GL_ERROR();
const Color4ub inData[] = {
{ 10, 20, 30, 40},
{ 50, 60, 70, 80},
{ 90, 100, 110, 120},
{130, 140, 150, 160}
};
#ifndef MAGNUM_TARGET_GLES
const Color4ub outData[] = {
{ 15, 30, 45, 60},
{ 75, 90, 105, 120},
{135, 150, 165, 180},
{195, 210, 225, 240}
};
#endif
Texture2D in;
in.setStorage(1, TextureFormat::RGBA8UI, {2, 2})
.setSubImage(0, {}, ImageView2D{PixelFormat::RGBAInteger, PixelType::UnsignedByte, {2, 2}, inData});
Texture2D out;
out.setStorage(1, TextureFormat::RGBA8UI, {2, 2});
/* The first element is skipped to verify the offset argument */
UnsignedInt workgroupCount[]{45, 1, 1, 1};
Buffer indirectBuffer{workgroupCount};
MAGNUM_VERIFY_NO_GL_ERROR();
shader.setImages(in, out)
.dispatchComputeIndirect(indirectBuffer, sizeof(UnsignedInt));
MAGNUM_VERIFY_NO_GL_ERROR();
/** @todo Test on ES */
#ifndef MAGNUM_TARGET_GLES
const auto data = out.image(0, {PixelFormat::RGBAInteger, PixelType::UnsignedByte}).release();
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_AS(Containers::arrayCast<const Color4ub>(data),
Containers::arrayView(outData),
TestSuite::Compare::Container);
#endif
}
#endif
#endif
@ -1320,10 +1413,47 @@ void AbstractShaderProgramGLTest::compute() {
names */
namespace {
struct ShaderSubclassDraw: Magnum::GL::AbstractShaderProgram {
explicit ShaderSubclassDraw() {
using namespace Magnum::GL; /* Here I allow myself to be lazy, yes */
#ifndef MAGNUM_TARGET_GLES
const Version version = Context::current().supportedVersion({Version::GL310, Version::GL300, Version::GL210});
#else
const Version version = Context::current().supportedVersion({Version::GLES300, Version::GLES200});
#endif
Shader vert{version, Shader::Type::Vertex};
vert.addSource("void main() {}");
Shader frag{version, Shader::Type::Fragment};
frag.addSource("void main() {}");
CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());
attachShaders({vert, frag});
CORRADE_INTERNAL_ASSERT_OUTPUT(link());
}
MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(ShaderSubclassDraw)
};
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
struct ShaderSubclassDispatch: Magnum::GL::AbstractShaderProgram {
explicit ShaderSubclassDispatch() {
using namespace Magnum::GL; /* Here I allow myself to be lazy, yes */
Shader compute(
#ifndef MAGNUM_TARGET_GLES
Version::GL430,
#else
Version::GLES310,
#endif
Shader::Type::Compute);
compute.addSource(
"layout(local_size_x = 1) in;\n"
"void main() {}");
CORRADE_INTERNAL_ASSERT_OUTPUT(compute.compile());
attachShader(compute);
CORRADE_INTERNAL_ASSERT_OUTPUT(link());
}
MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION(ShaderSubclassDispatch)
};
#endif
@ -1403,17 +1533,95 @@ void AbstractShaderProgramGLTest::subclassDraw() {
}
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
void AbstractShaderProgramGLTest::subclassDrawIndirect() {
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::draw_indirect>())
CORRADE_SKIP(GL::Extensions::ARB::draw_indirect::string() << "is not supported.");
#else
if(!GL::Context::current().isVersionSupported(Version::GLES310))
CORRADE_SKIP(Version::GLES310 << "is not supported.");
#endif
ShaderSubclassDraw shader;
Mesh mesh;
/* Zero counts, instance counts etc */
DrawArraysIndirect indirect[1]{};
Buffer indirectBuffer{indirect};
UnsignedInt count[1]{};
Buffer countBuffer{count};
/* Need to create a framebuffer because even an empty draw goes through all
GL validation */
Renderbuffer renderbuffer;
renderbuffer.setStorage(RenderbufferFormat::RGBA8, Vector2i{1});
Framebuffer framebuffer{{{}, Vector2i{1}}};
framebuffer
.attachRenderbuffer(Framebuffer::ColorAttachment{0}, renderbuffer)
.bind();
/* These should all be a no-op because we either specify all zeros in the
buffer or a zero multidraw count, and the shader itself does nothing.
And if everything is alright, the returned type should still be
ShaderSubclassDraw& even after all these. */
ShaderSubclassDraw& out = shader
.drawIndirect(mesh, indirectBuffer, 0)
/* These two require the multi_draw_indirect / indirect_parameters
extensions, but as the count / maxCount is 0 they exit early without
calling into GL at all, it's fine */
.drawIndirect(mesh, indirectBuffer, 0, 0, 0)
#ifndef MAGNUM_TARGET_GLES
.drawIndirect(mesh, indirectBuffer, 0, countBuffer, 0, 0, 0)
#endif
;
CORRADE_VERIFY(out.id());
MAGNUM_VERIFY_NO_GL_ERROR();
}
void AbstractShaderProgramGLTest::subclassDispatch() {
#ifndef MAGNUM_TARGET_GLES
if(!Context::current().isExtensionSupported<Extensions::ARB::compute_shader>())
CORRADE_SKIP(Extensions::ARB::compute_shader::string() << "is not supported.");
#else
if(!Context::current().isVersionSupported(Version::GLES310))
CORRADE_SKIP("OpenGL ES 3.1 is not supported.");
#endif
ShaderSubclassDispatch shader;
/* These should all be a no-op because the count is empty. And if
everything is alright, the returned type should still be
ShaderSubclassDispatch& again. */
/* This one should be a no-op because the count is empty. And if everything
is alright, the returned type should still be ShaderSubclassDispatch&
again. */
ShaderSubclassDispatch& out = shader.dispatchCompute({});
CORRADE_VERIFY(out.id());
MAGNUM_VERIFY_NO_GL_ERROR();
}
void AbstractShaderProgramGLTest::subclassDispatchIndirect() {
#ifndef MAGNUM_TARGET_GLES
if(!Context::current().isExtensionSupported<Extensions::ARB::compute_shader>())
CORRADE_SKIP(Extensions::ARB::compute_shader::string() << "is not supported.");
#else
if(!Context::current().isVersionSupported(Version::GLES310))
CORRADE_SKIP("OpenGL ES 3.1 is not supported.");
#endif
ShaderSubclassDispatch shader;
UnsignedInt indirect[3]{};
Buffer indirectBuffer{indirect};
/* This one should be a no-op because there are all zeros in the buffer,
and the shader itself does nothing. And if everything is alright, the
returned type should still be ShaderSubclassDispatch& again. */
ShaderSubclassDispatch& out = shader.dispatchComputeIndirect(indirectBuffer, 0);
CORRADE_VERIFY(out.id());
MAGNUM_VERIFY_NO_GL_ERROR();
}
#endif
}}}}

969
src/Magnum/GL/Test/MeshGLTest.cpp

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save