From 3cdc8c58842e046ccd15bbfa96da775238835443 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sun, 18 Sep 2022 20:38:45 +0200 Subject: [PATCH] fix: OpenGL textures not being cleaned up correctly --- .../include/hex/ui/imgui_imhex_extensions.h | 39 ++++-- .../source/ui/imgui_imhex_extensions.cpp | 128 ++++++++++-------- main/source/init/splash_window.cpp | 12 +- main/source/window/window.cpp | 4 +- .../include/content/views/view_about.hpp | 1 - .../source/content/data_processor_nodes.cpp | 9 +- .../source/content/views/view_about.cpp | 10 +- .../builtin/source/content/welcome_screen.cpp | 24 ++-- 8 files changed, 115 insertions(+), 112 deletions(-) diff --git a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h index 82e05e1df..a39a6ed8e 100644 --- a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h +++ b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h @@ -31,27 +31,40 @@ enum ImGuiCustomCol { namespace ImGui { - struct Texture { - ImTextureID textureId = nullptr; - int width = 0, height = 0; + class Texture { + public: + Texture() = default; + Texture(const ImU8 *buffer, int size); + Texture(const char *path); + Texture(const Texture&) = delete; + Texture(Texture&& other) noexcept; - [[nodiscard]] constexpr bool valid() const noexcept { - return this->textureId != nullptr; + ~Texture(); + + void operator=(const Texture&) = delete; + void operator=(Texture&& other) noexcept; + + [[nodiscard]] constexpr bool isValid() const noexcept { + return this->m_textureId != nullptr; } [[nodiscard]] constexpr operator ImTextureID() { - return this->textureId; + return this->m_textureId; } - [[nodiscard]] auto size() const noexcept { - return ImVec2(this->width, this->height); + [[nodiscard]] auto getSize() const noexcept { + return ImVec2(this->m_width, this->m_height); } - [[nodiscard]] constexpr auto aspectRatio() const noexcept { - if (this->height == 0) return 1.0F; + [[nodiscard]] constexpr auto getAspectRatio() const noexcept { + if (this->m_height == 0) return 1.0F; - return float(this->width) / float(this->height); + return float(this->m_width) / float(this->m_height); } + + private: + ImTextureID m_textureId = nullptr; + int m_width = 0, m_height = 0; }; int UpdateStringSizeCallback(ImGuiInputTextCallbackData *data); @@ -82,10 +95,6 @@ namespace ImGui { return static_cast(ImGui::GetTime() * 100) % 100 <= static_cast(ImGui::GetIO().DeltaTime * 100); } - Texture LoadImageFromPath(const char *path); - Texture LoadImageFromMemory(const ImU8 *buffer, int size); - void UnloadImage(Texture &texture); - void OpenPopupInWindow(const char *window_name, const char *popup_name); struct ImHexCustomData { diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp index 66e42bf2b..ad242eadb 100644 --- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp +++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp @@ -17,6 +17,74 @@ namespace ImGui { + Texture::Texture(const ImU8 *buffer, int size) { + unsigned char *imageData = stbi_load_from_memory(buffer, size, &this->m_width, &this->m_height, nullptr, 4); + if (imageData == nullptr) + return; + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + #if defined(GL_UNPACK_ROW_LENGTH) + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + #endif + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this->m_width, this->m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); + stbi_image_free(imageData); + + this->m_textureId = reinterpret_cast(static_cast(texture)); + } + + Texture::Texture(const char *path) { + unsigned char *imageData = stbi_load(path, &this->m_width, &this->m_height, nullptr, 4); + if (imageData == nullptr) + return; + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + #if defined(GL_UNPACK_ROW_LENGTH) + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + #endif + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this->m_width, this->m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); + stbi_image_free(imageData); + + this->m_textureId = reinterpret_cast(static_cast(texture)); + } + + Texture::Texture(Texture&& other) noexcept { + this->m_textureId = other.m_textureId; + this->m_width = other.m_width; + this->m_height = other.m_height; + + other.m_textureId = nullptr; + } + + void Texture::operator=(Texture&& other) noexcept { + this->m_textureId = other.m_textureId; + this->m_width = other.m_width; + this->m_height = other.m_height; + + other.m_textureId = nullptr; + } + + Texture::~Texture() { + if (this->m_textureId == nullptr) + return; + + auto glTextureId = static_cast(reinterpret_cast(this->m_textureId)); + glDeleteTextures(1, &glTextureId); + } + int UpdateStringSizeCallback(ImGuiInputTextCallbackData *data) { if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) { auto &string = *static_cast(data->UserData); @@ -298,66 +366,6 @@ namespace ImGui { colors[ImGuiCustomCol_Highlight] = ImColor(77, 198, 155); } - Texture LoadImageFromPath(const char *path) { - int imageWidth = 0; - int imageHeight = 0; - - unsigned char *imageData = stbi_load(path, &imageWidth, &imageHeight, nullptr, 4); - if (imageData == nullptr) - return { nullptr, -1, -1 }; - - - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - -#if defined(GL_UNPACK_ROW_LENGTH) - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); -#endif - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageWidth, imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); - stbi_image_free(imageData); - - return { reinterpret_cast(static_cast(texture)), imageWidth, imageHeight }; - } - - Texture LoadImageFromMemory(const ImU8 *buffer, int size) { - int imageWidth = 0; - int imageHeight = 0; - - - unsigned char *imageData = stbi_load_from_memory(buffer, size, &imageWidth, &imageHeight, nullptr, 4); - if (imageData == nullptr) - return { nullptr, -1, -1 }; - - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - -#if defined(GL_UNPACK_ROW_LENGTH) - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); -#endif - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageWidth, imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); - stbi_image_free(imageData); - - return { reinterpret_cast(static_cast(texture)), imageWidth, imageHeight }; - } - - void UnloadImage(Texture &texture) { - if (texture.textureId == nullptr) - return; - - auto glTextureId = static_cast(reinterpret_cast(texture.textureId)); - glDeleteTextures(1, &glTextureId); - - texture = { nullptr, 0, 0 }; - } - void OpenPopupInWindow(const char *window_name, const char *popup_name) { if (ImGui::Begin(window_name)) { ImGui::OpenPopup(popup_name); diff --git a/main/source/init/splash_window.cpp b/main/source/init/splash_window.cpp index 0e9ef0f6b..5c7a911e0 100644 --- a/main/source/init/splash_window.cpp +++ b/main/source/init/splash_window.cpp @@ -72,15 +72,13 @@ namespace hex::init { bool WindowSplash::loop() { auto splash = romfs::get("splash.png"); - ImGui::Texture splashTexture = ImGui::LoadImageFromMemory(reinterpret_cast(splash.data()), splash.size()); + ImGui::Texture splashTexture = ImGui::Texture(reinterpret_cast(splash.data()), splash.size()); - if (splashTexture == nullptr) { + if (!splashTexture.isValid()) { log::fatal("Could not load splash screen image!"); exit(EXIT_FAILURE); } - ON_SCOPE_EXIT { ImGui::UnloadImage(splashTexture); }; - auto tasksSucceeded = processTasksAsync(); auto scale = ImHexApi::System::getGlobalScale(); @@ -97,7 +95,7 @@ namespace hex::init { auto drawList = ImGui::GetForegroundDrawList(); - drawList->AddImage(splashTexture, ImVec2(0, 0), splashTexture.size() * scale); + drawList->AddImage(splashTexture, ImVec2(0, 0), splashTexture.getSize() * scale); drawList->AddText(ImVec2(15, 120) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv 2020 - {0}", &__DATE__[7]).c_str()); @@ -107,8 +105,8 @@ namespace hex::init { drawList->AddText(ImVec2(15, 140) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{0}", IMHEX_VERSION).c_str()); #endif - drawList->AddRectFilled(ImVec2(0, splashTexture.size().y - 5) * scale, ImVec2(splashTexture.size().x * this->m_progress, splashTexture.size().y) * scale, 0xFFFFFFFF); - drawList->AddText(ImVec2(15, splashTexture.size().y - 25) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("[{}] {}", "|/-\\"[ImU32(ImGui::GetTime() * 15) % 4], this->m_currTaskName).c_str()); + drawList->AddRectFilled(ImVec2(0, splashTexture.getSize().y - 5) * scale, ImVec2(splashTexture.getSize().x * this->m_progress, splashTexture.getSize().y) * scale, 0xFFFFFFFF); + drawList->AddText(ImVec2(15, splashTexture.getSize().y - 25) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("[{}] {}", "|/-\\"[ImU32(ImGui::GetTime() * 15) % 4], this->m_currTaskName).c_str()); } ImGui::Render(); diff --git a/main/source/window/window.cpp b/main/source/window/window.cpp index a3616dc65..6d1578b16 100644 --- a/main/source/window/window.cpp +++ b/main/source/window/window.cpp @@ -153,8 +153,8 @@ namespace hex { std::signal(SIGFPE, signalHandler); std::set_terminate([]{ signalHandler(SIGABRT); }); - auto imhexLogo = romfs::get("logo.png"); - this->m_logoTexture = ImGui::LoadImageFromMemory(reinterpret_cast(imhexLogo.data()), imhexLogo.size()); + auto logoData = romfs::get("logo.png"); + this->m_logoTexture = ImGui::Texture(reinterpret_cast(logoData.data()), logoData.size()); ContentRegistry::Settings::store(); EventManager::post(); diff --git a/plugins/builtin/include/content/views/view_about.hpp b/plugins/builtin/include/content/views/view_about.hpp index 86d973d60..a99b83ab9 100644 --- a/plugins/builtin/include/content/views/view_about.hpp +++ b/plugins/builtin/include/content/views/view_about.hpp @@ -14,7 +14,6 @@ namespace hex::plugin::builtin { class ViewAbout : public View { public: ViewAbout(); - ~ViewAbout() override; void drawContent() override; diff --git a/plugins/builtin/source/content/data_processor_nodes.cpp b/plugins/builtin/source/content/data_processor_nodes.cpp index 3ec72c4ab..100378a6e 100644 --- a/plugins/builtin/source/content/data_processor_nodes.cpp +++ b/plugins/builtin/source/content/data_processor_nodes.cpp @@ -960,10 +960,10 @@ namespace hex::plugin::builtin { NodeVisualizerImage() : Node("hex.builtin.nodes.visualizer.image.header", { dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "hex.builtin.nodes.common.input") }) { } void drawNode() override { - ImGui::Image(this->m_texture, scaled(ImVec2(this->m_texture.aspectRatio() * 200, 200))); + ImGui::Image(this->m_texture, scaled(ImVec2(this->m_texture.getAspectRatio() * 200, 200))); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::Image(this->m_texture, scaled(ImVec2(this->m_texture.aspectRatio() * 600, 600))); + ImGui::Image(this->m_texture, scaled(ImVec2(this->m_texture.getAspectRatio() * 600, 600))); ImGui::EndTooltip(); } } @@ -971,10 +971,7 @@ namespace hex::plugin::builtin { void process() override { auto rawData = this->getBufferOnInput(0); - if (this->m_texture.valid()) - ImGui::UnloadImage(this->m_texture); - - this->m_texture = ImGui::LoadImageFromMemory(rawData.data(), rawData.size()); + this->m_texture = ImGui::Texture(rawData.data(), rawData.size()); } private: diff --git a/plugins/builtin/source/content/views/view_about.cpp b/plugins/builtin/source/content/views/view_about.cpp index 92ac982fc..ee39a07c2 100644 --- a/plugins/builtin/source/content/views/view_about.cpp +++ b/plugins/builtin/source/content/views/view_about.cpp @@ -25,10 +25,6 @@ namespace hex::plugin::builtin { }); } - ViewAbout::~ViewAbout() { - ImGui::UnloadImage(this->m_logoTexture); - } - static void link(const std::string &name, const std::string &author, const std::string &url) { if (ImGui::BulletHyperlink(name.c_str())) hex::openWebpage(url); @@ -50,12 +46,12 @@ namespace hex::plugin::builtin { ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (!this->m_logoTexture.valid()) { + if (!this->m_logoTexture.isValid()) { auto logo = romfs::get("logo.png"); - this->m_logoTexture = ImGui::LoadImageFromMemory(reinterpret_cast(logo.data()), logo.size()); + this->m_logoTexture = ImGui::Texture(reinterpret_cast(logo.data()), logo.size()); } - ImGui::Image(this->m_logoTexture.textureId, scaled({ 64, 64 })); + ImGui::Image(this->m_logoTexture, scaled({ 64, 64 })); ImGui::TableNextColumn(); ImGui::TextFormatted("ImHex Hex Editor v{} by WerWolv - " ICON_FA_CODE_BRANCH, IMHEX_VERSION); diff --git a/plugins/builtin/source/content/welcome_screen.cpp b/plugins/builtin/source/content/welcome_screen.cpp index 7d94c3bc6..f7101817d 100644 --- a/plugins/builtin/source/content/welcome_screen.cpp +++ b/plugins/builtin/source/content/welcome_screen.cpp @@ -182,7 +182,7 @@ namespace hex::plugin::builtin { static void drawWelcomeScreenContent() { const auto availableSpace = ImGui::GetContentRegionAvail(); - ImGui::Image(s_bannerTexture, s_bannerTexture.size() / (2 * (1.0F / ImHexApi::System::getGlobalScale()))); + ImGui::Image(s_bannerTexture, s_bannerTexture.getSize() / (2 * (1.0F / ImHexApi::System::getGlobalScale()))); ImGui::Indent(); if (ImGui::BeginTable("Welcome Left", 1, ImGuiTableFlags_NoBordersInBody, ImVec2(availableSpace.x / 2, 0))) { @@ -432,14 +432,10 @@ namespace hex::plugin::builtin { }); (void)EventManager::subscribe([](u32 theme) { - auto changeTexture = [&](const std::string &path, const ImGui::Texture &texture) { + auto changeTexture = [&](const std::string &path) { auto textureData = romfs::get(path); - auto oldTexture = texture; - auto newTexture = ImGui::LoadImageFromMemory(reinterpret_cast(textureData.data()), textureData.size()); - if (oldTexture.valid()) { ImGui::UnloadImage(oldTexture); } - - return newTexture; + return ImGui::Texture(reinterpret_cast(textureData.data()), textureData.size()); }; switch (theme) { @@ -449,8 +445,8 @@ namespace hex::plugin::builtin { ImGui::StyleColorsDark(); ImGui::StyleCustomColorsDark(); ImPlot::StyleColorsDark(); - s_bannerTexture = changeTexture("banner_dark.png", s_bannerTexture); - s_backdropTexture = changeTexture("backdrop_dark.png", s_backdropTexture); + s_bannerTexture = changeTexture("banner_dark.png"); + s_backdropTexture = changeTexture("backdrop_dark.png"); break; } @@ -459,8 +455,8 @@ namespace hex::plugin::builtin { ImGui::StyleColorsLight(); ImGui::StyleCustomColorsLight(); ImPlot::StyleColorsLight(); - s_bannerTexture = changeTexture("banner_light.png", s_bannerTexture); - s_backdropTexture = changeTexture("backdrop_light.png", s_backdropTexture); + s_bannerTexture = changeTexture("banner_light.png"); + s_backdropTexture = changeTexture("backdrop_light.png"); break; } @@ -469,8 +465,8 @@ namespace hex::plugin::builtin { ImGui::StyleColorsClassic(); ImGui::StyleCustomColorsClassic(); ImPlot::StyleColorsClassic(); - s_bannerTexture = changeTexture("banner_dark.png", s_bannerTexture); - s_backdropTexture = changeTexture("backdrop_dark.png", s_backdropTexture); + s_bannerTexture = changeTexture("banner_dark.png"); + s_backdropTexture = changeTexture("backdrop_dark.png"); break; } @@ -481,7 +477,7 @@ namespace hex::plugin::builtin { ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive] = ImGui::GetStyle().Colors[ImGuiCol_MenuBarBg]; ImGui::GetStyle().Colors[ImGuiCol_TitleBgCollapsed] = ImGui::GetStyle().Colors[ImGuiCol_MenuBarBg]; - if (!s_bannerTexture.valid()) { + if (!s_bannerTexture.isValid()) { log::error("Failed to load banner texture!"); } });