1 #include <Engine/Data/ShaderProgram.hpp>
2 #include <Engine/OpenGL.hpp>
4 #include <glbinding/gl/enum.h>
5 #include <globjects/NamedString.h>
6 #include <globjects/Program.h>
7 #include <globjects/Shader.h>
8 #include <globjects/Texture.h>
9 #include <globjects/base/File.h>
10 #include <globjects/base/StaticStringSource.h>
12 #include <Core/Utils/Log.hpp>
13 #include <Engine/Data/Texture.hpp>
23 using namespace Core::Utils;
27 GLenum getTypeAsGLEnum( ShaderType type ) {
29 case ShaderType_VERTEX:
30 return GL_VERTEX_SHADER;
31 case ShaderType_FRAGMENT:
32 return GL_FRAGMENT_SHADER;
33 case ShaderType_GEOMETRY:
34 return GL_GEOMETRY_SHADER;
35 case ShaderType_TESS_EVALUATION:
36 return GL_TESS_EVALUATION_SHADER;
37 case ShaderType_TESS_CONTROL:
38 return GL_TESS_CONTROL_SHADER;
41 case ShaderType_COMPUTE:
42 return GL_COMPUTE_SHADER;
45 CORE_ERROR(
"Wrong ShaderType" );
52 ShaderType getGLenumAsType( GLenum type ) {
54 case GL_VERTEX_SHADER:
55 return ShaderType_VERTEX;
56 case GL_FRAGMENT_SHADER:
57 return ShaderType_FRAGMENT;
58 case GL_GEOMETRY_SHADER:
59 return ShaderType_GEOMETRY;
60 case GL_TESS_EVALUATION_SHADER:
61 return ShaderType_TESS_EVALUATION;
62 case GL_TESS_CONTROL_SHADER:
63 return ShaderType_TESS_CONTROL;
65 case GL_COMPUTE_SHADER:
66 return ShaderType_COMPUTE;
69 CORE_ERROR(
"Wrong GLenum" );
73 return ShaderType_COUNT;
76 ShaderProgram::ShaderProgram() : m_program { nullptr } {
77 std::generate( m_shaderObjects.begin(), m_shaderObjects.end(), []() {
78 return std::pair<bool, std::unique_ptr<globjects::Shader>> { false, nullptr };
80 std::fill( m_shaderSources.begin(), m_shaderSources.end(),
nullptr );
83 ShaderProgram::ShaderProgram(
const ShaderConfiguration& config ) : ShaderProgram() {
87 ShaderProgram::~ShaderProgram() {
91 for (
auto& s : m_shaderObjects ) {
92 s.second.reset(
nullptr );
94 for (
auto& s : m_shaderSources ) {
97 m_program.reset(
nullptr );
100 void ShaderProgram::loadShader( ShaderType type,
101 const std::string& name,
102 const std::set<std::string>& props,
103 const std::vector<std::pair<std::string, ShaderType>>& includes,
105 const std::string& version ) {
107 if ( type == ShaderType_COMPUTE ) {
108 LOG( logERROR ) <<
"No compute shader on OsX !";
118 std::string shaderHeader;
119 if ( type == ShaderType_VERTEX ) {
120 shaderHeader = std::string( version +
"\n\n"
121 "out gl_PerVertex {\n"
122 " vec4 gl_Position;\n"
123 " float gl_PointSize;\n"
124 " float gl_ClipDistance[];\n"
127 else { shaderHeader = std::string( version +
"\n\n" ); }
130 shaderHeader = std::accumulate(
131 props.begin(), props.end(), shaderHeader, []( std::string a,
const std::string& b ) {
132 return std::move( a ) + b + std::string(
"\n" );
136 shaderHeader = std::accumulate(
140 [type]( std::string a,
const std::pair<std::string, ShaderType>& b ) -> std::string {
141 if ( b.second == type ) { return std::move( a ) + b.first + std::string(
"\n" ); }
145 std::unique_ptr<globjects::StaticStringSource> fullsource {
nullptr };
147 auto loadedSource = globjects::Shader::sourceFromFile( name );
148 fullsource = globjects::Shader::sourceFromString( shaderHeader + loadedSource->string() );
150 else { fullsource = globjects::Shader::sourceFromString( shaderHeader + name ); }
153 auto shaderSource = globjects::Shader::applyGlobalReplacements( fullsource.get() );
158 std::string preprocessedSource = preprocessIncludes( name, shaderSource->string(), 0 );
160 auto ptrSource = globjects::Shader::sourceFromString( preprocessedSource );
163 auto ptrSource = globjects::Shader::sourceFromString( shaderSource->string() );
165 addShaderFromSource( type, std::move( ptrSource ), name, fromFile );
168 void ShaderProgram::addShaderFromSource( ShaderType type,
169 std::unique_ptr<globjects::StaticStringSource>&& ptrSource,
170 const std::string& name,
173 auto shader = globjects::Shader::create( getTypeAsGLEnum( type ) );
175 shader->setName( name );
176 shader->setSource( ptrSource.get() );
180 m_shaderObjects[type].first = fromFile;
181 m_shaderObjects[type].second.swap( shader );
183 m_shaderSources[type].swap( ptrSource );
189 m_configuration = shaderConfig;
191 CORE_ERROR_IF( m_configuration.isComplete(),
192 (
"Shader program " + shaderConfig.m_name +
193 " is incomplete (e.g. misses vertex or fragment shader)." )
196 for (
size_t i = 0; i < ShaderType_COUNT; ++i ) {
197 if ( !m_configuration.m_shaders[i].first.empty() ) {
198 loadShader( ShaderType( i ),
199 m_configuration.m_shaders[i].first,
200 m_configuration.getProperties(),
201 m_configuration.getIncludes(),
202 m_configuration.m_shaders[i].second,
203 m_configuration.m_version );
210 void ShaderProgram::link() {
211 m_program = globjects::Program::create();
213 for (
unsigned int i = 0; i < ShaderType_COUNT; ++i ) {
214 if ( m_shaderObjects[i].second ) { m_program->attach( m_shaderObjects[i].second.get() ); }
217 m_program->setParameter( GL_PROGRAM_SEPARABLE, GL_TRUE );
222 auto total = GLuint( m_program->get( GL_ACTIVE_UNIFORMS ) );
223 textureUnits.clear();
225 for ( GLuint i = 0; i < total; ++i ) {
226 auto name = m_program->getActiveUniformName( i );
227 auto type = m_program->getActiveUniform( i, GL_UNIFORM_TYPE );
230 if ( type == GL_SAMPLER_1D || type == GL_SAMPLER_2D || type == GL_SAMPLER_3D ||
231 type == GL_SAMPLER_CUBE || type == GL_SAMPLER_1D_SHADOW ||
232 type == GL_SAMPLER_2D_SHADOW || type == GL_SAMPLER_CUBE_SHADOW ||
233 type == GL_SAMPLER_2D_RECT || type == GL_SAMPLER_2D_RECT_SHADOW ||
234 type == GL_SAMPLER_1D_ARRAY || type == GL_SAMPLER_2D_ARRAY ||
235 type == GL_SAMPLER_BUFFER || type == GL_SAMPLER_1D_ARRAY_SHADOW ||
236 type == GL_SAMPLER_2D_ARRAY_SHADOW || type == GL_INT_SAMPLER_1D ||
237 type == GL_INT_SAMPLER_2D || type == GL_INT_SAMPLER_3D ||
238 type == GL_INT_SAMPLER_CUBE || type == GL_INT_SAMPLER_2D_RECT ||
239 type == GL_INT_SAMPLER_1D_ARRAY || type == GL_INT_SAMPLER_2D_ARRAY ||
240 type == GL_INT_SAMPLER_BUFFER || type == GL_UNSIGNED_INT_SAMPLER_1D ||
241 type == GL_UNSIGNED_INT_SAMPLER_2D || type == GL_UNSIGNED_INT_SAMPLER_3D ||
242 type == GL_UNSIGNED_INT_SAMPLER_CUBE || type == GL_UNSIGNED_INT_SAMPLER_2D_RECT ||
243 type == GL_UNSIGNED_INT_SAMPLER_1D_ARRAY || type == GL_UNSIGNED_INT_SAMPLER_2D_ARRAY ||
244 type == GL_UNSIGNED_INT_SAMPLER_BUFFER || type == GL_SAMPLER_CUBE_MAP_ARRAY ||
245 type == GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW || type == GL_INT_SAMPLER_CUBE_MAP_ARRAY ||
246 type == GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY || type == GL_SAMPLER_2D_MULTISAMPLE ||
247 type == GL_INT_SAMPLER_2D_MULTISAMPLE ||
248 type == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE ||
249 type == GL_SAMPLER_2D_MULTISAMPLE_ARRAY ||
250 type == GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY ||
251 type == GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY ) {
252 auto location = m_program->getUniformLocation( name );
253 textureUnits[name] = TextureBinding( texUnit++, location );
258 void ShaderProgram::bind()
const {
262 void ShaderProgram::validate()
const {
263 m_program->validate();
264 if ( !m_program->isValid() ) { LOG( logDEBUG ) << m_program->infoLog(); }
267 void ShaderProgram::unbind()
const {
268 m_program->release();
271 void ShaderProgram::reload() {
272 for (
auto& s : m_shaderObjects ) {
273 if ( s.second !=
nullptr ) {
274 if ( s.first ) { LOG( logDEBUG ) <<
"Reloading shader " << s.second->name(); }
276 m_program->detach( s.second.get() );
277 loadShader( getGLenumAsType( s.second->type() ),
279 m_configuration.getProperties(),
280 m_configuration.getIncludes(),
282 m_configuration.m_version );
289 ShaderConfiguration ShaderProgram::getBasicConfiguration()
const {
290 ShaderConfiguration basicConfig;
292 basicConfig.m_shaders = m_configuration.m_shaders;
293 basicConfig.m_name = m_configuration.m_name;
299 void ShaderProgram::setUniform(
const char* name,
const Core::Vector2d& value )
const {
300 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
304 void ShaderProgram::setUniform(
const char* name,
const Core::Vector3d& value )
const {
305 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
309 void ShaderProgram::setUniform(
const char* name,
const Core::Vector4d& value )
const {
310 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
314 void ShaderProgram::setUniform(
const char* name,
const Core::Matrix2d& value )
const {
315 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
319 void ShaderProgram::setUniform(
const char* name,
const Core::Matrix3d& value )
const {
320 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
324 void ShaderProgram::setUniform(
const char* name,
const Core::Matrix4d& value )
const {
325 m_program->setUniform( name, value.cast<GL_SCALAR_PLAIN>().eval() );
329 void ShaderProgram::setUniform(
const char* name,
const Scalar& value )
const {
330 m_program->setUniform( name,
static_cast<GL_SCALAR_PLAIN
>( value ) );
333 template <
typename T,
334 typename std::enable_if<!std::is_same<T, GL_SCALAR_PLAIN>::value>::type* =
nullptr>
335 void scalarVectorAdapter( globjects::Program* prog,
337 const std::vector<T>& value ) {
338 std::vector<GL_SCALAR_PLAIN> convertedValue;
339 std::transform( value.begin(),
341 std::back_inserter( convertedValue ),
342 []( Scalar c ) -> GL_SCALAR_PLAIN { return c; } );
343 prog->setUniform( name, convertedValue );
346 template <
typename T,
347 typename std::enable_if<std::is_same<T, GL_SCALAR_PLAIN>::value>::type* =
nullptr>
348 void scalarVectorAdapter( globjects::Program* prog,
350 const std::vector<T>& value ) {
351 prog->setUniform( name, value );
355 void ShaderProgram::setUniform(
const char* name,
const std::vector<Scalar>& value )
const {
356 scalarVectorAdapter<Scalar>( m_program.get(), name, value );
359 void ShaderProgram::setUniform(
const char* name, Texture* tex,
int texUnit )
const {
360 tex->bind( texUnit );
362 m_program->setUniform( name, texUnit );
365 void ShaderProgram::setUniformTexture(
const char* name,
Texture* tex )
const {
366 auto itr = textureUnits.find( std::string( name ) );
367 if ( itr != textureUnits.end() ) {
368 tex->
bind( itr->second.m_texUnit );
369 m_program->setUniform( itr->second.m_location, itr->second.m_texUnit );
373 globjects::Program* ShaderProgram::getProgramObject()
const {
374 return m_program.get();
380 std::string ShaderProgram::preprocessIncludes(
const std::string& name,
381 const std::string& shader,
385 CORE_ERROR_IF( level < 32,
"Shader inclusion depth limit reached." );
387 std::string result {};
388 std::vector<std::string> finalStrings;
392 static const std::regex reg(
"^[ ]*#[ ]*include[ ]+[\"<](.*)[\">].*" );
395 std::istringstream iss( shader );
396 std::string codeline;
397 while ( std::getline( iss, codeline,
'\n' ) ) {
399 if ( std::regex_search( codeline, match, reg ) ) {
401 auto includeNameString =
402 globjects::NamedString::getFromRegistry( std::string(
"/" ) + match[1].str() );
403 if ( includeNameString !=
nullptr ) {
406 preprocessIncludes( match[1].str(), includeNameString->string(), level + 1, 0 );
409 LOG( logWARNING ) <<
"Cannot open included file " << match[1].str() <<
" at line"
410 << nline <<
" of file " << name <<
". Ignored.";
434 finalStrings.push_back( codeline );
439 for (
const auto& l : finalStrings ) {
441 result.append(
"\n" );
444 result.append(
"\0" );
void bind(int unit=-1)
Bind the texture to enable its use in a shader.