Add persistent disk cache for library pixmaps

I'll reference #4379

When viewing the library with album covers visible, the covers load very slowly the first time
as they have to wait on the tagreaders. If I scroll down the library, it takes minutes for the
tagreader to catch up. The nice thing is that the pixmaps are cached. However, once
Clementine is restarted, the whole process has to happen again.

This patch adds a persistent disk cache in the form of a QNetworkDiskCache to store the
pixmaps on disk and load them into the QPixmapCache as required.

I've noted literally night and day performance improvements, not only when scrolling through the library.
There is much better interface responsiveness when searching, and I no longer see the no_cover_icon
temporarily anymore.
This commit is contained in:
Mark Furneaux 2014-05-27 17:40:25 -04:00
parent af42ccea38
commit 2332a74bbe
2 changed files with 44 additions and 1 deletions

View File

@ -21,7 +21,10 @@
#include <QFuture>
#include <QFutureWatcher>
#include <QIODevice>
#include <QMetaEnum>
#include <QNetworkCacheMetaData>
#include <QNetworkDiskCache>
#include <QPixmapCache>
#include <QSettings>
#include <QStringList>
@ -37,6 +40,7 @@
#include "core/database.h"
#include "core/logging.h"
#include "core/taskmanager.h"
#include "core/utilities.h"
#include "covers/albumcoverloader.h"
#include "playlist/songmimedata.h"
#include "smartplaylists/generator.h"
@ -58,6 +62,7 @@ const char* LibraryModel::kSmartPlaylistsSettingsGroup =
"SerialisedSmartPlaylists";
const int LibraryModel::kSmartPlaylistsVersion = 4;
const int LibraryModel::kPrettyCoverSize = 32;
const qint64 LibraryModel::kIconCacheSize = 100000000; //~100MB
typedef QFuture<LibraryModel::QueryResult> RootQueryFuture;
typedef QFutureWatcher<LibraryModel::QueryResult> RootQueryWatcher;
@ -84,6 +89,7 @@ LibraryModel::LibraryModel(LibraryBackend* backend, Application* app,
album_icon_(":/icons/22x22/x-clementine-album.png"),
playlists_dir_icon_(IconLoader::Load("folder-sound")),
playlist_icon_(":/icons/22x22/x-clementine-albums.png"),
icon_cache_(new QNetworkDiskCache(this)),
init_task_id_(-1),
use_pretty_covers_(false),
show_dividers_(true) {
@ -100,6 +106,10 @@ LibraryModel::LibraryModel(LibraryBackend* backend, Application* app,
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64, QImage)),
SLOT(AlbumArtLoaded(quint64, QImage)));
icon_cache_->setCacheDirectory(
Utilities::GetConfigPath(Utilities::Path_CacheRoot) + "/pixmapcache");
icon_cache_->setMaximumCacheSize(LibraryModel::kIconCacheSize);
no_cover_icon_ = QPixmap(":nocover.png")
.scaled(kPrettyCoverSize, kPrettyCoverSize,
Qt::KeepAspectRatio, Qt::SmoothTransformation);
@ -454,6 +464,20 @@ QVariant LibraryModel::AlbumIcon(const QModelIndex& index) {
return cached_pixmap;
}
// Try to load it from the disk cache
QIODevice* cache;
cache = icon_cache_->data(QUrl(cache_key));
if (cache != 0) {
QImage cached_pixmap;
if (cached_pixmap.load(cache, "XPM")) {
delete cache;
qLog(Debug) << "Loading pixmap from disk...";
QPixmapCache::insert(cache_key, QPixmap::fromImage(cached_pixmap));
return QPixmap::fromImage(cached_pixmap);
}
delete cache;
}
// Maybe we're loading a pixmap already?
if (pending_cache_keys_.contains(cache_key)) {
return no_cover_icon_;
@ -488,6 +512,20 @@ void LibraryModel::AlbumArtLoaded(quint64 id, const QImage& image) {
QPixmapCache::insert(cache_key, QPixmap::fromImage(image));
}
// if not already in the disk cache
if (icon_cache_->data(QUrl(cache_key)) == 0) {
qLog(Debug) << "Caching new pixmap...";
QNetworkCacheMetaData* item_metadata = new QNetworkCacheMetaData();
item_metadata->setSaveToDisk(true);
item_metadata->setUrl(QUrl(cache_key));
QIODevice* cache = icon_cache_->prepare(*item_metadata);
if (cache != 0) {
image.save(cache, "XPM");
icon_cache_->insert(cache);
}
delete item_metadata;
}
const QModelIndex index = ItemToIndex(item);
emit dataChanged(index, index);
}

View File

@ -20,6 +20,7 @@
#include <QAbstractItemModel>
#include <QIcon>
#include <QNetworkDiskCache>
#include "libraryitem.h"
#include "libraryquery.h"
@ -47,7 +48,8 @@ class LibraryModel : public SimpleTreeModel<LibraryItem> {
Q_ENUMS(GroupBy);
public:
LibraryModel(LibraryBackend* backend, Application* app, QObject* parent = nullptr);
LibraryModel(LibraryBackend* backend, Application* app,
QObject* parent = nullptr);
~LibraryModel();
static const char* kSmartPlaylistsMimeType;
@ -55,6 +57,7 @@ class LibraryModel : public SimpleTreeModel<LibraryItem> {
static const char* kSmartPlaylistsArray;
static const int kSmartPlaylistsVersion;
static const int kPrettyCoverSize;
static const qint64 kIconCacheSize;
enum Role {
Role_Type = Qt::UserRole + 1,
@ -279,6 +282,8 @@ signals:
QIcon playlists_dir_icon_;
QIcon playlist_icon_;
QNetworkDiskCache* icon_cache_;
int init_task_id_;
bool use_pretty_covers_;