#ifndef GLSHADER_SHADER_HPP_ #define GLSHADER_SHADER_HPP_ #include <map> #include <set> #include <string> #include <unordered_map> #include <GL/glew.h> class Shader { public: using Paths = std::set<std::string>; explicit Shader(Paths const & paths); virtual ~Shader(); Shader(Shader &&) noexcept; Shader(Shader const &) = delete; Shader & operator=(Shader &&) = delete; Shader & operator=(Shader const &) = delete; using Defines = std::map<std::string, std::string>; using Locations = std::map<std::string, GLuint>; static void root(std::string const & root); static void defines(Defines const & defines); static void verts(Locations const & verts); static void frags(Locations const & frags); GLuint program() const; Shader & validate(); Shader & use(); template<typename Value> Shader & uniform( std::string const & name, Value const & value, bool required = true ); template<typename Value> Shader & uniform( std::string const & name, Value * value, bool required = true ) = delete; template<typename Value> static void uniform_buffer( std::string const & name, Value const & value, bool required = true ); template<typename Value> static void uniform_buffer( std::string const & name, Value * value, bool required = true ) = delete; Shader & texture( std::string const & name, GLuint texture, GLenum target, bool required = true ); protected: struct Uniform { GLint location; bool set; }; struct UniformBuffer { GLuint buffer; GLsizeiptr size; GLuint binding; bool set; }; struct UniformBlock { UniformBuffer & buffer; }; void validate_() const; void current_( std::string const & error ) const; static void error_( std::string const & error, std::string const & error_hint = {} ); Uniform * uniform_( std::string const & error, std::string const & name, bool required ); UniformBlock * uniform_block_( std::string const & error, std::string const & name, bool required, GLsizeiptr size ); static UniformBuffer * uniform_buffer_( std::string const & error, std::string const & name, bool required, GLsizeiptr size ); template<typename Value> using ByName = std::unordered_map<std::string, Value>; GLuint program_; std::string program_name_; std::string static root_; Defines static defines_; Locations static verts_; Locations static frags_; ByName<Uniform> uniforms_; ByName<UniformBlock> uniform_blocks_; ByName<UniformBuffer> static uniform_buffers_; }; // Debug macros. #ifndef NDEBUG #define GLSHADER_DEBUG_(...) __VA_ARGS__ #define GLSHADER_DEBUG_ERROR_(...) \ auto error = std::string{} + __VA_ARGS__; #else #define GLSHADER_DEBUG_(...) #define GLSHADER_DEBUG_ERROR_(...) \ auto error = std::string{}; #endif // NDEBUG // Inline definitions. #define GLSHADER_SET_(TYPE, NAME) \ inline void Shader::NAME(TYPE const & NAME) \ { \ NAME##_ = NAME; \ } GLSHADER_SET_(std::string, root) GLSHADER_SET_(Defines, defines) GLSHADER_SET_(Locations, verts) GLSHADER_SET_(Locations, frags) inline GLuint Shader::program() const { return program_; } inline Shader & Shader::validate() { GLSHADER_DEBUG_(validate_();) return *this; } inline Shader & Shader::use() { glUseProgram(program_); return *this; } // Uniform template definitions. #define GLSHADER_UNIFORM_BUFFER_(BLOCK_OR_BUFFER, BUFFER, SET) \ if (auto block_or_buffer = BLOCK_OR_BUFFER( \ error, name, required, sizeof(value) \ )) \ { \ glBindBuffer(GL_UNIFORM_BUFFER, block_or_buffer->BUFFER); \ GLSHADER_DEBUG_(error_(error, "unprocessed previous error");) \ glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(value), &value); \ GLSHADER_DEBUG_(error_(error);) \ GLSHADER_DEBUG_(block_or_buffer->SET = true;) \ } template<typename Value> inline void Shader::uniform_buffer( std::string const & name, Value const & value, bool required ) { GLSHADER_DEBUG_ERROR_( "Failed to set uniform buffer '" + name + "'" ) GLSHADER_UNIFORM_BUFFER_(uniform_buffer_, buffer, set) } template<typename Value> inline Shader & Shader::uniform( std::string const & name, Value const & value, bool required ) { GLSHADER_DEBUG_ERROR_( "Failed to set uniform block '" + name + "' of " + program_name_ ) GLSHADER_DEBUG_(current_(error);) GLSHADER_UNIFORM_BUFFER_(uniform_block_, buffer.buffer, buffer.set) return *this; } // Uniform template specializations. #define GLSHADER_UNIFORM_SIGNATURE_(TYPE) \ template<> \ inline Shader & Shader::uniform( \ std::string const & name, \ TYPE const & value, \ bool required \ ) #define GLSHADER_UNIFORM_DELETE(TYPE) \ GLSHADER_UNIFORM_SIGNATURE_(TYPE) = delete; #define GLSHADER_UNIFORM(TYPE, CODE) \ GLSHADER_UNIFORM_SIGNATURE_(TYPE) \ { \ GLSHADER_DEBUG_ERROR_( \ "Failed to set uniform '" + name + "' of " + program_name_ \ ) \ GLSHADER_DEBUG_(current_(error);) \ if (auto * uniform = uniform_(error, name, required)) \ { \ GLint const & location = uniform->location; \ GLSHADER_DEBUG_(error_(error, "unprocessed previous error");) \ CODE \ GLSHADER_DEBUG_(error_(error, "wrong type?");) \ GLSHADER_DEBUG_(uniform->set = true;) \ } \ return *this; \ } // Uniform scalar template specializations. #define GLSHADER_UNIFORM_SCALAR_(TYPE, SUFFIX) \ GLSHADER_UNIFORM( \ TYPE, \ glUniform1##SUFFIX( \ location, value \ ); \ ) GLSHADER_UNIFORM_SCALAR_(bool, i) GLSHADER_UNIFORM_SCALAR_(GLboolean, i) GLSHADER_UNIFORM_SCALAR_(GLint, i) GLSHADER_UNIFORM_SCALAR_(GLuint, ui) GLSHADER_UNIFORM_SCALAR_(GLfloat, f) GLSHADER_UNIFORM_DELETE(GLdouble) #endif // GLSHADER_SHADER_HPP_