From 1caf96a3475383651e266788be265610855a8406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 8 Jan 2016 22:58:31 +0100 Subject: [PATCH] Finally completed ARB_uniform_buffer_object. Also with ES3/WebGL2 port. --- doc/opengl-mapping.dox | 4 +- doc/opengl-support.dox | 2 +- src/Magnum/AbstractShaderProgram.cpp | 9 + src/Magnum/AbstractShaderProgram.h | 99 +++++++++++ .../Test/AbstractShaderProgramGLTest.cpp | 159 +++++++++++++++++- .../UniformBlockShader.frag | 11 ++ .../UniformBlockShader.vert | 10 ++ .../resources.conf | 6 + 8 files changed, 296 insertions(+), 4 deletions(-) create mode 100644 src/Magnum/Test/AbstractShaderProgramGLTestFiles/UniformBlockShader.frag create mode 100644 src/Magnum/Test/AbstractShaderProgramGLTestFiles/UniformBlockShader.vert diff --git a/doc/opengl-mapping.dox b/doc/opengl-mapping.dox index 1edffe2be..90d45f817 100644 --- a/doc/opengl-mapping.dox +++ b/doc/opengl-mapping.dox @@ -219,7 +219,7 @@ OpenGL function | Matching API @fn_gl{GetTransformFeedback} | not queryable, @ref TransformFeedback::attachBuffer() and @ref TransformFeedback::attachBuffers() setters only @fn_gl{GetTransformFeedbackVarying} | not queryable, @ref AbstractShaderProgram::setTransformFeedbackOutputs() setter only @fn_gl{GetUniform}, \n `glGetnUniform()`, \n @fn_gl_extension{GetnUniform,ARB,robustness} | not queryable, @ref AbstractShaderProgram::setUniform() setter only -@fn_gl{GetUniformBlockIndex} | | +@fn_gl{GetUniformBlockIndex} | @ref AbstractShaderProgram::uniformBlockIndex() @fn_gl{GetUniformIndices} | | @fn_gl{GetUniformLocation} | @ref AbstractShaderProgram::uniformLocation() @fn_gl{GetUniformSubroutine} | | @@ -350,7 +350,7 @@ OpenGL function | Matching API --------------------------------------- | ------------ @fn_gl{Uniform}, \n @fn_gl{ProgramUniform}, \n @fn_gl_extension{ProgramUniform,EXT,direct_state_access} | @ref AbstractShaderProgram::setUniform() @fn_gl_extension{UniformHandle,ARB,bindless_texture}, \n @fn_gl_extension{ProgramUniformHandle,ARB,bindless_texture} | | -@fn_gl{UniformBlockBinding} | | +@fn_gl{UniformBlockBinding} | @ref AbstractShaderProgram::setUniformBlockBinding() @fn_gl{UniformSubroutines} | | @fn_gl{UseProgram} | @ref Mesh::draw(), @ref MeshView::draw() @fn_gl{UseProgramStages} | | diff --git a/doc/opengl-support.dox b/doc/opengl-support.dox index f8ac8924b..cfa0c079e 100644 --- a/doc/opengl-support.dox +++ b/doc/opengl-support.dox @@ -80,7 +80,7 @@ GLSL 1.40 | done @extension{ARB,texture_rectangle} | done @extension{ARB,draw_instanced} | done @extension{ARB,texture_buffer_object} | done -@extension{ARB,uniform_buffer_object} | missing uniform block binding +@extension{ARB,uniform_buffer_object} | done @extension{ARB,copy_buffer} | done @extension{EXT,texture_snorm} | done @extension{NV,primitive_restart} | | diff --git a/src/Magnum/AbstractShaderProgram.cpp b/src/Magnum/AbstractShaderProgram.cpp index 709dc3263..933c1b47e 100644 --- a/src/Magnum/AbstractShaderProgram.cpp +++ b/src/Magnum/AbstractShaderProgram.cpp @@ -401,6 +401,15 @@ Int AbstractShaderProgram::uniformLocationInternal(const Containers::ArrayView name) { + const GLuint index = glGetUniformBlockIndex(_id, name); + if(index == GL_INVALID_INDEX) + Warning() << "AbstractShaderProgram: index of uniform block \'" << Debug::nospace << std::string{name, name.size()} << Debug::nospace << "\' cannot be retrieved"; + return index; +} +#endif + void AbstractShaderProgram::setUniform(const Int location, const Containers::ArrayView values) { (this->*Context::current().state().shaderProgram->uniform1fvImplementation)(location, values.size(), values); } diff --git a/src/Magnum/AbstractShaderProgram.h b/src/Magnum/AbstractShaderProgram.h index f5178040b..ac0552193 100644 --- a/src/Magnum/AbstractShaderProgram.h +++ b/src/Magnum/AbstractShaderProgram.h @@ -238,6 +238,57 @@ Int normalMatrixUniform = uniformLocation("normalMatrix"); @requires_gles Explicit uniform location is not supported in WebGL. Use @ref uniformLocation() instead. +@anchor AbstractShaderProgram-uniform-block-binding +### Uniform block bindings + +The preferred workflow is to specify uniform block binding directly in the +shader code, e.g.: +@code +// GLSL 4.20, GLSL ES 3.10 or +#extension GL_ARB_shading_language_420pack: require +layout(std140, binding = 0) uniform matrices { + mat4 projectionMatrix; + mat4 transformationMatrix; +}; +layout(std140, binding = 1) uniform material { + vec4 diffuse; + vec4 specular; +}; +@endcode + +If you don't have the required version/extension, declare the uniform blocks +without the `layout()` qualifier, get uniform block index using +@ref uniformBlockIndex() and then map it to the uniform buffer binding using +@ref setUniformBlockBinding(). Note that additional syntax changes may be +needed for GLSL ES. +@code +layout(std140) uniform matrices { + mat4 projectionMatrix; + mat4 transformationMatrix; +}; +layout(std140) uniform material { + vec4 diffuse; + vec4 specular; +}; +@endcode +@code +setUniformBlockBinding(uniformBlockIndex("matrices"), 0); +setUniformBlockBinding(uniformBlockIndex("material"), 1); +@endcode + +@see @ref Buffer::maxUniformBindings() +@requires_gl31 Extension @extension{ARB,uniform_buffer_object} +@requires_gl42 Extension @extension{ARB,shading_language_420pack} for explicit + uniform block binding instead of using @ref uniformBlockIndex() and + @ref setUniformBlockBinding(). +@requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. +@requires_gles31 Explicit uniform block binding is not supported in OpenGL ES + 3.0 and older. Use @ref uniformBlockIndex() and @ref setUniformBlockBinding() + instead. +@requires_webgl20 Uniform buffers are not available in WebGL 1.0. +@requires_gles Explicit uniform block binding is not supported in WebGL. Use + @ref uniformBlockIndex() and @ref setUniformBlockBinding() instead. + @anchor AbstractShaderProgram-texture-units ### Specifying texture binding units @@ -878,6 +929,32 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject { return uniformLocationInternal({name, size - 1}); } + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Get uniform block index + * @param name Uniform block name + * + * If given uniform block name is not found in the linked shader, a + * warning is printed and `0xffffffffu` is returned. + * @see @ref setUniformBlockBinding(), @fn_gl{GetUniformBlockIndex} + * @requires_gl31 Extension @extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + * @deprecated_gl Preferred usage is to specify uniform block binding + * explicitly in the shader instead of using this function. See + * @ref AbstractShaderProgram-uniform-block-binding "class documentation" + * for more information. + */ + UnsignedInt uniformBlockIndex(const std::string& name) { + return uniformBlockIndexInternal({name.data(), name.size()}); + } + + /** @overload */ + template UnsignedInt uniformBlockIndex(const char(&name)[size]) { + return uniformBlockIndexInternal({name, size - 1}); + } + #endif + /** * @brief Set uniform value * @param location Uniform location @@ -1012,6 +1089,27 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject { } #endif + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Set uniform block binding + * @param index Uniform block index + * @param binding Uniform block binding + * + * @see @ref uniformBlockIndex(), @ref Buffer::maxUniformBindings(), + * @fn_gl{UniformBlockBinding} + * @requires_gl31 Extension @extension{ARB,uniform_buffer_object} + * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. + * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. + * @deprecated_gl Preferred usage is to specify uniform block binding + * explicitly in the shader instead of using this function. See + * @ref AbstractShaderProgram-uniform-block-binding "class documentation" + * for more information. + */ + void setUniformBlockBinding(UnsignedInt index, UnsignedInt binding) { + glUniformBlockBinding(_id, index, binding); + } + #endif + private: #ifndef MAGNUM_TARGET_WEBGL AbstractShaderProgram& setLabelInternal(Containers::ArrayView label); @@ -1021,6 +1119,7 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject { void bindFragmentDataLocationIndexedInternal(UnsignedInt location, UnsignedInt index, Containers::ArrayView name); void bindFragmentDataLocationInternal(UnsignedInt location, Containers::ArrayView name); Int uniformLocationInternal(Containers::ArrayView name); + UnsignedInt uniformBlockIndexInternal(Containers::ArrayView name); #ifndef MAGNUM_BUILD_DEPRECATED void use(); diff --git a/src/Magnum/Test/AbstractShaderProgramGLTest.cpp b/src/Magnum/Test/AbstractShaderProgramGLTest.cpp index 3c1bc5e89..6989508bc 100644 --- a/src/Magnum/Test/AbstractShaderProgramGLTest.cpp +++ b/src/Magnum/Test/AbstractShaderProgramGLTest.cpp @@ -56,6 +56,12 @@ struct AbstractShaderProgramGLTest: AbstractOpenGLTester { void uniformVector(); void uniformMatrix(); void uniformArray(); + + #ifndef MAGNUM_TARGET_GLES2 + void createUniformBlocks(); + void uniformBlockIndexNotFound(); + void uniformBlock(); + #endif }; AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() { @@ -75,7 +81,14 @@ AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() { &AbstractShaderProgramGLTest::uniform, &AbstractShaderProgramGLTest::uniformVector, &AbstractShaderProgramGLTest::uniformMatrix, - &AbstractShaderProgramGLTest::uniformArray}); + &AbstractShaderProgramGLTest::uniformArray, + + #ifndef MAGNUM_TARGET_GLES2 + &AbstractShaderProgramGLTest::createUniformBlocks, + &AbstractShaderProgramGLTest::uniformBlockIndexNotFound, + &AbstractShaderProgramGLTest::uniformBlock + #endif + }); } namespace { @@ -148,6 +161,9 @@ namespace { #endif using AbstractShaderProgram::link; using AbstractShaderProgram::uniformLocation; + #ifndef MAGNUM_TARGET_GLES2 + using AbstractShaderProgram::uniformBlockIndex; + #endif }; } @@ -473,6 +489,147 @@ void AbstractShaderProgramGLTest::uniformArray() { MAGNUM_VERIFY_NO_ERROR(); } +#ifndef MAGNUM_TARGET_GLES2 +void AbstractShaderProgramGLTest::createUniformBlocks() { + Utility::Resource rs("AbstractShaderProgramGLTest"); + + Shader vert( + #ifndef MAGNUM_TARGET_GLES + Version::GL310 + #else + Version::GLES300 + #endif + , Shader::Type::Vertex); + vert.addSource(rs.get("UniformBlockShader.vert")); + const bool vertCompiled = vert.compile(); + + Shader frag( + #ifndef MAGNUM_TARGET_GLES + Version::GL310 + #else + Version::GLES300 + #endif + , Shader::Type::Fragment); + frag.addSource(rs.get("UniformBlockShader.frag")); + const bool fragCompiled = frag.compile(); + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_VERIFY(vertCompiled); + CORRADE_VERIFY(fragCompiled); + + MyPublicShader program; + program.attachShaders({vert, frag}); + + MAGNUM_VERIFY_NO_ERROR(); + + const bool linked = program.link(); + const bool valid = program.validate().first; + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_VERIFY(linked); + { + #ifdef CORRADE_TARGET_APPLE + CORRADE_EXPECT_FAIL("OSX drivers need insane amount of state to validate properly."); + #endif + CORRADE_VERIFY(valid); + } + + const Int matricesUniformBlock = program.uniformBlockIndex("matrices"); + const Int materialUniformBlock = program.uniformBlockIndex("material"); + + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_VERIFY(matricesUniformBlock >= 0); + CORRADE_VERIFY(materialUniformBlock >= 0); +} + +void AbstractShaderProgramGLTest::uniformBlockIndexNotFound() { + MyPublicShader program; + + Shader vert( + #ifndef MAGNUM_TARGET_GLES + Version::GL310 + #else + Version::GLES200 + #endif + , Shader::Type::Vertex); + Shader frag( + #ifndef MAGNUM_TARGET_GLES + Version::GL310 + #else + Version::GLES200 + #endif + , Shader::Type::Fragment); + vert.addSource("void main() { gl_Position = vec4(0.0); }"); + frag.addSource("out vec4 color;\n" + "void main() { color = vec4(1.0); }"); + + CORRADE_VERIFY(Shader::compile({vert, frag})); + program.attachShaders({vert, frag}); + CORRADE_VERIFY(program.link()); + + std::ostringstream out; + Warning::setOutput(&out); + program.uniformBlockIndex("nonexistent"); + program.uniformBlockIndex(std::string{"another"}); + CORRADE_COMPARE(out.str(), + "AbstractShaderProgram: index of uniform block 'nonexistent' cannot be retrieved\n" + "AbstractShaderProgram: index of uniform block 'another' cannot be retrieved\n"); +} + +namespace { + struct UniformBlockShader: AbstractShaderProgram { + explicit UniformBlockShader(); + + using AbstractShaderProgram::setUniformBlockBinding; + + Int matricesUniformBlock, + materialUniformBlock; + }; +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +UniformBlockShader::UniformBlockShader() { + Utility::Resource rs("AbstractShaderProgramGLTest"); + + Shader vert( + #ifndef MAGNUM_TARGET_GLES + Version::GL310 + #else + Version::GLES300 + #endif + , Shader::Type::Vertex); + Shader frag( + #ifndef MAGNUM_TARGET_GLES + Version::GL310 + #else + Version::GLES300 + #endif + , Shader::Type::Fragment); + vert.addSource(rs.get("UniformBlockShader.vert")); + frag.addSource(rs.get("UniformBlockShader.frag")); + + Shader::compile({vert, frag}); + attachShaders({vert, frag}); + + link(); + + matricesUniformBlock = uniformBlockIndex("matrices"); + materialUniformBlock = uniformBlockIndex("material"); +} +#endif + +void AbstractShaderProgramGLTest::uniformBlock() { + UniformBlockShader shader; + + MAGNUM_VERIFY_NO_ERROR(); + + shader.setUniformBlockBinding(shader.matricesUniformBlock, 0); + shader.setUniformBlockBinding(shader.materialUniformBlock, 1); + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + }} MAGNUM_GL_TEST_MAIN(Magnum::Test::AbstractShaderProgramGLTest) diff --git a/src/Magnum/Test/AbstractShaderProgramGLTestFiles/UniformBlockShader.frag b/src/Magnum/Test/AbstractShaderProgramGLTestFiles/UniformBlockShader.frag new file mode 100644 index 000000000..e850f3bc3 --- /dev/null +++ b/src/Magnum/Test/AbstractShaderProgramGLTestFiles/UniformBlockShader.frag @@ -0,0 +1,11 @@ +layout(std140) uniform material { + lowp vec4 color; + lowp vec4 additions[3]; + lowp float multiplier; +}; + +out lowp vec4 fragColor; + +void main() { + fragColor = color*multiplier + additions[0] + additions[1] + additions[2]; +} diff --git a/src/Magnum/Test/AbstractShaderProgramGLTestFiles/UniformBlockShader.vert b/src/Magnum/Test/AbstractShaderProgramGLTestFiles/UniformBlockShader.vert new file mode 100644 index 000000000..3e0232d5f --- /dev/null +++ b/src/Magnum/Test/AbstractShaderProgramGLTestFiles/UniformBlockShader.vert @@ -0,0 +1,10 @@ +in mediump vec4 position; + +layout(std140) uniform matrices { + mediump mat4 transformation; + mediump mat4 projection; +}; + +void main() { + gl_Position = projection*transformation*position; +} diff --git a/src/Magnum/Test/AbstractShaderProgramGLTestFiles/resources.conf b/src/Magnum/Test/AbstractShaderProgramGLTestFiles/resources.conf index 80d20b68e..4bcbfb585 100644 --- a/src/Magnum/Test/AbstractShaderProgramGLTestFiles/resources.conf +++ b/src/Magnum/Test/AbstractShaderProgramGLTestFiles/resources.conf @@ -8,3 +8,9 @@ filename=MyShader.vert [file] filename=MyShaderFragmentOutputs.frag + +[file] +filename=UniformBlockShader.frag + +[file] +filename=UniformBlockShader.vert