diff --git a/main/gui/include/window.hpp b/main/gui/include/window.hpp index 288b29c9f..48a5b82c0 100644 --- a/main/gui/include/window.hpp +++ b/main/gui/include/window.hpp @@ -53,6 +53,9 @@ namespace hex { void drawImGui(); void drawWithShader(); + void unlockFrameRate(); + void forceNewFrame(); + GLFWwindow *m_window = nullptr; std::string m_windowTitle, m_windowTitleFull; @@ -64,17 +67,22 @@ namespace hex { std::list m_popupsToOpen; std::set m_pressedKeys; - std::atomic m_unlockFrameRate = true; - ImGuiExt::ImHexCustomData m_imguiCustomData; u32 m_searchBarPosition = 0; bool m_emergencyPopupOpen = false; std::jthread m_frameRateThread; + std::chrono::duration m_remainingUnlockedTime; + + std::mutex m_sleepMutex; std::atomic m_sleepFlag; std::condition_variable m_sleepCondVar; - std::mutex m_sleepMutex; + + std::mutex m_wakeupMutex; + std::atomic m_wakeupFlag; + std::condition_variable m_wakeupCondVar; + gl::Shader m_postProcessingShader; }; diff --git a/main/gui/source/window/win_window.cpp b/main/gui/source/window/win_window.cpp index f8ff04a52..a4c1bbec1 100644 --- a/main/gui/source/window/win_window.cpp +++ b/main/gui/source/window/win_window.cpp @@ -618,7 +618,7 @@ namespace hex { glfwSetFramebufferSizeCallback(m_window, [](GLFWwindow* window, int width, int height) { auto *win = static_cast(glfwGetWindowUserPointer(window)); - win->m_unlockFrameRate = true; + win->unlockFrameRate(); glViewport(0, 0, width, height); ImHexApi::System::impl::setMainWindowSize(width, height); diff --git a/main/gui/source/window/window.cpp b/main/gui/source/window/window.cpp index f804bfd07..558de0e84 100644 --- a/main/gui/source/window/window.cpp +++ b/main/gui/source/window/window.cpp @@ -311,7 +311,7 @@ namespace hex { // Unlock frame rate if any mouse button is being held down to allow drag scrolling to be smooth if (ImGui::IsAnyMouseDown()) - m_unlockFrameRate = true; + this->unlockFrameRate(); // Unlock frame rate if any modifier key is held down since they don't generate key repeat events if ( @@ -320,12 +320,12 @@ namespace hex { ImGui::IsKeyPressed(ImGuiKey_LeftSuper) || ImGui::IsKeyPressed(ImGuiKey_RightSuper) || ImGui::IsKeyPressed(ImGuiKey_LeftAlt) || ImGui::IsKeyPressed(ImGuiKey_RightAlt) ) { - m_unlockFrameRate = true; + this->unlockFrameRate(); } // Unlock frame rate if there's more than one viewport since these don't call the glfw callbacks registered here if (ImGui::GetPlatformIO().Viewports.size() > 1) - m_unlockFrameRate = true; + this->unlockFrameRate(); } // Hide the window as soon as the render loop exits to make the window @@ -929,6 +929,23 @@ namespace hex { #endif } + void Window::unlockFrameRate() { + { + std::scoped_lock lock(m_wakeupMutex); + m_remainingUnlockedTime = std::chrono::seconds(2); + } + + this->forceNewFrame(); + } + + void Window::forceNewFrame() { + std::scoped_lock lock(m_wakeupMutex); + m_wakeupFlag = true; + m_wakeupCondVar.notify_all(); + } + + + void Window::initGLFW() { auto initialWindowProperties = ImHexApi::System::getInitialWindowProperties(); glfwSetErrorCallback([](int error, const char *desc) { @@ -1029,7 +1046,7 @@ namespace hex { if (win == nullptr) return; - win->m_unlockFrameRate = true; + win->unlockFrameRate(); }; static const auto isMainWindow = [](GLFWwindow *window) { @@ -1165,7 +1182,7 @@ namespace hex { Duration passedTime = {}; std::chrono::steady_clock::time_point startTime = {}, endTime = {}; - Duration requestedFrameTime = {}, remainingUnlockedTime = {}; + Duration requestedFrameTime = {}; float targetFps = 0; const auto nativeFps = []() -> float { @@ -1184,32 +1201,36 @@ namespace hex { targetFps = ImHexApi::System::getTargetFPS(); - if (m_unlockFrameRate.exchange(false)) { - remainingUnlockedTime = std::chrono::seconds(2); - } - // If the target frame rate is below 15, use the current monitor's refresh rate if (targetFps < 15) { targetFps = nativeFps; } passedTime += iterationTime; - if (remainingUnlockedTime > std::chrono::nanoseconds(0)) { - remainingUnlockedTime -= iterationTime; - } else { - targetFps = 5; - } - - requestedFrameTime = (Duration(1.0E9) / targetFps) / 1.3; - if (passedTime >= requestedFrameTime) { + { std::scoped_lock lock(m_sleepMutex); - m_sleepFlag = true; - m_sleepCondVar.notify_all(); - passedTime = {}; + if (m_remainingUnlockedTime > std::chrono::nanoseconds(0)) { + m_remainingUnlockedTime -= iterationTime; + } else { + targetFps = 0.01; + } + + requestedFrameTime = (Duration(1.0E9) / targetFps) / 1.3; + if (passedTime >= requestedFrameTime) { + m_sleepFlag = true; + m_sleepCondVar.notify_all(); + + passedTime = {}; + } } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + { + std::unique_lock lock(m_wakeupMutex); + m_wakeupCondVar.wait_for(lock, requestedFrameTime, [&] { + return m_wakeupFlag || stopToken.stop_requested(); + }); + } endTime = std::chrono::steady_clock::now(); }