#include #include #include 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 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(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> shaders) { logger_->debug("Load()"); OpenGLFunctions& gl = p->gl_; GLint glSuccess; bool success = true; char infoLog[kInfoLogBufSize]; GLsizei logLength; std::vector 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