supercell-wx/scwx-qt/source/scwx/qt/gl/shader_program.cpp

183 lines
4.3 KiB
C++

#include <scwx/qt/gl/shader_program.hpp>
#include <scwx/util/logger.hpp>
#include <QFile>
namespace scwx
{
namespace qt
{
namespace gl
{
static const std::string logPrefix_ = "scwx::qt::gl::shader_program";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
static constexpr GLsizei kInfoLogBufSize = 512;
static const std::unordered_map<GLenum, std::string> kShaderNames_ {
{GL_VERTEX_SHADER, "vertex"},
{GL_GEOMETRY_SHADER, "geometry"},
{GL_FRAGMENT_SHADER, "fragment"}};
class ShaderProgram::Impl
{
public:
explicit Impl(OpenGLFunctions& gl) : gl_(gl), id_ {GL_INVALID_INDEX}
{
// Create shader program
id_ = gl_.glCreateProgram();
}
~Impl()
{
// Delete shader program
gl_.glDeleteProgram(id_);
}
static std::string ShaderName(GLenum type);
OpenGLFunctions& gl_;
GLuint id_;
};
ShaderProgram::ShaderProgram(OpenGLFunctions& gl) :
p(std::make_unique<Impl>(gl))
{
}
ShaderProgram::~ShaderProgram() = default;
ShaderProgram::ShaderProgram(ShaderProgram&&) noexcept = default;
ShaderProgram& ShaderProgram::operator=(ShaderProgram&&) noexcept = default;
GLuint ShaderProgram::id() const
{
return p->id_;
}
GLint ShaderProgram::GetUniformLocation(const std::string& name)
{
GLint location = p->gl_.glGetUniformLocation(p->id_, name.c_str());
if (location == -1)
{
logger_->warn("Could not find {}", name);
}
return location;
}
std::string ShaderProgram::Impl::ShaderName(GLenum type)
{
auto it = kShaderNames_.find(type);
if (it != kShaderNames_.cend())
{
return it->second;
}
return fmt::format("{:#06x}", type);
}
bool ShaderProgram::Load(const std::string& vertexPath,
const std::string& fragmentPath)
{
return Load({{GL_VERTEX_SHADER, vertexPath}, //
{GL_FRAGMENT_SHADER, fragmentPath}});
}
bool ShaderProgram::Load(
std::initializer_list<std::pair<GLenum, std::string>> shaders)
{
logger_->debug("Load()");
OpenGLFunctions& gl = p->gl_;
GLint glSuccess;
bool success = true;
char infoLog[kInfoLogBufSize];
GLsizei logLength;
std::vector<GLuint> shaderIds {};
for (auto& shader : shaders)
{
logger_->debug("Loading {} shader: {}",
Impl::ShaderName(shader.first),
shader.second);
QFile file(shader.second.c_str());
file.open(QIODevice::ReadOnly | QIODevice::Text);
if (!file.isOpen())
{
logger_->error("Could not load shader");
success = false;
break;
}
QTextStream shaderStream(&file);
shaderStream.setEncoding(QStringConverter::Utf8);
std::string shaderSource = shaderStream.readAll().toStdString();
const char* shaderSourceC = shaderSource.c_str();
// Create a shader
GLuint shaderId = gl.glCreateShader(shader.first);
shaderIds.push_back(shaderId);
// Attach the shader source code and compile the shader
gl.glShaderSource(shaderId, 1, &shaderSourceC, NULL);
gl.glCompileShader(shaderId);
// Check for errors
gl.glGetShaderiv(shaderId, GL_COMPILE_STATUS, &glSuccess);
gl.glGetShaderInfoLog(shaderId, kInfoLogBufSize, &logLength, infoLog);
if (!glSuccess)
{
logger_->error("Shader compilation failed: {}", infoLog);
success = false;
break;
}
else if (logLength > 0)
{
logger_->error("Shader compiled with warnings: {}", infoLog);
}
}
if (success)
{
for (auto& shaderId : shaderIds)
{
gl.glAttachShader(p->id_, shaderId);
}
gl.glLinkProgram(p->id_);
// Check for errors
gl.glGetProgramiv(p->id_, GL_LINK_STATUS, &glSuccess);
gl.glGetProgramInfoLog(p->id_, kInfoLogBufSize, &logLength, infoLog);
if (!glSuccess)
{
logger_->error("Shader program link failed: {}", infoLog);
success = false;
}
else if (logLength > 0)
{
logger_->error("Shader program linked with warnings: {}", infoLog);
}
}
// Delete shaders
for (auto& shaderId : shaderIds)
{
gl.glDeleteShader(shaderId);
}
return success;
}
void ShaderProgram::Use() const
{
p->gl_.glUseProgram(p->id_);
}
} // namespace gl
} // namespace qt
} // namespace scwx