Browse Source

Finally implemented parallelizable shader compilation and linking.

As g_truc said long ago:
https://twitter.com/g_truc/status/352778836657700866

Currently there is not much use of this as the stock shaders are
compiled one by one (and doing it differently would make things
needlessly overcomplicated), but the users can do parallel compilation
of their own shaders.

Also removed a bunch of now-unneeded TODOs and made the linker/compiler
code nearly similar. Also the whole Shader::compile() call now does two
allocations in total instead of two allocations for each shader.
pull/51/head
Vladimír Vondruš 12 years ago
parent
commit
7b504a9bb1
  1. 77
      src/Magnum/AbstractShaderProgram.cpp
  2. 30
      src/Magnum/AbstractShaderProgram.h
  3. 112
      src/Magnum/Shader.cpp
  4. 28
      src/Magnum/Shader.h

77
src/Magnum/AbstractShaderProgram.cpp

@ -269,40 +269,53 @@ void AbstractShaderProgram::bindFragmentDataLocationIndexed(UnsignedInt location
} }
#endif #endif
bool AbstractShaderProgram::link() { bool AbstractShaderProgram::link(std::initializer_list<std::reference_wrapper<AbstractShaderProgram>> shaders) {
/* Link shader program */ bool allSuccess = true;
glLinkProgram(_id);
/* Invoke (possibly parallel) linking on all shaders */
/* Check link status */ for(AbstractShaderProgram& shader: shaders) glLinkProgram(shader._id);
GLint success, logLength;
glGetProgramiv(_id, GL_LINK_STATUS, &success); /* After linking phase, check status of all shaders */
glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &logLength); Int i = 1;
for(AbstractShaderProgram& shader: shaders) {
/* Error or warning message. The string is returned null-terminated, scrap GLint success, logLength;
the \0 at the end afterwards */ glGetProgramiv(shader._id, GL_LINK_STATUS, &success);
std::string message(logLength, '\n'); glGetProgramiv(shader._id, GL_INFO_LOG_LENGTH, &logLength);
if(message.size() > 1)
glGetProgramInfoLog(_id, message.size(), nullptr, &message[0]); /* Error or warning message. The string is returned null-terminated,
message.resize(std::max(logLength, 1)-1); scrap the \0 at the end afterwards */
std::string message(logLength, '\n');
/* Show error log and delete shader */ if(message.size() > 1)
if(!success) { glGetProgramInfoLog(shader._id, message.size(), nullptr, &message[0]);
Error out; message.resize(std::max(logLength, 1)-1);
out.setFlag(Debug::NewLineAtTheEnd, false);
out.setFlag(Debug::SpaceAfterEachValue, false); /* Show error log */
out << "AbstractShaderProgram: linking failed with the following message:\n" if(!success) {
<< message; Error out;
out.setFlag(Debug::NewLineAtTheEnd, false);
/* Or just warnings, if there are any */ out.setFlag(Debug::SpaceAfterEachValue, false);
} else if(!message.empty()) { out << "AbstractShaderProgram::link(): linking";
Debug out; if(shaders.size() != 1) out << " of shader " << std::to_string(i);
out.setFlag(Debug::NewLineAtTheEnd, false); out << " failed with the following message:\n"
out.setFlag(Debug::SpaceAfterEachValue, false); << message;
out << "AbstractShaderProgram: linking succeeded with the following message:\n"
<< message; /* Or just warnings, if any */
} else if(!message.empty()) {
Debug out;
out.setFlag(Debug::NewLineAtTheEnd, false);
out.setFlag(Debug::SpaceAfterEachValue, false);
out << "AbstractShaderProgram::link(): linking";
if(shaders.size() != 1) out << " of shader " << std::to_string(i);
out << " succeeded with the following message:\n"
<< message;
}
/* Success of all depends on each of them */
allSuccess = allSuccess && success;
++i;
} }
return success; return allSuccess;
} }
Int AbstractShaderProgram::uniformLocation(const std::string& name) { Int AbstractShaderProgram::uniformLocation(const std::string& name) {

30
src/Magnum/AbstractShaderProgram.h

@ -29,6 +29,7 @@
* @brief Class @ref Magnum::AbstractShaderProgram * @brief Class @ref Magnum::AbstractShaderProgram
*/ */
#include <functional>
#include <string> #include <string>
#include <Corrade/Containers/EnumSet.h> #include <Corrade/Containers/EnumSet.h>
@ -320,8 +321,6 @@ comes in handy.
@see @ref portability-shaders @see @ref portability-shaders
@todo Use Containers::ArrayReference for setting uniform arrays? @todo Use Containers::ArrayReference for setting uniform arrays?
@todo Compiling and linking more than one shader in parallel, then checking
status, should be faster -- https://twitter.com/g_truc/status/352778836657700866
@todo `GL_NUM_{PROGRAM,SHADER}_BINARY_FORMATS` + `GL_{PROGRAM,SHADER}_BINARY_FORMATS` (vector), (@extension{ARB,ES2_compatibility}) @todo `GL_NUM_{PROGRAM,SHADER}_BINARY_FORMATS` + `GL_{PROGRAM,SHADER}_BINARY_FORMATS` (vector), (@extension{ARB,ES2_compatibility})
*/ */
class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject { class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject {
@ -562,6 +561,21 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject {
#endif #endif
protected: protected:
/**
* @brief Link the shader
*
* Returns `false` if linking of any shader failed, `true` 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
* way that allows the driver to link multiple shaders simultaenously
* (i.e. in multiple threads).
* @see @fn_gl{LinkProgram}, @fn_gl{GetProgram} with
* @def_gl{LINK_STATUS} and @def_gl{INFO_LOG_LENGTH},
* @fn_gl{GetProgramInfoLog}
*/
static bool link(std::initializer_list<std::reference_wrapper<AbstractShaderProgram>> shaders);
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
/** /**
* @brief Allow retrieving program binary * @brief Allow retrieving program binary
@ -655,14 +669,12 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject {
/** /**
* @brief Link the shader * @brief Link the shader
* *
* Returns `false` if linking failed, `true` otherwise. Compiler * Links single shader. If possible, prefer to link multiple shaders
* message (if any) is printed to error output. All attached shaders * at once using @ref link(std::initializer_list<std::reference_wrapper<AbstractShaderProgram>>)
* must be compiled with @ref Shader::compile() before linking. * for improved performance, see its documentation for more
* @see @fn_gl{LinkProgram}, @fn_gl{GetProgram} with * information.
* @def_gl{LINK_STATUS} and @def_gl{INFO_LOG_LENGTH},
* @fn_gl{GetProgramInfoLog}
*/ */
bool link(); bool link() { return link({*this}); }
/** /**
* @brief Get uniform location * @brief Get uniform location

112
src/Magnum/Shader.cpp

@ -622,57 +622,75 @@ Shader& Shader::addFile(const std::string& filename) {
return *this; return *this;
} }
bool Shader::compile() { bool Shader::compile(std::initializer_list<std::reference_wrapper<Shader>> shaders) {
CORRADE_ASSERT(_sources.size() > 1, "Shader::compile(): no files added", false); bool allSuccess = true;
/* Array of source string pointers and their lengths */ /* Allocate large enough array for source pointers and sizes (to avoid
/** @todo Use `Containers::ArrayTuple` to avoid one allocation if it ever reallocating it for each of them) */
gets to be implemented (we need properly aligned memory too) */ std::size_t maxSourceCount = 0;
Containers::Array<const GLchar*> pointers(_sources.size()); for(Shader& shader: shaders) {
Containers::Array<GLint> sizes(_sources.size()); CORRADE_ASSERT(shader._sources.size() > 1, "Shader::compile(): no files added", false);
for(std::size_t i = 0; i != _sources.size(); ++i) { maxSourceCount = std::max(shader._sources.size(), maxSourceCount);
pointers[i] = static_cast<const GLchar*>(_sources[i].data());
sizes[i] = _sources[i].size();
} }
Containers::Array<const GLchar*> pointers(maxSourceCount);
Containers::Array<GLint> sizes(maxSourceCount);
/* Create shader and set its source */ /* Upload sources of all shaders */
glShaderSource(_id, _sources.size(), pointers, sizes); for(Shader& shader: shaders) {
for(std::size_t i = 0; i != shader._sources.size(); ++i) {
/* Compile shader */ pointers[i] = static_cast<const GLchar*>(shader._sources[i].data());
glCompileShader(_id); sizes[i] = shader._sources[i].size();
}
/* Check compilation status */
GLint success, logLength; glShaderSource(shader._id, shader._sources.size(), pointers, sizes);
glGetShaderiv(_id, GL_COMPILE_STATUS, &success); }
glGetShaderiv(_id, GL_INFO_LOG_LENGTH, &logLength);
/* Invoke (possibly parallel) compilation on all shaders */
/* Error or warning message. The string is returned null-terminated, scrap for(Shader& shader: shaders) glCompileShader(shader._id);
the \0 at the end afterwards */
std::string message(logLength, '\0'); /* After compilation phase, check status of all shaders */
if(message.size() > 1) Int i = 1;
glGetShaderInfoLog(_id, message.size(), nullptr, &message[0]); for(Shader& shader: shaders) {
message.resize(std::max(logLength, 1)-1); GLint success, logLength;
glGetShaderiv(shader._id, GL_COMPILE_STATUS, &success);
/* Show error log */ glGetShaderiv(shader._id, GL_INFO_LOG_LENGTH, &logLength);
if(!success) {
Error out; /* Error or warning message. The string is returned null-terminated,
out.setFlag(Debug::NewLineAtTheEnd, false); scrap the \0 at the end afterwards */
out.setFlag(Debug::SpaceAfterEachValue, false); std::string message(logLength, '\0');
out << "Shader: " << shaderName(_type) if(message.size() > 1)
<< " shader failed to compile with the following message:\n" glGetShaderInfoLog(shader._id, message.size(), nullptr, &message[0]);
<< message; message.resize(std::max(logLength, 1)-1);
/* Or just message, if any */ /* Show error log */
} else if(!message.empty()) { if(!success) {
Error out; Error out;
out.setFlag(Debug::NewLineAtTheEnd, false); out.setFlag(Debug::NewLineAtTheEnd, false);
out.setFlag(Debug::SpaceAfterEachValue, false); out.setFlag(Debug::SpaceAfterEachValue, false);
out << "Shader: " << shaderName(_type) out << "Shader::compile(): compilation of " << shaderName(shader._type)
<< " shader was successfully compiled with the following message:\n" << " shader";
<< message; if(shaders.size() != 1) out << ' ' << std::to_string(i);
out << " failed with the following message:\n"
<< message;
/* Or just warnings, if any */
} else if(!message.empty()) {
Error out;
out.setFlag(Debug::NewLineAtTheEnd, false);
out.setFlag(Debug::SpaceAfterEachValue, false);
out << "Shader::compile(): compilation of " << shaderName(shader._type)
<< " shader";
if(shaders.size() != 1) out << ' ' << std::to_string(i);
out << " succeeded with the following message:\n"
<< message;
}
/* Success of all depends on each of them */
allSuccess = allSuccess && success;
++i;
} }
return success; return allSuccess;
} }
#ifndef DOXYGEN_GENERATING_OUTPUT #ifndef DOXYGEN_GENERATING_OUTPUT

28
src/Magnum/Shader.h

@ -29,8 +29,9 @@
* @brief Class @ref Magnum::Shader * @brief Class @ref Magnum::Shader
*/ */
#include <vector> #include <functional>
#include <string> #include <string>
#include <vector>
#include "Magnum/AbstractObject.h" #include "Magnum/AbstractObject.h"
#include "Magnum/Magnum.h" #include "Magnum/Magnum.h"
@ -431,6 +432,20 @@ class MAGNUM_EXPORT Shader: public AbstractObject {
static Int maxCombinedUniformComponents(Type type); static Int maxCombinedUniformComponents(Type type);
#endif #endif
/**
* @brief Compile multiple shaders simultaenously
*
* Returns `false` if compilation of any shader failed, `true` if
* everything succeeded. Compiler messages (if any) are printed to
* error output. The operation is batched in a way that allows the
* driver to perform multiple compilations simultaenously (i.e. in
* multiple threads).
* @see @fn_gl{ShaderSource}, @fn_gl{CompileShader}, @fn_gl{GetShader}
* with @def_gl{COMPILE_STATUS} and @def_gl{INFO_LOG_LENGTH},
* @fn_gl{GetShaderInfoLog}
*/
static bool compile(std::initializer_list<std::reference_wrapper<Shader>> shaders);
/** /**
* @brief Constructor * @brief Constructor
* @param version Target version * @param version Target version
@ -524,13 +539,12 @@ class MAGNUM_EXPORT Shader: public AbstractObject {
/** /**
* @brief Compile shader * @brief Compile shader
* *
* Returns `false` if compilation failed, `true` otherwise. Compiler * Compiles single shader. Prefer to compile multiple shaders at once
* message (if any) is printed to error output. * using @ref compile(std::initializer_list<std::reference_wrapper<Shader>>)
* @see @fn_gl{ShaderSource}, @fn_gl{CompileShader}, @fn_gl{GetShader} * for improved performance, see its documentation for more
* with @def_gl{COMPILE_STATUS} and @def_gl{INFO_LOG_LENGTH}, * information.
* @fn_gl{GetShaderInfoLog}
*/ */
bool compile(); bool compile() { return compile({*this}); }
private: private:
Type _type; Type _type;

Loading…
Cancel
Save