2016-02-07 22:32:38 +00:00
//
// Shader.cpp
// Clock Signal
//
// Created by Thomas Harte on 07/02/2016.
2018-05-13 19:19:52 +00:00
// Copyright 2016 Thomas Harte. All rights reserved.
2016-02-07 22:32:38 +00:00
//
# include "Shader.hpp"
2016-02-08 00:21:22 +00:00
2019-01-26 03:45:47 +00:00
# include "../../Log.hpp"
2019-02-19 03:08:03 +00:00
# include <vector>
2016-02-08 00:21:22 +00:00
2018-11-11 20:11:32 +00:00
using namespace Outputs : : Display : : OpenGL ;
2016-02-08 00:21:22 +00:00
2017-03-26 18:34:47 +00:00
namespace {
2024-01-20 03:34:42 +00:00
thread_local const Shader * bound_shader = nullptr ;
2024-01-19 20:38:40 +00:00
Log : : Logger < Log : : Source : : OpenGL > logger ;
2016-05-01 15:07:49 +00:00
}
2017-03-26 18:34:47 +00:00
GLuint Shader : : compile_shader ( const std : : string & source , GLenum type ) {
2016-02-08 00:21:22 +00:00
GLuint shader = glCreateShader ( type ) ;
2017-02-20 15:35:33 +00:00
const char * c_str = source . c_str ( ) ;
2019-02-18 15:29:40 +00:00
test_gl ( glShaderSource , shader , 1 , & c_str , NULL ) ;
test_gl ( glCompileShader , shader ) ;
2016-02-08 00:21:22 +00:00
2024-01-19 20:38:40 +00:00
if constexpr ( logger . enabled ) {
GLint isCompiled = 0 ;
test_gl ( glGetShaderiv , shader , GL_COMPILE_STATUS , & isCompiled ) ;
if ( isCompiled = = GL_FALSE ) {
GLint logLength ;
test_gl ( glGetShaderiv , shader , GL_INFO_LOG_LENGTH , & logLength ) ;
if ( logLength > 0 ) {
const auto length = std : : vector < GLchar > : : size_type ( logLength ) ;
std : : vector < GLchar > log ( length ) ;
test_gl ( glGetShaderInfoLog , shader , logLength , & logLength , log . data ( ) ) ;
logger . error ( ) . append ( " Compile log: %s " , log . data ( ) ) ;
}
throw ( type = = GL_VERTEX_SHADER ) ? VertexShaderCompilationError : FragmentShaderCompilationError ;
2016-02-08 00:21:22 +00:00
}
}
return shader ;
}
2017-11-24 23:45:24 +00:00
Shader : : Shader ( const std : : string & vertex_shader , const std : : string & fragment_shader , const std : : vector < AttributeBinding > & attribute_bindings ) {
2019-01-26 02:56:55 +00:00
init ( vertex_shader , fragment_shader , attribute_bindings ) ;
}
Shader : : Shader ( const std : : string & vertex_shader , const std : : string & fragment_shader , const std : : vector < std : : string > & binding_names ) {
std : : vector < AttributeBinding > bindings ;
GLuint index = 0 ;
for ( const auto & name : binding_names ) {
bindings . emplace_back ( name , index ) ;
2019-02-10 03:45:20 +00:00
+ + index ;
2019-01-26 02:56:55 +00:00
}
init ( vertex_shader , fragment_shader , bindings ) ;
}
void Shader : : init ( const std : : string & vertex_shader , const std : : string & fragment_shader , const std : : vector < AttributeBinding > & attribute_bindings ) {
2016-11-21 03:57:45 +00:00
shader_program_ = glCreateProgram ( ) ;
2019-01-05 23:11:39 +00:00
const GLuint vertex = compile_shader ( vertex_shader , GL_VERTEX_SHADER ) ;
const GLuint fragment = compile_shader ( fragment_shader , GL_FRAGMENT_SHADER ) ;
2016-02-08 00:21:22 +00:00
2019-02-18 15:29:40 +00:00
test_gl ( glAttachShader , shader_program_ , vertex ) ;
test_gl ( glAttachShader , shader_program_ , fragment ) ;
2016-04-19 11:23:15 +00:00
2018-05-01 02:23:57 +00:00
for ( const auto & binding : attribute_bindings ) {
2019-02-18 15:29:40 +00:00
test_gl ( glBindAttribLocation , shader_program_ , binding . index , binding . name . c_str ( ) ) ;
2024-01-19 20:38:40 +00:00
if constexpr ( logger . enabled ) {
const auto error = glGetError ( ) ;
switch ( error ) {
case 0 : break ;
case GL_INVALID_VALUE :
logger . error ( ) . append ( " GL_INVALID_VALUE when attempting to bind %s to index %d (i.e. index is greater than or equal to GL_MAX_VERTEX_ATTRIBS) " , binding . name . c_str ( ) , binding . index ) ;
break ;
case GL_INVALID_OPERATION :
logger . error ( ) . append ( " GL_INVALID_OPERATION when attempting to bind %s to index %d (i.e. name begins with gl_) " , binding . name . c_str ( ) , binding . index ) ;
break ;
default :
logger . error ( ) . append ( " Error %d when attempting to bind %s to index %d " , error , binding . name . c_str ( ) , binding . index ) ;
break ;
}
2019-02-10 03:45:20 +00:00
}
2016-04-19 11:23:15 +00:00
}
2019-02-18 15:29:40 +00:00
test_gl ( glLinkProgram , shader_program_ ) ;
2016-04-19 01:32:48 +00:00
2024-01-19 20:38:40 +00:00
if constexpr ( logger . enabled ) {
GLint logLength ;
test_gl ( glGetProgramiv , shader_program_ , GL_INFO_LOG_LENGTH , & logLength ) ;
if ( logLength > 0 ) {
std : : vector < GLchar > log ( logLength ) ;
test_gl ( glGetProgramInfoLog , shader_program_ , logLength , & logLength , log . data ( ) ) ;
logger . error ( ) . append ( " Link log: %s " , log . data ( ) ) ;
}
2019-01-05 23:11:39 +00:00
2024-01-19 20:38:40 +00:00
GLint didLink = 0 ;
test_gl ( glGetProgramiv , shader_program_ , GL_LINK_STATUS , & didLink ) ;
if ( didLink = = GL_FALSE ) {
throw ProgramLinkageError ;
}
2016-04-19 01:32:48 +00:00
}
2016-02-08 00:21:22 +00:00
}
2017-03-26 18:34:47 +00:00
Shader : : ~ Shader ( ) {
2024-01-20 03:34:42 +00:00
if ( bound_shader = = this ) Shader : : unbind ( ) ;
2016-11-21 03:57:45 +00:00
glDeleteProgram ( shader_program_ ) ;
2016-02-08 00:21:22 +00:00
}
2018-11-24 03:33:28 +00:00
void Shader : : bind ( ) const {
2024-01-20 03:34:42 +00:00
if ( bound_shader ! = this ) {
2019-02-18 15:29:40 +00:00
test_gl ( glUseProgram , shader_program_ ) ;
2024-01-20 03:34:42 +00:00
bound_shader = this ;
}
2016-05-14 22:06:55 +00:00
flush_functions ( ) ;
2016-05-01 15:07:49 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : unbind ( ) {
2024-01-20 03:34:42 +00:00
bound_shader = nullptr ;
2019-02-18 15:29:40 +00:00
test_gl ( glUseProgram , 0 ) ;
2016-02-08 00:21:22 +00:00
}
2018-11-24 03:33:28 +00:00
GLint Shader : : get_attrib_location ( const std : : string & name ) const {
2017-11-24 23:45:24 +00:00
return glGetAttribLocation ( shader_program_ , name . c_str ( ) ) ;
2016-02-08 00:21:22 +00:00
}
2018-11-24 03:33:28 +00:00
GLint Shader : : get_uniform_location ( const std : : string & name ) const {
2017-11-24 23:45:24 +00:00
return glGetUniformLocation ( shader_program_ , name . c_str ( ) ) ;
2016-02-08 00:21:22 +00:00
}
2016-05-10 23:11:48 +00:00
2017-11-24 23:45:24 +00:00
void Shader : : enable_vertex_attribute_with_pointer ( const std : : string & name , GLint size , GLenum type , GLboolean normalised , GLsizei stride , const GLvoid * pointer , GLuint divisor ) {
2016-05-10 23:11:48 +00:00
GLint location = get_attrib_location ( name ) ;
2018-11-13 03:51:44 +00:00
if ( location > = 0 ) {
2019-02-18 15:29:40 +00:00
test_gl ( glEnableVertexAttribArray , GLuint ( location ) ) ;
test_gl ( glVertexAttribPointer , GLuint ( location ) , size , type , normalised , stride , pointer ) ;
test_gl ( glVertexAttribDivisor , GLuint ( location ) , divisor ) ;
2019-01-26 03:45:47 +00:00
} else {
2024-01-19 20:38:40 +00:00
logger . error ( ) . append ( " Couldn't enable vertex attribute %s " , name . c_str ( ) ) ;
2018-11-13 03:51:44 +00:00
}
2016-05-10 23:11:48 +00:00
}
2016-05-14 02:08:32 +00:00
// The various set_uniforms...
2019-02-01 02:17:49 +00:00
# define with_location(func, ...) {\
const GLint location = glGetUniformLocation ( shader_program_ , name . c_str ( ) ) ; \
if ( location = = - 1 ) { \
2024-01-19 20:38:40 +00:00
logger . error ( ) . append ( " Couldn't get location for uniform %s " , name . c_str ( ) ) ; \
2019-02-01 02:17:49 +00:00
} else { \
func ( location , __VA_ARGS__ ) ; \
2024-01-19 20:38:40 +00:00
if ( glGetError ( ) ) logger . error ( ) . append ( " Error setting uniform %s via %s " , name . c_str ( ) , # func ) ; \
2019-02-01 02:17:49 +00:00
} \
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLint value ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform1i , value ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLuint value ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform1ui , value ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLfloat value ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform1f , value ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLint value1 , GLint value2 ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value1 , value2 , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform2i , value1 , value2 ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLfloat value1 , GLfloat value2 ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value1 , value2 , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform2f , value1 , value2 ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLuint value1 , GLuint value2 ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value1 , value2 , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform2ui , value1 , value2 ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLint value1 , GLint value2 , GLint value3 ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value1 , value2 , value3 , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform3i , value1 , value2 , value3 ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLfloat value1 , GLfloat value2 , GLfloat value3 ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value1 , value2 , value3 , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform3f , value1 , value2 , value3 ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLuint value1 , GLuint value2 , GLuint value3 ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value1 , value2 , value3 , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform3ui , value1 , value2 , value3 ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLint value1 , GLint value2 , GLint value3 , GLint value4 ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value1 , value2 , value3 , value4 , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform4i , value1 , value2 , value3 , value4 ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLfloat value1 , GLfloat value2 , GLfloat value3 , GLfloat value4 ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value1 , value2 , value3 , value4 , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform4f , value1 , value2 , value3 , value4 ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLuint value1 , GLuint value2 , GLuint value3 , GLuint value4 ) {
2016-05-14 22:06:55 +00:00
enqueue_function ( [ name , value1 , value2 , value3 , value4 , this ] {
2019-02-01 02:17:49 +00:00
with_location ( glUniform4ui , value1 , value2 , value3 , value4 ) ;
2016-05-14 22:06:55 +00:00
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLint size , GLsizei count , const GLint * values ) {
2019-02-18 15:29:40 +00:00
std : : size_t number_of_values = std : : size_t ( count ) * std : : size_t ( size ) ;
2019-01-26 03:54:23 +00:00
std : : vector < GLint > values_copy ( values , values + number_of_values ) ;
2016-05-15 18:59:59 +00:00
2016-05-14 22:15:10 +00:00
enqueue_function ( [ name , size , count , values_copy , this ] {
2017-03-26 18:34:47 +00:00
switch ( size ) {
2019-02-01 02:17:49 +00:00
case 1 : with_location ( glUniform1iv , count , values_copy . data ( ) ) ; break ;
case 2 : with_location ( glUniform2iv , count , values_copy . data ( ) ) ; break ;
case 3 : with_location ( glUniform3iv , count , values_copy . data ( ) ) ; break ;
case 4 : with_location ( glUniform4iv , count , values_copy . data ( ) ) ; break ;
2016-05-14 22:06:55 +00:00
}
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLint size , GLsizei count , const GLfloat * values ) {
2019-02-18 15:29:40 +00:00
std : : size_t number_of_values = std : : size_t ( count ) * std : : size_t ( size ) ;
2019-01-26 03:54:23 +00:00
std : : vector < GLfloat > values_copy ( values , values + number_of_values ) ;
2016-05-15 18:59:59 +00:00
2016-05-14 22:15:10 +00:00
enqueue_function ( [ name , size , count , values_copy , this ] {
2017-03-26 18:34:47 +00:00
switch ( size ) {
2019-02-01 02:17:49 +00:00
case 1 : with_location ( glUniform1fv , count , values_copy . data ( ) ) ; break ;
case 2 : with_location ( glUniform2fv , count , values_copy . data ( ) ) ; break ;
case 3 : with_location ( glUniform3fv , count , values_copy . data ( ) ) ; break ;
case 4 : with_location ( glUniform4fv , count , values_copy . data ( ) ) ; break ;
2016-05-14 22:06:55 +00:00
}
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform ( const std : : string & name , GLint size , GLsizei count , const GLuint * values ) {
2019-02-18 15:29:40 +00:00
std : : size_t number_of_values = std : : size_t ( count ) * std : : size_t ( size ) ;
2019-01-26 03:54:23 +00:00
std : : vector < GLuint > values_copy ( values , values + number_of_values ) ;
2016-05-15 18:59:59 +00:00
2016-05-14 22:15:10 +00:00
enqueue_function ( [ name , size , count , values_copy , this ] {
2017-03-26 18:34:47 +00:00
switch ( size ) {
2019-02-01 02:17:49 +00:00
case 1 : with_location ( glUniform1uiv , count , values_copy . data ( ) ) ; break ;
case 2 : with_location ( glUniform2uiv , count , values_copy . data ( ) ) ; break ;
case 3 : with_location ( glUniform3uiv , count , values_copy . data ( ) ) ; break ;
case 4 : with_location ( glUniform4uiv , count , values_copy . data ( ) ) ; break ;
2016-05-14 22:06:55 +00:00
}
} ) ;
2016-05-14 02:08:32 +00:00
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform_matrix ( const std : : string & name , GLint size , bool transpose , const GLfloat * values ) {
2016-05-14 02:08:32 +00:00
set_uniform_matrix ( name , size , 1 , transpose , values ) ;
}
2017-03-26 18:34:47 +00:00
void Shader : : set_uniform_matrix ( const std : : string & name , GLint size , GLsizei count , bool transpose , const GLfloat * values ) {
2019-02-18 15:29:40 +00:00
std : : size_t number_of_values = std : : size_t ( count ) * std : : size_t ( size ) * std : : size_t ( size ) ;
2019-01-26 03:54:23 +00:00
std : : vector < GLfloat > values_copy ( values , values + number_of_values ) ;
2016-05-15 18:59:59 +00:00
2016-05-14 22:15:10 +00:00
enqueue_function ( [ name , size , count , transpose , values_copy , this ] {
GLboolean glTranspose = transpose ? GL_TRUE : GL_FALSE ;
2017-03-26 18:34:47 +00:00
switch ( size ) {
2019-02-01 02:17:49 +00:00
case 2 : with_location ( glUniformMatrix2fv , count , glTranspose , values_copy . data ( ) ) ; break ;
case 3 : with_location ( glUniformMatrix3fv , count , glTranspose , values_copy . data ( ) ) ; break ;
case 4 : with_location ( glUniformMatrix4fv , count , glTranspose , values_copy . data ( ) ) ; break ;
2016-05-14 22:06:55 +00:00
}
} ) ;
}
2017-03-26 18:34:47 +00:00
void Shader : : enqueue_function ( std : : function < void ( void ) > function ) {
2020-06-15 04:24:10 +00:00
std : : lock_guard function_guard ( function_mutex_ ) ;
2016-11-21 03:57:45 +00:00
enqueued_functions_ . push_back ( function ) ;
2016-05-14 22:06:55 +00:00
}
2018-11-24 03:33:28 +00:00
void Shader : : flush_functions ( ) const {
2020-06-15 04:24:10 +00:00
std : : lock_guard function_guard ( function_mutex_ ) ;
2017-03-26 18:34:47 +00:00
for ( std : : function < void ( void ) > function : enqueued_functions_ ) {
2016-05-14 22:06:55 +00:00
function ( ) ;
2016-05-14 02:08:32 +00:00
}
2016-11-21 03:57:45 +00:00
enqueued_functions_ . clear ( ) ;
2016-05-14 02:08:32 +00:00
}