1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 18:53:28 +01:00

Qt: multithreaded trophy icon refresh

This commit is contained in:
Megamouse 2021-10-30 20:44:25 +02:00
parent 4e6b37ca04
commit 20331a77ce
4 changed files with 132 additions and 58 deletions

View File

@ -204,12 +204,12 @@ bool TROPUSRLoader::Generate(const std::string& filepath, const std::string& con
return Save(filepath);
}
u32 TROPUSRLoader::GetTrophiesCount()
u32 TROPUSRLoader::GetTrophiesCount() const
{
return ::size32(m_table6);
}
u32 TROPUSRLoader::GetUnlockedTrophiesCount()
u32 TROPUSRLoader::GetUnlockedTrophiesCount() const
{
u32 count = 0;
for (const auto& trophy : m_table6)
@ -296,7 +296,7 @@ u32 TROPUSRLoader::GetUnlockedPlatinumID(u32 trophy_id, const std::string& confi
return pid;
}
u32 TROPUSRLoader::GetTrophyGrade(u32 id)
u32 TROPUSRLoader::GetTrophyGrade(u32 id) const
{
if (id >= m_table4.size())
{
@ -307,7 +307,7 @@ u32 TROPUSRLoader::GetTrophyGrade(u32 id)
return m_table4[id].trophy_grade; // Let's assume the trophies are stored ordered
}
u32 TROPUSRLoader::GetTrophyUnlockState(u32 id)
u32 TROPUSRLoader::GetTrophyUnlockState(u32 id) const
{
if (id >= m_table6.size())
{
@ -318,7 +318,7 @@ u32 TROPUSRLoader::GetTrophyUnlockState(u32 id)
return m_table6[id].trophy_state; // Let's assume the trophies are stored ordered
}
u64 TROPUSRLoader::GetTrophyTimestamp(u32 id)
u64 TROPUSRLoader::GetTrophyTimestamp(u32 id) const
{
if (id >= m_table6.size())
{

View File

@ -89,14 +89,14 @@ public:
virtual load_result Load(const std::string& filepath, const std::string& configpath);
virtual bool Save(const std::string& filepath);
virtual u32 GetTrophiesCount();
virtual u32 GetUnlockedTrophiesCount();
virtual u32 GetTrophiesCount() const;
virtual u32 GetUnlockedTrophiesCount() const;
virtual u32 GetUnlockedPlatinumID(u32 trophy_id, const std::string& config_path);
virtual u32 GetTrophyGrade(u32 id);
virtual u32 GetTrophyUnlockState(u32 id);
virtual u64 GetTrophyTimestamp(u32 id);
virtual u32 GetTrophyGrade(u32 id) const;
virtual u32 GetTrophyUnlockState(u32 id) const;
virtual u64 GetTrophyTimestamp(u32 id) const;
virtual bool UnlockTrophy(u32 id, u64 timestamp1, u64 timestamp2);
};

View File

@ -111,6 +111,7 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
m_trophy_table->setHorizontalHeaderLabels(QStringList{ tr("Icon"), tr("Name"), tr("Description"), tr("Type"), tr("Status"), tr("ID"), tr("Platinum Relevant") });
m_trophy_table->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
m_trophy_table->horizontalHeader()->setStretchLastSection(true);
m_trophy_table->horizontalHeader()->setSectionResizeMode(TrophyColumns::Icon, QHeaderView::Fixed);
m_trophy_table->verticalHeader()->setVisible(false);
m_trophy_table->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
m_trophy_table->setContextMenuPolicy(Qt::CustomContextMenu);
@ -338,6 +339,24 @@ trophy_manager_dialog::trophy_manager_dialog(std::shared_ptr<gui_settings> gui_s
m_game_combo->setCurrentText(item->text());
});
connect(&m_trophy_repaint_watcher, &QFutureWatcher<QPixmap>::finished, this, &trophy_manager_dialog::ReadjustTrophyTable);
connect(&m_trophy_repaint_watcher, &QFutureWatcher<QPixmap>::resultReadyAt, this, [this](int index)
{
if (QTableWidgetItem* icon_item = m_trophy_table->item(index, TrophyColumns::Icon))
{
icon_item->setData(Qt::DecorationRole, m_trophy_repaint_watcher.resultAt(index));
}
});
connect(&m_game_repaint_watcher, &QFutureWatcher<QPixmap>::finished, this, &trophy_manager_dialog::ReadjustGameTable);
connect(&m_game_repaint_watcher, &QFutureWatcher<QPixmap>::resultReadyAt, this, [this](int index)
{
if (QTableWidgetItem* icon_item = m_game_table->item(index, GameColumns::GameIcon))
{
icon_item->setData(Qt::DecorationRole, m_game_repaint_watcher.resultAt(index));
}
});
RepaintUI(true);
StartTrophyLoadThreads();
@ -486,12 +505,33 @@ QPixmap trophy_manager_dialog::GetResizedGameIcon(int index) const
QTableWidgetItem* item = m_game_table->item(index, GameColumns::GameIcon);
if (!item)
{
return QPixmap();
QPixmap placeholder(m_game_icon_size);
placeholder.fill(Qt::transparent);
return placeholder;
}
const QPixmap icon = item->data(Qt::UserRole).value<QPixmap>();
QPixmap icon;
if (!item->data(GameUserRole::GamePixmapLoaded).toBool())
{
// Load game icon
const int trophy_index = item->data(GameUserRole::GameIndex).toInt();
const std::string icon_path = m_trophies_db[trophy_index]->path + "ICON0.PNG";
if (!icon.load(qstr(icon_path)))
{
gui_log.warning("Could not load trophy game icon from path %s", icon_path);
}
item->setData(GameUserRole::GamePixmapLoaded, true);
item->setData(GameUserRole::GamePixmap, icon);
}
else
{
icon = item->data(GameUserRole::GamePixmap).value<QPixmap>();
}
const qreal dpr = devicePixelRatioF();
QPixmap new_icon = QPixmap(icon.size() * dpr);
QPixmap new_icon(icon.size() * dpr);
new_icon.setDevicePixelRatio(dpr);
new_icon.fill(m_game_icon_color);
@ -511,26 +551,34 @@ void trophy_manager_dialog::ResizeGameIcons()
if (m_game_combo->count() <= 0)
return;
if (m_game_repaint_watcher.isRunning())
{
m_game_repaint_watcher.cancel();
m_game_repaint_watcher.waitForFinished();
}
QPixmap placeholder(m_game_icon_size);
placeholder.fill(Qt::transparent);
QList<int> indices;
for (int i = 0; i < m_game_table->rowCount(); ++i)
{
indices.append(i);
if (QTableWidgetItem* icon_item = m_game_table->item(i, GameColumns::GameIcon))
{
icon_item->setData(Qt::DecorationRole, placeholder);
}
}
const std::function<QPixmap(const int&)> get_scaled = [this](const int& i)
{
return GetResizedGameIcon(i);
};
// NOTE: Due to a Qt bug in Qt 5.15.2, QtConcurrent::blockingMapped has a high risk of deadlocking. So let's just use QtConcurrent::mapped.
const QList<QPixmap> scaled = QtConcurrent::mapped(indices, get_scaled).results();
for (int i = 0; i < m_game_table->rowCount() && i < scaled.count(); ++i)
{
QTableWidgetItem* icon_item = m_game_table->item(i, TrophyColumns::Icon);
if (icon_item)
icon_item->setData(Qt::DecorationRole, scaled[i]);
}
ReadjustGameTable();
m_game_repaint_watcher.setFuture(QtConcurrent::mapped(indices, get_scaled));
}
void trophy_manager_dialog::ResizeTrophyIcons()
@ -538,10 +586,19 @@ void trophy_manager_dialog::ResizeTrophyIcons()
if (m_game_combo->count() <= 0)
return;
if (m_trophy_repaint_watcher.isRunning())
{
m_trophy_repaint_watcher.cancel();
m_trophy_repaint_watcher.waitForFinished();
}
const int db_pos = m_game_combo->currentData().toInt();
const qreal dpr = devicePixelRatioF();
const int new_height = m_icon_height * dpr;
QPixmap placeholder(m_icon_height, m_icon_height);
placeholder.fill(Qt::transparent);
QList<int> trophy_ids;
for (int i = 0; i < m_trophy_table->rowCount(); ++i)
{
@ -549,11 +606,20 @@ void trophy_manager_dialog::ResizeTrophyIcons()
{
trophy_ids.append(item->text().toInt());
}
if (QTableWidgetItem* icon_item = m_trophy_table->item(i, TrophyColumns::Icon))
{
icon_item->setData(Qt::DecorationRole, placeholder);
}
}
const std::function<QPixmap(const int&)> get_scaled = [this, data = m_trophies_db.at(db_pos).get(), dpr, new_height](const int& trophy_id)
ReadjustTrophyTable();
const std::function<QPixmap(const int&)> get_scaled = [this, data = m_trophies_db.at(db_pos).get(), dpr, new_height](const int& trophy_id) -> QPixmap
{
QPixmap icon;
if (data)
{
std::scoped_lock lock(m_trophies_db_mtx);
if (data->trophy_images.contains(trophy_id))
@ -570,7 +636,7 @@ void trophy_manager_dialog::ResizeTrophyIcons()
}
}
QPixmap new_icon = QPixmap(icon.size() * dpr);
QPixmap new_icon(icon.size() * dpr);
new_icon.setDevicePixelRatio(dpr);
new_icon.fill(m_game_icon_color);
@ -585,18 +651,7 @@ void trophy_manager_dialog::ResizeTrophyIcons()
return new_icon.scaledToHeight(new_height, Qt::SmoothTransformation);
};
// NOTE: Due to a Qt bug in Qt 5.15.2, QtConcurrent::blockingMapped has a high risk of deadlocking. So let's just use QtConcurrent::mapped.
const QList<QPixmap> scaled = QtConcurrent::mapped(trophy_ids, get_scaled).results();
for (int i = 0; i < m_trophy_table->rowCount() && i < scaled.count(); ++i)
{
QTableWidgetItem* icon_item = m_trophy_table->item(i, TrophyColumns::Icon);
if (icon_item)
icon_item->setData(Qt::DecorationRole, scaled.at(i));
}
ReadjustTrophyTable();
m_trophy_repaint_watcher.setFuture(QtConcurrent::mapped(trophy_ids, get_scaled));
}
void trophy_manager_dialog::ApplyFilter()
@ -605,10 +660,10 @@ void trophy_manager_dialog::ApplyFilter()
return;
const int db_pos = m_game_combo->currentData().toInt();
if (db_pos + 0u >= m_trophies_db.size() || !m_trophies_db[db_pos])
if (db_pos < 0 || static_cast<usz>(db_pos) >= m_trophies_db.size() || !m_trophies_db[db_pos])
return;
const auto trop_usr = m_trophies_db[db_pos]->trop_usr.get();
const TROPUSRLoader* trop_usr = m_trophies_db[db_pos]->trop_usr.get();
if (!trop_usr)
return;
@ -679,6 +734,16 @@ void trophy_manager_dialog::ShowContextMenu(const QPoint& pos)
void trophy_manager_dialog::StartTrophyLoadThreads()
{
if (m_game_repaint_watcher.isRunning())
{
m_game_repaint_watcher.waitForFinished();
}
if (m_trophy_repaint_watcher.isRunning())
{
m_trophy_repaint_watcher.waitForFinished();
}
m_trophies_db.clear();
const QString trophy_path = qstr(vfs::get(m_trophy_dir));
@ -738,6 +803,12 @@ void trophy_manager_dialog::StartTrophyLoadThreads()
void trophy_manager_dialog::PopulateGameTable()
{
if (m_game_repaint_watcher.isRunning())
{
m_game_repaint_watcher.cancel();
m_game_repaint_watcher.waitForFinished();
}
m_game_table->setSortingEnabled(false); // Disable sorting before using setItem calls
m_game_table->clearContents();
@ -749,20 +820,8 @@ void trophy_manager_dialog::PopulateGameTable()
for (usz i = 0; i < m_trophies_db.size(); ++i)
indices.append(static_cast<int>(i));
const std::function<QPixmap(const int&)> get_icon = [this](const int& i)
{
// Load game icon
QPixmap icon;
const std::string icon_path = m_trophies_db[i]->path + "ICON0.PNG";
if (!icon.load(qstr(icon_path)))
{
gui_log.warning("Could not load trophy game icon from path %s", icon_path);
}
return icon;
};
// NOTE: Due to a Qt bug in Qt 5.15.2, QtConcurrent::blockingMapped has a high risk of deadlocking. So let's just use QtConcurrent::mapped.
const QList<QPixmap> icons = QtConcurrent::mapped(indices, get_icon).results();
QPixmap placeholder(m_game_icon_size);
placeholder.fill(Qt::transparent);
for (int i = 0; i < indices.count(); ++i)
{
@ -773,8 +832,10 @@ void trophy_manager_dialog::PopulateGameTable()
const QString name = qstr(m_trophies_db[i]->game_name).simplified();
custom_table_widget_item* icon_item = new custom_table_widget_item;
if (icons.count() > i)
icon_item->setData(Qt::UserRole, icons[i]);
icon_item->setData(Qt::DecorationRole, placeholder);
icon_item->setData(GameUserRole::GameIndex, i);
icon_item->setData(GameUserRole::GamePixmapLoaded, false);
icon_item->setData(GameUserRole::GamePixmap, QPixmap());
m_game_table->setItem(i, GameColumns::GameIcon, icon_item);
m_game_table->setItem(i, GameColumns::GameName, new custom_table_widget_item(name));
@ -785,10 +846,10 @@ void trophy_manager_dialog::PopulateGameTable()
m_game_combo->model()->sort(0, Qt::AscendingOrder);
ResizeGameIcons();
m_game_table->setSortingEnabled(true); // Enable sorting only after using setItem calls
ResizeGameIcons();
gui::utils::resize_combo_box_view(m_game_combo);
}
@ -821,6 +882,9 @@ void trophy_manager_dialog::PopulateTrophyTable()
return;
}
QPixmap placeholder(m_icon_height, m_icon_height);
placeholder.fill(Qt::transparent);
int i = 0;
for (std::shared_ptr<rXmlNode> n = trophy_base->GetChildren(); n; n = n->GetNext())
{
@ -876,6 +940,7 @@ void trophy_manager_dialog::PopulateTrophyTable()
custom_table_widget_item* icon_item = new custom_table_widget_item();
icon_item->setData(Qt::UserRole, hidden, true);
icon_item->setData(Qt::DecorationRole, placeholder);
custom_table_widget_item* type_item = new custom_table_widget_item(trophy_type);
type_item->setData(Qt::UserRole, static_cast<uint>(details.trophyGrade), true);
@ -920,7 +985,6 @@ void trophy_manager_dialog::ReadjustTrophyTable() const
// Resize and fixate icon column
m_trophy_table->resizeColumnToContents(TrophyColumns::Icon);
m_trophy_table->horizontalHeader()->setSectionResizeMode(TrophyColumns::Icon, QHeaderView::Fixed);
// Shorten the last section to remove horizontal scrollbar if possible
m_trophy_table->resizeColumnToContents(TrophyColumns::Count - 1);

View File

@ -4,6 +4,7 @@
#include <QWidget>
#include <QComboBox>
#include <QFutureWatcher>
#include <QLabel>
#include <QPixmap>
#include <QTableWidget>
@ -50,6 +51,13 @@ enum GameColumns
GameColumnsCount
};
enum GameUserRole
{
GameIndex = Qt::UserRole,
GamePixmapLoaded,
GamePixmap
};
class trophy_manager_dialog : public QWidget
{
Q_OBJECT
@ -103,6 +111,8 @@ private:
QSplitter* m_splitter; //! Contains the game and trophy tables
game_list* m_trophy_table; //! UI element to display trophy stuff.
QTableWidget* m_game_table; //! UI element to display games.
QFutureWatcher<QPixmap> m_game_repaint_watcher;
QFutureWatcher<QPixmap> m_trophy_repaint_watcher;
bool m_show_hidden_trophies = false;
bool m_show_unlocked_trophies = true;