Improved responsiveness when refreshing game list.

This commit is contained in:
Christian Aguilera 2020-09-27 04:01:54 +02:00
parent 4ca92464c0
commit ee13e6ec80
9 changed files with 86 additions and 28 deletions

View File

@ -203,9 +203,13 @@ void GameList::UpdateColumnVisibility()
void GameList::MakeEmptyView() void GameList::MakeEmptyView()
{ {
const QString refreshing_msg = tr("Refreshing...");
const QString empty_msg = tr("Dolphin could not find any GameCube/Wii ISOs or WADs.\n"
"Double-click here to set a games directory...");
m_empty = new QLabel(this); m_empty = new QLabel(this);
m_empty->setText(tr("Dolphin could not find any GameCube/Wii ISOs or WADs.\n" m_empty->setText(refreshing_msg);
"Double-click here to set a games directory...")); m_empty->setEnabled(false);
m_empty->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_empty->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
auto event_filter = new DoubleClickEventFilter{m_empty}; auto event_filter = new DoubleClickEventFilter{m_empty};
@ -216,6 +220,21 @@ void GameList::MakeEmptyView()
if (!dir.isEmpty()) if (!dir.isEmpty())
Settings::Instance().AddPath(dir); Settings::Instance().AddPath(dir);
}); });
QSizePolicy size_policy{m_empty->sizePolicy()};
size_policy.setRetainSizeWhenHidden(true);
m_empty->setSizePolicy(size_policy);
connect(&Settings::Instance(), &Settings::GameListRefreshRequested, this,
[this, refreshing_msg = refreshing_msg] {
m_empty->setText(refreshing_msg);
m_empty->setEnabled(false);
});
connect(&Settings::Instance(), &Settings::GameListRefreshCompleted, this,
[this, empty_msg = empty_msg] {
m_empty->setText(empty_msg);
m_empty->setEnabled(true);
});
} }
void GameList::resizeEvent(QResizeEvent* event) void GameList::resizeEvent(QResizeEvent* event)

View File

@ -35,7 +35,10 @@ GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
qRegisterMetaType<std::shared_ptr<const UICommon::GameFile>>(); qRegisterMetaType<std::shared_ptr<const UICommon::GameFile>>();
qRegisterMetaType<std::string>(); qRegisterMetaType<std::string>();
connect(qApp, &QApplication::aboutToQuit, this, [this] { m_load_thread.Cancel(); }); connect(qApp, &QApplication::aboutToQuit, this, [this] {
m_processing_halted = true;
m_load_thread.Cancel();
});
connect(this, &QFileSystemWatcher::directoryChanged, this, &GameTracker::UpdateDirectory); connect(this, &QFileSystemWatcher::directoryChanged, this, &GameTracker::UpdateDirectory);
connect(this, &QFileSystemWatcher::fileChanged, this, &GameTracker::UpdateFile); connect(this, &QFileSystemWatcher::fileChanged, this, &GameTracker::UpdateFile);
connect(&Settings::Instance(), &Settings::AutoRefreshToggled, this, [] { connect(&Settings::Instance(), &Settings::AutoRefreshToggled, this, [] {
@ -75,27 +78,24 @@ GameTracker::GameTracker(QObject* parent) : QFileSystemWatcher(parent)
break; break;
case CommandType::UpdateMetadata: case CommandType::UpdateMetadata:
m_cache.UpdateAdditionalMetadata( m_cache.UpdateAdditionalMetadata(
[this](const std::shared_ptr<const UICommon::GameFile>& game) { [this](const std::shared_ptr<const UICommon::GameFile>& game) { emit GameUpdated(game); },
emit GameUpdated(game); m_processing_halted);
});
QueueOnObject(this, [] { Settings::Instance().NotifyMetadataRefreshComplete(); }); QueueOnObject(this, [] { Settings::Instance().NotifyMetadataRefreshComplete(); });
break; break;
case CommandType::ResumeProcessing:
m_processing_halted = false;
break;
case CommandType::PurgeCache: case CommandType::PurgeCache:
m_cache.Clear(UICommon::GameFileCache::DeleteOnDisk::Yes); m_cache.Clear(UICommon::GameFileCache::DeleteOnDisk::Yes);
break; break;
case CommandType::BeginRefresh: case CommandType::BeginRefresh:
if (m_busy_count++ == 0) QueueOnObject(this, [] { Settings::Instance().NotifyRefreshGameListStarted(); });
{ for (auto& file : m_tracked_files.keys())
for (auto& file : m_tracked_files.keys()) emit GameRemoved(file.toStdString());
emit GameRemoved(file.toStdString()); m_tracked_files.clear();
m_tracked_files.clear();
}
break; break;
case CommandType::EndRefresh: case CommandType::EndRefresh:
if (--m_busy_count == 0) QueueOnObject(this, [] { Settings::Instance().NotifyRefreshGameListComplete(); });
{
QueueOnObject(this, [] { Settings::Instance().NotifyRefreshGameListComplete(); });
}
break; break;
} }
}); });
@ -135,6 +135,8 @@ void GameTracker::StartInternal()
m_started = true; m_started = true;
QueueOnObject(this, [] { Settings::Instance().NotifyRefreshGameListStarted(); });
std::vector<std::string> paths; std::vector<std::string> paths;
paths.reserve(m_tracked_files.size()); paths.reserve(m_tracked_files.size());
for (const QString& path : m_tracked_files.keys()) for (const QString& path : m_tracked_files.keys())
@ -150,8 +152,9 @@ void GameTracker::StartInternal()
m_initial_games_emitted_event.Wait(); m_initial_games_emitted_event.Wait();
bool cache_updated = m_cache.Update(paths, emit_game_loaded, emit_game_removed); bool cache_updated =
cache_updated |= m_cache.UpdateAdditionalMetadata(emit_game_updated); m_cache.Update(paths, emit_game_loaded, emit_game_removed, m_processing_halted);
cache_updated |= m_cache.UpdateAdditionalMetadata(emit_game_updated, m_processing_halted);
if (cache_updated) if (cache_updated)
m_cache.Save(); m_cache.Save();
@ -196,6 +199,16 @@ void GameTracker::RemoveDirectory(const QString& dir)
void GameTracker::RefreshAll() void GameTracker::RefreshAll()
{ {
m_processing_halted = true;
m_load_thread.Clear();
m_load_thread.EmplaceItem(Command{CommandType::ResumeProcessing, {}});
if (m_needs_purge)
{
m_load_thread.EmplaceItem(Command{CommandType::PurgeCache, {}});
m_needs_purge = false;
}
m_load_thread.EmplaceItem(Command{CommandType::BeginRefresh}); m_load_thread.EmplaceItem(Command{CommandType::BeginRefresh});
for (const QString& dir : Settings::Instance().GetPaths()) for (const QString& dir : Settings::Instance().GetPaths())
@ -257,7 +270,7 @@ void GameTracker::RemoveDirectoryInternal(const QString& dir)
void GameTracker::UpdateDirectoryInternal(const QString& dir) void GameTracker::UpdateDirectoryInternal(const QString& dir)
{ {
auto it = GetIterator(dir); auto it = GetIterator(dir);
while (it->hasNext() && !m_load_thread.IsCancelled()) while (it->hasNext() && !m_processing_halted)
{ {
QString path = QFileInfo(it->next()).canonicalFilePath(); QString path = QFileInfo(it->next()).canonicalFilePath();
@ -277,6 +290,9 @@ void GameTracker::UpdateDirectoryInternal(const QString& dir)
for (const auto& missing : FindMissingFiles(dir)) for (const auto& missing : FindMissingFiles(dir))
{ {
if (m_processing_halted)
break;
auto& tracked_file = m_tracked_files[missing]; auto& tracked_file = m_tracked_files[missing];
tracked_file.remove(dir); tracked_file.remove(dir);
@ -347,6 +363,6 @@ void GameTracker::LoadGame(const QString& path)
void GameTracker::PurgeCache() void GameTracker::PurgeCache()
{ {
m_load_thread.EmplaceItem(Command{CommandType::PurgeCache, {}}); m_needs_purge = true;
Settings::Instance().RefreshGameList(); Settings::Instance().RefreshGameList();
} }

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <atomic>
#include <memory> #include <memory>
#include <string> #include <string>
@ -72,6 +73,7 @@ private:
UpdateDirectory, UpdateDirectory,
UpdateFile, UpdateFile,
UpdateMetadata, UpdateMetadata,
ResumeProcessing,
PurgeCache, PurgeCache,
BeginRefresh, BeginRefresh,
EndRefresh, EndRefresh,
@ -92,8 +94,8 @@ private:
Common::Event m_initial_games_emitted_event; Common::Event m_initial_games_emitted_event;
bool m_initial_games_emitted = false; bool m_initial_games_emitted = false;
bool m_started = false; bool m_started = false;
// Count of currently running refresh jobs bool m_needs_purge = false;
u32 m_busy_count = 0; std::atomic_bool m_processing_halted = false;
}; };
Q_DECLARE_METATYPE(std::shared_ptr<const UICommon::GameFile>) Q_DECLARE_METATYPE(std::shared_ptr<const UICommon::GameFile>)

View File

@ -511,7 +511,7 @@ void MenuBar::AddViewMenu()
purge_action->setEnabled(false); purge_action->setEnabled(false);
connect(&Settings::Instance(), &Settings::GameListRefreshRequested, purge_action, connect(&Settings::Instance(), &Settings::GameListRefreshRequested, purge_action,
[purge_action] { purge_action->setEnabled(false); }); [purge_action] { purge_action->setEnabled(false); });
connect(&Settings::Instance(), &Settings::GameListRefreshCompleted, purge_action, connect(&Settings::Instance(), &Settings::GameListRefreshStarted, purge_action,
[purge_action] { purge_action->setEnabled(true); }); [purge_action] { purge_action->setEnabled(true); });
view_menu->addSeparator(); view_menu->addSeparator();
view_menu->addAction(tr("Search"), this, &MenuBar::ShowSearch, QKeySequence::Find); view_menu->addAction(tr("Search"), this, &MenuBar::ShowSearch, QKeySequence::Find);

View File

@ -147,6 +147,11 @@ void Settings::RefreshGameList()
emit GameListRefreshRequested(); emit GameListRefreshRequested();
} }
void Settings::NotifyRefreshGameListStarted()
{
emit GameListRefreshStarted();
}
void Settings::NotifyRefreshGameListComplete() void Settings::NotifyRefreshGameListComplete()
{ {
emit GameListRefreshCompleted(); emit GameListRefreshCompleted();

View File

@ -72,6 +72,7 @@ public:
QString GetDefaultGame() const; QString GetDefaultGame() const;
void SetDefaultGame(QString path); void SetDefaultGame(QString path);
void RefreshGameList(); void RefreshGameList();
void NotifyRefreshGameListStarted();
void NotifyRefreshGameListComplete(); void NotifyRefreshGameListComplete();
void RefreshMetadata(); void RefreshMetadata();
void NotifyMetadataRefreshComplete(); void NotifyMetadataRefreshComplete();
@ -150,6 +151,7 @@ signals:
void PathRemoved(const QString&); void PathRemoved(const QString&);
void DefaultGameChanged(const QString&); void DefaultGameChanged(const QString&);
void GameListRefreshRequested(); void GameListRefreshRequested();
void GameListRefreshStarted();
void GameListRefreshCompleted(); void GameListRefreshCompleted();
void TitleDBReloadRequested(); void TitleDBReloadRequested();
void MetadataRefreshRequested(); void MetadataRefreshRequested();

View File

@ -48,7 +48,7 @@ ToolBar::ToolBar(QWidget* parent) : QToolBar(parent)
connect(&Settings::Instance(), &Settings::GameListRefreshRequested, this, connect(&Settings::Instance(), &Settings::GameListRefreshRequested, this,
[this] { m_refresh_action->setEnabled(false); }); [this] { m_refresh_action->setEnabled(false); });
connect(&Settings::Instance(), &Settings::GameListRefreshCompleted, this, connect(&Settings::Instance(), &Settings::GameListRefreshStarted, this,
[this] { m_refresh_action->setEnabled(true); }); [this] { m_refresh_action->setEnabled(true); });
OnEmulationStateChanged(Core::GetState()); OnEmulationStateChanged(Core::GetState());

View File

@ -90,7 +90,8 @@ std::shared_ptr<const GameFile> GameFileCache::AddOrGet(const std::string& path,
bool GameFileCache::Update( bool GameFileCache::Update(
const std::vector<std::string>& all_game_paths, const std::vector<std::string>& all_game_paths,
std::function<void(const std::shared_ptr<const GameFile>&)> game_added_to_cache, std::function<void(const std::shared_ptr<const GameFile>&)> game_added_to_cache,
std::function<void(const std::string&)> game_removed_from_cache) std::function<void(const std::string&)> game_removed_from_cache,
const std::atomic_bool& processing_halted)
{ {
// Copy game paths into a set, except ones that match DiscIO::ShouldHideFromGameList. // Copy game paths into a set, except ones that match DiscIO::ShouldHideFromGameList.
// TODO: Prevent DoFileSearch from looking inside /files/ directories of DirectoryBlobs at all? // TODO: Prevent DoFileSearch from looking inside /files/ directories of DirectoryBlobs at all?
@ -113,6 +114,9 @@ bool GameFileCache::Update(
auto end = m_cached_files.end(); auto end = m_cached_files.end();
while (it != end) while (it != end)
{ {
if (processing_halted)
break;
if (game_paths.erase((*it)->GetFilePath())) if (game_paths.erase((*it)->GetFilePath()))
{ {
++it; ++it;
@ -134,6 +138,9 @@ bool GameFileCache::Update(
// aren't in m_cached_files, so we simply add all of them to m_cached_files. // aren't in m_cached_files, so we simply add all of them to m_cached_files.
for (const std::string& path : game_paths) for (const std::string& path : game_paths)
{ {
if (processing_halted)
break;
auto file = std::make_shared<GameFile>(path); auto file = std::make_shared<GameFile>(path);
if (file->IsValid()) if (file->IsValid())
{ {
@ -149,12 +156,16 @@ bool GameFileCache::Update(
} }
bool GameFileCache::UpdateAdditionalMetadata( bool GameFileCache::UpdateAdditionalMetadata(
std::function<void(const std::shared_ptr<const GameFile>&)> game_updated) std::function<void(const std::shared_ptr<const GameFile>&)> game_updated,
const std::atomic_bool& processing_halted)
{ {
bool cache_changed = false; bool cache_changed = false;
for (std::shared_ptr<GameFile>& file : m_cached_files) for (std::shared_ptr<GameFile>& file : m_cached_files)
{ {
if (processing_halted)
break;
const bool updated = UpdateAdditionalMetadata(&file); const bool updated = UpdateAdditionalMetadata(&file);
cache_changed |= updated; cache_changed |= updated;
if (game_updated && updated) if (game_updated && updated)

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <atomic>
#include <cstddef> #include <cstddef>
#include <functional> #include <functional>
#include <memory> #include <memory>
@ -44,9 +45,11 @@ public:
// These functions return true if the call modified the cache. // These functions return true if the call modified the cache.
bool Update(const std::vector<std::string>& all_game_paths, bool Update(const std::vector<std::string>& all_game_paths,
std::function<void(const std::shared_ptr<const GameFile>&)> game_added_to_cache = {}, std::function<void(const std::shared_ptr<const GameFile>&)> game_added_to_cache = {},
std::function<void(const std::string&)> game_removed_from_cache = {}); std::function<void(const std::string&)> game_removed_from_cache = {},
const std::atomic_bool& processing_halted = false);
bool UpdateAdditionalMetadata( bool UpdateAdditionalMetadata(
std::function<void(const std::shared_ptr<const GameFile>&)> game_updated = {}); std::function<void(const std::shared_ptr<const GameFile>&)> game_updated = {},
const std::atomic_bool& processing_halted = false);
bool Load(); bool Load();
bool Save(); bool Save();