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. 41
      src/Magnum/AbstractShaderProgram.cpp
  2. 30
      src/Magnum/AbstractShaderProgram.h
  3. 72
      src/Magnum/Shader.cpp
  4. 28
      src/Magnum/Shader.h

41
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);
/* Check link status */ /* 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; GLint success, logLength;
glGetProgramiv(_id, GL_LINK_STATUS, &success); glGetProgramiv(shader._id, GL_LINK_STATUS, &success);
glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &logLength); glGetProgramiv(shader._id, GL_INFO_LOG_LENGTH, &logLength);
/* Error or warning message. The string is returned null-terminated, scrap /* Error or warning message. The string is returned null-terminated,
the \0 at the end afterwards */ scrap the \0 at the end afterwards */
std::string message(logLength, '\n'); std::string message(logLength, '\n');
if(message.size() > 1) if(message.size() > 1)
glGetProgramInfoLog(_id, message.size(), nullptr, &message[0]); glGetProgramInfoLog(shader._id, message.size(), nullptr, &message[0]);
message.resize(std::max(logLength, 1)-1); message.resize(std::max(logLength, 1)-1);
/* Show error log and delete shader */ /* Show error log */
if(!success) { 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 << "AbstractShaderProgram: linking failed with the following message:\n" out << "AbstractShaderProgram::link(): linking";
if(shaders.size() != 1) out << " of shader " << std::to_string(i);
out << " failed with the following message:\n"
<< message; << message;
/* Or just warnings, if there are any */ /* Or just warnings, if any */
} else if(!message.empty()) { } else if(!message.empty()) {
Debug out; Debug out;
out.setFlag(Debug::NewLineAtTheEnd, false); out.setFlag(Debug::NewLineAtTheEnd, false);
out.setFlag(Debug::SpaceAfterEachValue, false); out.setFlag(Debug::SpaceAfterEachValue, false);
out << "AbstractShaderProgram: linking succeeded with the following message:\n" out << "AbstractShaderProgram::link(): linking";
if(shaders.size() != 1) out << " of shader " << std::to_string(i);
out << " succeeded with the following message:\n"
<< message; << message;
} }
return success; /* Success of all depends on each of them */
allSuccess = allSuccess && success;
++i;
}
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

72
src/Magnum/Shader.cpp

@ -622,35 +622,44 @@ 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);
/* 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();
} }
/* Create shader and set its source */ glShaderSource(shader._id, shader._sources.size(), pointers, sizes);
glShaderSource(_id, _sources.size(), pointers, sizes); }
/* Compile shader */ /* Invoke (possibly parallel) compilation on all shaders */
glCompileShader(_id); for(Shader& shader: shaders) glCompileShader(shader._id);
/* Check compilation status */ /* After compilation phase, check status of all shaders */
Int i = 1;
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, scrap /* Error or warning message. The string is returned null-terminated,
the \0 at the end afterwards */ scrap 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(std::max(logLength, 1)-1); message.resize(std::max(logLength, 1)-1);
/* Show error log */ /* Show error log */
@ -658,21 +667,30 @@ bool Shader::compile() {
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 failed to compile with the following message:\n" << " shader";
if(shaders.size() != 1) out << ' ' << std::to_string(i);
out << " failed with the following message:\n"
<< message; << message;
/* Or just message, if any */ /* Or just warnings, if any */
} else if(!message.empty()) { } else if(!message.empty()) {
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";
if(shaders.size() != 1) out << ' ' << std::to_string(i);
out << " succeeded with the following message:\n"
<< message; << message;
} }
return success; /* Success of all depends on each of them */
allSuccess = allSuccess && success;
++i;
}
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