1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-26 12:42:41 +01:00

Qt: Lazy load game grid icons and optimize paint method for invisible items

This commit is contained in:
Megamouse 2023-04-22 12:15:19 +02:00
parent b47db88ded
commit 823b23f800
6 changed files with 103 additions and 105 deletions

View File

@ -149,10 +149,9 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
m_serials.clear();
m_games.pop_all();
});
connect(&m_repaint_watcher, &QFutureWatcher<movie_item*>::finished, this, &game_list_frame::OnRepaintFinished);
connect(this, &game_list_frame::IconReady, this, [this](movie_item* item)
{
if (!m_is_list_layout || !item) return;
if (!item) return;
item->call_icon_func();
});
connect(this, &game_list_frame::SizeOnDiskReady, this, [this](const game_info& game)
@ -882,29 +881,6 @@ void game_list_frame::OnRefreshFinished()
}
}
void game_list_frame::OnRepaintFinished()
{
if (!m_is_list_layout)
{
// The game grid needs to be recreated from scratch
int games_per_row = 0;
if (m_icon_size.width() > 0 && m_icon_size.height() > 0)
{
games_per_row = width() / (m_icon_size.width() + m_icon_size.width() * m_game_grid->getMarginFactor() * 2);
}
const int scroll_position = m_game_grid->verticalScrollBar()->value();
PopulateGameGrid(games_per_row, m_icon_size, m_icon_color);
connect(m_game_grid, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
connect(m_game_grid, &QTableWidget::itemSelectionChanged, this, &game_list_frame::ItemSelectionChangedSlot);
connect(m_game_grid, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot);
m_central_widget->addWidget(m_game_grid);
m_central_widget->setCurrentWidget(m_game_grid);
m_game_grid->verticalScrollBar()->setValue(scroll_position);
}
}
void game_list_frame::OnCompatFinished()
{
for (const auto& game : m_game_data)
@ -2409,7 +2385,7 @@ void game_list_frame::RepaintIcons(const bool& from_settings)
QPixmap placeholder(m_icon_size);
placeholder.fill(Qt::transparent);
for (auto& game : m_game_data)
for (game_info& game : m_game_data)
{
game->pxmap = placeholder;
@ -2417,46 +2393,7 @@ void game_list_frame::RepaintIcons(const bool& from_settings)
{
item->set_icon_load_func([this, game, cancel = item->icon_loading_aborted()]()
{
if (cancel && cancel->load())
{
return;
}
static std::unordered_set<std::string> warn_once_list;
static shared_mutex s_mtx;
if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(qstr(game->info.icon_path))))
{
if (game_list_log.warning)
{
bool logged = false;
{
std::lock_guard lock(s_mtx);
logged = !warn_once_list.emplace(game->info.icon_path).second;
}
if (!logged)
{
game_list_log.warning("Could not load image from path %s", sstr(QDir(qstr(game->info.icon_path)).absolutePath()));
}
}
}
if (!game->item || (cancel && cancel->load()))
{
return;
}
const QColor color = getGridCompatibilityColor(game->compat.color);
{
std::lock_guard lock(game->item->pixmap_mutex);
game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color);
}
if (!cancel || !cancel->load())
{
Q_EMIT IconReady(game->item);
}
IconLoadFunction(game, cancel);
});
item->call_icon_func();
}
@ -2471,37 +2408,26 @@ void game_list_frame::RepaintIcons(const bool& from_settings)
// Shorten the last section to remove horizontal scrollbar if possible
m_game_list->resizeColumnToContents(gui::column_count - 1);
}
else
{
// The game grid needs to be recreated from scratch
int games_per_row = 0;
return;
if (m_icon_size.width() > 0 && m_icon_size.height() > 0)
{
games_per_row = width() / (m_icon_size.width() + m_icon_size.width() * m_game_grid->getMarginFactor() * 2);
}
const std::function func = [this](const game_info& game) -> movie_item*
{
static std::unordered_set<std::string> warn_once_list;
static shared_mutex s_mtx;
if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(qstr(game->info.icon_path))))
{
if (game_list_log.warning)
{
bool logged = false;
{
std::lock_guard lock(s_mtx);
logged = !warn_once_list.emplace(game->info.icon_path).second;
const int scroll_position = m_game_grid->verticalScrollBar()->value();
PopulateGameGrid(games_per_row, m_icon_size, m_icon_color);
connect(m_game_grid, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
connect(m_game_grid, &QTableWidget::itemSelectionChanged, this, &game_list_frame::ItemSelectionChangedSlot);
connect(m_game_grid, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot);
m_central_widget->addWidget(m_game_grid);
m_central_widget->setCurrentWidget(m_game_grid);
m_game_grid->verticalScrollBar()->setValue(scroll_position);
}
if (!logged)
{
game_list_log.warning("Could not load image from path %s", sstr(QDir(qstr(game->info.icon_path)).absolutePath()));
}
}
}
const QColor color = getGridCompatibilityColor(game->compat.color);
game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color);
return game->item;
};
m_repaint_watcher.setFuture(QtConcurrent::mapped(m_game_data, func));
}
void game_list_frame::SetShowHidden(bool show)
@ -2647,7 +2573,10 @@ void game_list_frame::PopulateGameList()
icon_item->set_icon_func([this, icon_item, game](int)
{
ensure(icon_item && game);
if (!icon_item || !game)
{
return;
}
if (std::shared_ptr<QMovie> movie = icon_item->movie(); movie && icon_item->get_active())
{
@ -2821,7 +2750,7 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con
const QString game_icon_path = m_play_hover_movies ? qstr(fs::get_config_dir() + "/Icons/game_icons/") : "";
for (const auto& app : matching_apps)
for (const game_info& app : matching_apps)
{
const QString serial = qstr(app->info.serial);
const QString title = m_titles.value(serial, qstr(app->info.name));
@ -2831,6 +2760,10 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con
ensure(item);
app->item = item;
item->setData(gui::game_role, QVariant::fromValue(app));
item->set_icon_load_func([this, app, cancel = item->icon_loading_aborted()]()
{
IconLoadFunction(app, cancel);
});
if (!notes.isEmpty())
{
@ -3032,10 +2965,52 @@ std::string game_list_frame::GetGameVersion(const game_info& game)
return game->info.app_ver;
}
void game_list_frame::IconLoadFunction(game_info game, std::shared_ptr<atomic_t<bool>> cancel)
{
if (cancel && cancel->load())
{
return;
}
static std::unordered_set<std::string> warn_once_list;
static shared_mutex s_mtx;
if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(qstr(game->info.icon_path))))
{
if (game_list_log.warning)
{
bool logged = false;
{
std::lock_guard lock(s_mtx);
logged = !warn_once_list.emplace(game->info.icon_path).second;
}
if (!logged)
{
game_list_log.warning("Could not load image from path %s", sstr(QDir(qstr(game->info.icon_path)).absolutePath()));
}
}
}
if (!game->item || (cancel && cancel->load()))
{
return;
}
const QColor color = getGridCompatibilityColor(game->compat.color);
{
std::lock_guard lock(game->item->pixmap_mutex);
game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color);
}
if (!cancel || !cancel->load())
{
Q_EMIT IconReady(game->item);
}
}
void game_list_frame::WaitAndAbortRepaintThreads()
{
gui::utils::stop_future_watcher(m_repaint_watcher, true);
for (const game_info& game : m_game_data)
{
if (game && game->item)

View File

@ -80,7 +80,6 @@ public Q_SLOTS:
private Q_SLOTS:
void OnRefreshFinished();
void OnRepaintFinished();
void OnCompatFinished();
void OnColClicked(int col);
void ShowContextMenu(const QPoint &pos);
@ -102,6 +101,7 @@ protected:
private:
QPixmap PaintedPixmap(const QPixmap& icon, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& color = QColor()) const;
QColor getGridCompatibilityColor(const QString& string) const;
void IconLoadFunction(game_info game, std::shared_ptr<atomic_t<bool>> cancel);
/** Sets the custom config icon. Only call this for list title items. */
void SetCustomConfigIcon(QTableWidgetItem* title_item, const game_info& game);
@ -174,7 +174,6 @@ private:
QMutex m_games_mutex;
lf_queue<game_info> m_games;
QFutureWatcher<void> m_refresh_watcher;
QFutureWatcher<movie_item*> m_repaint_watcher;
QSet<QString> m_hidden_list;
bool m_show_hidden{false};

View File

@ -66,7 +66,10 @@ movie_item* game_list_grid::addItem(const game_info& app, const QString& name, c
item->set_icon_func([this, app, item](int)
{
ensure(item);
if (!item)
{
return;
}
const qreal device_pixel_ratio = devicePixelRatioF();

View File

@ -1,4 +1,7 @@
#include "game_list_grid_delegate.h"
#include "movie_item.h"
#include <QTableWidget>
game_list_grid_delegate::game_list_grid_delegate(const QSize& size, const qreal& margin_factor, const qreal& text_factor, QObject *parent)
: QStyledItemDelegate(parent), m_size(size), m_margin_factor(margin_factor), m_text_factor(text_factor)
@ -23,13 +26,31 @@ void game_list_grid_delegate::paint(QPainter* painter, const QStyleOptionViewIte
painter->setRenderHints(QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
painter->eraseRect(r);
// Paint from our stylesheet
QStyledItemDelegate::paint(painter, option, index);
// Check if the item is visible
if (const QTableWidget* table = static_cast<const QTableWidget*>(parent()))
{
if (movie_item* item = static_cast<movie_item*>(table->item(index.row(), index.column())))
{
if (!table->visibleRegion().intersects(table->visualItemRect(item)))
{
// Skip all further actions if the item is not visible
return;
}
if (!item->icon_loading())
{
item->call_icon_load_func();
}
}
}
// Get title and image
const QPixmap image = qvariant_cast<QPixmap>(index.data(Qt::DecorationRole));
const QString title = index.data(Qt::DisplayRole).toString();
// Paint from our stylesheet
QStyledItemDelegate::paint(painter, option, index);
// image
if (image.isNull() == false)
{

View File

@ -129,7 +129,7 @@ void movie_item::set_size_calc_func(const size_calc_callback_t& func)
void movie_item::wait_for_icon_loading(bool abort)
{
if (m_icon_load_thread)
if (m_icon_load_thread && m_icon_load_thread->isRunning())
{
*m_icon_loading_aborted = abort;
m_icon_load_thread->wait();
@ -139,7 +139,7 @@ void movie_item::wait_for_icon_loading(bool abort)
void movie_item::wait_for_size_on_disk_loading(bool abort)
{
if (m_size_calc_thread)
if (m_size_calc_thread && m_size_calc_thread->isRunning())
{
*m_size_on_disk_loading_aborted = abort;
m_size_calc_thread->wait();

View File

@ -61,12 +61,12 @@ public:
return m_size_on_disk_loading;
}
std::shared_ptr<atomic_t<bool>> icon_loading_aborted() const
[[nodiscard]] std::shared_ptr<atomic_t<bool>> icon_loading_aborted() const
{
return m_icon_loading_aborted;
}
std::shared_ptr<atomic_t<bool>> size_on_disk_loading_aborted() const
[[nodiscard]] std::shared_ptr<atomic_t<bool>> size_on_disk_loading_aborted() const
{
return m_size_on_disk_loading_aborted;
}