From fb0a970ecd5f71f68c127c26cd8c7d15829bd0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 11 Nov 2022 22:14:49 +0100 Subject: [PATCH] GL: create a macro for AbstractShaderProgram subclass method chaining. A bunch of new GLES- and WebGL-specific draw() variants got added in b30d313ecded07a226f7beab81c304bb421045aa over a year ago, but so far they weren't exposed in any of the Shaders because it'd mean a lot of nasty copypasting and I just didn't like that. So instead, there's a new macro to handle this messy work, and also tested in order to ensure everything is still as it should be and that it works even outside the Magnum namespace. This makes it much easier to add new draw() variants (such as indirect draws, *finally*), without having to update every shader implementation under the sun. One difference is that I'm now allowing drawTransformFeedback() always -- because that makes sense. It *is* possible to use a regular shader to draw a result of a XFB, so it doesn't make sense to attempt to block that. The change to Shaders will be done in the next commit. --- doc/Doxyfile | 4 +- doc/changelog.dox | 4 + doc/snippets/MagnumGL.cpp | 35 +--- src/Magnum/GL/AbstractShaderProgram.h | 157 +++++++++++++++++- .../GL/Test/AbstractShaderProgramGLTest.cpp | 123 +++++++++++++- 5 files changed, 281 insertions(+), 42 deletions(-) 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)