#include "shader.hpp" #include #include #include #include #include #include #include "str.hpp" static void checked_action( void (glAction)( GLuint object ), GLuint object, GLenum status_enum, void (glGetObjectiv)( GLuint object, GLenum pname, GLint *params ), void (glGetObjectInfoLog)( GLuint object, GLsizei maxLength, GLsizei *length, GLchar *infoLog ), std::string const & what ) { // Perform action. glAction(object); // Check status. GLint status; glGetObjectiv(object, status_enum, &status); if (status) return; // Get info log length. GLint info_log_length; glGetObjectiv(object, GL_INFO_LOG_LENGTH, &info_log_length); // Get info log content. std::string info_log; if (info_log_length) { std::vector info_log_buf(info_log_length); glGetObjectInfoLog(object, info_log_length, nullptr, &info_log_buf[0]); info_log = STR("\n" << &info_log[0]); } // Throw. throw std::runtime_error{STR(what << info_log)}; } static GLenum shader_type(std::string type_str) { return type_str == "vert" ? GL_VERTEX_SHADER : type_str == "tesc" ? GL_TESS_CONTROL_SHADER : type_str == "tese" ? GL_TESS_EVALUATION_SHADER : type_str == "geom" ? GL_GEOMETRY_SHADER : type_str == "frag" ? GL_FRAGMENT_SHADER : type_str == "comp" ? GL_COMPUTE_SHADER : 0; } Shader::Shader(std::vector paths, std::string name) : program_{0}, paths_{std::move(paths)}, name_{!name.empty() ? std::move(name) : STR_JOIN(", ", "'" << it << "'", paths_) } { new_(); } Shader::Shader(Shader const & other) : program_{0}, paths_ {other.paths_}, name_ {other.name_} { new_(); } Shader & Shader::operator=(Shader const & other) { delete_(); program_ = 0; paths_ = other.paths_; name_ = other.name_; new_(); return *this; } Shader::Shader(Shader && other) : program_{std::move(other.program_)}, paths_ {std::move(other.paths_)}, name_ {std::move(other.name_)} { other.program_ = 0; } Shader & Shader::operator=(Shader && other) { delete_(); program_ = std::move(other.program_); paths_ = std::move(other.paths_); name_ = std::move(other.name_); other.program_ = 0; return *this; } Shader::~Shader() { delete_(); } Shader & Shader::use() { glUseProgram(program_); return *this; } Shader & Shader::validate() { // Validate shader and check for errors. checked_action( glValidateProgram, program_, GL_VALIDATE_STATUS, glGetProgramiv, glGetProgramInfoLog, STR("Failed to validate shader program " << name_ << ".") ); return *this; } void Shader::new_() { try { // Create program. program_ = glCreateProgram(); if (!program_) throw std::runtime_error{STR( "Failed to create shader program for " << name_ << "." )}; // Label program. glObjectLabel(GL_PROGRAM, program_, -1, name_.c_str()); // Process shader paths. std::vector shaders; for (auto const & path : paths_) { // Infer shader type from path extension. // https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/ auto pos = path.rfind("."); if (pos == path.npos) throw std::runtime_error{STR( "Failed to infer shader type of '" << path << "' of shader program " << name_ << "; no file extension." )}; auto type_str = path.substr(pos + 1); auto type = shader_type(type_str); if (!type) throw std::runtime_error{STR( "Failed to infer shader type of '" << path << "' of shader program " << name_ << "; unknown file extension '" << type_str << "'." )}; // Create, attach, and flag shader for deletion when detached. auto shader = glCreateShader(type); if (!shader) throw std::runtime_error{STR( "Failed to create " << type_str << " shader for " << path << " of shader program " << name_ << "." )}; glAttachShader(program_, shader); glDeleteShader(shader); shaders.push_back(shader); // Label shader. glObjectLabel(GL_SHADER, shader, -1, path.c_str()); // Set shader source. std::ifstream source_file(path); if (!source_file) throw std::runtime_error{STR( "Failed to open " << type_str << " shader '" << path << "' of shader program " << name_ << "." )}; auto source_str = STR(source_file.rdbuf()); std::vector sources = { source_str.c_str() }; glShaderSource(shader, sources.size(), &sources[0], nullptr); // Compile shader and check for errors. checked_action( glCompileShader, shader, GL_COMPILE_STATUS, glGetShaderiv, glGetShaderInfoLog, STR( "Failed to compile " << type_str << " shader '" << path << "' of shader program " << name_ << "." ) ); } // Link program and check for errors. checked_action( glLinkProgram, program_, GL_LINK_STATUS, glGetProgramiv, glGetProgramInfoLog, STR("Failed to link shader program " << name_ << ".") ); // Detach shaders. for (auto shader : shaders) glDetachShader(program_, shader); } catch (...) { delete_(); throw; } } void Shader::delete_() { // Delete program (and detach and delete shaders). glDeleteProgram(program_); } void Shader::ensure_current_( std::string const & operation, std::string const & name ) { GLuint current_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)¤t_program); if (current_program != program_) { auto action = name.empty() ? operation : STR(operation << " '" << name << "'"); throw std::runtime_error{STR( "Failed to " << action << " of shader program " << name_ << "; program is not current." )}; } }