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. 70
      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
bool AbstractShaderProgram::link() {
/* Link shader program */
glLinkProgram(_id);
bool AbstractShaderProgram::link(std::initializer_list<std::reference_wrapper<AbstractShaderProgram>> shaders) {
bool allSuccess = true;
/* 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;
glGetProgramiv(_id, GL_LINK_STATUS, &success);
glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &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, scrap
the \0 at the end afterwards */
/* 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]);
glGetProgramInfoLog(shader._id, message.size(), nullptr, &message[0]);
message.resize(std::max(logLength, 1)-1);
/* Show error log and delete shader */
/* Show error log */
if(!success) {
Error out;
out.setFlag(Debug::NewLineAtTheEnd, 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;
/* Or just warnings, if there are any */
/* Or just warnings, if any */
} else if(!message.empty()) {
Debug out;
out.setFlag(Debug::NewLineAtTheEnd, 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;
}
return success;
/* Success of all depends on each of them */
allSuccess = allSuccess && success;
++i;
}
return allSuccess;
}
Int AbstractShaderProgram::uniformLocation(const std::string& name) {

30
src/Magnum/AbstractShaderProgram.h

@ -29,6 +29,7 @@
* @brief Class @ref Magnum::AbstractShaderProgram
*/
#include <functional>
#include <string>
#include <Corrade/Containers/EnumSet.h>
@ -320,8 +321,6 @@ comes in handy.
@see @ref portability-shaders
@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})
*/
class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject {
@ -562,6 +561,21 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject {
#endif
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
/**
* @brief Allow retrieving program binary
@ -655,14 +669,12 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject {
/**
* @brief Link the shader
*
* Returns `false` if linking failed, `true` otherwise. Compiler
* message (if any) is printed to error output. All attached shaders
* must be compiled with @ref Shader::compile() before linking.
* @see @fn_gl{LinkProgram}, @fn_gl{GetProgram} with
* @def_gl{LINK_STATUS} and @def_gl{INFO_LOG_LENGTH},
* @fn_gl{GetProgramInfoLog}
* Links single shader. If possible, prefer to link multiple shaders
* at once using @ref link(std::initializer_list<std::reference_wrapper<AbstractShaderProgram>>)
* for improved performance, see its documentation for more
* information.
*/
bool link();
bool link() { return link({*this}); }
/**
* @brief Get uniform location

70
src/Magnum/Shader.cpp

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

28
src/Magnum/Shader.h

@ -29,8 +29,9 @@
* @brief Class @ref Magnum::Shader
*/
#include <vector>
#include <functional>
#include <string>
#include <vector>
#include "Magnum/AbstractObject.h"
#include "Magnum/Magnum.h"
@ -431,6 +432,20 @@ class MAGNUM_EXPORT Shader: public AbstractObject {
static Int maxCombinedUniformComponents(Type type);
#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
* @param version Target version
@ -524,13 +539,12 @@ class MAGNUM_EXPORT Shader: public AbstractObject {
/**
* @brief Compile shader
*
* Returns `false` if compilation failed, `true` otherwise. Compiler
* message (if any) is printed to error output.
* @see @fn_gl{ShaderSource}, @fn_gl{CompileShader}, @fn_gl{GetShader}
* with @def_gl{COMPILE_STATUS} and @def_gl{INFO_LOG_LENGTH},
* @fn_gl{GetShaderInfoLog}
* Compiles single shader. Prefer to compile multiple shaders at once
* using @ref compile(std::initializer_list<std::reference_wrapper<Shader>>)
* for improved performance, see its documentation for more
* information.
*/
bool compile();
bool compile() { return compile({*this}); }
private:
Type _type;

Loading…
Cancel
Save