Qt: Fix 100% CPU usage while downloading files

The wonders of having fast internet, you never realize when this happens
because it completes too quickly...
This commit is contained in:
Stenzek 2024-09-20 21:46:09 +10:00
parent 5f80cb1188
commit d07c7e4b68
No known key found for this signature in database
4 changed files with 58 additions and 31 deletions

View File

@ -544,11 +544,13 @@ void AutoUpdaterDialog::downloadUpdateClicked()
m_http_poll_timer->stop(); m_http_poll_timer->stop();
// Block until completion. // Block until completion.
while (m_http->HasAnyRequests()) QtUtils::ProcessEventsWithSleep(
{ QEventLoop::AllEvents,
QApplication::processEvents(QEventLoop::AllEvents, HTTP_POLL_INTERVAL); [this]() {
m_http->PollRequests(); m_http->PollRequests();
} return m_http->HasAnyRequests();
},
HTTP_POLL_INTERVAL);
if (download_result.value_or(false)) if (download_result.value_or(false))
{ {

View File

@ -281,7 +281,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
m_display_widget->setFocus(); m_display_widget->setFocus();
updateWindowState(); updateWindowState();
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
return m_display_widget->getWindowInfo(); return m_display_widget->getWindowInfo();
} }
@ -710,8 +710,7 @@ void MainWindow::quit()
if (s_system_valid) if (s_system_valid)
{ {
g_emu_thread->shutdownSystem(false, true); g_emu_thread->shutdownSystem(false, true);
while (s_system_valid) QtUtils::ProcessEventsWithSleep(QEventLoop::ExcludeUserInputEvents, []() { return s_system_valid; });
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
} }
// Big picture might still be active. // Big picture might still be active.
@ -749,9 +748,8 @@ void MainWindow::recreate()
if (was_display_created) if (was_display_created)
{ {
g_emu_thread->setSurfaceless(true); g_emu_thread->setSurfaceless(true);
while (m_display_widget || !g_emu_thread->isSurfaceless()) QtUtils::ProcessEventsWithSleep(QEventLoop::ExcludeUserInputEvents,
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); [this]() { return (m_display_widget || !g_emu_thread->isSurfaceless()); });
m_display_created = false; m_display_created = false;
} }
@ -2005,8 +2003,8 @@ void MainWindow::switchToGameListView()
// switch to surfaceless. we have to wait until the display widget is gone before we swap over. // switch to surfaceless. we have to wait until the display widget is gone before we swap over.
g_emu_thread->setSurfaceless(true); g_emu_thread->setSurfaceless(true);
while (m_display_widget) QtUtils::ProcessEventsWithSleep(QEventLoop::ExcludeUserInputEvents,
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); [this]() { return static_cast<bool>(m_display_widget); });
} }
} }
@ -2915,12 +2913,11 @@ MainWindow::SystemLock MainWindow::pauseAndLockSystem()
g_emu_thread->setFullscreen(false, false); g_emu_thread->setFullscreen(false, false);
// Container could change... thanks Wayland. // Container could change... thanks Wayland.
QtUtils::ProcessEventsWithSleep(QEventLoop::ExcludeUserInputEvents, [this]() {
QWidget* container; QWidget* container;
while (s_system_valid && return (s_system_valid &&
(g_emu_thread->isFullscreen() || !(container = getDisplayContainer()) || container->isFullScreen())) (g_emu_thread->isFullscreen() || !(container = getDisplayContainer()) || container->isFullScreen()));
{ });
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
} }
if (!was_paused) if (!was_paused)
@ -2928,8 +2925,7 @@ MainWindow::SystemLock MainWindow::pauseAndLockSystem()
g_emu_thread->setSystemPaused(true); g_emu_thread->setSystemPaused(true);
// Need to wait for the pause to go through, and make the main window visible if needed. // Need to wait for the pause to go through, and make the main window visible if needed.
while (!s_system_paused) QtUtils::ProcessEventsWithSleep(QEventLoop::ExcludeUserInputEvents, []() { return !s_system_paused; });
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
// Ensure it's visible before we try to create any dialogs parented to us. // Ensure it's visible before we try to create any dialogs parented to us.
QApplication::sync(); QApplication::sync();

View File

@ -311,11 +311,13 @@ std::optional<bool> QtHost::DownloadFile(QWidget* parent, const QString& title,
&progress); &progress);
// Block until completion. // Block until completion.
while (http->HasAnyRequests()) QtUtils::ProcessEventsWithSleep(
{ QEventLoop::AllEvents,
QApplication::processEvents(QEventLoop::AllEvents, HTTP_POLL_INTERVAL); [http = http.get()]() {
http->PollRequests(); http->PollRequests();
} return http->HasAnyRequests();
},
HTTP_POLL_INTERVAL);
return download_result; return download_result;
} }
@ -806,9 +808,8 @@ void EmuThread::stopFullscreenUI()
QMetaObject::invokeMethod(this, &EmuThread::stopFullscreenUI, Qt::QueuedConnection); QMetaObject::invokeMethod(this, &EmuThread::stopFullscreenUI, Qt::QueuedConnection);
// wait until the host display is gone // wait until the host display is gone
while (!QtHost::IsSystemValid() && g_gpu_device) QtUtils::ProcessEventsWithSleep(QEventLoop::ExcludeUserInputEvents,
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1); []() { return (!QtHost::IsSystemValid() && g_gpu_device); });
return; return;
} }
@ -1742,8 +1743,7 @@ void EmuThread::stop()
AssertMsg(!g_emu_thread->isOnThread(), "Not called on the emu thread"); AssertMsg(!g_emu_thread->isOnThread(), "Not called on the emu thread");
QMetaObject::invokeMethod(g_emu_thread, &EmuThread::stopInThread, Qt::QueuedConnection); QMetaObject::invokeMethod(g_emu_thread, &EmuThread::stopInThread, Qt::QueuedConnection);
while (g_emu_thread->isRunning()) QtUtils::ProcessEventsWithSleep(QEventLoop::ExcludeUserInputEvents, []() { return (g_emu_thread->isRunning()); });
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
} }
void EmuThread::stopInThread() void EmuThread::stopInThread()

View File

@ -8,8 +8,11 @@
#include "common/types.h" #include "common/types.h"
#include <QtCore/QByteArray> #include <QtCore/QByteArray>
#include <QtCore/QCoreApplication>
#include <QtCore/QEventLoop>
#include <QtCore/QMetaType> #include <QtCore/QMetaType>
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QTimer>
#include <QtGui/QIcon> #include <QtGui/QIcon>
#include <QtWidgets/QWidget> #include <QtWidgets/QWidget>
#include <functional> #include <functional>
@ -121,4 +124,30 @@ bool SaveWindowGeometry(std::string_view window_name, QWidget* widget, bool auto
/// Restores a window's geometry from configuration. Returns false if it was not found in the configuration. /// Restores a window's geometry from configuration. Returns false if it was not found in the configuration.
bool RestoreWindowGeometry(std::string_view window_name, QWidget* widget); bool RestoreWindowGeometry(std::string_view window_name, QWidget* widget);
/// CPU-friendly way of blocking the UI thread while some predicate holds true.
template<typename Predicate>
[[maybe_unused]] static void ProcessEventsWithSleep(QEventLoop::ProcessEventsFlags flags, const Predicate& pred,
int sleep_time_ms = 10)
{
if (sleep_time_ms == 0)
{
while (pred())
QCoreApplication::processEvents(flags);
}
if (!pred())
return;
QEventLoop loop;
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, &timer, [&loop, &pred]() {
if (pred())
return;
loop.exit();
});
timer.start(sleep_time_ms);
loop.exec(flags);
}
} // namespace QtUtils } // namespace QtUtils