From 56a249fd5a8aeb1c04ce3ad949fa5e256e8aa43b Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 27 Jul 2019 16:30:34 +0200 Subject: [PATCH] Qt: multi thread trophy loading --- rpcs3/rpcs3qt/game_list_frame.cpp | 7 +- rpcs3/rpcs3qt/save_manager_dialog.cpp | 4 +- rpcs3/rpcs3qt/trophy_manager_dialog.cpp | 92 +++++++++++-------------- rpcs3/rpcs3qt/trophy_manager_dialog.h | 31 +-------- 4 files changed, 47 insertions(+), 87 deletions(-) diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 77acc71a2c..37b9e45476 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -449,11 +449,11 @@ void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter) QSet serials; - QList indices; - for (int i = 0; i < path_list.size(); ++i) + QList indices; + for (size_t i = 0; i < path_list.size(); ++i) indices.append(i); - QtConcurrent::blockingMap(indices, [&](int& i) + QtConcurrent::blockingMap(indices, [&](size_t& i) { const std::string dir = path_list[i]; @@ -536,7 +536,6 @@ void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter) { LOG_FATAL(GENERAL, "Failed to update game list at %s\n%s thrown: %s", dir, typeid(e).name(), e.what()); return; - // Blame MSVC for double }} } }); diff --git a/rpcs3/rpcs3qt/save_manager_dialog.cpp b/rpcs3/rpcs3qt/save_manager_dialog.cpp index c18cfb9136..cb70ff6799 100644 --- a/rpcs3/rpcs3qt/save_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/save_manager_dialog.cpp @@ -245,8 +245,8 @@ void save_manager_dialog::UpdateList() } QList indices; - for (int i = 0; i < m_save_entries.size(); ++i) - indices.append(i); + for (size_t i = 0; i < m_save_entries.size(); ++i) + indices.append(static_cast(i)); QtConcurrent::blockingMap(indices, [this, currNotes](int& row) { diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp index 4c74465268..c48bb9c797 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp @@ -17,6 +17,7 @@ #include "yaml-cpp/yaml.h" #include +#include #include #include #include @@ -332,20 +333,11 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr gui_s RepaintUI(true); - StartTrophyLoadThread(); + StartTrophyLoadThreads(); } trophy_manager_dialog::~trophy_manager_dialog() { - if (m_thread_state != TrophyThreadState::CLOSED) - { - TrophyThreadState expected = TrophyThreadState::RUNNING; - m_thread_state.compare_exchange_strong(expected, TrophyThreadState::CLOSING); - while (m_thread_state != TrophyThreadState::CLOSED) - { - std::this_thread::yield(); - } - } } bool trophy_manager_dialog::LoadTrophyFolderToDB(const std::string& trop_name) @@ -652,46 +644,45 @@ void trophy_manager_dialog::ShowContextMenu(const QPoint& loc) menu->exec(globalPos); } -void trophy_manager_dialog::StartTrophyLoadThread() +void trophy_manager_dialog::StartTrophyLoadThreads() { - auto progressDialog = new QProgressDialog( - tr("Loading trophy data, please wait..."), tr("Cancel"), 0, 1, this, - Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); - progressDialog->setWindowTitle(tr("Loading trophies")); - connect(progressDialog, &QProgressDialog::canceled, [this]() - { - TrophyThreadState expected = TrophyThreadState::RUNNING; - m_thread_state.compare_exchange_strong(expected, TrophyThreadState::CLOSING); - this->close(); // It's pointless to show an empty window - }); - progressDialog->show(); + m_trophies_db.clear(); - auto trophyThread = new trophy_manager_dialog::trophy_load_thread(this); - connect(trophyThread, &QThread::finished, trophyThread, &QThread::deleteLater); - connect(trophyThread, &QThread::finished, progressDialog, &QProgressDialog::deleteLater); - connect(trophyThread, &trophy_manager_dialog::trophy_load_thread::TotalCountChanged, progressDialog, &QProgressDialog::setMaximum); - connect(trophyThread, &trophy_manager_dialog::trophy_load_thread::ProcessedCountChanged, progressDialog, &QProgressDialog::setValue); - connect(trophyThread, &trophy_manager_dialog::trophy_load_thread::FinishedSuccessfully, [this]() { RepaintUI(true); }); - m_thread_state = TrophyThreadState::RUNNING; - trophyThread->start(); -} - -void trophy_manager_dialog::trophy_load_thread::run() -{ - m_manager->m_trophies_db.clear(); - - QDir trophy_dir(qstr(vfs::get(m_manager->m_trophy_dir))); + QDir trophy_dir(qstr(vfs::get(m_trophy_dir))); const auto folder_list = trophy_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); const int count = folder_list.count(); - Q_EMIT TotalCountChanged(count); - for (int i = 0; m_manager->m_thread_state == TrophyThreadState::RUNNING && i < count; i++) + if (count <= 0) { - std::string dir_name = sstr(folder_list.value(i)); + RepaintUI(true); + return; + } + + QList indices; + for (int i = 0; i < count; ++i) + indices.append(i); + + QFutureWatcher futureWatcher; + + QProgressDialog progressDialog(tr("Loading trophy data, please wait..."), tr("Cancel"), 0, 1, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); + progressDialog.setWindowTitle(tr("Loading trophies")); + + connect(&futureWatcher, &QFutureWatcher::progressRangeChanged, &progressDialog, &QProgressDialog::setRange); + connect(&futureWatcher, &QFutureWatcher::progressValueChanged, &progressDialog, &QProgressDialog::setValue); + connect(&futureWatcher, &QFutureWatcher::finished, [this]() { RepaintUI(true); }); + connect(&progressDialog, &QProgressDialog::canceled, [this, &futureWatcher]() + { + futureWatcher.cancel(); + this->close(); // It's pointless to show an empty window + }); + + futureWatcher.setFuture(QtConcurrent::map(indices, [this, folder_list, &progressDialog](const int& i) + { + const std::string dir_name = sstr(folder_list.value(i)); LOG_TRACE(GENERAL, "Loading trophy dir: %s", dir_name); try { - m_manager->LoadTrophyFolderToDB(dir_name); + LoadTrophyFolderToDB(dir_name); } catch (const std::exception& e) { @@ -699,15 +690,11 @@ void trophy_manager_dialog::trophy_load_thread::run() // Also add a way of showing the number of corrupted/invalid folders in UI somewhere. LOG_ERROR(GENERAL, "Exception occurred while parsing folder %s for trophies: %s", dir_name, e.what()); } - Q_EMIT ProcessedCountChanged(i + 1); - } + })); - if (m_manager->m_thread_state == TrophyThreadState::RUNNING) - { - Q_EMIT FinishedSuccessfully(); - } + progressDialog.exec(); - m_manager->m_thread_state = TrophyThreadState::CLOSED; + futureWatcher.waitForFinished(); } void trophy_manager_dialog::PopulateGameTable() @@ -721,15 +708,16 @@ void trophy_manager_dialog::PopulateGameTable() QList names; QList indices; - for (int i = 0; i < m_trophies_db.size(); ++i) + for (size_t i = 0; i < m_trophies_db.size(); ++i) { + const int index = static_cast(i); const QString name = qstr(m_trophies_db[i]->game_name).simplified(); - m_game_combo->addItem(name, i); + m_game_combo->addItem(name, index); names.append(name); - indices.append(i); + indices.append(index); } - QtConcurrent::blockingMap(indices, [this, names](int& i) + QtConcurrent::blockingMap(indices, [this, &names](int& i) { const int all_trophies = m_trophies_db[i]->trop_usr->GetTrophiesCount(); const int unlocked_trophies = m_trophies_db[i]->trop_usr->GetUnlockedTrophiesCount(); diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.h b/rpcs3/rpcs3qt/trophy_manager_dialog.h index e8be03bb7b..dce1df1b0a 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.h +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.h @@ -45,13 +45,6 @@ enum GameColumns GameColumnsCount }; -enum TrophyThreadState -{ - RUNNING, - CLOSING, - CLOSED -}; - class trophy_manager_dialog : public QWidget { Q_OBJECT @@ -82,8 +75,8 @@ private: */ bool LoadTrophyFolderToDB(const std::string& trop_name); - /** Populate the trophy database (in another thread). */ - void StartTrophyLoadThread(); + /** Populate the trophy database (multithreaded). */ + void StartTrophyLoadThreads(); /** Fills game table with information. Takes results from LoadTrophyFolderToDB and puts it into the UI. @@ -110,9 +103,6 @@ private: QTableWidget* m_trophy_table; //! UI element to display trophy stuff. QTableWidget* m_game_table; //! UI element to display games. - class trophy_load_thread; //Qt cannot parse nested classes, declaration is below - std::atomic m_thread_state = TrophyThreadState::CLOSED; - bool m_show_hidden_trophies = false; bool m_show_unlocked_trophies = true; bool m_show_locked_trophies = true; @@ -132,20 +122,3 @@ private: QSlider* m_game_icon_slider = nullptr; QColor m_game_icon_color; }; - -class trophy_manager_dialog::trophy_load_thread : public QThread -{ - Q_OBJECT - - public: - explicit trophy_load_thread(trophy_manager_dialog *manager) : m_manager(manager) {} - void run() override; - - Q_SIGNALS: - void TotalCountChanged(int count); - void ProcessedCountChanged(int processed); - void FinishedSuccessfully(); - - private: - trophy_manager_dialog *m_manager; -};