#include #include #include #include #include namespace scwx::qt::gl { static const std::string logPrefix_ = "scwx::qt::gl::gl_context"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); class GlContext::Impl { public: explicit Impl() = default; ~Impl() = default; Impl(const Impl&) = delete; Impl& operator=(const Impl&) = delete; Impl(const Impl&&) = delete; Impl& operator=(const Impl&&) = delete; void InitializeGL(); static std::size_t GetShaderKey(std::initializer_list> shaders); bool glInitialized_ {false}; std::unordered_map> shaderProgramMap_ {}; std::mutex shaderProgramMutex_ {}; GLuint textureAtlas_ {GL_INVALID_INDEX}; std::mutex textureMutex_ {}; std::uint64_t textureBufferCount_ {}; }; GlContext::GlContext() : p(std::make_unique()) {} GlContext::~GlContext() = default; GlContext::GlContext(GlContext&&) noexcept = default; GlContext& GlContext::operator=(GlContext&&) noexcept = default; std::uint64_t GlContext::texture_buffer_count() const { return p->textureBufferCount_; } void GlContext::Impl::InitializeGL() { if (glInitialized_) { return; } const int gladVersion = gladLoaderLoadGL(); if (!gladVersion) { logger_->error("gladLoaderLoadGL failed"); QMessageBox::critical( nullptr, "Supercell Wx", "Unable to initialize OpenGL"); throw std::runtime_error("Unable to initialize OpenGL"); } logger_->info("GLAD initialization complete: OpenGL {}.{}", GLAD_VERSION_MAJOR(gladVersion), GLAD_VERSION_MINOR(gladVersion)); auto glVersion = reinterpret_cast(glGetString(GL_VERSION)); auto glVendor = reinterpret_cast(glGetString(GL_VENDOR)); auto glRenderer = reinterpret_cast(glGetString(GL_RENDERER)); logger_->info("OpenGL Version: {}", glVersion); logger_->info("OpenGL Vendor: {}", glVendor); logger_->info("OpenGL Renderer: {}", glRenderer); // Get OpenGL version GLint major = 0; GLint minor = 0; glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MINOR_VERSION, &minor); if (major < 3 || (major == 3 && minor < 3) || !GLAD_GL_VERSION_3_3) { logger_->error( "OpenGL 3.3 or greater is required, found {}.{}", major, minor); QMessageBox::critical( nullptr, "Supercell Wx", QString("OpenGL 3.3 or greater is required, found %1.%2\n\n%3\n%4\n%5") .arg(major) .arg(minor) .arg(glVersion) .arg(glVendor) .arg(glRenderer)); throw std::runtime_error("OpenGL version too low"); } glGenTextures(1, &textureAtlas_); glInitialized_ = true; } std::shared_ptr GlContext::GetShaderProgram(const std::string& vertexPath, const std::string& fragmentPath) { return GetShaderProgram( {{GL_VERTEX_SHADER, vertexPath}, {GL_FRAGMENT_SHADER, fragmentPath}}); } std::shared_ptr GlContext::GetShaderProgram( std::initializer_list> shaders) { const auto key = Impl::GetShaderKey(shaders); std::shared_ptr shaderProgram; std::unique_lock lock(p->shaderProgramMutex_); auto it = p->shaderProgramMap_.find(key); if (it == p->shaderProgramMap_.end()) { shaderProgram = std::make_shared(); shaderProgram->Load(shaders); p->shaderProgramMap_[key] = shaderProgram; } else { shaderProgram = it->second; } return shaderProgram; } GLuint GlContext::GetTextureAtlas() { p->InitializeGL(); std::unique_lock lock(p->textureMutex_); auto& textureAtlas = util::TextureAtlas::Instance(); if (p->textureBufferCount_ != textureAtlas.BuildCount()) { p->textureBufferCount_ = textureAtlas.BuildCount(); textureAtlas.BufferAtlas(p->textureAtlas_); } return p->textureAtlas_; } void GlContext::Initialize() { p->InitializeGL(); } void GlContext::StartFrame() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); } std::size_t GlContext::Impl::GetShaderKey( std::initializer_list> shaders) { std::size_t seed = 0; for (auto& shader : shaders) { boost::hash_combine(seed, shader.first); boost::hash_combine(seed, shader.second); } return seed; } } // namespace scwx::qt::gl