diff --git a/doc/Doxyfile b/doc/Doxyfile index 6a91faca1..e52934b54 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -2260,7 +2260,9 @@ PREDEFINED = DOXYGEN_GENERATING_OUTPUT \ CORRADE_VISIBILITY_LOCAL= CORRADE_VISIBILITY_IMPORT= \ CORRADE_IGNORE_DEPRECATED_PUSH= CORRADE_IGNORE_DEPRECATED_POP= \ CORRADE_ENUMSET_OPERATORS(message)= MAGNUM_BUILD_DEPRECATED \ - MAGNUM_TARGET_GL MAGNUM_TARGET_VK + MAGNUM_TARGET_GL MAGNUM_TARGET_VK \ + MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(type)= \ + MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION(type)= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/doc/changelog.dox b/doc/changelog.dox index 882c5b0b1..561352224 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -445,6 +445,10 @@ See also: @relativeref{GL::Framebuffer::InvalidationAttachment,Stencil} (see [mosra/magnum#554](https://github.com/mosra/magnum/pull/554)) - Added @ref GL::Shader::wrap() and @relativeref{GL::Shader,release()} +- New @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION() and + @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION() + macros for easier and more robust implementation of method chaining in + @ref GL::AbstractShaderProgram subclasses @subsubsection changelog-latest-changes-math Math library diff --git a/doc/snippets/MagnumGL.cpp b/doc/snippets/MagnumGL.cpp index 0ae3196ab..e0c682d53 100644 --- a/doc/snippets/MagnumGL.cpp +++ b/doc/snippets/MagnumGL.cpp @@ -107,38 +107,9 @@ enum: UnsignedInt { }; /* [AbstractShaderProgram-output-attributes] */ -#if !defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) -/* [AbstractShaderProgram-return-hide-irrelevant] */ -public: - MyShader& draw(GL::Mesh& mesh) { - return static_cast(GL::AbstractShaderProgram::draw(mesh)); - } - MyShader& draw(GL::Mesh&& mesh) { - return static_cast(GL::AbstractShaderProgram::draw(mesh)); - } - MyShader& draw(GL::MeshView& mesh) { - return static_cast(GL::AbstractShaderProgram::draw(mesh)); - } - MyShader& draw(GL::MeshView&& mesh) { - return static_cast(GL::AbstractShaderProgram::draw(mesh)); - } - /* Omit these if the shader is not ready for multidraw */ - MyShader& draw(GL::Mesh& mesh, const Containers::StridedArrayView1D& counts, const Containers::StridedArrayView1D& vertexOffsets, const Containers::StridedArrayView1D& indexOffsets) { - return static_cast(GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets)); - } - MyShader& draw(Containers::ArrayView> meshes) { - return static_cast(GL::AbstractShaderProgram::draw(meshes)); - } - MyShader& draw(std::initializer_list> meshes) { - return static_cast(GL::AbstractShaderProgram::draw(meshes)); - } - -private: - using GL::AbstractShaderProgram::drawTransformFeedback; - using GL::AbstractShaderProgram::dispatchCompute; -/* [AbstractShaderProgram-return-hide-irrelevant] */ -public: -#endif +/* [AbstractShaderProgram-subclass-macro] */ +MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(MyShader) +/* [AbstractShaderProgram-subclass-macro] */ /* [AbstractShaderProgram-constructor] */ explicit MyShader() { diff --git a/src/Magnum/GL/AbstractShaderProgram.h b/src/Magnum/GL/AbstractShaderProgram.h index 94607c76d..afb1db82e 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -28,7 +28,7 @@ */ /** @file - * @brief Class @ref Magnum::GL::AbstractShaderProgram + * @brief Class @ref Magnum::GL::AbstractShaderProgram, macro @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(), @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION() */ #include @@ -108,13 +108,16 @@ functions and properties: @snippet MagnumGL.cpp AbstractShaderProgram-xfb
  • And optionally, **return derived type from relevant draw/dispatch functions** - to make it possible for users to easily chain draw calls, and on the other + to make it possible for users to easily chain draw calls; and on the other hand **hide the irrelevant APIs** to prevent users from accidentally - calling @ref draw() on compute shaders, @ref drawTransformFeedback() on - shaders that don't have transform feedback or @ref dispatchCompute() on - shaders that aren't compute. For example: - - @snippet MagnumGL.cpp AbstractShaderProgram-return-hide-irrelevant + calling @ref draw() / @ref drawTransformFeedback() on compute shaders, or + @ref dispatchCompute() on shaders that aren't compute. Because there's many + overloads of those APIs and they differ based on target platform, it's + recommended to be done via either + @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION() or + @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION(): + + @snippet MagnumGL.cpp AbstractShaderProgram-subclass-macro @subsection GL-AbstractShaderProgram-attribute-location Binding attribute and fragment data location @@ -2015,6 +2018,146 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { #endif }; +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef CORRADE_TARGET_32BIT +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_NOT_32BIT(...) \ + __VA_ARGS__& draw(Magnum::GL::Mesh& mesh, const Corrade::Containers::StridedArrayView1D& counts, const Corrade::Containers::StridedArrayView1D& vertexOffsets, const Corrade::Containers::StridedArrayView1D& indexOffsets) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets)); \ + } \ + __VA_ARGS__& draw(Magnum::GL::Mesh& mesh, const Corrade::Containers::StridedArrayView1D& counts, const Corrade::Containers::StridedArrayView1D& vertexOffsets, std::nullptr_t) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, nullptr)); \ + } +#else +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_NOT_32BIT(...) +#endif +#ifdef MAGNUM_TARGET_GLES +#ifndef MAGNUM_TARGET_GLES2 +#ifndef CORRADE_TARGET_32BIT +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES_NOT_GLES2_NOT_32BIT(...) \ + __VA_ARGS__& draw(Magnum::GL::Mesh& mesh, const Corrade::Containers::StridedArrayView1D& counts, const Corrade::Containers::StridedArrayView1D& instanceCounts, const Corrade::Containers::StridedArrayView1D& vertexOffsets, const Corrade::Containers::StridedArrayView1D& indexOffsets, const Corrade::Containers::StridedArrayView1D& instanceOffsets) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh, counts, instanceCounts, vertexOffsets, indexOffsets, instanceOffsets)); \ + } \ + __VA_ARGS__& draw(Magnum::GL::Mesh& mesh, const Corrade::Containers::StridedArrayView1D& counts, const Corrade::Containers::StridedArrayView1D& instanceCounts, const Corrade::Containers::StridedArrayView1D& vertexOffsets, std::nullptr_t, const Corrade::Containers::StridedArrayView1D& instanceOffsets) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh, counts, instanceCounts, vertexOffsets, nullptr, instanceOffsets)); \ + } +#else +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES_NOT_GLES2_NOT_32BIT(...) +#endif +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES_NOT_GLES2(...) \ + __VA_ARGS__& draw(Magnum::GL::Mesh& mesh, const Corrade::Containers::StridedArrayView1D& counts, const Corrade::Containers::StridedArrayView1D& instanceCounts, const Corrade::Containers::StridedArrayView1D& vertexOffsets, const Corrade::Containers::StridedArrayView1D& indexOffsets, const Corrade::Containers::StridedArrayView1D& instanceOffsets) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh, counts, instanceCounts, vertexOffsets, indexOffsets, instanceOffsets)); \ + } \ + _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES_NOT_GLES2_NOT_32BIT(__VA_ARGS__) +#else +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES_NOT_GLES2(...) +#endif + #ifndef CORRADE_TARGET_32BIT +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES_NOT_32BIT(...) \ + __VA_ARGS__& draw(Magnum::GL::Mesh& mesh, const Corrade::Containers::StridedArrayView1D& counts, const Corrade::Containers::StridedArrayView1D& instanceCounts, const Corrade::Containers::StridedArrayView1D& vertexOffsets, const Corrade::Containers::StridedArrayView1D& indexOffsets) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh, counts, instanceCounts, vertexOffsets, indexOffsets)); \ + } \ + __VA_ARGS__& draw(Magnum::GL::Mesh& mesh, const Corrade::Containers::StridedArrayView1D& counts, const Corrade::Containers::StridedArrayView1D& instanceCounts, const Corrade::Containers::StridedArrayView1D& vertexOffsets, std::nullptr_t) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh, counts, instanceCounts, vertexOffsets, nullptr)); \ + } +#else +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES_NOT_32BIT(...) +#endif +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES(...) \ + _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES_NOT_GLES2(__VA_ARGS__) \ + _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES_NOT_32BIT(__VA_ARGS__) \ + __VA_ARGS__& draw(Magnum::GL::Mesh& mesh, const Corrade::Containers::StridedArrayView1D& counts, const Corrade::Containers::StridedArrayView1D& instanceCounts, const Corrade::Containers::StridedArrayView1D& vertexOffsets, const Corrade::Containers::StridedArrayView1D& indexOffsets) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh, counts, instanceCounts, vertexOffsets, indexOffsets)); \ + } +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_NOT_GLES(...) +#else +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES(...) +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_NOT_GLES(...) \ + __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)); \ + } \ + __VA_ARGS__& drawTransformFeedback(Magnum::GL::MeshView& mesh, Magnum::GL::TransformFeedback& xfb, Magnum::UnsignedInt stream = 0) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::drawTransformFeedback(mesh, xfb, stream)); \ + } +#endif +#ifndef MAGNUM_TARGET_GLES +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_XFB \ + using Magnum::GL::AbstractShaderProgram::drawTransformFeedback; +#else +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_XFB +#endif +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_COMPUTE \ + using Magnum::GL::AbstractShaderProgram::dispatchCompute; +#else +#define _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_COMPUTE +#endif +#endif + +/** +@brief @relativeref{Magnum::GL,AbstractShaderProgram} subclass method chaining implementation for draws +@m_since_latest + +Overrides all variants of @relativeref{Magnum::GL,AbstractShaderProgram::draw()} +and @relativeref{Magnum::GL::AbstractShaderProgram,drawTransformFeedback()} in +given subclass to return a reference to that class type instead of +@relativeref{Magnum::GL,AbstractShaderProgram}, and additionally prevents +accidental calls to +@relativeref{Magnum::GL,AbstractShaderProgram::dispatchCompute()}. See +@ref GL-AbstractShaderProgram-subclassing for more information. +*/ +#define MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(...) \ + private: \ + _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_HIDE_COMPUTE \ + public: \ + __VA_ARGS__& draw(Magnum::GL::Mesh& mesh) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh)); \ + } \ + __VA_ARGS__& draw(Magnum::GL::Mesh&& mesh) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh)); \ + } \ + __VA_ARGS__& draw(Magnum::GL::MeshView& mesh) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh)); \ + } \ + __VA_ARGS__& draw(Magnum::GL::MeshView&& mesh) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh)); \ + } \ + __VA_ARGS__& draw(Magnum::GL::Mesh& mesh, const Corrade::Containers::StridedArrayView1D& counts, const Corrade::Containers::StridedArrayView1D& vertexOffsets, const Corrade::Containers::StridedArrayView1D& indexOffsets) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(mesh, counts, vertexOffsets, indexOffsets)); \ + } \ + _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_NOT_32BIT(__VA_ARGS__) \ + _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_GLES(__VA_ARGS__) \ + __VA_ARGS__& draw(Corrade::Containers::ArrayView> meshes) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(meshes)); \ + } \ + __VA_ARGS__& draw(std::initializer_list> meshes) { \ + return static_cast<__VA_ARGS__&>(Magnum::GL::AbstractShaderProgram::draw(meshes)); \ + } \ + _MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION_NOT_GLES(__VA_ARGS__) + +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +/** +@brief @relativeref{Magnum::GL,AbstractShaderProgram} subclass method chaining implementation for compute dispatch +@m_since_latest + +Overrides @relativeref{Magnum::GL,AbstractShaderProgram::dispatchCompute()} in +given subclass to return a reference to that class type instead of +@relativeref{Magnum::GL,AbstractShaderProgram}, and additionally prevents +accidental calls to @relativeref{Magnum::GL,AbstractShaderProgram::draw()} and +@relativeref{Magnum::GL::AbstractShaderProgram,drawTransformFeedback()}. See +@ref GL-AbstractShaderProgram-subclassing for more information. +@requires_gles31 Not defined on OpenGL ES 2.0 builds. +@requires_gles Not defined on WebGL builds. +*/ +#define MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION(...) \ + private: \ + using Magnum::GL::AbstractShaderProgram::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)); \ + } +#endif + }} #endif diff --git a/src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp b/src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp index 201e5fee4..261dcf71b 100644 --- a/src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp +++ b/src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include /** @todo remove when Shader is -free */ #include #include @@ -41,11 +42,16 @@ #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) #include "Magnum/GL/ImageFormat.h" #endif +#include "Magnum/GL/Mesh.h" +#include "Magnum/GL/MeshView.h" +#include "Magnum/GL/OpenGLTester.h" #include "Magnum/GL/PixelFormat.h" #include "Magnum/GL/Shader.h" #include "Magnum/GL/Texture.h" #include "Magnum/GL/TextureFormat.h" -#include "Magnum/GL/OpenGLTester.h" +#ifndef MAGNUM_TARGET_GLES +#include "Magnum/GL/TransformFeedback.h" +#endif #include "Magnum/Math/Matrix.h" #include "Magnum/Math/Vector4.h" #include "Magnum/Math/Color.h" @@ -99,6 +105,11 @@ struct AbstractShaderProgramGLTest: OpenGLTester { void compute(); #endif #endif + + void subclassDraw(); + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + void subclassDispatch(); + #endif }; AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() { @@ -138,9 +149,14 @@ AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() { &AbstractShaderProgramGLTest::uniformBlock, #ifndef MAGNUM_TARGET_WEBGL - &AbstractShaderProgramGLTest::compute + &AbstractShaderProgramGLTest::compute, #endif #endif + + &AbstractShaderProgramGLTest::subclassDraw, + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + &AbstractShaderProgramGLTest::subclassDispatch + #endif }); } @@ -1103,4 +1119,107 @@ void AbstractShaderProgramGLTest::compute() { }}}} +/* These are outside of any namespace to verify the macros fully qualify all + names */ +namespace { + struct ShaderSubclassDraw: Magnum::GL::AbstractShaderProgram { + MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(ShaderSubclassDraw) + }; + #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + struct ShaderSubclassDispatch: Magnum::GL::AbstractShaderProgram { + MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION(ShaderSubclassDispatch) + }; + #endif +} + +namespace Magnum { namespace GL { namespace Test { namespace { + +void AbstractShaderProgramGLTest::subclassDraw() { + ShaderSubclassDraw shader; + Mesh mesh; + mesh + .setCount(0); + MeshView meshView{mesh}; + meshView + .setCount(0); + Mesh meshNoInstances; + meshNoInstances + .setInstanceCount(0); + Mesh meshViewNoInstances; + meshViewNoInstances + .setInstanceCount(0); + #ifndef MAGNUM_TARGET_GLES + TransformFeedback xfb{NoCreate}; + #endif + + Containers::StridedArrayView1D counts; + #ifdef MAGNUM_TARGET_GLES + Containers::StridedArrayView1D instanceCounts; + #endif + Containers::StridedArrayView1D vertexOffsets; + Containers::StridedArrayView1D indexOffsets; + #ifndef CORRADE_TARGET_32BIT + Containers::StridedArrayView1D indexOffsetsLong; + #endif + #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) + Containers::StridedArrayView1D instanceOffsets; + #endif + + /* These should all be a no-op because the mesh is empty and/or because we + specify no multidraw items. And if everything is alright, the returned + type should still be ShaderSubclassDraw& even after all these. */ + ShaderSubclassDraw& out = shader + .draw(mesh) + .draw(std::move(mesh)) + .draw(meshView) + .draw(std::move(meshView)) + .draw(mesh, counts, vertexOffsets, indexOffsets) + #ifndef CORRADE_TARGET_32BIT + .draw(mesh, counts, vertexOffsets, indexOffsetsLong) + .draw(mesh, counts, vertexOffsets, nullptr) + #endif + #ifdef MAGNUM_TARGET_GLES + #ifndef MAGNUM_TARGET_GLES2 + .draw(mesh, counts, instanceCounts, vertexOffsets, indexOffsets, instanceOffsets) + #ifndef CORRADE_TARGET_32BIT + .draw(mesh, counts, instanceCounts, vertexOffsets, indexOffsetsLong, instanceOffsets) + .draw(mesh, counts, instanceCounts, vertexOffsets, nullptr, instanceOffsets) + #endif + #endif + .draw(mesh, counts, instanceCounts, vertexOffsets, indexOffsets) + #ifndef CORRADE_TARGET_32BIT + .draw(mesh, counts, instanceCounts, vertexOffsets, indexOffsetsLong) + .draw(mesh, counts, instanceCounts, vertexOffsets, nullptr) + #endif + #endif + .draw(Containers::arrayView>({})) + .draw(std::initializer_list>{}) + #ifndef MAGNUM_TARGET_GLES + .drawTransformFeedback(meshNoInstances, xfb) + .drawTransformFeedback(meshNoInstances, xfb, 0) + .drawTransformFeedback(meshViewNoInstances, xfb) + .drawTransformFeedback(meshViewNoInstances, xfb, 0) + #endif + ; + + CORRADE_VERIFY(out.id()); + MAGNUM_VERIFY_NO_GL_ERROR(); +} + +#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) +void AbstractShaderProgramGLTest::subclassDispatch() { + 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. */ + ShaderSubclassDispatch& out = shader.dispatchCompute({}); + + CORRADE_VERIFY(out.id()); + MAGNUM_VERIFY_NO_GL_ERROR(); +} +#endif + +}}}} + CORRADE_TEST_MAIN(Magnum::GL::Test::AbstractShaderProgramGLTest)