Qt: Avoid game list refresh on shutdown

We only need to invalidate the entries that have had their play times
changed, not the entire list.
This commit is contained in:
Stenzek 2025-01-25 19:45:40 +10:00
parent 4e97420b3b
commit 2b7a4f8d19
No known key found for this signature in database
8 changed files with 68 additions and 43 deletions

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "game_list.h"
@ -24,6 +24,7 @@
#include "common/path.h"
#include "common/progress_callback.h"
#include "common/string_util.h"
#include "common/thirdparty/SmallVector.h"
#include "common/timer.h"
#include "fmt/format.h"
@ -1223,50 +1224,35 @@ void GameList::AddPlayedTimeForSerial(const std::string& serial, std::time_t las
static_cast<unsigned>(pt.total_played_time));
std::unique_lock<std::recursive_mutex> lock(s_mutex);
for (GameList::Entry& entry : s_entries)
{
if (entry.serial != serial)
continue;
const GameDatabase::Entry* dbentry = GameDatabase::GetEntryForSerial(serial);
llvm::SmallVector<u32, 32> changed_indices;
entry.last_played_time = pt.last_played_time;
entry.total_played_time = pt.total_played_time;
}
// We don't need to update the disc sets if we're not running Big Picture, because Qt refreshes on system destory,
// which causes the disc set entries to get recreated.
if (FullscreenUI::IsInitialized())
for (size_t i = 0; i < s_entries.size(); i++)
{
const GameDatabase::Entry* dbentry = GameDatabase::GetEntryForSerial(serial);
if (dbentry && !dbentry->disc_set_serials.empty())
Entry& entry = s_entries[i];
if (entry.IsDisc())
{
for (GameList::Entry& entry : s_entries)
{
if (entry.type != EntryType::DiscSet || entry.path != dbentry->disc_set_name)
continue;
if (entry.serial != serial)
continue;
entry.last_played_time = 0;
entry.total_played_time = 0;
entry.last_played_time = pt.last_played_time;
entry.total_played_time = pt.total_played_time;
changed_indices.push_back(static_cast<u32>(i));
}
else if (entry.IsDiscSet())
{
if (!dbentry || entry.path != dbentry->disc_set_name)
continue;
// We shouldn't ever have duplicates for disc sets, so this should be fine.
const PlayedTimeMap ptm = LoadPlayedTimeMap(GetPlayedTimeFile());
for (const std::string& dsserial : dbentry->disc_set_serials)
{
const auto it = ptm.find(dsserial);
if (it == ptm.end())
continue;
entry.last_played_time =
(entry.last_played_time == 0) ?
it->second.last_played_time :
((it->second.last_played_time != 0) ? std::max(entry.last_played_time, it->second.last_played_time) :
entry.last_played_time);
entry.total_played_time += it->second.total_played_time;
}
break;
}
// have to add here, because other discs are already included in the sum
entry.last_played_time = pt.last_played_time;
entry.total_played_time += add_time;
changed_indices.push_back(static_cast<u32>(i));
}
}
if (!changed_indices.empty())
Host::OnGameListEntriesChanged(std::span<const u32>(changed_indices.begin(), changed_indices.end()));
}
void GameList::ClearPlayedTimeForSerial(const std::string& serial)

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
@ -150,4 +150,7 @@ void RefreshGameListAsync(bool invalidate_cache);
/// Cancels game list refresh, if there is one in progress.
void CancelGameListRefresh();
/// Called when game list rows are updated.
void OnGameListEntriesChanged(std::span<const u32> changed_indices);
} // namespace Host

View File

@ -194,6 +194,8 @@ GameListModel::GameListModel(float cover_scale, bool show_cover_titles, bool sho
if (m_show_game_icons)
GameList::ReloadMemcardTimestampCache();
connect(g_emu_thread, &EmuThread::gameListRowsChanged, this, &GameListModel::rowsChanged);
}
GameListModel::~GameListModel()
@ -271,6 +273,28 @@ void GameListModel::coverLoaded(const std::string& path, const QPixmap& pixmap)
invalidateCoverForPath(path);
}
void GameListModel::rowsChanged(const QList<int>& rows)
{
const QList<int> roles_changed = {Qt::DisplayRole};
// try to collapse multiples
size_t start = 0;
size_t idx = 0;
const size_t size = rows.size();
for (; idx < size;)
{
if ((idx + 1) < size && rows[idx + 1] == (rows[idx] + 1))
{
idx++;
}
else
{
emit dataChanged(createIndex(rows[start], 0), createIndex(rows[idx], Column_Count - 1), roles_changed);
start = ++idx;
}
}
}
void GameListModel::invalidateCoverForPath(const std::string& path)
{
std::optional<u32> row;

View File

@ -89,6 +89,7 @@ Q_SIGNALS:
private Q_SLOTS:
void coverLoaded(const std::string& path, const QPixmap& pixmap);
void rowsChanged(const QList<int>& rows);
private:
QVariant data(const QModelIndex& index, int role, const GameList::Entry* ge) const;

View File

@ -621,10 +621,6 @@ void MainWindow::onSystemDestroyed()
updateDisplayWidgetCursor();
else
switchToGameListView();
// reload played time
if (m_game_list_widget->isShowingGameList())
m_game_list_widget->refresh(false);
}
void MainWindow::onRunningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title)

View File

@ -1397,6 +1397,15 @@ void Host::CancelGameListRefresh()
QMetaObject::invokeMethod(g_main_window, "cancelGameListRefresh", Qt::BlockingQueuedConnection);
}
void Host::OnGameListEntriesChanged(std::span<const u32> changed_indices)
{
QList<int> changed_rows;
changed_rows.reserve(changed_indices.size());
for (const u32 row : changed_indices)
changed_rows.push_back(static_cast<int>(row));
emit g_emu_thread->gameListRowsChanged(changed_rows);
}
void EmuThread::loadState(const QString& filename)
{
if (!isCurrentThread())

View File

@ -141,6 +141,7 @@ Q_SIGNALS:
void systemPaused();
void systemResumed();
void gameListRefreshed();
void gameListRowsChanged(const QList<int>& rows_changed);
std::optional<WindowInfo> onAcquireRenderWindowRequested(RenderAPI render_api, bool fullscreen, bool render_to_main,
bool surfaceless, bool use_main_window_pos, Error* error);
void onResizeRenderWindowRequested(qint32 width, qint32 height);

View File

@ -597,6 +597,11 @@ void Host::CancelGameListRefresh()
// noop
}
void Host::OnGameListEntriesChanged(std::span<const u32> changed_indices)
{
// noop
}
BEGIN_HOTKEY_LIST(g_host_hotkeys)
END_HOTKEY_LIST()