mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
IconCache: Expire only avatars after a day, other icons after 30 days
Achievement badges and game icons don't change, so re-downloading them daily was wasted. InsertIcon now takes a max age, and each entry saves its expiry time. Leaderboard avatars get a day, everything else 30 days. Bumps the cache version. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
70b046285c
commit
19c57faadc
5 files changed
+21
-18
No files matched your search
+9
-11
@@ -9,8 +9,8 @@
|
||||
#include "Common/GPU/thin3d.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
|
||||
// 2: insertedTimestamp became wall-clock time.
|
||||
#define ICON_CACHE_VERSION 2
|
||||
// 3: Entries store a wall-clock expiry time.
|
||||
#define ICON_CACHE_VERSION 3
|
||||
#define MK_FOURCC(str) (str[0] | ((uint8_t)str[1] << 8) | ((uint8_t)str[2] << 16) | ((uint8_t)str[3] << 24))
|
||||
|
||||
#define MAX_RUNTIME_CACHE_SIZE (1024 * 1024 * 4)
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
// Seconds before MarkPending accepts a key whose download failed.
|
||||
constexpr double FAILED_RETRY_DELAY = 30.0;
|
||||
// Seconds a saved icon is kept across sessions, so that ones that change (user avatars) get refreshed.
|
||||
constexpr double MAX_SAVED_ICON_AGE = 24 * 60 * 60.0;
|
||||
// Seconds before BindIconTexture tries again to create a texture that failed.
|
||||
constexpr double UPLOAD_RETRY_DELAY = 5.0;
|
||||
|
||||
@@ -38,7 +36,7 @@ struct DiskCacheEntry {
|
||||
uint32_t dataLen;
|
||||
IconFormat format;
|
||||
uint32_t padding; // Explicit, so that 32-bit x86 Linux (which aligns double to 4) has the same layout.
|
||||
double insertedTimestamp;
|
||||
double expireTimestamp;
|
||||
};
|
||||
static_assert(sizeof(DiskCacheEntry) == 24, "DiskCacheEntry is written to disk as is");
|
||||
|
||||
@@ -75,7 +73,7 @@ void IconCache::SaveToFile(FILE *file) {
|
||||
const auto &entry = iter.second;
|
||||
entryHeader.dataLen = (uint32_t)entry.data.size();
|
||||
entryHeader.format = entry.format;
|
||||
entryHeader.insertedTimestamp = entry.insertedTimeStamp;
|
||||
entryHeader.expireTimestamp = entry.expireTimeStamp;
|
||||
fwrite(&entryHeader, 1, sizeof(entryHeader), file);
|
||||
fwrite(iter.first.c_str(), 1, iter.first.size(), file);
|
||||
fwrite(entry.data.data(), 1, entry.data.size(), file);
|
||||
@@ -114,8 +112,8 @@ bool IconCache::LoadFromFile(FILE *file) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip it if we already have the entry somehow, or it's too old.
|
||||
if (cache_.find(key) != cache_.end() || nowUnix - entryHeader.insertedTimestamp > MAX_SAVED_ICON_AGE) {
|
||||
// Skip it if we already have the entry somehow, or it has expired.
|
||||
if (cache_.find(key) != cache_.end() || nowUnix > entryHeader.expireTimestamp) {
|
||||
// Seek past the data and go to the next entry.
|
||||
File::Fseek(file, entryHeader.dataLen, SEEK_CUR);
|
||||
continue;
|
||||
@@ -132,7 +130,7 @@ bool IconCache::LoadFromFile(FILE *file) {
|
||||
Entry entry{};
|
||||
entry.data = data;
|
||||
entry.format = entryHeader.format;
|
||||
entry.insertedTimeStamp = entryHeader.insertedTimestamp;
|
||||
entry.expireTimeStamp = entryHeader.expireTimestamp;
|
||||
entry.usedTimeStamp = now;
|
||||
PeekIconSize(entry.data, entry.format, &entry.width, &entry.height);
|
||||
cache_.emplace(key, entry);
|
||||
@@ -295,7 +293,7 @@ void IconCache::CancelPending(std::string_view key) {
|
||||
pending_.erase(iter);
|
||||
}
|
||||
|
||||
bool IconCache::InsertIcon(std::string_view key, IconFormat format, std::string &&data) {
|
||||
bool IconCache::InsertIcon(std::string_view key, IconFormat format, std::string &&data, double maxAge) {
|
||||
if (key.empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -323,7 +321,7 @@ bool IconCache::InsertIcon(std::string_view key, IconFormat format, std::string
|
||||
}
|
||||
|
||||
double now = time_now_d();
|
||||
Entry entry{ std::move(data), format, nullptr, time_now_unix_utc(), now };
|
||||
Entry entry{ std::move(data), format, nullptr, time_now_unix_utc() + maxAge, now };
|
||||
PeekIconSize(entry.data, entry.format, &entry.width, &entry.height);
|
||||
cache_.emplace(key, std::move(entry));
|
||||
return true;
|
||||
|
||||
@@ -19,6 +19,10 @@ namespace Draw {
|
||||
class Texture;
|
||||
}
|
||||
|
||||
// How long an icon is kept in the saved cache, in seconds. It's downloaded again after that.
|
||||
constexpr double ICON_MAX_AGE_DEFAULT = 30 * 24 * 60 * 60.0; // Most icons, like achievement badges, don't change.
|
||||
constexpr double ICON_MAX_AGE_AVATAR = 24 * 60 * 60.0; // User avatars can change at any time.
|
||||
|
||||
// TODO: Possibly make this smarter and use instead of ManagedTexture?
|
||||
|
||||
struct IconCacheStats {
|
||||
@@ -39,7 +43,7 @@ public:
|
||||
void CancelPending(std::string_view key);
|
||||
// Like CancelPending, but MarkPending refuses the key for a while, so a failing download isn't retried every frame.
|
||||
void MarkFailed(std::string_view key);
|
||||
bool InsertIcon(std::string_view key, IconFormat format, std::string &&pngData);
|
||||
bool InsertIcon(std::string_view key, IconFormat format, std::string &&pngData, double maxAge = ICON_MAX_AGE_DEFAULT);
|
||||
bool GetDimensions(std::string_view key, int *width, int *height);
|
||||
bool Contains(std::string_view key);
|
||||
|
||||
@@ -60,7 +64,7 @@ private:
|
||||
std::string data;
|
||||
IconFormat format;
|
||||
Draw::Texture *texture;
|
||||
double insertedTimeStamp; // Wall-clock (time_now_unix_utc), since it's saved.
|
||||
double expireTimeStamp; // Wall-clock (time_now_unix_utc), since it's saved.
|
||||
double usedTimeStamp;
|
||||
double uploadFailedTime; // When CreateTexture last failed, or 0.
|
||||
int width; // From the image header, 0 if unknown.
|
||||
|
||||
@@ -1061,11 +1061,11 @@ bool HasAchievementsOrLeaderboards() {
|
||||
return IsActive();
|
||||
}
|
||||
|
||||
void DownloadImageIfMissing(std::string_view url) {
|
||||
void DownloadImageIfMissing(std::string_view url, double maxAge) {
|
||||
// On Linux for example, we currently have no way of doing a HTTPS request.
|
||||
if (g_iconCache.MarkPending(url)) {
|
||||
INFO_LOG(Log::Achievements, "Downloading image: %.*s", STR_VIEW(url));
|
||||
g_DownloadManager.StartDownload(url, Path(), http::RequestFlags::Default, nullptr, "", [](http::Request &download) {
|
||||
g_DownloadManager.StartDownload(url, Path(), http::RequestFlags::Default, nullptr, "", [maxAge](http::Request &download) {
|
||||
std::string data;
|
||||
if (download.ResultCode() == 200) {
|
||||
download.buffer().TakeAll(&data);
|
||||
@@ -1075,7 +1075,7 @@ void DownloadImageIfMissing(std::string_view url) {
|
||||
g_iconCache.MarkFailed(download.url());
|
||||
return;
|
||||
}
|
||||
g_iconCache.InsertIcon(download.url(), IconFormat::PNG, std::move(data));
|
||||
g_iconCache.InsertIcon(download.url(), IconFormat::PNG, std::move(data), maxAge);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <set>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/UI/IconCache.h" // for ICON_MAX_AGE_DEFAULT
|
||||
#include "Core/Loaders.h" // for IdentifiedFileType
|
||||
|
||||
class Path;
|
||||
@@ -82,7 +83,7 @@ bool HasToken();
|
||||
/// Pass false only when pending requests are about to be cancelled, as at app exit.
|
||||
bool Shutdown(bool waitForRequests = true);
|
||||
|
||||
void DownloadImageIfMissing(std::string_view url);
|
||||
void DownloadImageIfMissing(std::string_view url, double maxAge = ICON_MAX_AGE_DEFAULT);
|
||||
|
||||
/// Called once a frame at vsync time on the CPU thread, during gameplay.
|
||||
void FrameUpdate();
|
||||
|
||||
@@ -796,7 +796,7 @@ static void RenderLeaderboardEntry(UIContext &dc, const rc_client_leaderboard_en
|
||||
char userImageUrl[512];
|
||||
if (RC_OK == rc_client_leaderboard_entry_get_user_image_url(entry, userImageUrl, sizeof(userImageUrl))) {
|
||||
std::string imageUrl = http::RemoveHttpsIfNeeded(userImageUrl);
|
||||
Achievements::DownloadImageIfMissing(imageUrl);
|
||||
Achievements::DownloadImageIfMissing(imageUrl, ICON_MAX_AGE_AVATAR);
|
||||
if (g_iconCache.BindIconTexture(&dc, imageUrl)) {
|
||||
dc.Draw()->DrawTexRect(Bounds(bounds.x + iconLeft, bounds.y + 4.0f, 64.0f, 64.0f), 0.0f, 0.0f, 1.0f, 1.0f, whiteAlpha(alpha));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user