Browse Source

GL: implement APIs for async shader compilation & linking.

pull/589/head
Vladislav Oleshko 4 years ago committed by Vladimír Vondruš
parent
commit
96f97d4e7a
  1. 88
      src/Magnum/GL/AbstractShaderProgram.cpp
  2. 53
      src/Magnum/GL/AbstractShaderProgram.h
  3. 7
      src/Magnum/GL/Implementation/ShaderProgramState.cpp
  4. 3
      src/Magnum/GL/Implementation/ShaderProgramState.h
  5. 12
      src/Magnum/GL/Implementation/ShaderState.cpp
  6. 3
      src/Magnum/GL/Implementation/ShaderState.h
  7. 113
      src/Magnum/GL/Shader.cpp
  8. 42
      src/Magnum/GL/Shader.h
  9. 123
      src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp
  10. 88
      src/Magnum/GL/Test/ShaderGLTest.cpp

88
src/Magnum/GL/AbstractShaderProgram.cpp

@ -585,53 +585,55 @@ void AbstractShaderProgram::transformFeedbackVaryingsImplementationDanglingWorka
bool AbstractShaderProgram::link() { return link({*this}); }
bool AbstractShaderProgram::link(std::initializer_list<Containers::Reference<AbstractShaderProgram>> shaders) {
bool allSuccess = true;
void AbstractShaderProgram::submitLink() {
glLinkProgram(_id);
}
bool AbstractShaderProgram::checkLink() {
GLint success, logLength;
glGetProgramiv(_id, GL_LINK_STATUS, &success);
glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &logLength);
/* Invoke (possibly parallel) linking on all shaders */
for(AbstractShaderProgram& shader: shaders) glLinkProgram(shader._id);
/* After linking phase, check status of all shaders */
Int i = 1;
for(AbstractShaderProgram& shader: shaders) {
GLint success, logLength;
glGetProgramiv(shader._id, GL_LINK_STATUS, &success);
glGetProgramiv(shader._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(shader._id, message.size(), nullptr, &message[0]);
message.resize(Math::max(logLength, 1)-1);
/* Some drivers are chatty and can't keep shut when there's nothing to
be said, handle that as well. */
Context::current().state().shaderProgram.cleanLogImplementation(message);
/* Show error log */
if(!success) {
Error out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::AbstractShaderProgram::link(): linking";
if(shaders.size() != 1) out << "of shader" << i;
out << "failed with the following message:" << Debug::newline << message;
/* Or just warnings, if any */
} else if(!message.empty()) {
Warning out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::AbstractShaderProgram::link(): linking";
if(shaders.size() != 1) out << "of shader" << i;
out << "succeeded with the following message:" << Debug::newline << message;
}
/* Success of all depends on each of them */
allSuccess = allSuccess && success;
++i;
/* 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);
/* Some drivers are chatty and can't keep shut when there's nothing to
be said, handle that as well. */
Context::current().state().shaderProgram.cleanLogImplementation(message);
/* Show error log */
if(!success) {
Error out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::AbstractShaderProgram::link(): linking failed with the following message:"
<< Debug::newline << message;
/* Or just warnings, if any */
} else if(!message.empty()) {
Warning out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::AbstractShaderProgram::link(): linking succeeded with the following message:"
<< Debug::newline << message;
}
return success;
}
bool AbstractShaderProgram::link(std::initializer_list<Containers::Reference<AbstractShaderProgram>> shaders) {
for(AbstractShaderProgram& shader: shaders) shader.submitLink();
bool allSuccess = true;
for(AbstractShaderProgram& shader: shaders) allSuccess = allSuccess && shader.checkLink();
return allSuccess;
}
bool AbstractShaderProgram::isLinkFinished() {
GLint success;
Context::current().state().shaderProgram.completionStatusImplementation(_id, GL_COMPLETION_STATUS_KHR, &success);
return success == GL_TRUE;
}
void AbstractShaderProgram::cleanLogImplementationNoOp(std::string&) {}
#if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES)
@ -646,6 +648,10 @@ void AbstractShaderProgram::cleanLogImplementationAngle(std::string& message) {
}
#endif
void AbstractShaderProgram::completionStatusImplementationFallback(GLuint, GLenum, GLint* value) {
*value = GL_TRUE;
}
Int AbstractShaderProgram::uniformLocationInternal(const Containers::ArrayView<const char> name) {
const GLint location = glGetUniformLocation(_id, name);
if(location == -1)

53
src/Magnum/GL/AbstractShaderProgram.h

@ -1258,19 +1258,26 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
AbstractShaderProgram& dispatchCompute(const Vector3ui& workgroupCount);
#endif
protected:
/**
* @brief Link the shader
* @brief Non-blocking linking status check
* @return @cpp true @ce if linking finished, @cpp false @ce otherwise
*
* On some drivers this might return false even after
* @ref checkLink() reported successful linking.
*
* @see @fn_gl_keyword{GetProgram} with
* @def_gl_extension{COMPLETION_STATUS,KHR,parallel_shader_compile}
*/
bool isLinkFinished();
protected:
/** @brief Link the shaders
*
* Calls @ref submitLink() on all shaders first, then @ref checkLink().
* Returns @cpp false @ce if linking of any shader failed, @cpp true @ce
* if everything succeeded. Linker message (if any) is printed to error
* output. All attached shaders must be compiled with
* @ref Shader::compile() before linking. The operation is batched in a
* if everything succeeded. The operation is batched in a
* way that allows the driver to link multiple shaders simultaneously
* (i.e. in multiple threads).
* @see @fn_gl_keyword{LinkProgram}, @fn_gl_keyword{GetProgram} with
* @def_gl{LINK_STATUS} and @def_gl{INFO_LOG_LENGTH},
* @fn_gl_keyword{GetProgramInfoLog}
*/
static bool link(std::initializer_list<Containers::Reference<AbstractShaderProgram>> shaders);
@ -1446,13 +1453,37 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
/**
* @brief Link the shader
*
* Links single shader. If possible, prefer to link multiple shaders
* at once using @ref link(std::initializer_list<Containers::Reference<AbstractShaderProgram>>)
* Calls @ref submitLink(), then @ref checkLink().
* If possible, prefer to link multiple shaders at once using
* @ref link(std::initializer_list<Containers::Reference<AbstractShaderProgram>>)
* for improved performance, see its documentation for more
* information.
*/
bool link();
/**
* @brief Submit for linking
*
* The attached shaders must be compiled with @ref Shader::compile()
* or @ref Shader::submitCompile() before linking.
*
* @see @fn_gl_keyword{LinkProgram}
*/
void submitLink();
/**
* @brief Check link status and await completion
*
* Returns @cpp false @ce if linking failed, @cpp true @ce on success.
* Linker message (if any) is printed to error output. This function
* must be called only after @ref submitLink().
*
* @see @fn_gl_keyword{GetProgram} with
* @def_gl{LINK_STATUS} and @def_gl{INFO_LOG_LENGTH},
* @fn_gl_keyword{GetProgramInfoLog}
*/
bool checkLink();
/**
* @brief Get uniform location
* @param name Uniform name
@ -1668,6 +1699,8 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
static MAGNUM_GL_LOCAL void cleanLogImplementationAngle(std::string& message);
#endif
MAGNUM_GL_LOCAL static void APIENTRY completionStatusImplementationFallback(GLuint, GLenum, GLint*);
MAGNUM_GL_LOCAL static void use(GLuint id);
void use();

7
src/Magnum/GL/Implementation/ShaderProgramState.cpp

@ -78,6 +78,13 @@ ShaderProgramState::ShaderProgramState(Context& context, Containers::StaticArray
cleanLogImplementation = &AbstractShaderProgram::cleanLogImplementationNoOp;
}
if(context.isExtensionSupported<Extensions::KHR::parallel_shader_compile>()) {
extensions[Extensions::KHR::parallel_shader_compile::Index] = Extensions::KHR::parallel_shader_compile::string();
completionStatusImplementation = glGetProgramiv;
} else {
completionStatusImplementation = &AbstractShaderProgram::completionStatusImplementationFallback;
}
#ifndef MAGNUM_TARGET_WEBGL
#ifndef MAGNUM_TARGET_GLES2
#ifndef MAGNUM_TARGET_GLES

3
src/Magnum/GL/Implementation/ShaderProgramState.h

@ -47,6 +47,9 @@ struct ShaderProgramState {
void(AbstractShaderProgram::*transformFeedbackVaryingsImplementation)(Containers::ArrayView<const std::string>, AbstractShaderProgram::TransformFeedbackBufferMode);
#endif
void(*cleanLogImplementation)(std::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);
#ifndef MAGNUM_TARGET_WEBGL
void(APIENTRY *uniform1fvImplementation)(GLuint, GLint, GLsizei, const GLfloat*);

12
src/Magnum/GL/Implementation/ShaderState.cpp

@ -31,12 +31,13 @@
#include "Magnum/GL/Context.h"
#include "Magnum/GL/Shader.h"
#include "Magnum/GL/Extensions.h"
namespace Magnum { namespace GL { namespace Implementation {
using namespace Containers::Literals;
ShaderState::ShaderState(Context& context, Containers::StaticArrayView<Implementation::ExtensionCount, const char*>):
ShaderState::ShaderState(Context& context, Containers::StaticArrayView<Implementation::ExtensionCount, const char*> extensions):
maxVertexOutputComponents{}, maxFragmentInputComponents{},
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
maxTessellationControlInputComponents{}, maxTessellationControlOutputComponents{}, maxTessellationControlTotalOutputComponents{}, maxTessellationEvaluationInputComponents{}, maxTessellationEvaluationOutputComponents{}, maxGeometryInputComponents{}, maxGeometryOutputComponents{}, maxGeometryTotalOutputComponents{}, maxAtomicCounterBuffers{}, maxCombinedAtomicCounterBuffers{}, maxAtomicCounters{}, maxCombinedAtomicCounters{}, maxImageUniforms{}, maxCombinedImageUniforms{}, maxShaderStorageBlocks{}, maxCombinedShaderStorageBlocks{},
@ -68,9 +69,12 @@ ShaderState::ShaderState(Context& context, Containers::StaticArrayView<Implement
cleanLogImplementation = &Shader::cleanLogImplementationNoOp;
}
/* Needed only if neither of these ifdefs above hits, but I won't bother
crafting the preprocessor logic for this. */
static_cast<void>(context);
if(context.isExtensionSupported<GL::Extensions::KHR::parallel_shader_compile>()) {
extensions[Extensions::KHR::parallel_shader_compile::Index] = Extensions::KHR::parallel_shader_compile::string();
completionStatusImplementation = glGetShaderiv;
} else {
completionStatusImplementation = &Shader::completionStatusImplementationFallback;
}
}
}}}

3
src/Magnum/GL/Implementation/ShaderState.h

@ -53,6 +53,9 @@ struct ShaderState {
void(Shader::*addSourceImplementation)(std::string);
void(*cleanLogImplementation)(std::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);
GLint maxVertexOutputComponents,
maxFragmentInputComponents;

113
src/Magnum/GL/Shader.cpp

@ -749,74 +749,69 @@ Shader& Shader::addFile(const std::string& filename) {
bool Shader::compile() { return compile({*this}); }
bool Shader::compile(std::initializer_list<Containers::Reference<Shader>> shaders) {
bool allSuccess = true;
void Shader::submitCompile() {
CORRADE_ASSERT(_sources.size() > 1, "GL::Shader::compile(): no files added", );
/* Allocate large enough array for source pointers and sizes (to avoid
reallocating it for each of them) */
std::size_t maxSourceCount = 0;
for(Shader& shader: shaders) {
CORRADE_ASSERT(shader._sources.size() > 1, "GL::Shader::compile(): no files added", false);
maxSourceCount = Math::max(shader._sources.size(), maxSourceCount);
}
/** @todo ArrayTuple/VLAs */
Containers::Array<const GLchar*> pointers(maxSourceCount);
Containers::Array<GLint> sizes(maxSourceCount);
Containers::Array<const GLchar*> pointers(_sources.size());
Containers::Array<GLint> sizes(_sources.size());
/* Upload sources of all shaders */
for(Shader& shader: shaders) {
for(std::size_t i = 0; i != shader._sources.size(); ++i) {
pointers[i] = static_cast<const GLchar*>(shader._sources[i].data());
sizes[i] = shader._sources[i].size();
}
glShaderSource(shader._id, shader._sources.size(), pointers, sizes);
for(std::size_t i = 0; i != _sources.size(); ++i) {
pointers[i] = static_cast<const GLchar*>(_sources[i].data());
sizes[i] = _sources[i].size();
}
/* Invoke (possibly parallel) compilation on all shaders */
for(Shader& shader: shaders) glCompileShader(shader._id);
/* After compilation phase, check status of all shaders */
Int i = 1;
for(Shader& shader: shaders) {
GLint success, logLength;
glGetShaderiv(shader._id, GL_COMPILE_STATUS, &success);
glGetShaderiv(shader._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(shader._id, message.size(), nullptr, &message[0]);
message.resize(Math::max(logLength, 1)-1);
/* Some drivers are chatty and can't keep shut when there's nothing to
be said, handle that as well. */
Context::current().state().shader.cleanLogImplementation(message);
/* Show error log */
if(!success) {
Error out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::Shader::compile(): compilation of" << shaderName(shader._type) << "shader";
if(shaders.size() != 1) out << i;
out << "failed with the following message:" << Debug::newline << message;
/* Or just warnings, if any */
} else if(!message.empty()) {
Warning out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::Shader::compile(): compilation of" << shaderName(shader._type) << "shader";
if(shaders.size() != 1) out << i;
out << "succeeded with the following message:" << Debug::newline << message;
}
/* Success of all depends on each of them */
allSuccess = allSuccess && success;
++i;
glShaderSource(_id, _sources.size(), pointers, sizes);
glCompileShader(_id);
}
bool Shader::checkCompile() { /* After compilation phase, check status of all shaders */
GLint success, logLength;
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);
/* Some drivers are chatty and can't keep shut when there's nothing to
be said, handle that as well. */
Context::current().state().shader.cleanLogImplementation(message);
/* Show error log */
if(!success) {
Error out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::Shader::compile(): compilation of" << shaderName(_type) << "shader"
<< "failed with the following message:" << Debug::newline << message;
/* Or just warnings, if any */
} else if(!message.empty()) {
Warning out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::Shader::compile(): compilation of" << shaderName(_type) << "shader"
<< "succeeded with the following message:" << Debug::newline << message;
}
return success;
}
bool Shader::compile(std::initializer_list<Containers::Reference<Shader>> shaders) {
/* Invoke (possibly parallel) compilation on all shaders */
for(Shader& shader: shaders) shader.submitCompile();
bool allSuccess = true;
for(Shader& shader: shaders) allSuccess = allSuccess && shader.checkCompile();
return allSuccess;
}
bool Shader::isCompileFinished() {
GLint success;
Context::current().state().shader.completionStatusImplementation(_id, GL_COMPLETION_STATUS_KHR, &success);
return success == GL_TRUE;
}
void Shader::cleanLogImplementationNoOp(std::string&) {}
#if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES)
@ -825,6 +820,10 @@ void Shader::cleanLogImplementationIntelWindows(std::string& message) {
}
#endif
void Shader::completionStatusImplementationFallback(GLuint, GLenum, GLint* value) {
*value = GL_TRUE;
}
#ifndef DOXYGEN_GENERATING_OUTPUT
Debug& operator<<(Debug& debug, const Shader::Type value) {
debug << "GL::Shader::Type" << Debug::nospace;

42
src/Magnum/GL/Shader.h

@ -510,14 +510,11 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
/**
* @brief Compile multiple shaders simultaneously
*
* Calls @ref submitCompile() on all shaders first, then @ref checkCompile().
* Returns @cpp false @ce if compilation of any shader failed,
* @cpp true @ce if everything succeeded. Compiler messages (if any)
* are printed to error output. The operation is batched in a way that
* @cpp true @ce if everything succeeded. The operation is batched in a way that
* allows the driver to perform multiple compilations simultaneously
* (i.e. in multiple threads).
* @see @fn_gl_keyword{ShaderSource}, @fn_gl_keyword{CompileShader},
* @fn_gl_keyword{GetShader} with @def_gl{COMPILE_STATUS} and
* @def_gl{INFO_LOG_LENGTH}, @fn_gl_keyword{GetShaderInfoLog}
*/
static bool compile(std::initializer_list<Containers::Reference<Shader>> shaders);
@ -636,13 +633,42 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
/**
* @brief Compile shader
*
* Compiles single shader. Prefer to compile multiple shaders at once
* using @ref compile(std::initializer_list<Containers::Reference<Shader>>)
* Calls @ref submitCompile(), then @ref checkCompile().
* Prefer to compile multiple shaders at once using
* @ref compile(std::initializer_list<Containers::Reference<Shader>>)
* for improved performance, see its documentation for more
* information.
*/
bool compile();
/**
* @brief Submit shader for compilation
*
* @see @fn_gl_keyword{ShaderSource}, @fn_gl_keyword{CompileShader}
*/
void submitCompile();
/**
* @brief Check compilation status and await completion
*
* Returns @cpp false @ce if compilation of failed, @cpp true @ce on success.
* This function must be called only after @ref submitCompile().
*
* @see @fn_gl_keyword{GetShader} with @def_gl{COMPILE_STATUS} and
* @def_gl{INFO_LOG_LENGTH}, @fn_gl_keyword{GetShaderInfoLog}
*/
bool checkCompile();
/**
* @brief Non-blocking compilation status check
* @return @cpp true @ce if shader compilation finished, @cpp false @ce otherwise
*
* @see @fn_gl_keyword{GetProgram} with
* @def_gl_extension{COMPLETION_STATUS,KHR,parallel_shader_compile}
*
*/
bool isCompileFinished();
private:
void MAGNUM_GL_LOCAL addSourceImplementationDefault(std::string source);
#if defined(CORRADE_TARGET_EMSCRIPTEN) && defined(__EMSCRIPTEN_PTHREADS__)
@ -654,6 +680,8 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
static MAGNUM_GL_LOCAL void cleanLogImplementationIntelWindows(std::string& message);
#endif
MAGNUM_GL_LOCAL static void APIENTRY completionStatusImplementationFallback(GLuint, GLenum, GLint*);
Type _type;
GLuint _id;

123
src/Magnum/GL/Test/AbstractShaderProgramGLTest.cpp

@ -27,8 +27,10 @@
#include <Corrade/Containers/Reference.h>
#include <Corrade/Containers/StringStl.h> /** @todo remove when Shader is <string>-free */
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/TestSuite/Compare/String.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/Resource.h>
#include <Corrade/Utility/System.h>
#include "Magnum/Image.h"
#include "Magnum/ImageView.h"
@ -68,8 +70,11 @@ struct AbstractShaderProgramGLTest: OpenGLTester {
#ifndef MAGNUM_TARGET_GLES
void createMultipleOutputsIndexed();
#endif
void createAsync();
void linkFailure();
void linkFailureAsync();
void uniformNotFound();
void uniform();
@ -103,12 +108,14 @@ AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() {
#endif
&AbstractShaderProgramGLTest::create,
&AbstractShaderProgramGLTest::createAsync,
&AbstractShaderProgramGLTest::createMultipleOutputs,
#ifndef MAGNUM_TARGET_GLES
&AbstractShaderProgramGLTest::createMultipleOutputsIndexed,
#endif
&AbstractShaderProgramGLTest::linkFailure,
&AbstractShaderProgramGLTest::linkFailureAsync,
&AbstractShaderProgramGLTest::uniformNotFound,
&AbstractShaderProgramGLTest::uniform,
@ -205,6 +212,8 @@ struct MyPublicShader: AbstractShaderProgram {
using AbstractShaderProgram::bindFragmentDataLocation;
#endif
using AbstractShaderProgram::link;
using AbstractShaderProgram::submitLink;
using AbstractShaderProgram::checkLink;
using AbstractShaderProgram::uniformLocation;
#ifndef MAGNUM_TARGET_GLES2
using AbstractShaderProgram::uniformBlockIndex;
@ -257,6 +266,80 @@ void AbstractShaderProgramGLTest::create() {
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_VERIFY(linked);
// Some drivers need a bit of time to update this result
Utility::System::sleep(200);
CORRADE_VERIFY(program.isLinkFinished());
{
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
#endif
CORRADE_VERIFY(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");
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_VERIFY(matrixUniform >= 0);
CORRADE_VERIFY(multiplierUniform >= 0);
CORRADE_VERIFY(colorUniform >= 0);
CORRADE_VERIFY(additionsUniform >= 0);
}
void AbstractShaderProgramGLTest::createAsync() {
Utility::Resource rs("AbstractShaderProgramGLTest");
Shader vert(
#ifndef MAGNUM_TARGET_GLES
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
#else
Version::GLES200
#endif
, Shader::Type::Vertex);
vert.addSource(rs.getString("MyShader.vert"));
const bool vertCompiled = vert.compile();
Shader frag(
#ifndef MAGNUM_TARGET_GLES
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
#else
Version::GLES200
#endif
, Shader::Type::Fragment);
frag.addSource(rs.getString("MyShader.frag"));
const bool fragCompiled = frag.compile();
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_VERIFY(vertCompiled);
CORRADE_VERIFY(fragCompiled);
MyPublicShader program;
program.attachShaders({vert, frag});
MAGNUM_VERIFY_NO_GL_ERROR();
program.bindAttributeLocation(0, "position");
program.submitLink();
while(!program.isLinkFinished())
Utility::System::sleep(100);
CORRADE_VERIFY(program.checkLink());
CORRADE_VERIFY(program.isLinkFinished());
const bool valid = program.validate().first;
MAGNUM_VERIFY_NO_GL_ERROR();
{
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
@ -410,6 +493,46 @@ void AbstractShaderProgramGLTest::linkFailure() {
MyPublicShader program;
program.attachShaders({shader});
CORRADE_VERIFY(!program.link());
Utility::System::sleep(200);
CORRADE_VERIFY(program.isLinkFinished());
}
void AbstractShaderProgramGLTest::linkFailureAsync() {
Shader shader(
#ifndef MAGNUM_TARGET_GLES
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
#else
Version::GLES200
#endif
, Shader::Type::Fragment);
shader.addSource("[fu] bleh error #:! stuff\n");
{
Error redirectError{nullptr};
CORRADE_VERIFY(!shader.compile());
}
MyPublicShader program;
program.attachShaders({shader});
std::ostringstream out;
Error redirectError{&out};
program.submitLink();
while(!program.isLinkFinished())
Utility::System::sleep(100);
CORRADE_VERIFY(out.str().empty());
CORRADE_VERIFY(!program.checkLink());
CORRADE_VERIFY(program.isLinkFinished());
CORRADE_COMPARE_AS(out.str(), "GL::AbstractShaderProgram::link(): linking failed with the following message:",
TestSuite::Compare::StringHasPrefix);
}
void AbstractShaderProgramGLTest::uniformNotFound() {

88
src/Magnum/GL/Test/ShaderGLTest.cpp

@ -23,8 +23,11 @@
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/StringStl.h> /** @todo remove once Shader is <string>-free */
#include <Corrade/TestSuite/Compare/String.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/System.h>
#include <Corrade/Utility/Path.h>
#include "Magnum/GL/Context.h"
@ -55,6 +58,9 @@ struct ShaderGLTest: OpenGLTester {
void addSourceNoVersion();
void addFile();
void compile();
void compileFailure();
void compileAsync();
void compileAsyncFailure();
void compileUtf8();
void compileNoVersion();
};
@ -72,6 +78,9 @@ ShaderGLTest::ShaderGLTest() {
&ShaderGLTest::addSourceNoVersion,
&ShaderGLTest::addFile,
&ShaderGLTest::compile,
&ShaderGLTest::compileFailure,
&ShaderGLTest::compileAsync,
&ShaderGLTest::compileAsyncFailure,
&ShaderGLTest::compileUtf8,
&ShaderGLTest::compileNoVersion});
}
@ -276,11 +285,84 @@ void ShaderGLTest::compile() {
Shader shader(v, Shader::Type::Fragment);
shader.addSource("void main() {}\n");
CORRADE_VERIFY(shader.compile());
CORRADE_VERIFY(shader.isCompileFinished());
}
void ShaderGLTest::compileFailure() {
#ifndef MAGNUM_TARGET_GLES
constexpr Version v =
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
;
#else
constexpr Version v = Version::GLES200;
#endif
Shader shader(v, Shader::Type::Fragment);
shader.addSource("[fu] bleh error #:! stuff\n");
CORRADE_VERIFY(!shader.compile());
CORRADE_VERIFY(shader.isCompileFinished());
}
void ShaderGLTest::compileAsync() {
#ifndef MAGNUM_TARGET_GLES
constexpr Version v =
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
;
#else
constexpr Version v = Version::GLES200;
#endif
Shader shader(v, Shader::Type::Fragment);
shader.addSource("void main() {}\n");
shader.submitCompile();
while(!shader.isCompileFinished())
Utility::System::sleep(100);
CORRADE_VERIFY(shader.checkCompile());
CORRADE_VERIFY(shader.isCompileFinished());
}
void ShaderGLTest::compileAsyncFailure() {
#ifndef MAGNUM_TARGET_GLES
constexpr Version v =
#ifndef CORRADE_TARGET_APPLE
Version::GL210
#else
Version::GL310
#endif
;
#else
constexpr Version v = Version::GLES200;
#endif
Shader shader(v, Shader::Type::Fragment);
shader.addSource("[fu] bleh error #:! stuff\n");
std::ostringstream out;
Error redirectError{&out};
shader.submitCompile();
while(!shader.isCompileFinished())
Utility::System::sleep(100);
CORRADE_VERIFY(out.str().empty());
Shader shader2(v, Shader::Type::Fragment);
shader2.addSource("[fu] bleh error #:! stuff\n");
CORRADE_VERIFY(!shader2.compile());
CORRADE_VERIFY(!shader.checkCompile());
CORRADE_VERIFY(shader.isCompileFinished());
CORRADE_COMPARE_AS(out.str(), "GL::Shader::compile(): compilation of fragment shader failed with the following message:",
TestSuite::Compare::StringHasPrefix);
}
void ShaderGLTest::compileUtf8() {

Loading…
Cancel
Save