From c07842d7cacccdd4d995a0f56e516bdc41071906 Mon Sep 17 00:00:00 2001 From: iTrooz Date: Thu, 7 Sep 2023 20:33:49 +0200 Subject: [PATCH] refactor: separate loop and frame logic (#1300) --- main/include/init/splash_window.hpp | 18 ++ main/include/window.hpp | 1 + main/source/init/splash_window.cpp | 265 ++++++++++++++-------------- main/source/main.cpp | 2 + main/source/window/window.cpp | 18 +- 5 files changed, 167 insertions(+), 137 deletions(-) diff --git a/main/include/init/splash_window.hpp b/main/include/init/splash_window.hpp index 82880ca03..feb55a4c9 100644 --- a/main/include/init/splash_window.hpp +++ b/main/include/init/splash_window.hpp @@ -12,6 +12,14 @@ namespace hex::init { using TaskFunction = std::function; + enum FrameResult{ success, failure, wait }; + + struct Highlight { + ImVec2 start; + size_t count; + ImColor color; + }; + class WindowSplash { public: WindowSplash(); @@ -19,6 +27,9 @@ namespace hex::init { bool loop(); + FrameResult fullFrame(); + void startStartupTasks(); + void addStartupTask(const std::string &taskName, const TaskFunction &task, bool async) { this->m_tasks.emplace_back(taskName, task, async); } @@ -31,6 +42,7 @@ namespace hex::init { void initGLFW(); void initImGui(); + void initMyself(); void exitGLFW(); void exitImGui(); @@ -40,6 +52,12 @@ namespace hex::init { std::vector> m_tasks; std::string m_gpuVendor; + + ImGui::Texture splashBackgroundTexture; + ImGui::Texture splashTextTexture; + std::future tasksSucceeded; + std::array highlights; + float progressLerp = 0.0F; }; } diff --git a/main/include/window.hpp b/main/include/window.hpp index f845d8967..de061d56d 100644 --- a/main/include/window.hpp +++ b/main/include/window.hpp @@ -24,6 +24,7 @@ namespace hex { ~Window(); void loop(); + void fullFrame(); static void initNative(); diff --git a/main/source/init/splash_window.cpp b/main/source/init/splash_window.cpp index 43db8ea55..ed1554b07 100644 --- a/main/source/init/splash_window.cpp +++ b/main/source/init/splash_window.cpp @@ -43,6 +43,7 @@ namespace hex::init { WindowSplash::WindowSplash() : m_window(nullptr) { this->initGLFW(); this->initImGui(); + this->initMyself(); ImHexApi::System::impl::setGPUVendor(reinterpret_cast(glGetString(GL_VENDOR))); } @@ -111,165 +112,122 @@ namespace hex::init { }); } - bool WindowSplash::loop() { - // Load splash screen image from romfs - ImGui::Texture splashBackgroundTexture = ImGui::Texture(romfs::get("splash_background.png").span()); - ImGui::Texture splashTextTexture = ImGui::Texture(romfs::get("splash_text.png").span()); + FrameResult WindowSplash::fullFrame() { + glfwPollEvents(); - // If the image couldn't be loaded correctly, something went wrong during the build process - // Close the application since this would lead to errors later on anyway. - if (!splashBackgroundTexture.isValid() || !splashTextTexture.isValid()) { - log::fatal("Could not load splash screen image!"); - std::exit(EXIT_FAILURE); - } - - // Launch init tasks in background - auto tasksSucceeded = processTasksAsync(); + // Start a new ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); auto scale = ImHexApi::System::getGlobalScale(); - struct Highlight { - ImVec2 start; - size_t count; - ImColor color; - }; + // Draw the splash screen background + auto drawList = ImGui::GetBackgroundDrawList(); + { - std::array highlights; - - std::mt19937 rng(std::random_device{}()); - - u32 lastPos = 0; - u32 lastCount = 0; - for (auto &highlight : highlights) { - auto newPos = lastPos + lastCount + (rng() % 40); - auto newCount = (rng() % 7) + 3; - highlight.start.x = newPos % 13; - highlight.start.y = newPos / 13; - highlight.count = newCount; + drawList->AddImage(this->splashBackgroundTexture, ImVec2(0, 0), this->splashBackgroundTexture.getSize() * scale); { - float r, g, b; - ImGui::ColorConvertHSVtoRGB( - (rng() % 360) / 100.0F, - (25 + rng() % 70) / 100.0F, - (85 + rng() % 10) / 100.0F, - r, g, b); - highlight.color = ImColor(r, g, b, 0x50 / 255.0F); - } + const auto highlightBytes = [&](ImVec2 start, size_t count, ImColor color, float opacity) { + const auto hexSize = ImVec2(29, 18) * scale; + const auto hexSpacing = ImVec2(17.4, 15) * scale; + const auto hexStart = ImVec2(27, 127) * scale; - lastPos = newPos; - lastCount = newCount; - } + const auto hexCount = ImVec2(13, 7); - // Splash window rendering loop + bool isStart = true; - float progressLerp = 0.0F; - while (!glfwWindowShouldClose(this->m_window)) { - glfwPollEvents(); + color.Value.w *= opacity; - // Start a new ImGui frame - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); + for (u32 y = u32(start.y); y < u32(hexCount.y); y += 1) { + for (u32 x = u32(start.x); x < u32(hexCount.x); x += 1) { + if (count-- == 0) + return; - // Draw the splash screen background - auto drawList = ImGui::GetBackgroundDrawList(); - { + auto pos = hexStart + ImVec2(float(x), float(y)) * (hexSize + hexSpacing); - drawList->AddImage(splashBackgroundTexture, ImVec2(0, 0), splashBackgroundTexture.getSize() * scale); + drawList->AddRectFilled(pos + ImVec2(0, -hexSpacing.y / 2), pos + hexSize + ImVec2(0, hexSpacing.y / 2), color); - { - const auto highlightBytes = [&](ImVec2 start, size_t count, ImColor color, float opacity) { - const auto hexSize = ImVec2(29, 18) * scale; - const auto hexSpacing = ImVec2(17.4, 15) * scale; - const auto hexStart = ImVec2(27, 127) * scale; + if (count > 0 && x != u32(hexCount.x) - 1) + drawList->AddRectFilled(pos + ImVec2(hexSize.x, -hexSpacing.y / 2), pos + hexSize + ImVec2(hexSpacing.x, hexSpacing.y / 2), color); - const auto hexCount = ImVec2(13, 7); - - bool isStart = true; - - color.Value.w *= opacity; - - for (u32 y = u32(start.y); y < u32(hexCount.y); y += 1) { - for (u32 x = u32(start.x); x < u32(hexCount.x); x += 1) { - if (count-- == 0) - return; - - auto pos = hexStart + ImVec2(float(x), float(y)) * (hexSize + hexSpacing); - - drawList->AddRectFilled(pos + ImVec2(0, -hexSpacing.y / 2), pos + hexSize + ImVec2(0, hexSpacing.y / 2), color); - - if (count > 0 && x != u32(hexCount.x) - 1) - drawList->AddRectFilled(pos + ImVec2(hexSize.x, -hexSpacing.y / 2), pos + hexSize + ImVec2(hexSpacing.x, hexSpacing.y / 2), color); - - if (isStart) { - isStart = false; - drawList->AddRectFilled(pos - hexSpacing / 2, pos + ImVec2(0, hexSize.y + hexSpacing.y / 2), color); - } - if (count == 0) { - drawList->AddRectFilled(pos + ImVec2(hexSize.x, -hexSpacing.y / 2), pos + hexSize + hexSpacing / 2, color); - } + if (isStart) { + isStart = false; + drawList->AddRectFilled(pos - hexSpacing / 2, pos + ImVec2(0, hexSize.y + hexSpacing.y / 2), color); + } + if (count == 0) { + drawList->AddRectFilled(pos + ImVec2(hexSize.x, -hexSpacing.y / 2), pos + hexSize + hexSpacing / 2, color); } - - start.x = 0; } - }; - for (const auto &highlight : highlights) - highlightBytes(highlight.start, highlight.count, highlight.color, progressLerp); - } + start.x = 0; + } + }; - progressLerp += (this->m_progress - progressLerp) * 0.1F; - - drawList->AddImage(splashTextTexture, ImVec2(0, 0), splashTextTexture.getSize() * scale); - - drawList->AddText(ImVec2(35, 85) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv\n2020 - {0}", &__DATE__[7]).c_str()); - - #if defined(DEBUG) - const static auto VersionInfo = hex::format("{0} : {1} {2}@{3}", ImHexApi::System::getImHexVersion(), ICON_FA_CODE_BRANCH, ImHexApi::System::getCommitBranch(), ImHexApi::System::getCommitHash()); - #else - const static auto VersionInfo = hex::format("{0}", ImHexApi::System::getImHexVersion()); - #endif - - drawList->AddText(ImVec2((splashBackgroundTexture.getSize().x * scale - ImGui::CalcTextSize(VersionInfo.c_str()).x) / 2, 105 * scale), ImColor(0xFF, 0xFF, 0xFF, 0xFF), VersionInfo.c_str()); + for (const auto &highlight : this->highlights) + highlightBytes(highlight.start, highlight.count, highlight.color, this->progressLerp); } - // Draw the task progress bar - { - std::lock_guard guard(this->m_progressMutex); + this->progressLerp += (this->m_progress - this->progressLerp) * 0.1F; - const auto progressBackgroundStart = ImVec2(99, 357) * scale; - const auto progressBackgroundSize = ImVec2(442, 30) * scale; + drawList->AddImage(this->splashTextTexture, ImVec2(0, 0), this->splashTextTexture.getSize() * scale); - const auto progressStart = progressBackgroundStart + ImVec2(0, 20) * scale; - const auto progressSize = ImVec2(progressBackgroundSize.x * this->m_progress, 10 * scale); - drawList->AddRectFilled(progressStart, progressStart + progressSize, 0xD0FFFFFF); + drawList->AddText(ImVec2(35, 85) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv\n2020 - {0}", &__DATE__[7]).c_str()); - if (!this->m_currTaskNames.empty()) { - drawList->PushClipRect(progressBackgroundStart, progressBackgroundStart + progressBackgroundSize, true); - drawList->AddText(progressStart + ImVec2(5, -20) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{}", fmt::join(this->m_currTaskNames, " | ")).c_str()); - drawList->PopClipRect(); - } - } + #if defined(DEBUG) + const static auto VersionInfo = hex::format("{0} : {1} {2}@{3}", ImHexApi::System::getImHexVersion(), ICON_FA_CODE_BRANCH, ImHexApi::System::getCommitBranch(), ImHexApi::System::getCommitHash()); + #else + const static auto VersionInfo = hex::format("{0}", ImHexApi::System::getImHexVersion()); + #endif - // Render the frame - ImGui::Render(); - int displayWidth, displayHeight; - glfwGetFramebufferSize(this->m_window, &displayWidth, &displayHeight); - glViewport(0, 0, displayWidth, displayHeight); - glClearColor(0.00F, 0.00F, 0.00F, 0.00F); - glClear(GL_COLOR_BUFFER_BIT); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + drawList->AddText(ImVec2((this->splashBackgroundTexture.getSize().x * scale - ImGui::CalcTextSize(VersionInfo.c_str()).x) / 2, 105 * scale), ImColor(0xFF, 0xFF, 0xFF, 0xFF), VersionInfo.c_str()); + } - glfwSwapBuffers(this->m_window); + // Draw the task progress bar + { + std::lock_guard guard(this->m_progressMutex); - // Check if all background tasks have finished so the splash screen can be closed - if (tasksSucceeded.wait_for(0s) == std::future_status::ready) { - return tasksSucceeded.get(); + const auto progressBackgroundStart = ImVec2(99, 357) * scale; + const auto progressBackgroundSize = ImVec2(442, 30) * scale; + + const auto progressStart = progressBackgroundStart + ImVec2(0, 20) * scale; + const auto progressSize = ImVec2(progressBackgroundSize.x * this->m_progress, 10 * scale); + drawList->AddRectFilled(progressStart, progressStart + progressSize, 0xD0FFFFFF); + + if (!this->m_currTaskNames.empty()) { + drawList->PushClipRect(progressBackgroundStart, progressBackgroundStart + progressBackgroundSize, true); + drawList->AddText(progressStart + ImVec2(5, -20) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{}", fmt::join(this->m_currTaskNames, " | ")).c_str()); + drawList->PopClipRect(); } } - return false; + // Render the frame + ImGui::Render(); + int displayWidth, displayHeight; + glfwGetFramebufferSize(this->m_window, &displayWidth, &displayHeight); + glViewport(0, 0, displayWidth, displayHeight); + glClearColor(0.00F, 0.00F, 0.00F, 0.00F); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + glfwSwapBuffers(this->m_window); + + // Check if all background tasks have finished so the splash screen can be closed + if (this->tasksSucceeded.wait_for(0s) == std::future_status::ready) { + return this->tasksSucceeded.get() ? FrameResult::success : FrameResult::failure; + } + + return FrameResult::wait; + } + + bool WindowSplash::loop() { + // Splash window rendering loop + while (true) { + auto res = this->fullFrame(); + if (res == FrameResult::success) return true; + else if (res == FrameResult::failure) return false; + } } static void centerWindow(GLFWwindow *window) { @@ -414,6 +372,55 @@ namespace hex::init { io.IniFilename = nullptr; } + /** + * @brief Initialize resources for the splash window + */ + void WindowSplash::initMyself() { + + // Load splash screen image from romfs + this->splashBackgroundTexture = ImGui::Texture(romfs::get("splash_background.png").span()); + this->splashTextTexture = ImGui::Texture(romfs::get("splash_text.png").span()); + + // If the image couldn't be loaded correctly, something went wrong during the build process + // Close the application since this would lead to errors later on anyway. + if (!this->splashBackgroundTexture.isValid() || !this->splashTextTexture.isValid()) { + log::fatal("Could not load splash screen image!"); + std::exit(EXIT_FAILURE); + } + + + + std::mt19937 rng(std::random_device{}()); + + u32 lastPos = 0; + u32 lastCount = 0; + for (auto &highlight : this->highlights) { + auto newPos = lastPos + lastCount + (rng() % 40); + auto newCount = (rng() % 7) + 3; + highlight.start.x = newPos % 13; + highlight.start.y = newPos / 13; + highlight.count = newCount; + + { + float r, g, b; + ImGui::ColorConvertHSVtoRGB( + (rng() % 360) / 100.0F, + (25 + rng() % 70) / 100.0F, + (85 + rng() % 10) / 100.0F, + r, g, b); + highlight.color = ImColor(r, g, b, 0x50 / 255.0F); + } + + lastPos = newPos; + lastCount = newCount; + } + } + + void WindowSplash::startStartupTasks() { + // Launch init tasks in background + this->tasksSucceeded = processTasksAsync(); + } + void WindowSplash::exitGLFW() { glfwDestroyWindow(this->m_window); glfwTerminate(); diff --git a/main/source/main.cpp b/main/source/main.cpp index 26ebc4599..3f32e35c2 100644 --- a/main/source/main.cpp +++ b/main/source/main.cpp @@ -76,6 +76,8 @@ namespace { for (const auto &[name, task, async] : init::getInitTasks()) splashWindow.addStartupTask(name, task, async); + splashWindow.startStartupTasks(); + // Draw the splash window while tasks are running if (!splashWindow.loop()) ImHexApi::System::getInitArguments().insert({ "tasks-failed", {} }); diff --git a/main/source/window/window.cpp b/main/source/window/window.cpp index 6b92fe75f..193c163f7 100644 --- a/main/source/window/window.cpp +++ b/main/source/window/window.cpp @@ -155,6 +155,15 @@ namespace hex { }); } + void Window::fullFrame() { + glfwPollEvents(); + + // Render frame + this->frameBegin(); + this->frame(); + this->frameEnd(); + } + void Window::loop() { while (!glfwWindowShouldClose(this->m_window)) { this->m_lastFrameTime = glfwGetTime(); @@ -163,8 +172,6 @@ namespace hex { // If the application is minimized or not visible, don't render anything glfwWaitEvents(); } else { - glfwPollEvents(); - // If no events have been received in a while, lower the frame rate { // If the mouse is down, the mouse is moving or a popup is open, we don't want to lower the frame rate @@ -197,12 +204,7 @@ namespace hex { } } - // Render frame - this->frameBegin(); - this->frame(); - this->frameEnd(); - - glfwSwapInterval(0); + this->fullFrame(); // Limit frame rate // If the target FPS are below 15, use the monitor refresh rate, if it's above 200, don't limit the frame rate