UI: Refactor game icon/bg loading.

This also default-initializes the atomic flags, which seems to fix a race
condition I was sometimes experiencing with missing icons.
This commit is contained in:
Unknown W. Brackets 2017-03-26 00:00:57 -07:00
parent 43d0a2bcf0
commit 562288b09c
6 changed files with 131 additions and 168 deletions

View File

@ -45,17 +45,13 @@
GameInfoCache *g_gameInfoCache;
GameInfo::GameInfo()
: region(-1), fileType(IdentifiedFileType::UNKNOWN), paramSFOLoaded(false),
hasConfig(false), iconTexture(nullptr), pic0Texture(nullptr), pic1Texture(nullptr), wantFlags(0),
lastAccessedTime(0.0), timeIconWasLoaded(0.0), timePic0WasLoaded(0.0), timePic1WasLoaded(0.0),
gameSize(0), saveDataSize(0), installDataSize(0), pending(true), working(false), fileLoader(nullptr) {
GameInfo::GameInfo() : fileType(IdentifiedFileType::UNKNOWN) {
}
GameInfo::~GameInfo() {
delete iconTexture;
delete pic0Texture;
delete pic1Texture;
delete icon.texture;
delete pic0.texture;
delete pic1.texture;
delete fileLoader;
}
@ -412,23 +408,23 @@ public:
// Then, ICON0.PNG.
if (pbp.GetSubFileSize(PBP_ICON0_PNG) > 0) {
std::lock_guard<std::mutex> lock(info_->lock);
pbp.GetSubFileAsString(PBP_ICON0_PNG, &info_->iconTextureData);
pbp.GetSubFileAsString(PBP_ICON0_PNG, &info_->icon.data);
} else {
// Read standard icon
ReadVFSToString("unknown.png", &info_->iconTextureData, &info_->lock);
ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock);
}
info_->iconDataLoaded = true;
info_->icon.dataLoaded = true;
if (info_->wantFlags & GAMEINFO_WANTBG) {
if (pbp.GetSubFileSize(PBP_PIC0_PNG) > 0) {
std::lock_guard<std::mutex> lock(info_->lock);
pbp.GetSubFileAsString(PBP_PIC0_PNG, &info_->pic0TextureData);
info_->pic0DataLoaded = true;
pbp.GetSubFileAsString(PBP_PIC0_PNG, &info_->pic0.data);
info_->pic0.dataLoaded = true;
}
if (pbp.GetSubFileSize(PBP_PIC1_PNG) > 0) {
std::lock_guard<std::mutex> lock(info_->lock);
pbp.GetSubFileAsString(PBP_PIC1_PNG, &info_->pic1TextureData);
info_->pic1DataLoaded = true;
pbp.GetSubFileAsString(PBP_PIC1_PNG, &info_->pic1.data);
info_->pic1.dataLoaded = true;
}
}
if (info_->wantFlags & GAMEINFO_WANTSND) {
@ -453,8 +449,8 @@ handleELF:
// Read standard icon
DEBUG_LOG(LOADER, "Loading unknown.png because there was an ELF");
ReadVFSToString("unknown.png", &info_->iconTextureData, &info_->lock);
info_->iconDataLoaded = true;
ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true;
break;
case IdentifiedFileType::PSP_SAVEDATA_DIRECTORY:
@ -470,11 +466,11 @@ handleELF:
info_->ParseParamSFO();
}
ReadFileToString(&umd, "/ICON0.PNG", &info_->iconTextureData, &info_->lock);
info_->iconDataLoaded = true;
ReadFileToString(&umd, "/ICON0.PNG", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true;
if (info_->wantFlags & GAMEINFO_WANTBG) {
ReadFileToString(&umd, "/PIC1.PNG", &info_->pic1TextureData, &info_->lock);
info_->pic1DataLoaded = true;
ReadFileToString(&umd, "/PIC1.PNG", &info_->pic1.data, &info_->lock);
info_->pic1.dataLoaded = true;
}
break;
}
@ -488,8 +484,8 @@ handleELF:
// Let's use the screenshot as an icon, too.
std::string screenshotPath = ReplaceAll(gamePath_, ".ppst", ".jpg");
if (File::Exists(screenshotPath)) {
if (readFileToString(false, screenshotPath.c_str(), info_->iconTextureData)) {
info_->iconDataLoaded = true;
if (readFileToString(false, screenshotPath.c_str(), info_->icon.data)) {
info_->icon.dataLoaded = true;
}
}
break;
@ -509,17 +505,17 @@ handleELF:
info_->ParseParamSFO();
}
ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->iconTextureData, &info_->lock);
info_->iconDataLoaded = true;
ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true;
if (info_->wantFlags & GAMEINFO_WANTBG) {
ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0TextureData, &info_->lock);
info_->pic0DataLoaded = true;
ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1TextureData, &info_->lock);
info_->pic1DataLoaded = true;
ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0.data, &info_->lock);
info_->pic0.dataLoaded = true;
ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1.data, &info_->lock);
info_->pic1.dataLoaded = true;
}
if (info_->wantFlags & GAMEINFO_WANTSND) {
ReadFileToString(&umd, "/PSP_GAME/SND0.AT3", &info_->sndFileData, &info_->lock);
info_->pic1DataLoaded = true;
info_->pic1.dataLoaded = true;
}
break;
}
@ -548,47 +544,47 @@ handleELF:
info_->ParseParamSFO();
if (info_->wantFlags & GAMEINFO_WANTBG) {
ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0TextureData, nullptr);
info_->pic0DataLoaded = true;
ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1TextureData, nullptr);
info_->pic1DataLoaded = true;
ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0.data, nullptr);
info_->pic0.dataLoaded = true;
ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1.data, nullptr);
info_->pic1.dataLoaded = true;
}
if (info_->wantFlags & GAMEINFO_WANTSND) {
ReadFileToString(&umd, "/PSP_GAME/SND0.AT3", &info_->sndFileData, nullptr);
info_->pic1DataLoaded = true;
info_->pic1.dataLoaded = true;
}
}
// Fall back to unknown icon if ISO is broken/is a homebrew ISO, override is allowed though
if (!ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->iconTextureData, &info_->lock)) {
if (!ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->icon.data, &info_->lock)) {
DEBUG_LOG(LOADER, "Loading unknown.png because no icon was found");
ReadVFSToString("unknown.png", &info_->iconTextureData, &info_->lock);
ReadVFSToString("unknown.png", &info_->icon.data, &info_->lock);
}
info_->iconDataLoaded = true;
info_->icon.dataLoaded = true;
break;
}
case IdentifiedFileType::ARCHIVE_ZIP:
info_->paramSFOLoaded = true;
{
ReadVFSToString("zip.png", &info_->iconTextureData, &info_->lock);
info_->iconDataLoaded = true;
ReadVFSToString("zip.png", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true;
}
break;
case IdentifiedFileType::ARCHIVE_RAR:
info_->paramSFOLoaded = true;
{
ReadVFSToString("rargray.png", &info_->iconTextureData, &info_->lock);
info_->iconDataLoaded = true;
ReadVFSToString("rargray.png", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true;
}
break;
case IdentifiedFileType::ARCHIVE_7Z:
info_->paramSFOLoaded = true;
{
ReadVFSToString("7z.png", &info_->iconTextureData, &info_->lock);
info_->iconDataLoaded = true;
ReadVFSToString("7z.png", &info_->icon.data, &info_->lock);
info_->icon.dataLoaded = true;
}
break;
@ -653,30 +649,9 @@ void GameInfoCache::Clear() {
for (auto iter = info_.begin(); iter != info_.end(); iter++) {
{
std::lock_guard<std::mutex> lock(iter->second->lock);
if (!iter->second->pic0TextureData.empty()) {
iter->second->pic0TextureData.clear();
iter->second->pic0DataLoaded = false;
}
if (iter->second->pic0Texture) {
delete iter->second->pic0Texture;
iter->second->pic0Texture = 0;
}
if (!iter->second->pic1TextureData.empty()) {
iter->second->pic1TextureData.clear();
iter->second->pic1DataLoaded = false;
}
if (iter->second->pic1Texture) {
delete iter->second->pic1Texture;
iter->second->pic1Texture = 0;
}
if (!iter->second->iconTextureData.empty()) {
iter->second->iconTextureData.clear();
iter->second->iconDataLoaded = false;
}
if (iter->second->iconTexture) {
delete iter->second->iconTexture;
iter->second->iconTexture = 0;
}
iter->second->pic0.Clear();
iter->second->pic1.Clear();
iter->second->icon.Clear();
if (!iter->second->sndFileData.empty()) {
iter->second->sndFileData.clear();
@ -691,23 +666,8 @@ void GameInfoCache::Clear() {
void GameInfoCache::FlushBGs() {
for (auto iter = info_.begin(); iter != info_.end(); iter++) {
std::lock_guard<std::mutex> lock(iter->second->lock);
if (!iter->second->pic0TextureData.empty()) {
iter->second->pic0TextureData.clear();
iter->second->pic0DataLoaded = false;
}
if (iter->second->pic0Texture) {
delete iter->second->pic0Texture;
iter->second->pic0Texture = 0;
}
if (!iter->second->pic1TextureData.empty()) {
iter->second->pic1TextureData.clear();
iter->second->pic1DataLoaded = false;
}
if (iter->second->pic1Texture) {
delete iter->second->pic1Texture;
iter->second->pic1Texture = 0;
}
iter->second->pic0.Clear();
iter->second->pic1.Clear();
if (!iter->second->sndFileData.empty()) {
iter->second->sndFileData.clear();
@ -749,27 +709,22 @@ GameInfo *GameInfoCache::GetInfo(Draw::DrawContext *draw, const std::string &gam
auto iter = info_.find(gamePath);
if (iter != info_.end()) {
info = iter->second;
if ((info->wantFlags & wantFlags) != wantFlags) {
// Need to start over. We'll just add a new work item.
goto again;
}
if (draw && info->iconDataLoaded) {
SetupTexture(info, info->iconTextureData, draw, info->iconTexture, info->timeIconWasLoaded);
info->iconDataLoaded = false;
}
if (draw && info->pic0DataLoaded) {
SetupTexture(info, info->pic0TextureData, draw, info->pic0Texture, info->timePic0WasLoaded);
info->pic0DataLoaded = false;
}
if (draw && info->pic1DataLoaded) {
SetupTexture(info, info->pic1TextureData, draw, info->pic1Texture, info->timePic1WasLoaded);
info->pic1DataLoaded = false;
}
iter->second->lastAccessedTime = time_now_d();
return iter->second;
}
again:
// If wantFlags don't match, we need to start over. We'll just queue the work item again.
if (info && (info->wantFlags & wantFlags) == wantFlags) {
if (draw && info->icon.dataLoaded && !info->icon.texture) {
SetupTexture(info, draw, info->icon);
}
if (draw && info->pic0.dataLoaded && !info->pic0.texture) {
SetupTexture(info, draw, info->pic0);
}
if (draw && info->pic1.dataLoaded && !info->pic1.texture) {
SetupTexture(info, draw, info->pic1);
}
info->lastAccessedTime = time_now_d();
return info;
}
if (!info) {
info = new GameInfo();
@ -794,15 +749,16 @@ again:
return info;
}
void GameInfoCache::SetupTexture(GameInfo *info, std::string &textureData, Draw::DrawContext *thin3d, ManagedTexture *&tex, double &loadTime) {
void GameInfoCache::SetupTexture(GameInfo *info, Draw::DrawContext *thin3d, GameInfoTex &icon) {
using namespace Draw;
if (textureData.size()) {
if (!tex) {
tex = CreateTextureFromFileData(thin3d, (const uint8_t *)textureData.data(), (int)textureData.size(), ImageFileType::DETECT);
if (tex) {
loadTime = time_now_d();
if (icon.data.size()) {
if (!icon.texture) {
icon.texture = CreateTextureFromFileData(thin3d, (const uint8_t *)icon.data.data(), (int)icon.data.size(), ImageFileType::DETECT);
if (icon.texture) {
icon.timeLoaded = time_now_d();
}
}
textureData.clear();
icon.data.clear();
icon.dataLoaded = false;
}
}

View File

@ -58,6 +58,26 @@ enum GameInfoWantFlags {
class FileLoader;
enum class IdentifiedFileType;
struct GameInfoTex {
std::string data;
ManagedTexture *texture = nullptr;
// The time at which the Icon and the BG were loaded.
// Can be useful to fade them in smoothly once they appear.
double timeLoaded = 0.0;
std::atomic<bool> dataLoaded = false;
void Clear() {
if (!data.empty()) {
data.clear();
dataLoaded = false;
}
if (texture) {
delete texture;
texture = nullptr;
}
}
};
class GameInfo {
public:
GameInfo();
@ -97,45 +117,32 @@ public:
int region = -1;
IdentifiedFileType fileType;
ParamSFOData paramSFO;
bool paramSFOLoaded;
bool hasConfig;
bool paramSFOLoaded = false;
bool hasConfig = false;
// Pre read the data, create a texture the next time (GL thread..)
std::string iconTextureData;
ManagedTexture *iconTexture;
std::string pic0TextureData;
ManagedTexture *pic0Texture;
std::string pic1TextureData;
ManagedTexture *pic1Texture;
GameInfoTex icon;
GameInfoTex pic0;
GameInfoTex pic1;
std::string sndFileData;
std::atomic<bool> sndDataLoaded = false;
int wantFlags;
int wantFlags = 0;
double lastAccessedTime;
double lastAccessedTime = 0.0;
// The time at which the Icon and the BG were loaded.
// Can be useful to fade them in smoothly once they appear.
double timeIconWasLoaded;
double timePic0WasLoaded;
double timePic1WasLoaded;
std::atomic<bool> iconDataLoaded;
std::atomic<bool> pic0DataLoaded;
std::atomic<bool> pic1DataLoaded;
std::atomic<bool> sndDataLoaded;
u64 gameSize;
u64 saveDataSize;
u64 installDataSize;
bool pending;
bool working;
u64 gameSize = 0;
u64 saveDataSize = 0;
u64 installDataSize = 0;
bool pending = true;
bool working = false;
protected:
// Note: this can change while loading, use GetTitle().
std::string title;
FileLoader *fileLoader;
FileLoader *fileLoader = nullptr;
std::string filePath_;
};
@ -148,7 +155,7 @@ public:
void Clear();
void PurgeType(IdentifiedFileType fileType);
// All data in GameInfo including iconTexture may be zero the first time you call this
// All data in GameInfo including icon.texture may be zero the first time you call this
// but filled in later asynchronously in the background. So keep calling this,
// redrawing the UI often. Only set flags to GAMEINFO_WANTBG or WANTSND if you really want them
// because they're big. bgTextures and sound may be discarded over time as well.
@ -162,7 +169,7 @@ public:
private:
void Init();
void Shutdown();
void SetupTexture(GameInfo *info, std::string &textureData, Draw::DrawContext *draw, ManagedTexture *&tex, double &loadTime);
void SetupTexture(GameInfo *info, Draw::DrawContext *draw, GameInfoTex &icon);
// Maps ISO path to info.
std::map<std::string, GameInfo *> info_;

View File

@ -202,15 +202,15 @@ void GameScreen::update() {
if (tvTitle_)
tvTitle_->SetText(info->GetTitle() + " (" + info->id + ")");
if (info->iconTexture && texvGameIcon_) {
texvGameIcon_->SetTexture(info->iconTexture->GetTexture());
if (info->icon.texture && texvGameIcon_) {
texvGameIcon_->SetTexture(info->icon.texture->GetTexture());
// Fade the icon with the background.
double loadTime = info->timeIconWasLoaded;
if (info->pic1Texture) {
loadTime = std::max(loadTime, info->timePic1WasLoaded);
double loadTime = info->icon.timeLoaded;
if (info->pic1.texture) {
loadTime = std::max(loadTime, info->pic1.timeLoaded);
}
if (info->pic0Texture) {
loadTime = std::max(loadTime, info->timePic0WasLoaded);
if (info->pic0.texture) {
loadTime = std::max(loadTime, info->pic0.timeLoaded);
}
uint32_t color = whiteAlpha(ease((time_now_d() - loadTime) * 3));
texvGameIcon_->SetColor(color);

View File

@ -180,8 +180,8 @@ void GameButton::Draw(UIContext &dc) {
u32 color = 0, shadowColor = 0;
using namespace UI;
if (ginfo->iconTexture) {
texture = ginfo->iconTexture->GetTexture();
if (ginfo->icon.texture) {
texture = ginfo->icon.texture->GetTexture();
}
int x = bounds_.x;
@ -207,8 +207,8 @@ void GameButton::Draw(UIContext &dc) {
}
if (texture) {
color = whiteAlpha(ease((time_now_d() - ginfo->timeIconWasLoaded) * 2));
shadowColor = blackAlpha(ease((time_now_d() - ginfo->timeIconWasLoaded) * 2));
color = whiteAlpha(ease((time_now_d() - ginfo->icon.timeLoaded) * 2));
shadowColor = blackAlpha(ease((time_now_d() - ginfo->icon.timeLoaded) * 2));
float tw = texture->Width();
float th = texture->Height();
@ -1017,17 +1017,17 @@ bool MainScreen::DrawBackgroundFor(UIContext &dc, const std::string &gamePath, f
dc.RebindTexture();
// Let's not bother if there's no picture.
if (!ginfo || (!ginfo->pic1Texture && !ginfo->pic0Texture)) {
if (!ginfo || (!ginfo->pic1.texture && !ginfo->pic0.texture)) {
return false;
}
} else {
return false;
}
if (ginfo->pic1Texture) {
dc.GetDrawContext()->BindTexture(0, ginfo->pic1Texture->GetTexture());
} else if (ginfo->pic0Texture) {
dc.GetDrawContext()->BindTexture(0, ginfo->pic0Texture->GetTexture());
if (ginfo->pic1.texture) {
dc.GetDrawContext()->BindTexture(0, ginfo->pic1.texture->GetTexture());
} else if (ginfo->pic0.texture) {
dc.GetDrawContext()->BindTexture(0, ginfo->pic0.texture->GetTexture());
}
uint32_t color = whiteAlpha(ease(progress)) & 0xFFc0c0c0;

View File

@ -109,13 +109,13 @@ void DrawGameBackground(UIContext &dc, const std::string &gamePath) {
bool hasPic = false;
double loadTime;
if (ginfo && ginfo->pic1Texture) {
dc.GetDrawContext()->BindTexture(0, ginfo->pic1Texture->GetTexture());
loadTime = ginfo->timePic1WasLoaded;
if (ginfo && ginfo->pic1.texture) {
dc.GetDrawContext()->BindTexture(0, ginfo->pic1.texture->GetTexture());
loadTime = ginfo->pic1.timeLoaded;
hasPic = true;
} else if (ginfo && ginfo->pic0Texture) {
dc.GetDrawContext()->BindTexture(0, ginfo->pic0Texture->GetTexture());
loadTime = ginfo->timePic0WasLoaded;
} else if (ginfo && ginfo->pic0.texture) {
dc.GetDrawContext()->BindTexture(0, ginfo->pic0.texture->GetTexture());
loadTime = ginfo->pic0.timeLoaded;
hasPic = true;
}
if (hasPic) {

View File

@ -80,8 +80,8 @@ public:
std::string savedata_detail = ginfo->paramSFO.GetValueString("SAVEDATA_DETAIL");
std::string savedata_title = ginfo->paramSFO.GetValueString("SAVEDATA_TITLE");
if (ginfo->iconTexture) {
toprow->Add(new TextureView(ginfo->iconTexture->GetTexture(), IS_FIXED, new LinearLayoutParams(Margins(10, 5))));
if (ginfo->icon.texture) {
toprow->Add(new TextureView(ginfo->icon.texture->GetTexture(), IS_FIXED, new LinearLayoutParams(Margins(10, 5))));
}
LinearLayout *topright = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1.0f));
topright->SetSpacing(1.0f);
@ -159,8 +159,8 @@ void SavedataButton::Draw(UIContext &dc) {
u32 color = 0, shadowColor = 0;
using namespace UI;
if (ginfo->iconTexture) {
texture = ginfo->iconTexture->GetTexture();
if (ginfo->icon.texture) {
texture = ginfo->icon.texture->GetTexture();
}
int x = bounds_.x;
@ -184,8 +184,8 @@ void SavedataButton::Draw(UIContext &dc) {
dc.Draw()->Flush();
if (texture) {
color = whiteAlpha(ease((time_now_d() - ginfo->timeIconWasLoaded) * 2));
shadowColor = blackAlpha(ease((time_now_d() - ginfo->timeIconWasLoaded) * 2));
color = whiteAlpha(ease((time_now_d() - ginfo->icon.timeLoaded) * 2));
shadowColor = blackAlpha(ease((time_now_d() - ginfo->icon.timeLoaded) * 2));
float tw = texture->Width();
float th = texture->Height();