diff --git a/doc/changelog.dox b/doc/changelog.dox index 5a6a92c1c..12175f7c2 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -1137,6 +1137,12 @@ See also: - As part of the ongoing STL header dependency cleanup, the following breaking changes related to @ref std::string and a @ref std::vector are done: + - @ref GL::AbstractShaderProgram::validate() now returns a + @relativeref{Corrade,Containers::String} instead of a @ref std::string; + @ref GL::Shader::sources() now returns a + @relativeref{Corrade,Containers::StringIterable} instead of a + @ref std::vector of a @ref std::string See also [mosra/magnum#499](https://github.com/mosra/magnum/pull/499) + and [mosra/magnum#608](https://github.com/mosra/magnum/pull/608). - @ref GL::Context::vendorString(), @relativeref{GL::Context,rendererString()}, @relativeref{GL::Context,versionString()}, diff --git a/doc/snippets/MagnumGL.cpp b/doc/snippets/MagnumGL.cpp index e0c682d53..423de2efd 100644 --- a/doc/snippets/MagnumGL.cpp +++ b/doc/snippets/MagnumGL.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include diff --git a/doc/snippets/MagnumShaders-gl.cpp b/doc/snippets/MagnumShaders-gl.cpp index 9e85f9db3..617402907 100644 --- a/doc/snippets/MagnumShaders-gl.cpp +++ b/doc/snippets/MagnumShaders-gl.cpp @@ -28,7 +28,8 @@ #include #include #include -#include +#include +#include #include "Magnum/ImageView.h" #include "Magnum/PixelFormat.h" @@ -683,7 +684,7 @@ bindAttributeLocation(Shaders::GenericGL3D::Normal::Location, "normal"); { GL::Shader vert{GL::Version::None, GL::Shader::Type::Vertex}; /* [GenericGL-custom-preprocessor] */ -vert.addSource(Utility::formatString( +vert.addSource(Utility::format( "#define POSITION_ATTRIBUTE_LOCATION {}\n" "#define NORMAL_ATTRIBUTE_LOCATION {}\n", Shaders::GenericGL3D::Position::Location, diff --git a/src/Magnum/DebugTools/TextureImage.cpp b/src/Magnum/DebugTools/TextureImage.cpp index df2bf5020..bf01784f5 100644 --- a/src/Magnum/DebugTools/TextureImage.cpp +++ b/src/Magnum/DebugTools/TextureImage.cpp @@ -37,7 +37,6 @@ #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) #include #include -#include /** @todo remove once GL::Shader is -free */ #include #include "Magnum/GL/AbstractShaderProgram.h" diff --git a/src/Magnum/GL/AbstractShaderProgram.cpp b/src/Magnum/GL/AbstractShaderProgram.cpp index fb9ad3b68..611820117 100644 --- a/src/Magnum/GL/AbstractShaderProgram.cpp +++ b/src/Magnum/GL/AbstractShaderProgram.cpp @@ -29,16 +29,12 @@ #include #include -#include -#ifndef MAGNUM_TARGET_WEBGL -#include -#endif -#include /** @todo remove once -free */ #ifdef MAGNUM_BUILD_DEPRECATED #include #endif -#include -#include +#include +#include +#include #include "Magnum/GL/Context.h" #include "Magnum/GL/Extensions.h" @@ -343,7 +339,7 @@ AbstractShaderProgram& AbstractShaderProgram::setLabel(const Containers::StringV } #endif -std::pair AbstractShaderProgram::validate() { +std::pair AbstractShaderProgram::validate() { glValidateProgram(_id); /* Check validation status */ @@ -351,18 +347,21 @@ std::pair AbstractShaderProgram::validate() { glGetProgramiv(_id, GL_VALIDATE_STATUS, &success); glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &logLength); - /* Error or warning message. The string is returned null-terminated, scrap - the \0 at the end afterwards */ - std::string message(logLength, '\n'); - if(message.size() > 1) - glGetProgramInfoLog(_id, message.size(), nullptr, &message[0]); - message.resize(Math::max(logLength, 1)-1); + /* Error or warning message. The length is reported including the null + terminator and the string implicitly has a storage for that, thus + specify one byte less. */ + Containers::String message{NoInit, std::size_t(Math::max(logLength, 1) - 1)}; + if(logLength > 1) + glGetProgramInfoLog(_id, logLength, nullptr, message.data()); /* On some drivers (such as SwiftShader) the message contains a newline at the end, on some (such as Mesa) it doesn't. Same as with link() or compile() message trimming it doesn't make sense to add driver-specific workarounds for this, so just trim it always. */ - return {success, Utility::String::trim(std::move(message))}; + /** @todo this allocates a new string, revisit once String is capable of + trimming in-place, e.g. `std::move(message).trimmed()` would just + shift the data around */ + return {success, message.trimmed()}; } AbstractShaderProgram& AbstractShaderProgram::draw(Mesh& mesh) { @@ -547,48 +546,62 @@ void AbstractShaderProgram::attachShaders(const Containers::Iterable& sh for(Shader& s: shaders) attachShader(s); } -void AbstractShaderProgram::bindAttributeLocationInternal(const UnsignedInt location, const Containers::ArrayView name) { - glBindAttribLocation(_id, location, name); +void AbstractShaderProgram::bindAttributeLocation(const UnsignedInt location, const Containers::StringView name) { + glBindAttribLocation(_id, location, Containers::String::nullTerminatedView(name).data()); } #ifndef MAGNUM_TARGET_GLES -void AbstractShaderProgram::bindFragmentDataLocationInternal(const UnsignedInt location, const Containers::ArrayView name) { - glBindFragDataLocation(_id, location, name); +void AbstractShaderProgram::bindFragmentDataLocation(const UnsignedInt location, const Containers::StringView name) { + glBindFragDataLocation(_id, location, Containers::String::nullTerminatedView(name).data()); } -void AbstractShaderProgram::bindFragmentDataLocationIndexedInternal(const UnsignedInt location, UnsignedInt index, const Containers::ArrayView name) { - glBindFragDataLocationIndexed(_id, location, index, name); +void AbstractShaderProgram::bindFragmentDataLocationIndexed(const UnsignedInt location, UnsignedInt index, const Containers::StringView name) { + glBindFragDataLocationIndexed(_id, location, index, Containers::String::nullTerminatedView(name).data()); } #endif #ifndef MAGNUM_TARGET_GLES2 -void AbstractShaderProgram::setTransformFeedbackOutputs(const Containers::ArrayView outputs, const TransformFeedbackBufferMode bufferMode) { +void AbstractShaderProgram::setTransformFeedbackOutputs(const Containers::StringIterable& outputs, const TransformFeedbackBufferMode bufferMode) { (this->*Context::current().state().shaderProgram.transformFeedbackVaryingsImplementation)(outputs, bufferMode); } -void AbstractShaderProgram::setTransformFeedbackOutputs(const std::initializer_list outputs, const TransformFeedbackBufferMode bufferMode) { - setTransformFeedbackOutputs(Containers::arrayView(outputs), bufferMode); -} - -void AbstractShaderProgram::transformFeedbackVaryingsImplementationDefault(const Containers::ArrayView outputs, const TransformFeedbackBufferMode bufferMode) { - /** @todo VLAs */ - Containers::Array names{outputs.size()}; - - Int i = 0; - for(const std::string& output: outputs) names[i++] = output.data(); +void AbstractShaderProgram::transformFeedbackVaryingsImplementationDefault(const Containers::StringIterable& outputs, const TransformFeedbackBufferMode bufferMode) { + Containers::ArrayView nameData; + Containers::ArrayView names; + Containers::ArrayTuple data{ + {NoInit, outputs.size(), nameData}, + {NoInit, outputs.size(), names} + }; + for(std::size_t i = 0; i != outputs.size(); ++i) { + new(&nameData[i]) Containers::String{Containers::String::nullTerminatedView(outputs[i])}; + names[i] = nameData[i].data(); + } glTransformFeedbackVaryings(_id, outputs.size(), names, GLenum(bufferMode)); } #ifdef CORRADE_TARGET_WINDOWS -void AbstractShaderProgram::transformFeedbackVaryingsImplementationDanglingWorkaround(const Containers::ArrayView outputs, const TransformFeedbackBufferMode bufferMode) { +void AbstractShaderProgram::transformFeedbackVaryingsImplementationDanglingWorkaround(const Containers::StringIterable& outputs, const TransformFeedbackBufferMode bufferMode) { /* NVidia on Windows doesn't copy the names when calling - glTransformFeedbackVaryings() so it then fails at link time because the - char* are dangling. We have to do the copy on the engine side and keep - the values until link time (which can happen any time and multiple - times, so basically for the remaining lifetime of the shader program) */ - _transformFeedbackVaryingNames.assign(outputs.begin(), outputs.end()); + glTransformFeedbackVaryings() so it then might fail at link time because + the char* get dangling. We have to do the copy on the engine side and + keep the values until link time (which can happen any time and multiple + times, so basically for the remaining lifetime of the shader program). + + Compared to the above case we thus do a copy also if the view is not + global in addition to not null-terminated, and we keep the string memory + in a member variable. */ + Containers::ArrayView nameData; + Containers::ArrayView names; + _transformFeedbackVaryingNames = Containers::ArrayTuple{ + {NoInit, outputs.size(), nameData}, + {NoInit, outputs.size(), names} + }; + for(std::size_t i = 0; i != outputs.size(); ++i) { + new(&nameData[i]) Containers::String{Containers::String::nullTerminatedGlobalView(outputs[i])}; + names[i] = nameData[i].data(); + } - transformFeedbackVaryingsImplementationDefault({_transformFeedbackVaryingNames.data(), _transformFeedbackVaryingNames.size()}, bufferMode); + glTransformFeedbackVaryings(_id, outputs.size(), names, GLenum(bufferMode)); } #endif #endif @@ -613,12 +626,12 @@ bool AbstractShaderProgram::checkLink(const Containers::Iterable& shader glGetProgramiv(_id, GL_LINK_STATUS, &success); glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &logLength); - /* Error or warning message. The string is returned null-terminated, - strip the \0 at the end afterwards. */ - std::string message(logLength, '\n'); - if(message.size() > 1) - glGetProgramInfoLog(_id, message.size(), nullptr, &message[0]); - message.resize(Math::max(logLength, 1)-1); + /* Error or warning message. The length is reported including the null + terminator and the string implicitly has a storage for that, thus + specify one byte less. */ + Containers::String message{NoInit, std::size_t(Math::max(logLength, 1)) - 1}; + if(logLength > 1) + glGetProgramInfoLog(_id, logLength, nullptr, message.data()); /* Some drivers are chatty and can't keep shut when there's nothing to be said, handle that as well. */ @@ -662,16 +675,16 @@ bool AbstractShaderProgram::isLinkFinished() { return success == GL_TRUE; } -void AbstractShaderProgram::cleanLogImplementationNoOp(std::string&) {} +void AbstractShaderProgram::cleanLogImplementationNoOp(Containers::String&) {} #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) -void AbstractShaderProgram::cleanLogImplementationIntelWindows(std::string& message) { +void AbstractShaderProgram::cleanLogImplementationIntelWindows(Containers::String& message) { if(message == "No errors.\n") message = {}; } #endif #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) -void AbstractShaderProgram::cleanLogImplementationAngle(std::string& message) { +void AbstractShaderProgram::cleanLogImplementationAngle(Containers::String& message) { if(message == "\n") message = {}; } #endif @@ -680,18 +693,18 @@ void AbstractShaderProgram::completionStatusImplementationFallback(GLuint, GLenu *value = GL_TRUE; } -Int AbstractShaderProgram::uniformLocationInternal(const Containers::ArrayView name) { - const GLint location = glGetUniformLocation(_id, name); +Int AbstractShaderProgram::uniformLocation(const Containers::StringView name) { + const GLint location = glGetUniformLocation(_id, Containers::String::nullTerminatedView(name).data()); if(location == -1) - Warning{} << "GL::AbstractShaderProgram: location of uniform \'" << Debug::nospace << std::string{name, name.size()} << Debug::nospace << "\' cannot be retrieved"; + Warning{} << "GL::AbstractShaderProgram: location of uniform \'" << Debug::nospace << name << Debug::nospace << "\' cannot be retrieved"; return location; } #ifndef MAGNUM_TARGET_GLES2 -UnsignedInt AbstractShaderProgram::uniformBlockIndexInternal(const Containers::ArrayView name) { - const GLuint index = glGetUniformBlockIndex(_id, name); +UnsignedInt AbstractShaderProgram::uniformBlockIndex(const Containers::StringView name) { + const GLuint index = glGetUniformBlockIndex(_id, Containers::String::nullTerminatedView(name).data()); if(index == GL_INVALID_INDEX) - Warning{} << "GL::AbstractShaderProgram: index of uniform block \'" << Debug::nospace << std::string{name, name.size()} << Debug::nospace << "\' cannot be retrieved"; + Warning{} << "GL::AbstractShaderProgram: index of uniform block \'" << Debug::nospace << name << Debug::nospace << "\' cannot be retrieved"; return index; } #endif diff --git a/src/Magnum/GL/AbstractShaderProgram.h b/src/Magnum/GL/AbstractShaderProgram.h index b800a1cca..639eac9e8 100644 --- a/src/Magnum/GL/AbstractShaderProgram.h +++ b/src/Magnum/GL/AbstractShaderProgram.h @@ -31,15 +31,12 @@ * @brief Class @ref Magnum::GL::AbstractShaderProgram, macro @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(), @ref MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DISPATCH_IMPLEMENTATION() */ -#include -#include - #include "Magnum/Tags.h" #include "Magnum/GL/AbstractObject.h" #include "Magnum/GL/GL.h" #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES2) -#include +#include #endif #ifdef MAGNUM_BUILD_DEPRECATED @@ -47,8 +44,14 @@ /* For attachShaders(), which used to take a std::initializer_list, and draw({MeshView}) as well */ #include -/* For label() / setLabel(), which used to be a std::string */ +/* For label() / setLabel() and all uniform/attribute/... name stuff that used + to be a std::string. Since that's all only function parameters and no return + types, it should cover all cases. */ #include +/* For setTransformFeedbackOutputs(), which used to take + std::initializer_list. Usually people passed C string literals, + so it should convert directly to a StringIterable. */ +#include #endif namespace Magnum { namespace GL { @@ -831,7 +834,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @def_gl{VALIDATE_STATUS}, @def_gl{INFO_LOG_LENGTH}, * @fn_gl_keyword{GetProgramInfoLog} */ - std::pair validate(); + std::pair validate(); /** * @brief Draw a mesh @@ -1432,14 +1435,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @ref GL-AbstractShaderProgram-attribute-location "class documentation" * for more information. */ - void bindAttributeLocation(UnsignedInt location, const std::string& name) { - bindAttributeLocationInternal(location, {name.data(), name.size()}); - } - - /** @overload */ - template void bindAttributeLocation(UnsignedInt location, const char(&name)[size]) { - bindAttributeLocationInternal(location, {name, size - 1}); - } + void bindAttributeLocation(UnsignedInt location, Containers::StringView name); #ifndef MAGNUM_TARGET_GLES /** @@ -1461,14 +1457,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @requires_gl Multiple blend function inputs are not available in * OpenGL ES or WebGL. */ - void bindFragmentDataLocationIndexed(UnsignedInt location, UnsignedInt index, const std::string& name) { - bindFragmentDataLocationIndexedInternal(location, index, {name.data(), name.size()}); - } - - /** @overload */ - template void bindFragmentDataLocationIndexed(UnsignedInt location, UnsignedInt index, const char(&name)[size]) { - bindFragmentDataLocationIndexedInternal(location, index, {name, size - 1}); - } + void bindFragmentDataLocationIndexed(UnsignedInt location, UnsignedInt index, Containers::StringView name); /** * @brief Bind fragment data to given location and first color input index @@ -1488,15 +1477,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * in OpenGL ES 2.0 and @webgl_extension{WEBGL,draw_buffers} in * WebGL 1.0. */ - void bindFragmentDataLocation(UnsignedInt location, const std::string& name) { - bindFragmentDataLocationInternal(location, {name.data(), name.size()}); - } - - /** @overload */ - template void bindFragmentDataLocation(UnsignedInt location, const char(&name)[size]) { - /* Not using const char* parameter, because this way it avoids most accidents with non-zero-terminated strings */ - bindFragmentDataLocationInternal(location, {name, size - 1}); - } + void bindFragmentDataLocation(UnsignedInt location, Containers::StringView name); #endif #ifndef MAGNUM_TARGET_GLES2 @@ -1531,8 +1512,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @requires_gl Special output names `gl_NextBuffer` and * `gl_SkipComponents#` are not available in OpenGL ES or WebGL. */ - void setTransformFeedbackOutputs(Containers::ArrayView outputs, TransformFeedbackBufferMode bufferMode); - void setTransformFeedbackOutputs(std::initializer_list outputs, TransformFeedbackBufferMode bufferMode); /**< @overload */ + void setTransformFeedbackOutputs(const Containers::StringIterable& outputs, TransformFeedbackBufferMode bufferMode); #endif /** @@ -1600,14 +1580,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @ref GL-AbstractShaderProgram-uniform-location "class documentation" * for more information. */ - Int uniformLocation(const std::string& name) { - return uniformLocationInternal({name.data(), name.size()}); - } - - /** @overload */ - template Int uniformLocation(const char(&name)[size]) { - return uniformLocationInternal({name, size - 1}); - } + Int uniformLocation(Containers::StringView name); #ifndef MAGNUM_TARGET_GLES2 /** @@ -1626,14 +1599,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { * @ref GL-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}); - } + UnsignedInt uniformBlockIndex(Containers::StringView name); #endif /** @@ -1910,27 +1876,21 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { #endif private: - void bindAttributeLocationInternal(UnsignedInt location, Containers::ArrayView name); - #ifndef MAGNUM_TARGET_GLES - void bindFragmentDataLocationIndexedInternal(UnsignedInt location, UnsignedInt index, Containers::ArrayView name); - void bindFragmentDataLocationInternal(UnsignedInt location, Containers::ArrayView name); - #endif - Int uniformLocationInternal(Containers::ArrayView name); - UnsignedInt uniformBlockIndexInternal(Containers::ArrayView name); - #ifndef MAGNUM_TARGET_GLES2 - void MAGNUM_GL_LOCAL transformFeedbackVaryingsImplementationDefault(Containers::ArrayView outputs, TransformFeedbackBufferMode bufferMode); + void MAGNUM_GL_LOCAL transformFeedbackVaryingsImplementationDefault(const Containers::StringIterable& outputs, TransformFeedbackBufferMode bufferMode); #ifdef CORRADE_TARGET_WINDOWS - void MAGNUM_GL_LOCAL transformFeedbackVaryingsImplementationDanglingWorkaround(Containers::ArrayView outputs, TransformFeedbackBufferMode bufferMode); + /* See the "nv-windows-dangling-transform-feedback-varying-names" + workaround */ + void MAGNUM_GL_LOCAL transformFeedbackVaryingsImplementationDanglingWorkaround(const Containers::StringIterable& outputs, TransformFeedbackBufferMode bufferMode); #endif #endif - static MAGNUM_GL_LOCAL void cleanLogImplementationNoOp(std::string& message); + static MAGNUM_GL_LOCAL void cleanLogImplementationNoOp(Containers::String& message); #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) - static MAGNUM_GL_LOCAL void cleanLogImplementationIntelWindows(std::string& message); + static MAGNUM_GL_LOCAL void cleanLogImplementationIntelWindows(Containers::String& message); #endif #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) - static MAGNUM_GL_LOCAL void cleanLogImplementationAngle(std::string& message); + static MAGNUM_GL_LOCAL void cleanLogImplementationAngle(Containers::String& message); #endif MAGNUM_GL_LOCAL static void APIENTRY completionStatusImplementationFallback(GLuint, GLenum, GLint*); @@ -2008,7 +1968,7 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject { #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES2) /* Needed for the nv-windows-dangling-transform-feedback-varying-names workaround */ - std::vector _transformFeedbackVaryingNames; + Containers::ArrayTuple _transformFeedbackVaryingNames; #endif }; diff --git a/src/Magnum/GL/Implementation/ShaderProgramState.h b/src/Magnum/GL/Implementation/ShaderProgramState.h index f5473374a..613d2af6b 100644 --- a/src/Magnum/GL/Implementation/ShaderProgramState.h +++ b/src/Magnum/GL/Implementation/ShaderProgramState.h @@ -27,8 +27,6 @@ DEALINGS IN THE SOFTWARE. */ -#include - #include "Magnum/Magnum.h" #include "Magnum/GL/GL.h" #include "Magnum/GL/OpenGL.h" @@ -46,9 +44,9 @@ struct ShaderProgramState { void reset(); #ifndef MAGNUM_TARGET_GLES2 - void(AbstractShaderProgram::*transformFeedbackVaryingsImplementation)(Containers::ArrayView, AbstractShaderProgram::TransformFeedbackBufferMode); + void(AbstractShaderProgram::*transformFeedbackVaryingsImplementation)(const Containers::StringIterable&, AbstractShaderProgram::TransformFeedbackBufferMode); #endif - void(*cleanLogImplementation)(std::string&); + void(*cleanLogImplementation)(Containers::String&); /* This is a direct pointer to a GL function, so needs a __stdcall on Windows to compile properly on 32 bits */ void(APIENTRY *completionStatusImplementation)(GLuint, GLenum, GLint* value); diff --git a/src/Magnum/GL/Implementation/ShaderState.h b/src/Magnum/GL/Implementation/ShaderState.h index f4ad5e502..df0a72a58 100644 --- a/src/Magnum/GL/Implementation/ShaderState.h +++ b/src/Magnum/GL/Implementation/ShaderState.h @@ -26,8 +26,6 @@ DEALINGS IN THE SOFTWARE. */ -#include - #include "Magnum/Magnum.h" #include "Magnum/GL/GL.h" #include "Magnum/GL/OpenGL.h" @@ -52,8 +50,8 @@ struct ShaderState { #endif }; - void(Shader::*addSourceImplementation)(std::string); - void(*cleanLogImplementation)(std::string&); + void(Shader::*addSourceImplementation)(Containers::String&&); + void(*cleanLogImplementation)(Containers::String&); /* This is a direct pointer to a GL function, so needs a __stdcall on Windows to compile properly on 32 bits */ void(APIENTRY *completionStatusImplementation)(GLuint, GLenum, GLint* value); diff --git a/src/Magnum/GL/Shader.cpp b/src/Magnum/GL/Shader.cpp index eeb435856..63c28da34 100644 --- a/src/Magnum/GL/Shader.cpp +++ b/src/Magnum/GL/Shader.cpp @@ -27,16 +27,15 @@ #include "Shader.h" #include +#include #ifdef MAGNUM_BUILD_DEPRECATED #include #endif -#ifndef MAGNUM_TARGET_WEBGL #include -#endif -#include /** @todo remove once Shader is -free */ +#include #include #include -#include +#include #include #include "Magnum/GL/Context.h" @@ -55,18 +54,20 @@ typedef char GLchar; namespace Magnum { namespace GL { +using namespace Containers::Literals; + namespace { -std::string shaderName(const Shader::Type type) { +Containers::StringView shaderName(const Shader::Type type) { switch(type) { - case Shader::Type::Vertex: return "vertex"; + case Shader::Type::Vertex: return "vertex"_s; #if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) - case Shader::Type::Geometry: return "geometry"; - case Shader::Type::TessellationControl: return "tessellation control"; - case Shader::Type::TessellationEvaluation: return "tessellation evaluation"; - case Shader::Type::Compute: return "compute"; + case Shader::Type::Geometry: return "geometry"_s; + case Shader::Type::TessellationControl: return "tessellation control"_s; + case Shader::Type::TessellationEvaluation: return "tessellation evaluation"_s; + case Shader::Type::Compute: return "compute"_s; #endif - case Shader::Type::Fragment: return "fragment"; + case Shader::Type::Fragment: return "fragment"_s; } CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ @@ -659,34 +660,37 @@ Shader::Shader(const Version version, const Type type): _type{type}, _flags{Obje _offsetLineByOneOnOldGlsl = false; #endif + Containers::StringView versionString; switch(version) { #ifndef MAGNUM_TARGET_GLES - case Version::GL210: _sources.emplace_back("#version 120\n"); return; - case Version::GL300: _sources.emplace_back("#version 130\n"); return; - case Version::GL310: _sources.emplace_back("#version 140\n"); return; - case Version::GL320: _sources.emplace_back("#version 150\n"); return; - case Version::GL330: _sources.emplace_back("#version 330\n"); return; - case Version::GL400: _sources.emplace_back("#version 400\n"); return; - case Version::GL410: _sources.emplace_back("#version 410\n"); return; - case Version::GL420: _sources.emplace_back("#version 420\n"); return; - case Version::GL430: _sources.emplace_back("#version 430\n"); return; - case Version::GL440: _sources.emplace_back("#version 440\n"); return; - case Version::GL450: _sources.emplace_back("#version 450\n"); return; - case Version::GL460: _sources.emplace_back("#version 460\n"); return; + case Version::GL210: versionString = "#version 120\n"_s; break; + case Version::GL300: versionString = "#version 130\n"_s; break; + case Version::GL310: versionString = "#version 140\n"_s; break; + case Version::GL320: versionString = "#version 150\n"_s; break; + case Version::GL330: versionString = "#version 330\n"_s; break; + case Version::GL400: versionString = "#version 400\n"_s; break; + case Version::GL410: versionString = "#version 410\n"_s; break; + case Version::GL420: versionString = "#version 420\n"_s; break; + case Version::GL430: versionString = "#version 430\n"_s; break; + case Version::GL440: versionString = "#version 440\n"_s; break; + case Version::GL450: versionString = "#version 450\n"_s; break; + case Version::GL460: versionString = "#version 460\n"_s; break; #endif /* `#version 100` really is GLSL ES 1.00 and *not* GLSL 1.00. What a mess. */ - case Version::GLES200: _sources.emplace_back("#version 100\n"); return; - case Version::GLES300: _sources.emplace_back("#version 300 es\n"); return; + case Version::GLES200: versionString = "#version 100\n"_s; break; + case Version::GLES300: versionString = "#version 300 es\n"_s; break; #ifndef MAGNUM_TARGET_WEBGL - case Version::GLES310: _sources.emplace_back("#version 310 es\n"); return; - case Version::GLES320: _sources.emplace_back("#version 320 es\n"); return; + case Version::GLES310: versionString = "#version 310 es\n"_s; break; + case Version::GLES320: versionString = "#version 320 es\n"_s; break; #endif /* The user is responsible for (not) adding #version directive */ case Version::None: return; } - CORRADE_ASSERT_UNREACHABLE("GL::Shader::Shader(): unsupported version" << version, ); + CORRADE_ASSERT(!versionString.isEmpty(), "GL::Shader::Shader(): unsupported version" << version, ); + + arrayAppend(_sources, Containers::String::nullTerminatedGlobalView(versionString)); } Shader::Shader(const Type type, const GLuint id, ObjectFlags flags) noexcept: _type{type}, _id{id}, _flags{flags} @@ -695,6 +699,17 @@ Shader::Shader(const Type type, const GLuint id, ObjectFlags flags) noexcept: _t #endif {} +Shader::Shader(NoCreateT) noexcept: _type{}, _id{0} {} + +Shader::Shader(Shader&& other) noexcept: _type{other._type}, _id{other._id}, _flags{other._flags}, + #ifndef MAGNUM_TARGET_GLES + _offsetLineByOneOnOldGlsl{other._flags}, + #endif + _sources{std::move(other._sources)} +{ + other._id = 0; +} + Shader::~Shader() { /* Moved out or not deleting on destruction, nothing to do */ if(!_id || !(_flags & ObjectFlag::DeleteOnDestruction)) return; @@ -702,6 +717,18 @@ Shader::~Shader() { glDeleteShader(_id); } +Shader& Shader::operator=(Shader&& other) noexcept { + using std::swap; + swap(_type, other._type); + swap(_id, other._id); + swap(_flags, other._flags); + #ifndef MAGNUM_TARGET_GLES + swap(_offsetLineByOneOnOldGlsl, other._offsetLineByOneOnOldGlsl); + #endif + swap(_sources, other._sources); + return *this; +} + #ifndef MAGNUM_TARGET_WEBGL Containers::String Shader::label() const { #ifndef MAGNUM_TARGET_GLES2 @@ -721,12 +748,14 @@ Shader& Shader::setLabel(const Containers::StringView label) { } #endif -std::vector Shader::sources() const { return _sources; } +Containers::StringIterable Shader::sources() const { return _sources; } -Shader& Shader::addSource(std::string source) { - if(!source.empty()) { - auto addSource = Context::current().state().shader.addSourceImplementation; +Shader& Shader::addSource(const Containers::StringView source) { + return addSourceInternal(Containers::String::nullTerminatedGlobalView(source)); +} +Shader& Shader::addSourceInternal(Containers::String&& source) { + if(!source.isEmpty()) { /* Fix line numbers, so line 41 of third added file is marked as 3(41) in case shader version was not Version::None, because then source 0 is the #version directive added in constructor. @@ -738,42 +767,46 @@ Shader& Shader::addSource(std::string source) { order to avoid complex logic in compile() where we assert for at least some user-provided source, an empty string is added here instead. */ - if(!_sources.empty()) (this->*addSource)( - /* GLSL < 330 interprets #line 0 as the next line being line 1, - while GLSL >= 330 which interprets #line 1 as next line being - line 1; the latter is consistent with the C preprocessor. GLSL ES - behaves like GLSL >= 330 always. */ - ( + if(!_sources.isEmpty()) arrayAppend(_sources, + /* GLSL < 330 interprets #line 0 as the next line being line 1, + while GLSL >= 330 which interprets #line 1 as next line being + line 1; the latter is consistent with the C preprocessor. GLSL + ES behaves like GLSL >= 330 always. */ + Utility::format( #ifndef MAGNUM_TARGET_GLES - _offsetLineByOneOnOldGlsl ? "#line 0 " : + _offsetLineByOneOnOldGlsl ? "#line 0 {}\n" : #endif - "#line 1 " - ) + std::to_string((_sources.size()+1)/2) + '\n'); - else (this->*addSource)({}); + "#line 1 {}\n", + (_sources.size() + 1)/2)); + else arrayAppend(_sources, Containers::String::nullTerminatedGlobalView(""_s)); - (this->*addSource)(std::move(source)); + (this->*Context::current().state().shader.addSourceImplementation)(std::move(source)); } return *this; } -void Shader::addSourceImplementationDefault(std::string source) { - _sources.push_back(std::move(source)); +void Shader::addSourceImplementationDefault(Containers::String&& source) { + arrayAppend(_sources, std::move(source)); } #if defined(CORRADE_TARGET_EMSCRIPTEN) && defined(__EMSCRIPTEN_PTHREADS__) -void Shader::addSourceImplementationEmscriptenPthread(std::string source) { - /* See the "emscripten-pthreads-broken-unicode-shader-sources" - workaround description for details */ +void Shader::addSourceImplementationEmscriptenPthread(Containers::String&& source) { + /* Modify the string to remove non-ASCII characters. Ensure we're only + modifying a string we actually own, if not make a copy first. See the + "emscripten-pthreads-broken-unicode-shader-sources" workaround + description for details. */ + if(!source.isSmall() && !source.deleter()) + source = Containers::String{source}; for(char& c: source) if(c < 0) c = ' '; - _sources.push_back(std::move(source)); + arrayAppend(_sources, std::move(source)); } #endif -Shader& Shader::addFile(const std::string& filename) { - const Containers::Optional string = Utility::Path::readString(filename); +Shader& Shader::addFile(const Containers::StringView filename) { + Containers::Optional string = Utility::Path::readString(filename); CORRADE_ASSERT(string, "GL::Shader::addFile(): can't read" << filename, *this); - addSource(*string); + addSource(*std::move(string)); return *this; } @@ -804,12 +837,12 @@ bool Shader::checkCompile() { glGetShaderiv(_id, GL_COMPILE_STATUS, &success); glGetShaderiv(_id, GL_INFO_LOG_LENGTH, &logLength); - /* Error or warning message. The string is returned null-terminated, - strip the \0 at the end afterwards. */ - std::string message(logLength, '\0'); - if(message.size() > 1) - glGetShaderInfoLog(_id, message.size(), nullptr, &message[0]); - message.resize(Math::max(logLength, 1)-1); + /* Error or warning message. The length is reported including the null + terminator and the string implicitly has a storage for that, thus + specify one byte less. */ + Containers::String message{NoInit, std::size_t(Math::max(logLength, 1)) - 1}; + if(logLength > 1) + glGetShaderInfoLog(_id, logLength, nullptr, message.data()); /* Some drivers are chatty and can't keep shut when there's nothing to be said, handle that as well. */ @@ -856,10 +889,10 @@ bool Shader::isCompileFinished() { return success == GL_TRUE; } -void Shader::cleanLogImplementationNoOp(std::string&) {} +void Shader::cleanLogImplementationNoOp(Containers::String&) {} #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) -void Shader::cleanLogImplementationIntelWindows(std::string& message) { +void Shader::cleanLogImplementationIntelWindows(Containers::String& message) { if(message == "No errors.\n") message = {}; } #endif diff --git a/src/Magnum/GL/Shader.h b/src/Magnum/GL/Shader.h index db2e4f699..ec2bb154b 100644 --- a/src/Magnum/GL/Shader.h +++ b/src/Magnum/GL/Shader.h @@ -30,9 +30,7 @@ * @brief Class @ref Magnum::GL::Shader */ -#include -#include -#include +#include #include "Magnum/Tags.h" #include "Magnum/GL/AbstractObject.h" @@ -40,8 +38,8 @@ #ifdef MAGNUM_BUILD_DEPRECATED #include -/* For label() / setLabel(), which used to be a std::string. Not ideal for the - return type, but at least something. */ +/* For label() / setLabel() and all name/source stuff, which used to be a + std::string. Not ideal for the return types, but at least something. */ #include #endif @@ -570,14 +568,13 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject { * API, see the documentation of @ref NoCreate for alternatives. * @see @ref Shader() */ - explicit Shader(NoCreateT) noexcept: _type{}, _id{0} {} + explicit Shader(NoCreateT) noexcept; /** @brief Copying is not allowed */ Shader(const Shader&) = delete; /** @brief Move constructor */ - /* MinGW complains loudly if the declaration doesn't also have inline */ - inline Shader(Shader&& other) noexcept; + Shader(Shader&& other) noexcept; /** * @brief Destructor @@ -591,8 +588,7 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject { Shader& operator=(const Shader&) = delete; /** @brief Move assignment */ - /* MinGW complains loudly if the declaration doesn't also have inline */ - inline Shader& operator=(Shader&& other) noexcept; + Shader& operator=(Shader&& other) noexcept; /** @brief OpenGL shader ID */ GLuint id() const { return _id; } @@ -645,20 +641,46 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject { /** @brief Shader type */ Type type() const { return _type; } - /** @brief Shader sources */ - std::vector sources() const; + /** + * @brief Shader sources + * + * The returned views are all + * @relativeref{Corrade,Containers::StringViewFlag::NullTerminated}, + * @relativeref{Corrade,Containers::StringViewFlag::Global} is set for + * the initial @glsl #version @ce directive and for global + * null-terminated views that were passed to @ref addSource(). + */ + Containers::StringIterable sources() const; /** * @brief Add shader source * @param source String with shader source * @return Reference to self (for method chaining) * - * Adds given source to source list, preceded with a - * @glsl #line n 1 @ce directive for improved - * @ref GL-Shader-errors "compilation error reporting". + * If the string is not empty, adds it to the shader source list, + * preceded with a @glsl #line n 1 @ce directive for improved + * @ref GL-Shader-errors "compilation error reporting". If it's empty, + * the function is a no-op. + * + * If the view is both @relativeref{Corrade,Containers::StringViewFlag::NullTerminated} + * and @relativeref{Corrade,Containers::StringViewFlag::Global}, it's + * directly referenced, otherwise a copy is made internally. For + * dynamic strings prefer to use the @ref addSource(Containers::String&&) + * overload to avoid copies. * @see @ref addFile() */ - Shader& addSource(std::string source); + Shader& addSource(Containers::StringView source); + /** @overload */ + #ifdef DOXYGEN_GENERATING_OUTPUT + Shader& addSource(Containers::String&& source); + #else + /* Otherwise passing a char* is ambiguous between this and the + StringView overload. Sigh, C++. */ + template::value>::type> + Shader& addSource(U&& source) { + return addSourceInternal(std::move(source)); + } + #endif /** * @brief Add shader source file @@ -668,7 +690,7 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject { * The file must exist and must be readable. Calls @ref addSource() * with the contents. */ - Shader& addFile(const std::string& filename); + Shader& addFile(const Containers::StringView filename); /** * @brief Compile the shader @@ -745,14 +767,17 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject { private: explicit Shader(Type type, GLuint id, ObjectFlags flags) noexcept; - void MAGNUM_GL_LOCAL addSourceImplementationDefault(std::string source); + /* Used by addSource(Containers::String&&) */ + Shader& addSourceInternal(Containers::String&& source); + + void MAGNUM_GL_LOCAL addSourceImplementationDefault(Containers::String&& source); #if defined(CORRADE_TARGET_EMSCRIPTEN) && defined(__EMSCRIPTEN_PTHREADS__) - void MAGNUM_GL_LOCAL addSourceImplementationEmscriptenPthread(std::string source); + void MAGNUM_GL_LOCAL addSourceImplementationEmscriptenPthread(Containers::String&& source); #endif - static MAGNUM_GL_LOCAL void cleanLogImplementationNoOp(std::string& message); + static MAGNUM_GL_LOCAL void cleanLogImplementationNoOp(Containers::String& message); #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) - static MAGNUM_GL_LOCAL void cleanLogImplementationIntelWindows(std::string& message); + static MAGNUM_GL_LOCAL void cleanLogImplementationIntelWindows(Containers::String& message); #endif MAGNUM_GL_LOCAL static void APIENTRY completionStatusImplementationFallback(GLuint, GLenum, GLint*); @@ -765,33 +790,12 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject { bool _offsetLineByOneOnOldGlsl; #endif - std::vector _sources; + Containers::Array _sources; }; /** @debugoperatorclassenum{Shader,Shader::Type} */ MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, Shader::Type value); -inline Shader::Shader(Shader&& other) noexcept: _type{other._type}, _id{other._id}, _flags{other._flags}, - #ifndef MAGNUM_TARGET_GLES - _offsetLineByOneOnOldGlsl{other._flags}, - #endif - _sources{std::move(other._sources)} -{ - other._id = 0; -} - -inline Shader& Shader::operator=(Shader&& other) noexcept { - using std::swap; - swap(_type, other._type); - swap(_id, other._id); - swap(_flags, other._flags); - #ifndef MAGNUM_TARGET_GLES - swap(_offsetLineByOneOnOldGlsl, other._offsetLineByOneOnOldGlsl); - #endif - swap(_sources, other._sources); - return *this; -} - inline GLuint Shader::release() { const GLuint id = _id; _id = 0; diff --git a/src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp b/src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp index 9167a266b..85f8e79e5 100644 --- a/src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp +++ b/src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp @@ -28,7 +28,7 @@ #include #include #include -#include /** @todo remove once Debug is stream-free */ +#include /* StringHasPrefix / StringHasSuffix */ #include #include #include @@ -116,6 +116,52 @@ struct AbstractShaderProgramGLTest: OpenGLTester { #endif }; +using namespace Containers::Literals; + +const struct { + const char* name; + Containers::StringView positionName, matrixName, multiplierName, colorName, additionsName; +} CreateData[]{ + {"", + "position", + "matrix", + "multiplier", + "color", + "additions"}, + {"non-null-terminated strings", + "position!"_s.exceptSuffix(1), + "matrix!"_s.exceptSuffix(1), + "multiplier!"_s.exceptSuffix(1), + "color!"_s.exceptSuffix(1), + "additions!"_s.exceptSuffix(1)}, +}; + +const struct { + const char* name; + Containers::StringView firstName, secondName; +} CreateMultipleOutputsData[]{ + {"", + "first", + "second"}, + {"non-null-terminated strings", + "first!"_s.exceptSuffix(1), + "second!"_s.exceptSuffix(1)} +}; + +#ifndef MAGNUM_TARGET_GLES2 +const struct { + const char* name; + Containers::StringView matricesName, materialName; +} CreateUniformBlocksData[]{ + {"", + "matrices", + "material"}, + {"non-null-terminated strings", + "matrices!"_s.exceptSuffix(1), + "material!"_s.exceptSuffix(1)} +}; +#endif + AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() { addTests({&AbstractShaderProgramGLTest::construct, &AbstractShaderProgramGLTest::constructMove, @@ -123,15 +169,21 @@ AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() { #ifndef MAGNUM_TARGET_WEBGL &AbstractShaderProgramGLTest::label, #endif + }); - &AbstractShaderProgramGLTest::create, - &AbstractShaderProgramGLTest::createAsync, - &AbstractShaderProgramGLTest::createMultipleOutputs, - #ifndef MAGNUM_TARGET_GLES - &AbstractShaderProgramGLTest::createMultipleOutputsIndexed, - #endif + addInstancedTests({&AbstractShaderProgramGLTest::create}, + Containers::arraySize(CreateData)); + + addTests({&AbstractShaderProgramGLTest::createAsync}); + + addInstancedTests({ + &AbstractShaderProgramGLTest::createMultipleOutputs, + #ifndef MAGNUM_TARGET_GLES + &AbstractShaderProgramGLTest::createMultipleOutputsIndexed, + #endif + }, Containers::arraySize(CreateMultipleOutputsData)); - &AbstractShaderProgramGLTest::linkFailure, + addTests({&AbstractShaderProgramGLTest::linkFailure, &AbstractShaderProgramGLTest::linkFailureAsync, &AbstractShaderProgramGLTest::linkFailureAsyncShaderList, @@ -149,9 +201,15 @@ AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() { &AbstractShaderProgramGLTest::uniformDoubleMatrix, &AbstractShaderProgramGLTest::uniformDoubleArray, #endif + }); + + #ifndef MAGNUM_TARGET_GLES2 + addInstancedTests({&AbstractShaderProgramGLTest::createUniformBlocks}, + Containers::arraySize(CreateUniformBlocksData)); + #endif + addTests({ #ifndef MAGNUM_TARGET_GLES2 - &AbstractShaderProgramGLTest::createUniformBlocks, &AbstractShaderProgramGLTest::uniformBlockIndexNotFound, &AbstractShaderProgramGLTest::uniformBlock, @@ -248,6 +306,9 @@ struct MyPublicShader: AbstractShaderProgram { }; void AbstractShaderProgramGLTest::create() { + auto&& data = CreateData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + Utility::Resource rs("AbstractShaderProgramGLTest"); Shader vert( @@ -287,7 +348,7 @@ void AbstractShaderProgramGLTest::create() { MAGNUM_VERIFY_NO_GL_ERROR(); - program.bindAttributeLocation(0, "position"); + program.bindAttributeLocation(0, data.positionName); const bool linked = program.link(); const bool valid = program.validate().first; @@ -304,10 +365,10 @@ void AbstractShaderProgramGLTest::create() { CORRADE_VERIFY(valid); } - const Int matrixUniform = program.uniformLocation("matrix"); - const Int multiplierUniform = program.uniformLocation("multiplier"); - const Int colorUniform = program.uniformLocation("color"); - const Int additionsUniform = program.uniformLocation("additions"); + const Int matrixUniform = program.uniformLocation(data.matrixName); + const Int multiplierUniform = program.uniformLocation(data.multiplierName); + const Int colorUniform = program.uniformLocation(data.colorName); + const Int additionsUniform = program.uniformLocation(data.additionsName); MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_VERIFY(matrixUniform >= 0); @@ -387,6 +448,9 @@ void AbstractShaderProgramGLTest::createAsync() { } void AbstractShaderProgramGLTest::createMultipleOutputs() { + auto&& data = CreateMultipleOutputsData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); @@ -423,8 +487,10 @@ void AbstractShaderProgramGLTest::createMultipleOutputs() { MAGNUM_VERIFY_NO_GL_ERROR(); program.bindAttributeLocation(0, "position"); - program.bindFragmentDataLocation(0, "first"); - program.bindFragmentDataLocation(1, "second"); + /* Testing just what wasn't verified for non-null-terminated strings in + create() already */ + program.bindFragmentDataLocation(0, data.firstName); + program.bindFragmentDataLocation(1, data.secondName); const bool linked = program.link(); const bool valid = program.validate().first; @@ -445,6 +511,9 @@ void AbstractShaderProgramGLTest::createMultipleOutputs() { #ifndef MAGNUM_TARGET_GLES void AbstractShaderProgramGLTest::createMultipleOutputsIndexed() { + auto&& data = CreateMultipleOutputsData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported."); if(!GL::Context::current().isExtensionSupported()) @@ -482,8 +551,8 @@ void AbstractShaderProgramGLTest::createMultipleOutputsIndexed() { MAGNUM_VERIFY_NO_GL_ERROR(); program.bindAttributeLocation(0, "position"); - program.bindFragmentDataLocationIndexed(0, 0, "first"); - program.bindFragmentDataLocationIndexed(0, 1, "second"); + program.bindFragmentDataLocationIndexed(0, 0, data.firstName); + program.bindFragmentDataLocationIndexed(0, 1, data.secondName); const bool linked = program.link(); const bool valid = program.validate().first; @@ -713,7 +782,7 @@ void main() { program.setUniform(program.uniformLocation("textureData2D"), 0); program.setUniform(program.uniformLocation("textureData3D"), 0); - std::pair result = program.validate(); + std::pair result = program.validate(); MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_VERIFY(!result.first); /* The message shouldn't be empty */ @@ -769,10 +838,8 @@ void AbstractShaderProgramGLTest::uniformNotFound() { std::ostringstream out; Warning redirectWarning{&out}; program.uniformLocation("nonexistent"); - program.uniformLocation(std::string{"another"}); CORRADE_COMPARE(out.str(), - "GL::AbstractShaderProgram: location of uniform 'nonexistent' cannot be retrieved\n" - "GL::AbstractShaderProgram: location of uniform 'another' cannot be retrieved\n"); + "GL::AbstractShaderProgram: location of uniform 'nonexistent' cannot be retrieved\n"); } struct MyShader: AbstractShaderProgram { @@ -977,6 +1044,9 @@ void AbstractShaderProgramGLTest::uniformDoubleArray() { #ifndef MAGNUM_TARGET_GLES2 void AbstractShaderProgramGLTest::createUniformBlocks() { + auto&& data = CreateUniformBlocksData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported()) CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported."); @@ -1025,8 +1095,8 @@ void AbstractShaderProgramGLTest::createUniformBlocks() { CORRADE_VERIFY(valid); } - const Int matricesUniformBlock = program.uniformBlockIndex("matrices"); - const Int materialUniformBlock = program.uniformBlockIndex("material"); + const Int matricesUniformBlock = program.uniformBlockIndex(data.matricesName); + const Int materialUniformBlock = program.uniformBlockIndex(data.materialName); MAGNUM_VERIFY_NO_GL_ERROR(); CORRADE_VERIFY(matricesUniformBlock >= 0); @@ -1066,10 +1136,8 @@ void AbstractShaderProgramGLTest::uniformBlockIndexNotFound() { std::ostringstream out; Warning redirectWarning{&out}; program.uniformBlockIndex("nonexistent"); - program.uniformBlockIndex(std::string{"another"}); CORRADE_COMPARE(out.str(), - "GL::AbstractShaderProgram: index of uniform block 'nonexistent' cannot be retrieved\n" - "GL::AbstractShaderProgram: index of uniform block 'another' cannot be retrieved\n"); + "GL::AbstractShaderProgram: index of uniform block 'nonexistent' cannot be retrieved\n"); } struct UniformBlockShader: AbstractShaderProgram { diff --git a/src/Magnum/GL/Test/MeshGLTest.cpp b/src/Magnum/GL/Test/MeshGLTest.cpp index 496b3523c..a4ea500d1 100644 --- a/src/Magnum/GL/Test/MeshGLTest.cpp +++ b/src/Magnum/GL/Test/MeshGLTest.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -770,7 +771,7 @@ void MeshGLTest::construct() { } struct FloatShader: AbstractShaderProgram { - explicit FloatShader(const std::string& type, const std::string& conversion); + explicit FloatShader(Containers::StringView type, Containers::StringView conversion); }; void MeshGLTest::constructMove() { @@ -949,13 +950,13 @@ void MeshGLTest::label() { #ifndef MAGNUM_TARGET_GLES2 struct IntegerShader: AbstractShaderProgram { - explicit IntegerShader(const std::string& type); + explicit IntegerShader(Containers::StringView type); }; #endif #ifndef MAGNUM_TARGET_GLES struct DoubleShader: AbstractShaderProgram { - explicit DoubleShader(const std::string& type, const std::string& outputType, const std::string& conversion); + explicit DoubleShader(Containers::StringView type, Containers::StringView outputType, Containers::StringView conversion); }; #endif @@ -969,7 +970,7 @@ struct Checker { }; #ifndef DOXYGEN_GENERATING_OUTPUT -FloatShader::FloatShader(const std::string& type, const std::string& conversion) { +FloatShader::FloatShader(Containers::StringView type, Containers::StringView conversion) { /* We need special version for ES3, because GLSL in ES2 doesn't support rectangle matrices */ #ifndef MAGNUM_TARGET_GLES @@ -995,7 +996,7 @@ FloatShader::FloatShader(const std::string& type, const std::string& conversion) Shader frag(Version::GLES300, Shader::Type::Fragment); #endif - vert.addSource( + vert.addSource(Utility::format( "#if !defined(GL_ES) && __VERSION__ == 120\n" "#define mediump\n" "#endif\n" @@ -1003,14 +1004,14 @@ FloatShader::FloatShader(const std::string& type, const std::string& conversion) "#define in attribute\n" "#define out varying\n" "#endif\n" - "in mediump " + type + " value;\n" - "out mediump " + type + " valueInterpolated;\n" - "void main() {\n" + "in mediump {0} value;\n" + "out mediump {0} valueInterpolated;\n" + "void main() {{\n" " valueInterpolated = value;\n" " gl_PointSize = 1.0;\n" " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" - "}\n"); - frag.addSource( + "}}\n", type)); + frag.addSource(Utility::format( "#if !defined(GL_ES) && __VERSION__ == 120\n" "#define mediump\n" "#endif\n" @@ -1018,11 +1019,11 @@ FloatShader::FloatShader(const std::string& type, const std::string& conversion) "#define in varying\n" "#define result gl_FragColor\n" "#endif\n" - "in mediump " + type + " valueInterpolated;\n" + "in mediump {0} valueInterpolated;\n" "#if (defined(GL_ES) && __VERSION__ >= 300) || (!defined(GL_ES) && __VERSION__ >= 130)\n" "out mediump vec4 result;\n" "#endif\n" - "void main() { result = " + conversion + "; }\n"); + "void main() {{ result = {1}; }}\n", type, conversion)); CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile()); @@ -1034,7 +1035,7 @@ FloatShader::FloatShader(const std::string& type, const std::string& conversion) } #ifndef MAGNUM_TARGET_GLES2 -IntegerShader::IntegerShader(const std::string& type) { +IntegerShader::IntegerShader(const Containers::StringView type) { #ifndef MAGNUM_TARGET_GLES Shader vert( #ifndef CORRADE_TARGET_APPLE @@ -1055,16 +1056,18 @@ IntegerShader::IntegerShader(const std::string& type) { Shader frag(Version::GLES300, Shader::Type::Fragment); #endif - vert.addSource("in mediump " + type + " value;\n" - "flat out mediump " + type + " valueInterpolated;\n" - "void main() {\n" - " valueInterpolated = value;\n" - " gl_PointSize = 1.0;\n" - " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" - "}\n"); - frag.addSource("flat in mediump " + type + " valueInterpolated;\n" - "out mediump " + type + " result;\n" - "void main() { result = valueInterpolated; }\n"); + vert.addSource(Utility::format( + "in mediump {0} value;\n" + "flat out mediump {0} valueInterpolated;\n" + "void main() {{\n" + " valueInterpolated = value;\n" + " gl_PointSize = 1.0;\n" + " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" + "}}\n", type)); + frag.addSource(Utility::format( + "flat in mediump {0} valueInterpolated;\n" + "out mediump {0} result;\n" + "void main() {{ result = valueInterpolated; }}\n", type)); CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile()); @@ -1077,7 +1080,7 @@ IntegerShader::IntegerShader(const std::string& type) { #endif #ifndef MAGNUM_TARGET_GLES -DoubleShader::DoubleShader(const std::string& type, const std::string& outputType, const std::string& conversion) { +DoubleShader::DoubleShader(const Containers::StringView type, const Containers::StringView outputType, const Containers::StringView conversion) { constexpr const Version version = #ifndef CORRADE_TARGET_APPLE Version::GL300; @@ -1087,19 +1090,20 @@ DoubleShader::DoubleShader(const std::string& type, const std::string& outputTyp Shader vert{version, Shader::Type::Vertex}; Shader frag(version, Shader::Type::Fragment); - vert.addSource( + vert.addSource(Utility::format( "#extension GL_ARB_vertex_attrib_64bit: require\n" "#extension GL_ARB_gpu_shader_fp64: require\n" - "in " + type + " value;\n" - "out " + outputType + " valueInterpolated;\n" - "void main() {\n" - " valueInterpolated = " + conversion + ";\n" + "in {0} value;\n" + "out {1} valueInterpolated;\n" + "void main() {{\n" + " valueInterpolated = {2};\n" " gl_PointSize = 1.0;\n" " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" - "}\n"); - frag.addSource("in " + outputType + " valueInterpolated;\n" - "out " + outputType + " result;\n" - "void main() { result = valueInterpolated; }\n"); + "}}\n", type, outputType, conversion)); + frag.addSource(Utility::format( + "in {0} valueInterpolated;\n" + "out {0} result;\n" + "void main() {{ result = valueInterpolated; }}\n", outputType)); CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile()); diff --git a/src/Magnum/GL/Test/PrimitiveQueryGLTest.cpp b/src/Magnum/GL/Test/PrimitiveQueryGLTest.cpp index 70293f3d2..3831a4351 100644 --- a/src/Magnum/GL/Test/PrimitiveQueryGLTest.cpp +++ b/src/Magnum/GL/Test/PrimitiveQueryGLTest.cpp @@ -24,6 +24,7 @@ */ #include +#include #include #include diff --git a/src/Magnum/GL/Test/ShaderGLTest.cpp b/src/Magnum/GL/Test/ShaderGLTest.cpp index 3e31e74b9..ae2efd521 100644 --- a/src/Magnum/GL/Test/ShaderGLTest.cpp +++ b/src/Magnum/GL/Test/ShaderGLTest.cpp @@ -26,7 +26,9 @@ #include #include -#include /** @todo remove once Shader is -free */ +#include +#include /* StringHasPrefix / StringHasSuffix */ +#include #include #include #include @@ -126,9 +128,13 @@ void ShaderGLTest::construct() { CORRADE_VERIFY(shader.id() > 0); CORRADE_COMPARE(shader.type(), Shader::Type::Fragment); #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(shader.sources(), std::vector{"#version 130\n"}); + CORRADE_COMPARE_AS(shader.sources(), + Containers::StringIterable{"#version 130\n"}, + TestSuite::Compare::Container); #else - CORRADE_COMPARE(shader.sources(), std::vector{"#version 300 es\n"}); + CORRADE_COMPARE_AS(shader.sources(), + Containers::StringIterable{"#version 300 es\n"}, + TestSuite::Compare::Container); #endif } @@ -137,7 +143,7 @@ void ShaderGLTest::construct() { void ShaderGLTest::constructNoVersion() { const Shader shader(Version::None, Shader::Type::Fragment); - CORRADE_VERIFY(shader.sources().empty()); + CORRADE_VERIFY(shader.sources().isEmpty()); } void ShaderGLTest::constructMove() { @@ -157,9 +163,13 @@ void ShaderGLTest::constructMove() { CORRADE_COMPARE(b.id(), id); CORRADE_COMPARE(b.type(), Shader::Type::Fragment); #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(b.sources(), std::vector{"#version 130\n"}); + CORRADE_COMPARE_AS(b.sources(), + Containers::StringIterable{"#version 130\n"}, + TestSuite::Compare::Container); #else - CORRADE_COMPARE(b.sources(), std::vector{"#version 300 es\n"}); + CORRADE_COMPARE_AS(b.sources(), + Containers::StringIterable{"#version 300 es\n"}, + TestSuite::Compare::Container); #endif #ifndef MAGNUM_TARGET_GLES @@ -176,9 +186,13 @@ void ShaderGLTest::constructMove() { CORRADE_COMPARE(c.id(), id); CORRADE_COMPARE(c.type(), Shader::Type::Fragment); #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(c.sources(), std::vector{"#version 130\n"}); + CORRADE_COMPARE_AS(c.sources(), + Containers::StringIterable{"#version 130\n"}, + TestSuite::Compare::Container); #else - CORRADE_COMPARE(c.sources(), std::vector{"#version 300 es\n"}); + CORRADE_COMPARE_AS(c.sources(), + Containers::StringIterable{"#version 300 es\n"}, + TestSuite::Compare::Container); #endif CORRADE_VERIFY(std::is_nothrow_move_constructible::value); @@ -231,64 +245,106 @@ void ShaderGLTest::addSource() { Shader shader(Version::GLES200, Shader::Type::Fragment); #endif - shader.addSource("#define FOO BAR\n") - .addSource("void main() {}\n"); + const char* data = "// r-value String\n"; + shader.addSource("// global, null-terminated\n"_s) + .addSource("// global, non-null-terminated\n!"_s.exceptSuffix(1)) + .addSource("// local, null-terminated\n") + .addSource(Containers::StringView{"// local, non-null-terminated\n!"}.exceptSuffix(1)) + .addSource(Containers::String{data, [](char*, std::size_t) {}}) + .addSource("") /* gets ignored */ + .addSource("void main() {}\n"_s); + + /* On (desktop) GLSL < 330 the #line affect next line, not current + line; see compileFailure() for a correctness verification */ #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(shader.sources(), (std::vector{ - "#version 120\n", - /* On (desktop) GLSL < 330 the #line affect next line, not current - line; see compileFailure() for a correctness verification */ - "#line 0 1\n", - "#define FOO BAR\n", - "#line 0 2\n", - "void main() {}\n" - })); + #define _zero " 0 " #else - CORRADE_COMPARE(shader.sources(), (std::vector{ - "#version 100\n", - "#line 1 1\n", - "#define FOO BAR\n", - "#line 1 2\n", - "void main() {}\n" - })); + #define _zero " 1 " #endif + Containers::StringView expected[]{ + #ifndef MAGNUM_TARGET_GLES + "#version 120\n", // 0 + #else + "#version 100\n", // 0 + #endif + "#line" _zero "1\n", + "// global, null-terminated\n", // 2 + "#line" _zero "2\n", + "// global, non-null-terminated\n", + "#line" _zero "3\n", + "// local, null-terminated\n", + "#line" _zero "4\n", + "// local, non-null-terminated\n", + "#line" _zero "5\n", + "// r-value String\n", // isn't global but is moved + "#line" _zero "6\n", + /* Empty source gets ignored */ + "void main() {}\n" // 12 + }; + #undef _zero + CORRADE_COMPARE_AS(shader.sources(), + Containers::StringIterable{expected}, + TestSuite::Compare::Container); + + /* Verify that strings get copied only when not null terminated or not + global, and when not moved */ + for(std::size_t i: {0, 2, 12}) { + CORRADE_ITERATION(i); + CORRADE_COMPARE(shader.sources()[i].flags(), Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global); + } + for(std::size_t i: {1, 3, 4, 5, 6, 7, 8, 9, 10, 11}) { + CORRADE_ITERATION(i); + CORRADE_COMPARE(shader.sources()[i].flags(), Containers::StringViewFlag::NullTerminated); + } + CORRADE_VERIFY(shader.sources()[10].data() == data); } void ShaderGLTest::addSourceNoVersion() { Shader shader(Version::None, Shader::Type::Fragment); #ifndef MAGNUM_TARGET_GLES - shader.addSource("#version 120\n"); + shader.addSource("#version 120\n"_s); #else - shader.addSource("#version 100\n"); + shader.addSource("#version 100\n"_s); #endif - shader.addSource("#define FOO BAR\n") - .addSource("void main() {}\n"); + shader.addSource("#define FOO BAR\n"_s) + .addSource("void main() {}\n"_s); #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(shader.sources(), (std::vector{ + CORRADE_COMPARE_AS(shader.sources(), (Containers::StringIterable{ "", /* Here, even though there's #version 120 eventually added by the user, it assumes the specified version was new GLSL, not old. Explicitly specified old GLSL is such a rare use case that I don't bother looking for the #version directive and adjusting. */ "#version 120\n", - "#line 1 1\n", + "#line 1 1\n", // 2 "#define FOO BAR\n", - "#line 1 2\n", + "#line 1 2\n", // 4 "void main() {}\n" - })); + }), TestSuite::Compare::Container); #else - CORRADE_COMPARE(shader.sources(), (std::vector{ + CORRADE_COMPARE_AS(shader.sources(), (Containers::StringIterable{ "", "#version 100\n", "#line 1 1\n", "#define FOO BAR\n", "#line 1 2\n", "void main() {}\n" - })); + }), TestSuite::Compare::Container); #endif + + /* Everything except the line numbers should be global in this case, + including the empty string */ + for(std::size_t i: {0, 1, 3, 5}) { + CORRADE_ITERATION(i); + CORRADE_COMPARE(shader.sources()[i].flags(), Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global); + } + for(std::size_t i: {2, 4}) { + CORRADE_ITERATION(i); + CORRADE_COMPARE(shader.sources()[i].flags(), Containers::StringViewFlag::NullTerminated); + } } void ShaderGLTest::addFile() { @@ -301,20 +357,25 @@ void ShaderGLTest::addFile() { shader.addFile(Utility::Path::join(SHADERGLTEST_FILES_DIR, "shader.glsl")); #ifndef MAGNUM_TARGET_GLES - CORRADE_COMPARE(shader.sources(), (std::vector{ + CORRADE_COMPARE_AS(shader.sources(), (Containers::StringIterable{ "#version 120\n", /* On (desktop) GLSL < 330 the #line affect next line, not current line; see compileFailure() for a correctness verification */ "#line 0 1\n", "void main() {}\n" - })); + }), TestSuite::Compare::Container); #else - CORRADE_COMPARE(shader.sources(), (std::vector{ + CORRADE_COMPARE_AS(shader.sources(), (Containers::StringIterable{ "#version 100\n", "#line 1 1\n", "void main() {}\n" - })); + }), TestSuite::Compare::Container); #endif + + /* The file source and the line number isn't global */ + CORRADE_COMPARE(shader.sources()[0].flags(), Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global); + CORRADE_COMPARE(shader.sources()[1].flags(), Containers::StringViewFlag::NullTerminated); + CORRADE_COMPARE(shader.sources()[2].flags(), Containers::StringViewFlag::NullTerminated); } void ShaderGLTest::addFileNonexistent() { diff --git a/src/Magnum/GL/Test/TransformFeedbackGLTest.cpp b/src/Magnum/GL/Test/TransformFeedbackGLTest.cpp index aa7637739..be54b9bf0 100644 --- a/src/Magnum/GL/Test/TransformFeedbackGLTest.cpp +++ b/src/Magnum/GL/Test/TransformFeedbackGLTest.cpp @@ -25,7 +25,10 @@ #include #include +#include +#include #include +#include #include "Magnum/Image.h" #include "Magnum/GL/AbstractShaderProgram.h" @@ -44,10 +47,6 @@ #include "Magnum/GL/TransformFeedback.h" #include "Magnum/Math/Vector2.h" -#ifndef MAGNUM_TARGET_WEBGL -#include -#endif - namespace Magnum { namespace GL { namespace Test { namespace { struct TransformFeedbackGLTest: OpenGLTester { @@ -74,6 +73,38 @@ struct TransformFeedbackGLTest: OpenGLTester { }; #ifndef MAGNUM_TARGET_GLES +using namespace Containers::Literals; + +const struct { + const char* name; + Containers::StringView output1Name; + Containers::StringView glSkipComponents1Name; + Containers::StringView output2Name; +} InterleavedData[]{ + {"", + "output1"_s, + "gl_SkipComponents1"_s, + "output2"_s}, + {"non-null-terminated strings", + "output1!"_s.exceptSuffix(1), + "gl_SkipComponents1!"_s.exceptSuffix(1), + "output2!"_s.exceptSuffix(1)}, + #ifdef CORRADE_TARGET_WINDOWS + /* Testing the "nv-windows-dangling-transform-feedback-varying-names" + bug / workaround. "output1" alone *is* a global string, however a + StringView created from it doesn't know that and so it should get copied + to a local, null-terminated String first. Without the workaround present + this case would do the right thing as well tho (but the above not) -- + the driver assumes the string is global (which it is), while StringView + assumes the driver doesn't need a global string so it doesn't do + anything extra. */ + {"non-global strings", + "output1", + "gl_SkipComponents1", + "output2"}, + #endif +}; + const struct { const char* name; UnsignedInt stream; @@ -101,14 +132,12 @@ TransformFeedbackGLTest::TransformFeedbackGLTest() { &TransformFeedbackGLTest::attachBase, &TransformFeedbackGLTest::attachRange, &TransformFeedbackGLTest::attachBases, - &TransformFeedbackGLTest::attachRanges, - - #ifndef MAGNUM_TARGET_GLES - &TransformFeedbackGLTest::interleaved, - #endif - }); + &TransformFeedbackGLTest::attachRanges}); #ifndef MAGNUM_TARGET_GLES + addInstancedTests({&TransformFeedbackGLTest::interleaved}, + Containers::arraySize(InterleavedData)); + addInstancedTests({&TransformFeedbackGLTest::draw}, Containers::arraySize(DrawData)); #endif @@ -266,6 +295,7 @@ XfbShader::XfbShader() { attachShaders({vert, frag}); #endif bindAttributeLocation(Input::Location, "inputData"); + /* Non-null-terminated strings tested in interleaved() */ setTransformFeedbackOutputs({"outputData"}, TransformFeedbackBufferMode::SeparateAttributes); CORRADE_INTERNAL_ASSERT_OUTPUT(link()); } @@ -402,6 +432,7 @@ XfbMultiShader::XfbMultiShader() { attachShaders({vert, frag}); #endif bindAttributeLocation(Input::Location, "inputData"); + /* Non-null-terminated input tested in interleaved() */ setTransformFeedbackOutputs({"output1", "output2"}, TransformFeedbackBufferMode::SeparateAttributes); CORRADE_INTERNAL_ASSERT_OUTPUT(link()); } @@ -519,6 +550,9 @@ void TransformFeedbackGLTest::attachRanges() { #ifndef MAGNUM_TARGET_GLES void TransformFeedbackGLTest::interleaved() { + auto&& data = InterleavedData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + /* ARB_transform_feedback3 needed for gl_SkipComponents1 */ if(!Context::current().isExtensionSupported()) CORRADE_SKIP(Extensions::ARB::transform_feedback3::string() << "is not supported."); @@ -533,7 +567,7 @@ void TransformFeedbackGLTest::interleaved() { struct XfbInterleavedShader: AbstractShaderProgram { typedef Attribute<0, Vector2> Input; - explicit XfbInterleavedShader() { + explicit XfbInterleavedShader(Containers::StringView output1Name, Containers::StringView glSkipComponents1Name, Containers::StringView output2Name) { Shader vert( #ifndef CORRADE_TARGET_APPLE Version::GL300 @@ -554,10 +588,10 @@ void TransformFeedbackGLTest::interleaved() { "}\n").compile()); attachShader(vert); bindAttributeLocation(Input::Location, "inputData"); - setTransformFeedbackOutputs({"output1", "gl_SkipComponents1", "output2"}, TransformFeedbackBufferMode::InterleavedAttributes); + setTransformFeedbackOutputs({output1Name, glSkipComponents1Name, output2Name}, TransformFeedbackBufferMode::InterleavedAttributes); CORRADE_INTERNAL_ASSERT_OUTPUT(link()); } - } shader; + } shader{data.output1Name, data.glSkipComponents1Name, data.output2Name}; Buffer input{Buffer::TargetHint::Array}; input.setData(inputData, BufferUsage::StaticDraw); @@ -581,11 +615,11 @@ void TransformFeedbackGLTest::interleaved() { MAGNUM_VERIFY_NO_GL_ERROR(); - auto data = Containers::arrayCast(output.mapRead(0, 4*sizeof(Vector2))); - CORRADE_COMPARE(data[0], Vector2(1.0f, -1.0f)); - CORRADE_COMPARE(data[1].y(), 5.0f); - CORRADE_COMPARE(data[2], Vector2(0.0f, 0.0f)); - CORRADE_COMPARE(data[3].y(), 3.0f); + auto outputData = Containers::arrayCast(output.mapRead(0, 4*sizeof(Vector2))); + CORRADE_COMPARE(outputData[0], Vector2(1.0f, -1.0f)); + CORRADE_COMPARE(outputData[1].y(), 5.0f); + CORRADE_COMPARE(outputData[2], Vector2(0.0f, 0.0f)); + CORRADE_COMPARE(outputData[3].y(), 3.0f); output.unmap(); } @@ -617,11 +651,12 @@ void TransformFeedbackGLTest::draw() { " vertexOutput = vec2(0.3);\n" " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" "}\n"); - if(stream) geom.addSource( + if(stream) geom.addSource(Utility::format( "#extension GL_ARB_gpu_shader5: require\n" - "#define STREAM " + std::to_string(stream) + "\n" + - "layout(stream = 0) out mediump float otherOutput;\n" + - "layout(stream = STREAM) out mediump vec2 geomOutput;\n"); + "#define STREAM {}\n" + "layout(stream = 0) out mediump float otherOutput;\n" + "layout(stream = STREAM) out mediump vec2 geomOutput;\n", + stream)); else geom.addSource( "out mediump vec2 geomOutput;\n"); geom.addSource( diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp index 77de6d56d..7c6592445 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.cpp +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.cpp @@ -39,7 +39,8 @@ #include "Magnum/Math/Matrix4.h" #ifndef MAGNUM_TARGET_GLES2 -#include +#include +#include #include "Magnum/GL/Buffer.h" #endif @@ -110,7 +111,7 @@ template typename DistanceFieldVectorGL::Com .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n"); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", configuration.drawCount())); @@ -121,7 +122,7 @@ template typename DistanceFieldVectorGL::Com .addSource(rs.getString("Vector.vert")); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - frag.addSource(Utility::formatString( + frag.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define MATERIAL_COUNT {}\n" "#define DRAW_COUNT {}\n", diff --git a/src/Magnum/Shaders/DistanceFieldVectorGL.h b/src/Magnum/Shaders/DistanceFieldVectorGL.h index 7a31c67e5..d4d84af9f 100644 --- a/src/Magnum/Shaders/DistanceFieldVectorGL.h +++ b/src/Magnum/Shaders/DistanceFieldVectorGL.h @@ -31,6 +31,8 @@ * @m_since_latest */ +#include + #include "Magnum/DimensionTraits.h" #include "Magnum/GL/AbstractShaderProgram.h" #include "Magnum/Shaders/GenericGL.h" @@ -752,7 +754,7 @@ template class DistanceFieldVectorGL::Compil #ifndef MAGNUM_TARGET_GLES , GL::Version version #endif - ): DistanceFieldVectorGL{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)} + ): DistanceFieldVectorGL{Utility::move(shader)}, _vert{Utility::move(vert)}, _frag{Utility::move(frag)} #ifndef MAGNUM_TARGET_GLES , _version{version} #endif diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index 5f9e5aa14..3362cea71 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -41,7 +41,8 @@ #include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" #ifndef MAGNUM_TARGET_GLES2 -#include +#include +#include #include "Magnum/GL/Buffer.h" #include "Magnum/GL/TextureArray.h" @@ -178,7 +179,7 @@ template typename FlatGL::CompileState FlatG .addSource(configuration.flags() >= Flag::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : ""); #ifndef MAGNUM_TARGET_GLES2 if(configuration.jointCount()) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define JOINT_COUNT {}\n" "#define PER_VERTEX_JOINT_COUNT {}u\n" "#define SECONDARY_PER_VERTEX_JOINT_COUNT {}u\n" @@ -195,7 +196,7 @@ template typename FlatGL::CompileState FlatG out._perInstanceJointCountUniform)); } if(configuration.flags() >= Flag::DynamicPerVertexJointCount) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define DYNAMIC_PER_VERTEX_JOINT_COUNT\n" "#define PER_VERTEX_JOINT_COUNT_LOCATION {}\n", out._perVertexJointCountUniform)); @@ -203,7 +204,7 @@ template typename FlatGL::CompileState FlatG #endif #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", configuration.drawCount())); @@ -226,7 +227,7 @@ template typename FlatGL::CompileState FlatG ; #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - frag.addSource(Utility::formatString( + frag.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n" "#define MATERIAL_COUNT {}\n", diff --git a/src/Magnum/Shaders/FlatGL.h b/src/Magnum/Shaders/FlatGL.h index 9200d860b..8d1e364da 100644 --- a/src/Magnum/Shaders/FlatGL.h +++ b/src/Magnum/Shaders/FlatGL.h @@ -31,12 +31,18 @@ * @m_since_latest */ +#include + #include "Magnum/DimensionTraits.h" #include "Magnum/GL/AbstractShaderProgram.h" #include "Magnum/Shaders/GenericGL.h" #include "Magnum/Shaders/glShaderWrapper.h" #include "Magnum/Shaders/visibility.h" +#ifndef MAGNUM_TARGET_GLES2 +#include +#endif + namespace Magnum { namespace Shaders { namespace Implementation { @@ -1520,7 +1526,7 @@ template class FlatGL::CompileState: public #ifndef MAGNUM_TARGET_GLES , GL::Version version #endif - ): FlatGL{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)} + ): FlatGL{Utility::move(shader)}, _vert{Utility::move(vert)}, _frag{Utility::move(frag)} #ifndef MAGNUM_TARGET_GLES , _version{version} #endif diff --git a/src/Magnum/Shaders/Implementation/CreateCompatibilityShader.h b/src/Magnum/Shaders/Implementation/CreateCompatibilityShader.h index d8e82ab73..00344f0d7 100644 --- a/src/Magnum/Shaders/Implementation/CreateCompatibilityShader.h +++ b/src/Magnum/Shaders/Implementation/CreateCompatibilityShader.h @@ -25,7 +25,6 @@ DEALINGS IN THE SOFTWARE. */ -#include /** @todo remove when Shader is -free */ #include #include diff --git a/src/Magnum/Shaders/MeshVisualizerGL.cpp b/src/Magnum/Shaders/MeshVisualizerGL.cpp index fc5481b52..81103e3b8 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.cpp +++ b/src/Magnum/Shaders/MeshVisualizerGL.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include "Magnum/Math/Color.h" @@ -40,6 +39,9 @@ #include "Magnum/GL/Texture.h" #ifndef MAGNUM_TARGET_GLES2 +#include +#include + #include "Magnum/GL/Buffer.h" #include "Magnum/GL/TextureArray.h" #endif @@ -196,7 +198,7 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra ; #ifndef MAGNUM_TARGET_GLES2 if(jointCount) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define JOINT_COUNT {}\n" "#define PER_VERTEX_JOINT_COUNT {}u\n" "#define SECONDARY_PER_VERTEX_JOINT_COUNT {}u\n" @@ -213,7 +215,7 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra perInstanceJointCountUniform)); } if(flags >= FlagBase::DynamicPerVertexJointCount) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define DYNAMIC_PER_VERTEX_JOINT_COUNT\n" "#define PER_VERTEX_JOINT_COUNT_LOCATION {}\n", perVertexJointCountUniform)); @@ -221,7 +223,7 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra #endif #ifndef MAGNUM_TARGET_GLES2 if(flags >= FlagBase::UniformBuffers) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n" "#define MATERIAL_COUNT {}\n", @@ -245,7 +247,7 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra ; #ifndef MAGNUM_TARGET_GLES2 if(flags >= FlagBase::UniformBuffers) { - frag.addSource(Utility::formatString( + frag.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n" "#define MATERIAL_COUNT {}\n", @@ -539,7 +541,7 @@ MeshVisualizerGL2D::CompileState MeshVisualizerGL2D::compile(const Configuration "#define PRIMITIVE_ID\n") : ""); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - geom->addSource(Utility::formatString( + geom->addSource(Utility::format( "#define TWO_DIMENSIONS\n" "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n" @@ -1002,7 +1004,7 @@ MeshVisualizerGL3D::CompileState MeshVisualizerGL3D::compile(const Configuration geom = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Geometry); (*geom) - .addSource(Utility::formatString("#define MAX_VERTICES {}\n", maxVertices)) + .addSource(Utility::format("#define MAX_VERTICES {}\n", maxVertices)) .addSource(configuration.flags() & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") .addSource(baseFlags >= FlagBase::ObjectIdTexture ? "#define TEXTURED\n" : "") .addSource(baseFlags & FlagBase::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") @@ -1018,7 +1020,7 @@ MeshVisualizerGL3D::CompileState MeshVisualizerGL3D::compile(const Configuration .addSource(configuration.flags() & Flag::NormalDirection ? "#define NORMAL_DIRECTION\n" : ""); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - geom->addSource(Utility::formatString( + geom->addSource(Utility::format( "#define THREE_DIMENSIONS\n" "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n" diff --git a/src/Magnum/Shaders/MeshVisualizerGL.h b/src/Magnum/Shaders/MeshVisualizerGL.h index 634a03883..e6580ef42 100644 --- a/src/Magnum/Shaders/MeshVisualizerGL.h +++ b/src/Magnum/Shaders/MeshVisualizerGL.h @@ -31,6 +31,7 @@ * @m_since_latest */ +#include #include #include "Magnum/DimensionTraits.h" @@ -39,6 +40,10 @@ #include "Magnum/Shaders/glShaderWrapper.h" #include "Magnum/Shaders/visibility.h" +#ifndef MAGNUM_TARGET_GLES2 +#include +#endif + namespace Magnum { namespace Shaders { namespace Implementation { @@ -1320,13 +1325,13 @@ class MeshVisualizerGL2D::CompileState: public MeshVisualizerGL2D { #ifndef MAGNUM_TARGET_GLES , GL::Version version #endif - ): MeshVisualizerGL2D{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)} + ): MeshVisualizerGL2D{Utility::move(shader)}, _vert{Utility::move(vert)}, _frag{Utility::move(frag)} #ifndef MAGNUM_TARGET_GLES , _version{version} #endif { #if !defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2) - if(geom) _geom = Implementation::GLShaderWrapper{std::move(*geom)}; + if(geom) _geom = Implementation::GLShaderWrapper{Utility::move(*geom)}; #endif } @@ -3288,13 +3293,13 @@ class MeshVisualizerGL3D::CompileState: public MeshVisualizerGL3D { #ifndef MAGNUM_TARGET_GLES , GL::Version version #endif - ): MeshVisualizerGL3D{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)} + ): MeshVisualizerGL3D{Utility::move(shader)}, _vert{Utility::move(vert)}, _frag{Utility::move(frag)} #ifndef MAGNUM_TARGET_GLES , _version{version} #endif { #if !defined(MAGNUM_TARGET_WEBGL) && !defined(MAGNUM_TARGET_GLES2) - if(geom) _geom = Implementation::GLShaderWrapper{std::move(*geom)}; + if(geom) _geom = Implementation::GLShaderWrapper{Utility::move(*geom)}; #endif } diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 59a9531b7..36d35c956 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -32,8 +32,8 @@ #endif #include #include -#include -#include +#include +#include #include #include "Magnum/GL/Context.h" @@ -192,9 +192,9 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { /* Initializer for the light color / position / range arrays -- we need a list of initializers joined by commas. For GLES we'll simply upload the values directly. */ - std::string lightInitializer; + Containers::String lightInitializer; if(!(configuration.flags() >= Flag::UniformBuffers) && configuration.lightCount()) - lightInitializer = Utility::formatString( + lightInitializer = Utility::format( "#define LIGHT_POSITION_INITIALIZER {}\n" "#define LIGHT_COLOR_INITIALIZER {}\n" "#define LIGHT_RANGE_INITIALIZER {}\n", @@ -223,7 +223,7 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { .addSource(configuration.flags() >= Flag::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : ""); #ifndef MAGNUM_TARGET_GLES2 if(configuration.jointCount()) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define JOINT_COUNT {}\n" "#define PER_VERTEX_JOINT_COUNT {}u\n" "#define SECONDARY_PER_VERTEX_JOINT_COUNT {}u\n" @@ -242,7 +242,7 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { out._perInstanceJointCountUniform)); } if(configuration.flags() >= Flag::DynamicPerVertexJointCount) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define DYNAMIC_PER_VERTEX_JOINT_COUNT\n" "#define PER_VERTEX_JOINT_COUNT_LOCATION {}\n", out._perVertexJointCountUniform)); @@ -250,7 +250,7 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { #endif #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", configuration.drawCount(), @@ -279,7 +279,7 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { ; #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - frag.addSource(Utility::formatString( + frag.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n" "#define MATERIAL_COUNT {}\n" @@ -292,7 +292,7 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) { } else #endif { - frag.addSource(Utility::formatString( + frag.addSource(Utility::format( "#define LIGHT_COUNT {}\n" "#define LIGHT_COLORS_LOCATION {}\n" "#define LIGHT_SPECULAR_COLORS_LOCATION {}\n" diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 740f0d2ea..b6cc2af60 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -32,11 +32,17 @@ * @m_since_latest */ +#include + #include "Magnum/GL/AbstractShaderProgram.h" #include "Magnum/Shaders/GenericGL.h" #include "Magnum/Shaders/glShaderWrapper.h" #include "Magnum/Shaders/visibility.h" +#ifndef MAGNUM_TARGET_GLES2 +#include +#endif + namespace Magnum { namespace Shaders { /** @@ -2301,7 +2307,7 @@ class PhongGL::CompileState: public PhongGL { #ifndef MAGNUM_TARGET_GLES , GL::Version version #endif - ): PhongGL{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)} + ): PhongGL{Utility::move(shader)}, _vert{Utility::move(vert)}, _frag{Utility::move(frag)} #ifndef MAGNUM_TARGET_GLES , _version{version} #endif diff --git a/src/Magnum/Shaders/VectorGL.cpp b/src/Magnum/Shaders/VectorGL.cpp index 1a733d8c4..b04b64f15 100644 --- a/src/Magnum/Shaders/VectorGL.cpp +++ b/src/Magnum/Shaders/VectorGL.cpp @@ -39,7 +39,8 @@ #include "Magnum/Math/Matrix4.h" #ifndef MAGNUM_TARGET_GLES2 -#include +#include +#include #include "Magnum/GL/Buffer.h" #endif @@ -110,7 +111,7 @@ template typename VectorGL::CompileState Vec .addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n"); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", configuration.drawCount())); @@ -121,7 +122,7 @@ template typename VectorGL::CompileState Vec .addSource(rs.getString("Vector.vert")); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - frag.addSource(Utility::formatString( + frag.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n" "#define MATERIAL_COUNT {}\n", diff --git a/src/Magnum/Shaders/VectorGL.h b/src/Magnum/Shaders/VectorGL.h index d4ee04a3d..a88600c90 100644 --- a/src/Magnum/Shaders/VectorGL.h +++ b/src/Magnum/Shaders/VectorGL.h @@ -31,6 +31,8 @@ * @m_since_latest */ +#include + #include "Magnum/DimensionTraits.h" #include "Magnum/GL/AbstractShaderProgram.h" #include "Magnum/Shaders/GenericGL.h" @@ -702,7 +704,7 @@ template class VectorGL::CompileState: publi #ifndef MAGNUM_TARGET_GLES , GL::Version version #endif - ): VectorGL{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)} + ): VectorGL{Utility::move(shader)}, _vert{Utility::move(vert)}, _frag{Utility::move(frag)} #ifndef MAGNUM_TARGET_GLES , _version{version} #endif diff --git a/src/Magnum/Shaders/VertexColorGL.cpp b/src/Magnum/Shaders/VertexColorGL.cpp index e2b995587..d1018559c 100644 --- a/src/Magnum/Shaders/VertexColorGL.cpp +++ b/src/Magnum/Shaders/VertexColorGL.cpp @@ -37,7 +37,8 @@ #include "Magnum/Math/Matrix4.h" #ifndef MAGNUM_TARGET_GLES2 -#include +#include +#include #include "Magnum/GL/Buffer.h" #endif @@ -100,7 +101,7 @@ template typename VertexColorGL::CompileStat vert.addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n" : "#define THREE_DIMENSIONS\n"); #ifndef MAGNUM_TARGET_GLES2 if(configuration.flags() >= Flag::UniformBuffers) { - vert.addSource(Utility::formatString( + vert.addSource(Utility::format( "#define UNIFORM_BUFFERS\n" "#define DRAW_COUNT {}\n", configuration.drawCount())); diff --git a/src/Magnum/Shaders/VertexColorGL.h b/src/Magnum/Shaders/VertexColorGL.h index a45d0cc6e..ec86062c3 100644 --- a/src/Magnum/Shaders/VertexColorGL.h +++ b/src/Magnum/Shaders/VertexColorGL.h @@ -31,6 +31,8 @@ * @m_since_latest */ +#include + #include "Magnum/DimensionTraits.h" #include "Magnum/GL/AbstractShaderProgram.h" #include "Magnum/Shaders/GenericGL.h" @@ -519,7 +521,7 @@ template class VertexColorGL::CompileState: #ifndef MAGNUM_TARGET_GLES , GL::Version version #endif - ): VertexColorGL{std::move(shader)}, _vert{std::move(vert)}, _frag{std::move(frag)} + ): VertexColorGL{Utility::move(shader)}, _vert{Utility::move(vert)}, _frag{Utility::move(frag)} #ifndef MAGNUM_TARGET_GLES , _version{version} #endif diff --git a/src/Magnum/TextureTools/DistanceField.cpp b/src/Magnum/TextureTools/DistanceField.cpp index 841ead75a..6fd8cd1b3 100644 --- a/src/Magnum/TextureTools/DistanceField.cpp +++ b/src/Magnum/TextureTools/DistanceField.cpp @@ -26,7 +26,8 @@ #include "DistanceField.h" #include -#include +#include +#include #include #include "Magnum/Math/Range.h" @@ -101,7 +102,7 @@ DistanceFieldShader::DistanceFieldShader(const UnsignedInt radius) { vert.addSource(rs.getString("FullScreenTriangle.glsl")) .addSource(rs.getString("DistanceFieldShader.vert")); - frag.addSource(Utility::formatString("#define RADIUS {}\n", radius)) + frag.addSource(Utility::format("#define RADIUS {}\n", radius)) .addSource(rs.getString("DistanceFieldShader.frag")); CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile() && frag.compile());