diff --git a/src/Magnum/Shaders/Test/VectorGLTest.cpp b/src/Magnum/Shaders/Test/VectorGLTest.cpp index 670886fb7..7b8bff18f 100644 --- a/src/Magnum/Shaders/Test/VectorGLTest.cpp +++ b/src/Magnum/Shaders/Test/VectorGLTest.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #ifdef CORRADE_TARGET_APPLE @@ -83,8 +84,10 @@ struct VectorGLTest: GL::OpenGLTester { explicit VectorGLTest(); template void construct(); + template void constructAsync(); #ifndef MAGNUM_TARGET_GLES2 template void constructUniformBuffers(); + template void constructUniformBuffersAsync(); #endif template void constructMove(); @@ -247,11 +250,19 @@ VectorGLTest::VectorGLTest() { &VectorGLTest::construct<3>}, Containers::arraySize(ConstructData)); + addTests({ + &VectorGLTest::constructAsync<2>, + &VectorGLTest::constructAsync<3>}); + #ifndef MAGNUM_TARGET_GLES2 addInstancedTests({ &VectorGLTest::constructUniformBuffers<2>, &VectorGLTest::constructUniformBuffers<3>}, Containers::arraySize(ConstructUniformBuffersData)); + + addTests({ + &VectorGLTest::constructUniformBuffersAsync<2>, + &VectorGLTest::constructUniformBuffersAsync<3>}); #endif addTests({ @@ -370,6 +381,38 @@ template void VectorGLTest::construct() { MAGNUM_VERIFY_NO_GL_ERROR(); } +template void VectorGLTest::constructAsync() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + constexpr struct { + const char* name; + VectorGL2D::Flags flags; + } data { + "texture transformation", VectorGL2D::Flag::TextureTransformation + }; + setTestCaseDescription(data.name); + + auto compileState = VectorGL::compile(data.flags); + CORRADE_COMPARE(compileState.flags(), data.flags); + + while(!compileState.isLinkFinished()) + Utility::System::sleep(100); + + VectorGL shader{std::move(compileState)}; + CORRADE_VERIFY(shader.isLinkFinished()); + CORRADE_COMPARE(shader.flags(), data.flags); + 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 VectorGLTest::constructUniformBuffers() { setTestCaseTemplateName(Utility::format("{}", dimensions)); @@ -408,6 +451,57 @@ template void VectorGLTest::constructUniformBuffers() { MAGNUM_VERIFY_NO_GL_ERROR(); } + +template void VectorGLTest::constructUniformBuffersAsync() { + setTestCaseTemplateName(Utility::format("{}", dimensions)); + + constexpr struct { + const char* name; + VectorGL2D::Flags flags; + UnsignedInt materialCount, drawCount; + } data {"texture transformation", VectorGL2D::Flag::UniformBuffers|VectorGL2D::Flag::TextureTransformation, 1, 1}; + setTestCaseDescription(data.name); + + #ifndef MAGNUM_TARGET_GLES + if((data.flags & VectorGL::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported()) + CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); + #endif + + if(data.flags >= VectorGL2D::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 = VectorGL::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); + + VectorGL shader{std::move(compileState)}; + CORRADE_VERIFY(shader.isLinkFinished()); + CORRADE_COMPARE(shader.flags(), data.flags); + CORRADE_COMPARE(shader.drawCount(), data.drawCount); + 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 VectorGLTest::constructMove() { diff --git a/src/Magnum/Shaders/VectorGL.cpp b/src/Magnum/Shaders/VectorGL.cpp index addcb7433..d5f8224fd 100644 --- a/src/Magnum/Shaders/VectorGL.cpp +++ b/src/Magnum/Shaders/VectorGL.cpp @@ -63,21 +63,16 @@ namespace { #endif } -template VectorGL::VectorGL(const Flags flags +template typename VectorGL::CompileState VectorGL::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::VectorGL: material count can't be zero", ); + "Shaders::VectorGL: material count can't be zero", CompileState{NoCreate}); CORRADE_ASSERT(!(flags >= Flag::UniformBuffers) || drawCount, - "Shaders::VectorGL: draw count can't be zero", ); + "Shaders::VectorGL: draw count can't be zero", CompileState{NoCreate}); #endif #ifndef MAGNUM_TARGET_GLES @@ -141,9 +136,17 @@ template VectorGL::VectorGL(const Flags flag frag.addSource(rs.getString("generic.glsl")) .addSource(rs.getString("Vector.frag")); - CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); + vert.submitCompile(); + frag.submitCompile(); + + VectorGL 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) @@ -151,25 +154,41 @@ template VectorGL::VectorGL(const Flags flag 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 VectorGL::VectorGL(CompileState&& cs) +: VectorGL{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"); _backgroundColorUniform = uniformLocation("backgroundColor"); _colorUniform = uniformLocation("color"); @@ -182,10 +201,10 @@ template VectorGL::VectorGL(const Flags flag { setUniform(uniformLocation("vectorTexture"), TextureUnit); #ifndef MAGNUM_TARGET_GLES2 - if(flags >= Flag::UniformBuffers) { + if(_flags >= Flag::UniformBuffers) { setUniformBlockBinding(uniformBlockIndex("TransformationProjection"), TransformationProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Draw"), DrawBufferBinding); - if(flags & Flag::TextureTransformation) + if(_flags & Flag::TextureTransformation) setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding); setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding); } @@ -195,13 +214,13 @@ template VectorGL::VectorGL(const Flags flag /* 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}); /* Background color is zero by default */ setColor(Color4{1.0f}); @@ -209,10 +228,6 @@ template VectorGL::VectorGL(const Flags flag #endif } -#ifndef MAGNUM_TARGET_GLES2 -template VectorGL::VectorGL(const Flags flags): VectorGL{flags, 1, 1} {} -#endif - template VectorGL& VectorGL::setTransformationProjectionMatrix(const MatrixTypeFor& matrix) { #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index e1157ba9d..ae4a3aa6c 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.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" @@ -221,7 +222,7 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL * @ref VectorGL(Flags, UnsignedInt, UnsignedInt) with @p materialCount * and @p drawCount set to @cpp 1 @ce. */ - explicit VectorGL(Flags flags = {}); + explicit VectorGL(Flags flags = {}) : VectorGL{compile(flags)} {}; #ifndef MAGNUM_TARGET_GLES2 /** @@ -256,7 +257,8 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL for this might be too confusing); what if some parameters won't be (unsigned) integers? like a string with shader extensions? make a whole Configuration class? */ - explicit VectorGL(Flags flags, UnsignedInt materialCount, UnsignedInt drawCount); + explicit VectorGL(Flags flags, UnsignedInt materialCount, UnsignedInt drawCount) : + VectorGL{compile(flags, materialCount, drawCount)} {} #endif /** @@ -273,6 +275,22 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL */ explicit VectorGL(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {} + class CompileState; + + explicit VectorGL(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) { + return compile(flags, 1, 1); + } + #endif + /** @brief Copying is not allowed */ VectorGL(const VectorGL&) = delete; @@ -554,6 +572,9 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL #endif private: + /* Creates the GL shader program object but nothing else. Internal, used by compile(). */ + explicit VectorGL(NoInitT) {} + /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES using GL::AbstractShaderProgram::drawTransformFeedback; @@ -577,6 +598,19 @@ template class MAGNUM_SHADERS_EXPORT VectorGL: public GL #endif }; +template class VectorGL::CompileState : public VectorGL { +private: + friend class VectorGL; + + explicit CompileState(NoCreateT) : VectorGL{NoCreate}, _vert{NoCreate}, _frag{NoCreate} {} + + CompileState(VectorGL&& shader, GL::Shader&& vert, GL::Shader&& frag, GL::Version version) : + VectorGL{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)}, _version{version} {} + + GL::Shader _vert, _frag; + GL::Version _version; +}; + /** @brief Two-dimensional vector OpenGL shader @m_since_latest