Skip to content

Instantly share code, notes, and snippets.

@htmlboss
Created December 9, 2018 21:58
Show Gist options
  • Save htmlboss/1279c87e4e79c1b2fcee3ddb0e23a4e5 to your computer and use it in GitHub Desktop.
Save htmlboss/1279c87e4e79c1b2fcee3ddb0e23a4e5 to your computer and use it in GitHub Desktop.
OpenGL shader wrapper in C++17. It also scans and caches active shader uniforms in getUniforms() for fast lookups when rendering.
#include "GPUProgram.hpp"
#include "Shader.hpp"
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
#include <array>
/***********************************************************************************/
GPUProgram::GPUProgram(const std::string_view programName, const std::initializer_list<Shader>& shaders) : m_programName{ programName },
m_ID{ glCreateProgram() } {
for (const auto& shader : shaders) {
glAttachShader(m_ID, shader.m_ID);
}
if (linkAndValidate()) {
getUniforms();
}
// Cleanup
for (auto shader : shaders) {
glDetachShader(m_ID, shader.m_ID);
shader.Delete();
}
}
/***********************************************************************************/
GPUProgram::~GPUProgram() {
Delete();
}
/***********************************************************************************/
void GPUProgram::Delete() {
if (m_ID != 0) {
glDeleteProgram(m_ID);
m_ID = 0;
}
}
/***********************************************************************************/
GPUProgram& GPUProgram::SetUniform(const std::string& uniformName, const int value) {
glProgramUniform1i(m_ID, m_uniforms.at(uniformName), value);
return *this;
}
/***********************************************************************************/
GPUProgram& GPUProgram::SetUniform(const std::string& uniformName, const float value) {
glProgramUniform1f(m_ID, m_uniforms.at(uniformName), value);
return *this;
}
/***********************************************************************************/
GPUProgram& GPUProgram::SetUniform(const std::string& uniformName, const glm::ivec2& value) {
glProgramUniform2iv(m_ID, m_uniforms.at(uniformName), 1, glm::value_ptr(value));
return *this;
}
/***********************************************************************************/
GPUProgram& GPUProgram::SetUniform(const std::string& uniformName, const glm::vec2& value) {
glProgramUniform2fv(m_ID, m_uniforms.at(uniformName), 1, glm::value_ptr(value));
return *this;
}
/***********************************************************************************/
GPUProgram& GPUProgram::SetUniform(const std::string& uniformName, const glm::vec3& value) {
glProgramUniform3fv(m_ID, m_uniforms.at(uniformName), 1, glm::value_ptr(value));
return *this;
}
/***********************************************************************************/
GPUProgram& GPUProgram::SetUniform(const std::string& uniformName, const glm::vec4& value) {
glProgramUniform4fv(m_ID, m_uniforms.at(uniformName), 1, glm::value_ptr(value));
return *this;
}
/***********************************************************************************/
GPUProgram& GPUProgram::SetUniform(const std::string& uniformName, const glm::mat3x3& value) {
glProgramUniformMatrix3fv(m_ID, m_uniforms.at(uniformName), 1, GL_FALSE, glm::value_ptr(value));
return *this;
}
/***********************************************************************************/
GPUProgram& GPUProgram::SetUniform(const std::string& uniformName, const glm::mat4x4& value) {
glProgramUniformMatrix4fv(m_ID, m_uniforms.at(uniformName), 1, GL_FALSE, glm::value_ptr(value));
return *this;
}
/***********************************************************************************/
bool GPUProgram::linkAndValidate() const {
int success{ -1 };
std::array<char, 1024> infoLog;
glLinkProgram(m_ID);
glGetProgramiv(m_ID, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(m_ID, infoLog.size(), nullptr, infoLog.data());
std::cerr << m_programName << " linking error: " << infoLog.data() << std::endl;
return false;
}
glValidateProgram(m_ID);
glGetProgramiv(m_ID, GL_VALIDATE_STATUS, &success);
if (!success) {
glGetProgramInfoLog(m_ID, infoLog.size(), nullptr, infoLog.data());
std::cerr << m_programName << " validation error: " << infoLog.data() << std::endl;
return false;
}
return true;
}
/***********************************************************************************/
void GPUProgram::getUniforms() {
int total{ -1 };
glGetProgramiv(m_ID, GL_ACTIVE_UNIFORMS, &total);
for (auto i = 0; i < total; ++i) {
int name_len{ -1 }, num{ -1 };
GLenum type{ GL_ZERO };
char name[100];
glGetActiveUniform(m_ID, static_cast<GLuint>(i), sizeof(name) - 1, &name_len, &num, &type, name);
name[name_len] = 0;
const std::string nameStr{ name };
m_uniforms.try_emplace(nameStr, glGetUniformLocation(m_ID, name));
}
}
#pragma once
#include <glad/glad.h>
#include <glm/mat4x4.hpp>
#include <string>
#include <unordered_map>
/***********************************************************************************/
// Forward declarations
class Shader;
/***********************************************************************************/
class GPUProgram {
public:
GPUProgram(const std::string_view programName, const std::initializer_list<Shader>& shaders);
~GPUProgram();
GPUProgram(const GPUProgram&) = delete;
GPUProgram(GPUProgram&&) = default;
GPUProgram& operator=(const GPUProgram&) = delete;
GPUProgram& operator=(GPUProgram&&) = default;
void Delete();
auto GetName() const { return m_programName; }
GPUProgram& SetUniform(const std::string& uniformName, const int value);
GPUProgram& SetUniform(const std::string& uniformName, const float value);
GPUProgram& SetUniform(const std::string& uniformName, const glm::ivec2& value);
GPUProgram& SetUniform(const std::string& uniformName, const glm::vec2& value);
GPUProgram& SetUniform(const std::string& uniformName, const glm::vec3& value);
GPUProgram& SetUniform(const std::string& uniformName, const glm::vec4& value);
GPUProgram& SetUniform(const std::string& uniformName, const glm::mat3x3& value);
GPUProgram& SetUniform(const std::string& uniformName, const glm::mat4x4& value);
private:
//
[[nodiscard]] bool linkAndValidate() const;
//
void getUniforms();
std::string m_programName;
GLuint m_ID{ 0 };
std::unordered_map<std::string, GLint> m_uniforms;
};
#include "Shader.hpp"
#include <iostream>
#include <array>
/***********************************************************************************/
Shader::Shader(const std::string_view shaderCode, const Type type) {
m_ID = glCreateShader(type);
compile(shaderCode.data());
}
/***********************************************************************************/
Shader::~Shader() {
Delete();
}
/***********************************************************************************/
void Shader::Delete() {
if (m_ID != 0) {
glDeleteShader(m_ID);
m_ID = 0;
}
}
/***********************************************************************************/
void Shader::compile(const GLchar* shaderCode) const {
GLint success;
std::array<GLchar, 1024> infoLog;
glShaderSource(m_ID, 1, &shaderCode, nullptr);
glCompileShader(m_ID);
glGetShaderiv(m_ID, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(m_ID, infoLog.size(), nullptr, infoLog.data());
std::cerr << "Shader Error: " << infoLog.data() << std::endl;
}
}
#pragma once
#include <glad/glad.h>
#include <string_view>
/***********************************************************************************/
// Forward declarations
class GPUProgram;
/***********************************************************************************/
class Shader {
friend class GPUProgram;
public:
enum Type : decltype(GL_VERTEX_SHADER) {
VERTEX = GL_VERTEX_SHADER,
GEOMETRY = GL_GEOMETRY_SHADER,
FRAGMENT = GL_FRAGMENT_SHADER,
COMPUTE = GL_COMPUTE_SHADER,
TESS_CONTROL = GL_TESS_CONTROL_SHADER,
TESS_EVAL = GL_TESS_EVALUATION_SHADER
};
Shader(const std::string_view shaderCode, const Type type);
~Shader();
Shader(const Shader&) = default;
Shader(Shader&&) = default;
Shader& operator=(const Shader&) = default;
Shader& operator=(Shader&&) = default;
void Delete();
private:
// Compiles a shader file and checks for errors
void compile(const GLchar* shaderCode) const;
GLuint m_ID{ 0 };
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment