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() { return link({*this}); }
bool AbstractShaderProgram::link(std::initializer_list<Containers::Reference<AbstractShaderProgram>> shaders) { void AbstractShaderProgram::submitLink() {
bool allSuccess = true; 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 */ /* Error or warning message. The string is returned null-terminated,
for(AbstractShaderProgram& shader: shaders) glLinkProgram(shader._id); strip the \0 at the end afterwards. */
std::string message(logLength, '\n');
/* After linking phase, check status of all shaders */ if(message.size() > 1)
Int i = 1; glGetProgramInfoLog(_id, message.size(), nullptr, &message[0]);
for(AbstractShaderProgram& shader: shaders) { message.resize(Math::max(logLength, 1)-1);
GLint success, logLength;
glGetProgramiv(shader._id, GL_LINK_STATUS, &success); /* Some drivers are chatty and can't keep shut when there's nothing to
glGetProgramiv(shader._id, GL_INFO_LOG_LENGTH, &logLength); be said, handle that as well. */
Context::current().state().shaderProgram.cleanLogImplementation(message);
/* Error or warning message. The string is returned null-terminated,
strip the \0 at the end afterwards. */ /* Show error log */
std::string message(logLength, '\n'); if(!success) {
if(message.size() > 1) Error out{Debug::Flag::NoNewlineAtTheEnd};
glGetProgramInfoLog(shader._id, message.size(), nullptr, &message[0]); out << "GL::AbstractShaderProgram::link(): linking failed with the following message:"
message.resize(Math::max(logLength, 1)-1); << Debug::newline << message;
/* Some drivers are chatty and can't keep shut when there's nothing to /* Or just warnings, if any */
be said, handle that as well. */ } else if(!message.empty()) {
Context::current().state().shaderProgram.cleanLogImplementation(message); Warning out{Debug::Flag::NoNewlineAtTheEnd};
out << "GL::AbstractShaderProgram::link(): linking succeeded with the following message:"
/* Show error log */ << Debug::newline << message;
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;
} }
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; 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&) {} void AbstractShaderProgram::cleanLogImplementationNoOp(std::string&) {}
#if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES)
@ -646,6 +648,10 @@ void AbstractShaderProgram::cleanLogImplementationAngle(std::string& message) {
} }
#endif #endif
void AbstractShaderProgram::completionStatusImplementationFallback(GLuint, GLenum, GLint* value) {
*value = GL_TRUE;
}
Int AbstractShaderProgram::uniformLocationInternal(const Containers::ArrayView<const char> name) { Int AbstractShaderProgram::uniformLocationInternal(const Containers::ArrayView<const char> name) {
const GLint location = glGetUniformLocation(_id, name); const GLint location = glGetUniformLocation(_id, name);
if(location == -1) 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); AbstractShaderProgram& dispatchCompute(const Vector3ui& workgroupCount);
#endif #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 * Returns @cpp false @ce if linking of any shader failed, @cpp true @ce
* if everything succeeded. Linker message (if any) is printed to error * if everything succeeded. The operation is batched in a
* output. All attached shaders must be compiled with
* @ref Shader::compile() before linking. The operation is batched in a
* way that allows the driver to link multiple shaders simultaneously * way that allows the driver to link multiple shaders simultaneously
* (i.e. in multiple threads). * (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); 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 * @brief Link the shader
* *
* Links single shader. If possible, prefer to link multiple shaders * Calls @ref submitLink(), then @ref checkLink().
* at once using @ref link(std::initializer_list<Containers::Reference<AbstractShaderProgram>>) * 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 * for improved performance, see its documentation for more
* information. * information.
*/ */
bool link(); 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 * @brief Get uniform location
* @param name Uniform name * @param name Uniform name
@ -1668,6 +1699,8 @@ class MAGNUM_GL_EXPORT AbstractShaderProgram: public AbstractObject {
static MAGNUM_GL_LOCAL void cleanLogImplementationAngle(std::string& message); static MAGNUM_GL_LOCAL void cleanLogImplementationAngle(std::string& message);
#endif #endif
MAGNUM_GL_LOCAL static void APIENTRY completionStatusImplementationFallback(GLuint, GLenum, GLint*);
MAGNUM_GL_LOCAL static void use(GLuint id); MAGNUM_GL_LOCAL static void use(GLuint id);
void use(); void use();

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

@ -78,6 +78,13 @@ ShaderProgramState::ShaderProgramState(Context& context, Containers::StaticArray
cleanLogImplementation = &AbstractShaderProgram::cleanLogImplementationNoOp; 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_WEBGL
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
#ifndef MAGNUM_TARGET_GLES #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); void(AbstractShaderProgram::*transformFeedbackVaryingsImplementation)(Containers::ArrayView<const std::string>, AbstractShaderProgram::TransformFeedbackBufferMode);
#endif #endif
void(*cleanLogImplementation)(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);
#ifndef MAGNUM_TARGET_WEBGL #ifndef MAGNUM_TARGET_WEBGL
void(APIENTRY *uniform1fvImplementation)(GLuint, GLint, GLsizei, const GLfloat*); 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/Context.h"
#include "Magnum/GL/Shader.h" #include "Magnum/GL/Shader.h"
#include "Magnum/GL/Extensions.h"
namespace Magnum { namespace GL { namespace Implementation { namespace Magnum { namespace GL { namespace Implementation {
using namespace Containers::Literals; 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{}, maxVertexOutputComponents{}, maxFragmentInputComponents{},
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) #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{}, 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; cleanLogImplementation = &Shader::cleanLogImplementationNoOp;
} }
/* Needed only if neither of these ifdefs above hits, but I won't bother if(context.isExtensionSupported<GL::Extensions::KHR::parallel_shader_compile>()) {
crafting the preprocessor logic for this. */ extensions[Extensions::KHR::parallel_shader_compile::Index] = Extensions::KHR::parallel_shader_compile::string();
static_cast<void>(context); 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(Shader::*addSourceImplementation)(std::string);
void(*cleanLogImplementation)(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, GLint maxVertexOutputComponents,
maxFragmentInputComponents; 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() { return compile({*this}); }
bool Shader::compile(std::initializer_list<Containers::Reference<Shader>> shaders) { void Shader::submitCompile() {
bool allSuccess = true; 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 */ /** @todo ArrayTuple/VLAs */
Containers::Array<const GLchar*> pointers(maxSourceCount); Containers::Array<const GLchar*> pointers(_sources.size());
Containers::Array<GLint> sizes(maxSourceCount); Containers::Array<GLint> sizes(_sources.size());
/* Upload sources of all shaders */ /* Upload sources of all shaders */
for(Shader& shader: shaders) { for(std::size_t i = 0; i != _sources.size(); ++i) {
for(std::size_t i = 0; i != shader._sources.size(); ++i) { pointers[i] = static_cast<const GLchar*>(_sources[i].data());
pointers[i] = static_cast<const GLchar*>(shader._sources[i].data()); sizes[i] = _sources[i].size();
sizes[i] = shader._sources[i].size();
}
glShaderSource(shader._id, shader._sources.size(), pointers, sizes);
} }
/* Invoke (possibly parallel) compilation on all shaders */ glShaderSource(_id, _sources.size(), pointers, sizes);
for(Shader& shader: shaders) glCompileShader(shader._id); glCompileShader(_id);
}
/* After compilation phase, check status of all shaders */
Int i = 1; bool Shader::checkCompile() { /* After compilation phase, check status of all shaders */
for(Shader& shader: shaders) { GLint success, logLength;
GLint success, logLength; glGetShaderiv(_id, GL_COMPILE_STATUS, &success);
glGetShaderiv(shader._id, GL_COMPILE_STATUS, &success); glGetShaderiv(_id, GL_INFO_LOG_LENGTH, &logLength);
glGetShaderiv(shader._id, GL_INFO_LOG_LENGTH, &logLength);
/* Error or warning message. The string is returned null-terminated,
/* Error or warning message. The string is returned null-terminated, strip the \0 at the end afterwards. */
strip the \0 at the end afterwards. */ std::string message(logLength, '\0');
std::string message(logLength, '\0'); if(message.size() > 1)
if(message.size() > 1) glGetShaderInfoLog(_id, message.size(), nullptr, &message[0]);
glGetShaderInfoLog(shader._id, message.size(), nullptr, &message[0]); message.resize(Math::max(logLength, 1)-1);
message.resize(Math::max(logLength, 1)-1);
/* Some drivers are chatty and can't keep shut when there's nothing to
/* Some drivers are chatty and can't keep shut when there's nothing to be said, handle that as well. */
be said, handle that as well. */ Context::current().state().shader.cleanLogImplementation(message);
Context::current().state().shader.cleanLogImplementation(message);
/* Show error log */
/* Show error log */ if(!success) {
if(!success) { Error out{Debug::Flag::NoNewlineAtTheEnd};
Error out{Debug::Flag::NoNewlineAtTheEnd}; out << "GL::Shader::compile(): compilation of" << shaderName(_type) << "shader"
out << "GL::Shader::compile(): compilation of" << shaderName(shader._type) << "shader"; << "failed with the following message:" << Debug::newline << message;
if(shaders.size() != 1) out << i;
out << "failed with the following message:" << Debug::newline << message; /* Or just warnings, if any */
} else if(!message.empty()) {
/* Or just warnings, if any */ Warning out{Debug::Flag::NoNewlineAtTheEnd};
} else if(!message.empty()) { out << "GL::Shader::compile(): compilation of" << shaderName(_type) << "shader"
Warning out{Debug::Flag::NoNewlineAtTheEnd}; << "succeeded with the following message:" << Debug::newline << message;
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;
} }
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; 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&) {} void Shader::cleanLogImplementationNoOp(std::string&) {}
#if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES) #if defined(CORRADE_TARGET_WINDOWS) && !defined(MAGNUM_TARGET_GLES)
@ -825,6 +820,10 @@ void Shader::cleanLogImplementationIntelWindows(std::string& message) {
} }
#endif #endif
void Shader::completionStatusImplementationFallback(GLuint, GLenum, GLint* value) {
*value = GL_TRUE;
}
#ifndef DOXYGEN_GENERATING_OUTPUT #ifndef DOXYGEN_GENERATING_OUTPUT
Debug& operator<<(Debug& debug, const Shader::Type value) { Debug& operator<<(Debug& debug, const Shader::Type value) {
debug << "GL::Shader::Type" << Debug::nospace; 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 * @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, * Returns @cpp false @ce if compilation of any shader failed,
* @cpp true @ce if everything succeeded. Compiler messages (if any) * @cpp true @ce if everything succeeded. The operation is batched in a way that
* are printed to error output. The operation is batched in a way that
* allows the driver to perform multiple compilations simultaneously * allows the driver to perform multiple compilations simultaneously
* (i.e. in multiple threads). * (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); static bool compile(std::initializer_list<Containers::Reference<Shader>> shaders);
@ -636,13 +633,42 @@ class MAGNUM_GL_EXPORT Shader: public AbstractObject {
/** /**
* @brief Compile shader * @brief Compile shader
* *
* Compiles single shader. Prefer to compile multiple shaders at once * Calls @ref submitCompile(), then @ref checkCompile().
* using @ref compile(std::initializer_list<Containers::Reference<Shader>>) * Prefer to compile multiple shaders at once using
* @ref compile(std::initializer_list<Containers::Reference<Shader>>)
* for improved performance, see its documentation for more * for improved performance, see its documentation for more
* information. * information.
*/ */
bool compile(); 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: private:
void MAGNUM_GL_LOCAL addSourceImplementationDefault(std::string source); void MAGNUM_GL_LOCAL addSourceImplementationDefault(std::string source);
#if defined(CORRADE_TARGET_EMSCRIPTEN) && defined(__EMSCRIPTEN_PTHREADS__) #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); static MAGNUM_GL_LOCAL void cleanLogImplementationIntelWindows(std::string& message);
#endif #endif
MAGNUM_GL_LOCAL static void APIENTRY completionStatusImplementationFallback(GLuint, GLenum, GLint*);
Type _type; Type _type;
GLuint _id; GLuint _id;

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

@ -27,8 +27,10 @@
#include <Corrade/Containers/Reference.h> #include <Corrade/Containers/Reference.h>
#include <Corrade/Containers/StringStl.h> /** @todo remove when Shader is <string>-free */ #include <Corrade/Containers/StringStl.h> /** @todo remove when Shader is <string>-free */
#include <Corrade/TestSuite/Compare/Container.h> #include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/TestSuite/Compare/String.h>
#include <Corrade/Utility/DebugStl.h> #include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/Resource.h> #include <Corrade/Utility/Resource.h>
#include <Corrade/Utility/System.h>
#include "Magnum/Image.h" #include "Magnum/Image.h"
#include "Magnum/ImageView.h" #include "Magnum/ImageView.h"
@ -68,8 +70,11 @@ struct AbstractShaderProgramGLTest: OpenGLTester {
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
void createMultipleOutputsIndexed(); void createMultipleOutputsIndexed();
#endif #endif
void createAsync();
void linkFailure(); void linkFailure();
void linkFailureAsync();
void uniformNotFound(); void uniformNotFound();
void uniform(); void uniform();
@ -103,12 +108,14 @@ AbstractShaderProgramGLTest::AbstractShaderProgramGLTest() {
#endif #endif
&AbstractShaderProgramGLTest::create, &AbstractShaderProgramGLTest::create,
&AbstractShaderProgramGLTest::createAsync,
&AbstractShaderProgramGLTest::createMultipleOutputs, &AbstractShaderProgramGLTest::createMultipleOutputs,
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
&AbstractShaderProgramGLTest::createMultipleOutputsIndexed, &AbstractShaderProgramGLTest::createMultipleOutputsIndexed,
#endif #endif
&AbstractShaderProgramGLTest::linkFailure, &AbstractShaderProgramGLTest::linkFailure,
&AbstractShaderProgramGLTest::linkFailureAsync,
&AbstractShaderProgramGLTest::uniformNotFound, &AbstractShaderProgramGLTest::uniformNotFound,
&AbstractShaderProgramGLTest::uniform, &AbstractShaderProgramGLTest::uniform,
@ -205,6 +212,8 @@ struct MyPublicShader: AbstractShaderProgram {
using AbstractShaderProgram::bindFragmentDataLocation; using AbstractShaderProgram::bindFragmentDataLocation;
#endif #endif
using AbstractShaderProgram::link; using AbstractShaderProgram::link;
using AbstractShaderProgram::submitLink;
using AbstractShaderProgram::checkLink;
using AbstractShaderProgram::uniformLocation; using AbstractShaderProgram::uniformLocation;
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
using AbstractShaderProgram::uniformBlockIndex; using AbstractShaderProgram::uniformBlockIndex;
@ -257,6 +266,80 @@ void AbstractShaderProgramGLTest::create() {
MAGNUM_VERIFY_NO_GL_ERROR(); MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_VERIFY(linked); 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) #if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly."); CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
@ -410,6 +493,46 @@ void AbstractShaderProgramGLTest::linkFailure() {
MyPublicShader program; MyPublicShader program;
program.attachShaders({shader}); program.attachShaders({shader});
CORRADE_VERIFY(!program.link()); 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() { void AbstractShaderProgramGLTest::uniformNotFound() {

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

@ -23,8 +23,11 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include <sstream>
#include <Corrade/Containers/StringStl.h> /** @todo remove once Shader is <string>-free */ #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/DebugStl.h>
#include <Corrade/Utility/System.h>
#include <Corrade/Utility/Path.h> #include <Corrade/Utility/Path.h>
#include "Magnum/GL/Context.h" #include "Magnum/GL/Context.h"
@ -55,6 +58,9 @@ struct ShaderGLTest: OpenGLTester {
void addSourceNoVersion(); void addSourceNoVersion();
void addFile(); void addFile();
void compile(); void compile();
void compileFailure();
void compileAsync();
void compileAsyncFailure();
void compileUtf8(); void compileUtf8();
void compileNoVersion(); void compileNoVersion();
}; };
@ -72,6 +78,9 @@ ShaderGLTest::ShaderGLTest() {
&ShaderGLTest::addSourceNoVersion, &ShaderGLTest::addSourceNoVersion,
&ShaderGLTest::addFile, &ShaderGLTest::addFile,
&ShaderGLTest::compile, &ShaderGLTest::compile,
&ShaderGLTest::compileFailure,
&ShaderGLTest::compileAsync,
&ShaderGLTest::compileAsyncFailure,
&ShaderGLTest::compileUtf8, &ShaderGLTest::compileUtf8,
&ShaderGLTest::compileNoVersion}); &ShaderGLTest::compileNoVersion});
} }
@ -276,11 +285,84 @@ void ShaderGLTest::compile() {
Shader shader(v, Shader::Type::Fragment); Shader shader(v, Shader::Type::Fragment);
shader.addSource("void main() {}\n"); shader.addSource("void main() {}\n");
CORRADE_VERIFY(shader.compile()); 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); CORRADE_VERIFY(!shader.checkCompile());
shader2.addSource("[fu] bleh error #:! stuff\n"); CORRADE_VERIFY(shader.isCompileFinished());
CORRADE_VERIFY(!shader2.compile()); CORRADE_COMPARE_AS(out.str(), "GL::Shader::compile(): compilation of fragment shader failed with the following message:",
TestSuite::Compare::StringHasPrefix);
} }
void ShaderGLTest::compileUtf8() { void ShaderGLTest::compileUtf8() {

Loading…
Cancel
Save