diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp index a33a9386d..a404c5261 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp @@ -31,7 +31,6 @@ #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" -#include "Magnum/GL/Shader.h" #include "Magnum/GL/Texture.h" #include "Magnum/Math/Color.h" #include "Magnum/Math/Matrix3.h" @@ -63,22 +62,16 @@ namespace { #endif } -template DistanceFieldVectorGL::DistanceFieldVectorGL(const Flags flags +template typename DistanceFieldVectorGL::CompileState DistanceFieldVectorGL::compile(const Flags flags #ifndef MAGNUM_TARGET_GLES2 , const UnsignedInt materialCount, const UnsignedInt drawCount #endif -): - _flags{flags} - #ifndef MAGNUM_TARGET_GLES2 - , _materialCount{materialCount}, - _drawCount{drawCount} - #endif -{ +) { #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || materialCount, - "Shaders::DistanceFieldVectorGL: material count can't be zero", ); + "Shaders::DistanceFieldVectorGL: material count can't be zero", CompileState{NoCreate}); CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, - "Shaders::DistanceFieldVectorGL: draw count can't be zero", ); + "Shaders::DistanceFieldVectorGL: draw count can't be zero", CompileState{NoCreate}); #endif #ifndef MAGNUM_TARGET_GLES @@ -142,9 +135,17 @@ template DistanceFieldVectorGL::DistanceFiel frag.addSource(rs.getString("generic.glsl")) .addSource(rs.getString("DistanceFieldVector.frag")); - CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); + vert.submitCompile(); + frag.submitCompile(); + + DistanceFieldVectorGL out{NoInit}; + out._flags = flags; + #ifndef MAGNUM_TARGET_GLES2 + out._materialCount = materialCount; + out._drawCount = drawCount; + #endif - attachShaders({vert, frag}); + out.attachShaders({vert, frag}); /* ES3 has this done in the shader directly */ #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) @@ -152,25 +153,38 @@ template DistanceFieldVectorGL::DistanceFiel if(!context.isExtensionSupported(version)) #endif { - bindAttributeLocation(Position::Location, "position"); - bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); + out.bindAttributeLocation(Position::Location, "position"); + out.bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); } #endif - CORRADE_INTERNAL_ASSERT_OUTPUT(link()); + out.submitLink(); + return CompileState{std::move(out), std::move(vert), std::move(frag), version}; +} + +template DistanceFieldVectorGL::DistanceFieldVectorGL(CompileState&& cs) +: DistanceFieldVectorGL{static_cast(std::move(cs))} { + if (id() == 0) return; + + CORRADE_INTERNAL_ASSERT_OUTPUT(checkLink()); + CORRADE_INTERNAL_ASSERT_OUTPUT(cs._vert.checkCompile()); + CORRADE_INTERNAL_ASSERT_OUTPUT(cs._frag.checkCompile()); + + const GL::Context& context = GL::Context::current(); + const GL::Version version = cs._version; #ifndef MAGNUM_TARGET_GLES if(!context.isExtensionSupported(version)) #endif { #ifndef MAGNUM_TARGET_GLES2 - if(flags >= Flag::UniformBuffers) { + if(_flags >= Flag::UniformBuffers) { if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset"); } else #endif { _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); - if(flags & Flag::TextureTransformation) + if(_flags & Flag::TextureTransformation) _textureMatrixUniform = uniformLocation("textureMatrix"); _colorUniform = uniformLocation("color"); _outlineColorUniform = uniformLocation("outlineColor"); @@ -185,11 +199,11 @@ template DistanceFieldVectorGL::DistanceFiel { setUniform(uniformLocation("vectorTexture"), TextureUnit); #ifndef MAGNUM_TARGET_GLES2 - if(flags >= Flag::UniformBuffers) { + if(_flags >= Flag::UniformBuffers) { setUniformBlockBinding(uniformBlockIndex("TransformationProjection"), TransformationProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); - if(flags & Flag::TextureTransformation) + if(_flags & Flag::TextureTransformation) setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding); } #endif @@ -198,13 +212,13 @@ template DistanceFieldVectorGL::DistanceFiel /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ #ifdef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES2 - if(flags >= Flag::UniformBuffers) { + if(_flags >= Flag::UniformBuffers) { /* Draw offset is zero by default */ } else #endif { setTransformationProjectionMatrix(MatrixTypeFor{Math::IdentityInit}); - if(flags & Flag::TextureTransformation) + if(_flags & Flag::TextureTransformation) setTextureMatrix(Matrix3{Math::IdentityInit}); setColor(Color4{1.0f}); /* Outline color is zero by default */ @@ -214,8 +228,16 @@ template DistanceFieldVectorGL::DistanceFiel #endif } +template DistanceFieldVectorGL::DistanceFieldVectorGL(NoInitT) {} + +template DistanceFieldVectorGL::DistanceFieldVectorGL(const Flags flags): DistanceFieldVectorGL{compile(flags)} {} + #ifndef MAGNUM_TARGET_GLES2 -template DistanceFieldVectorGL::DistanceFieldVectorGL(const Flags flags): DistanceFieldVectorGL{flags, 1, 1} {} +template typename DistanceFieldVectorGL::CompileState DistanceFieldVectorGL::compile(const Flags flags) { + return compile(flags, 1, 1); +} + +template DistanceFieldVectorGL::DistanceFieldVectorGL(const Flags flags, UnsignedInt materialCount, UnsignedInt drawCount): DistanceFieldVectorGL{compile(flags, materialCount, drawCount)} {} #endif template DistanceFieldVectorGL& DistanceFieldVectorGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index 1be8f0465..2d180249f 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -32,6 +32,7 @@ #include "Magnum/DimensionTraits.h" #include "Magnum/GL/AbstractShaderProgram.h" +#include "Magnum/GL/Shader.h" #include "Magnum/Shaders/GenericGL.h" #include "Magnum/Shaders/visibility.h" @@ -280,6 +281,20 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector */ explicit DistanceFieldVectorGL(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {} + class CompileState; + + explicit DistanceFieldVectorGL(CompileState&& cs); + + static CompileState compile(Flags flags + #ifndef MAGNUM_TARGET_GLES2 + , UnsignedInt materialCount, UnsignedInt drawCount + #endif + ); + + #ifndef MAGNUM_TARGET_GLES2 + static CompileState compile(Flags flags); + #endif + /** @brief Copying is not allowed */ DistanceFieldVectorGL(const DistanceFieldVectorGL&) = delete; @@ -602,6 +617,8 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector #endif private: + explicit DistanceFieldVectorGL(NoInitT); + /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES using GL::AbstractShaderProgram::drawTransformFeedback; @@ -627,6 +644,20 @@ template class MAGNUM_SHADERS_EXPORT DistanceFieldVector #endif }; + +template class DistanceFieldVectorGL::CompileState : public DistanceFieldVectorGL { +private: + friend class DistanceFieldVectorGL; + + explicit CompileState(NoCreateT) : DistanceFieldVectorGL{NoCreate}, _vert{NoCreate}, _frag{NoCreate} {} + + CompileState(DistanceFieldVectorGL&& shader, GL::Shader&& vert, GL::Shader&& frag, GL::Version version) : + DistanceFieldVectorGL{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)}, _version{version} {} + + GL::Shader _vert, _frag; + GL::Version _version; +}; + /** @brief Two-dimensional distance field vector OpenGL shader @m_since_latest diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index f428d8302..ce9d8b092 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -522,7 +522,7 @@ MeshVisualizerGL2D::CompileState MeshVisualizerGL2D::compile(Flags flags MeshVisualizerGL2D::MeshVisualizerGL2D(Flags flags) : MeshVisualizerGL2D{compile(flags)} {} #ifndef MAGNUM_TARGET_GLES2 -MeshVisualizer2D::CompileState MeshVisualizer2D::compile(Flags flags) { +MeshVisualizerGL2D::CompileState MeshVisualizerGL2D::compile(Flags flags) { return compile(flags, 1, 1); } @@ -530,7 +530,7 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(Flags flags, UnsignedInt materialCount, U : MeshVisualizerGL2D{compile(flags, materialCount, drawCount)} {} #endif -MeshVisualizer2D::MeshVisualizerGL2D(CompileState&& cs) +MeshVisualizerGL2D::MeshVisualizerGL2D(CompileState&& cs) : MeshVisualizerGL2D{static_cast(std::move(cs))} { if (id() == 0) return; diff --git a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp index d8bf027ac..167c2446d 100644 --- a/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/DistanceFieldVectorGLTest.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #ifdef CORRADE_TARGET_APPLE @@ -84,8 +85,10 @@ struct DistanceFieldVectorGLTest: GL::OpenGLTester { explicit DistanceFieldVectorGLTest(); template void construct(); + template void constructAsync(); #ifndef MAGNUM_TARGET_GLES2 template void constructUniformBuffers(); + template void constructUniformBuffersAsync(); #endif template void constructMove(); @@ -251,11 +254,19 @@ DistanceFieldVectorGLTest::DistanceFieldVectorGLTest() { &DistanceFieldVectorGLTest::construct<3>}, Containers::arraySize(ConstructData)); + addTests({ + &DistanceFieldVectorGLTest::constructAsync<2>, + &DistanceFieldVectorGLTest::constructAsync<3>}); + #ifndef MAGNUM_TARGET_GLES2 addInstancedTests({ &DistanceFieldVectorGLTest::constructUniformBuffers<2>, &DistanceFieldVectorGLTest::constructUniformBuffers<3>}, Containers::arraySize(ConstructUniformBuffersData)); + + addTests({ + &DistanceFieldVectorGLTest::constructUniformBuffersAsync<2>, + &DistanceFieldVectorGLTest::constructUniformBuffersAsync<3>}); #endif addTests({ @@ -374,6 +385,37 @@ template void DistanceFieldVectorGLTest::construct() { MAGNUM_VERIFY_NO_GL_ERROR(); } +template void DistanceFieldVectorGLTest::constructAsync() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + constexpr struct { + const char* name; + DistanceFieldVectorGL2D::Flags flags; + } data{ + "texture transformation", DistanceFieldVectorGL2D::Flag::TextureTransformation + }; + setTestCaseDescription(data.name); + + auto compileState = DistanceFieldVectorGL::compile(data.flags); + CORRADE_COMPARE(compileState.flags(), data.flags); + + while(!compileState.isLinkFinished()) + Utility::System::sleep(100); + + DistanceFieldVectorGL shader{std::move(compileState)}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_VERIFY(shader.isLinkFinished()); + CORRADE_VERIFY(shader.id()); + { + #if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES) + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} + #ifndef MAGNUM_TARGET_GLES2 template void DistanceFieldVectorGLTest::constructUniformBuffers() { setTestCaseTemplateName(Utility::format("{}", dimensions)); @@ -413,6 +455,59 @@ template void DistanceFieldVectorGLTest::constructUnifor MAGNUM_VERIFY_NO_GL_ERROR(); } + +template void DistanceFieldVectorGLTest::constructUniformBuffersAsync() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + constexpr struct { + const char* name; + DistanceFieldVectorGL2D::Flags flags; + UnsignedInt materialCount, drawCount; + } data { + "multidraw with all the things", DistanceFieldVectorGL2D::Flag::MultiDraw|DistanceFieldVectorGL2D::Flag::TextureTransformation, 16, 48 + }; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & DistanceFieldVectorGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + if(data.flags >= DistanceFieldVectorGL2D::Flag::MultiDraw) { + #ifndef MAGNUM_TARGET_GLES + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported."); + #elif !defined(MAGNUM_TARGET_WEBGL) + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported."); + #else + if(!GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported."); + #endif + } + auto compileState = DistanceFieldVectorGL::compile(data.flags, data.materialCount, data.drawCount); + CORRADE_COMPARE(compileState.flags(), data.flags); + CORRADE_COMPARE(compileState.materialCount(), data.materialCount); + CORRADE_COMPARE(compileState.drawCount(), data.drawCount); + + while(!compileState.isLinkFinished()) + Utility::System::sleep(100); + + DistanceFieldVectorGL shader{std::move(compileState)}; + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_COMPARE(shader.materialCount(), data.materialCount); + CORRADE_COMPARE(shader.drawCount(), data.drawCount); + CORRADE_VERIFY(shader.isLinkFinished()); + CORRADE_VERIFY(shader.id()); + { + #if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES) + CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(shader.validate().first); + } + + MAGNUM_VERIFY_NO_GL_ERROR(); +} #endif template void DistanceFieldVectorGLTest::constructMove() {