*POSSIBLY DESTABLIZING CHANGE, PLS PING ME IF YOU SEE ISSUES*

Turned on the threaded card fetching code for win/linux.  PSP runs unthreaded.   There's an easy toggle for switching which mode the app runs in: check out WResourceManager's constructor.

To fully appreciate the difference, try going into the deck editor without these changes, and use the arrow keys to navigate around (esp. up/down, as it loads 7 cards at a time).  Then try again with these mods, you'll see the cards flicker briefly to the back card image and then load as they scroll onto the screen.
This commit is contained in:
wrenczes@gmail.com
2011-04-19 07:12:05 +00:00
parent 68060e5b6c
commit 7a10993114
8 changed files with 304 additions and 183 deletions
+4
View File
@@ -90,6 +90,7 @@ public:
UnthreadedCardRetriever(WCache<WCachedTexture,JTexture>& inCache) UnthreadedCardRetriever(WCache<WCachedTexture,JTexture>& inCache)
: CardRetrieverBase(inCache) : CardRetrieverBase(inCache)
{ {
DebugTrace("Unthreaded version");
} }
virtual ~UnthreadedCardRetriever() virtual ~UnthreadedCardRetriever()
@@ -115,6 +116,7 @@ public:
ThreadedCardRetriever(WCache<WCachedTexture,JTexture>& inCache) ThreadedCardRetriever(WCache<WCachedTexture,JTexture>& inCache)
: CardRetrieverBase(inCache), mProcessing(true) : CardRetrieverBase(inCache), mProcessing(true)
{ {
DebugTrace("Threaded Version");
mWorkerThread = boost::thread(ThreadProc, this); mWorkerThread = boost::thread(ThreadProc, this);
} }
@@ -186,7 +188,9 @@ protected:
// not sure this is necessary, adding it to potentially prevent SIGHUP on the psp // not sure this is necessary, adding it to potentially prevent SIGHUP on the psp
// rumour has it that if a worker thread doesn't allow the main thread a chance to run, it can hang the unit // rumour has it that if a worker thread doesn't allow the main thread a chance to run, it can hang the unit
#ifdef PSPENV
boost::this_thread::sleep(boost::posix_time::milliseconds(10)); boost::this_thread::sleep(boost::posix_time::milliseconds(10));
#endif
} }
boost::this_thread::sleep(kIdleTime); boost::this_thread::sleep(kIdleTime);
+70 -12
View File
@@ -9,6 +9,10 @@
#include "WCachedResource.h" #include "WCachedResource.h"
#include "WFont.h" #include "WFont.h"
#include <sstream>
#include "Threading.h"
#define HUGE_CACHE_LIMIT 20000000 // Size of the cache for Windows and Linux #define HUGE_CACHE_LIMIT 20000000 // Size of the cache for Windows and Linux
#define SAMPLES_CACHE_SIZE 1500000 // Size in bytes of the cached samples #define SAMPLES_CACHE_SIZE 1500000 // Size in bytes of the cached samples
#define PSI_CACHE_SIZE 500000 // Size in bytes of the cached particles #define PSI_CACHE_SIZE 500000 // Size in bytes of the cached particles
@@ -56,7 +60,7 @@ enum ENUM_RETRIEVE_STYLE
enum ENUM_CACHE_SUBTYPE enum ENUM_CACHE_SUBTYPE
{ {
CACHE_NORMAL = (1<<0), //Use default values. Not really a flag. CACHE_NORMAL = (1<<0), //Use default values. Not really a flag.
CACHE_EXISTING = (1<<1), //Retrieve it only if it already exists //CACHE_EXISTING = (1<<1), //Retrieve it only if it already exists
//Because these bits only modify how a cached resource's Attempt() is called, //Because these bits only modify how a cached resource's Attempt() is called,
//We can use them over and over for each resource type. //We can use them over and over for each resource type.
@@ -89,12 +93,14 @@ class WCache
{ {
public: public:
friend class WResourceManager; friend class WResourceManager;
friend class ThreadedCardRetriever;
friend class UnthreadedCardRetriever;
WCache(); WCache();
~WCache(); ~WCache();
cacheItem* Retrieve(int id, const string& filename, int style = RETRIEVE_NORMAL, int submode = CACHE_NORMAL); //Primary interface function. cacheItem* Retrieve(int id, const string& filename, int style = RETRIEVE_NORMAL, int submode = CACHE_NORMAL); //Primary interface function.
bool Release(cacheActual* actual); //Releases an item, and deletes it if unlocked. bool Release(cacheActual* actual); //Releases an item, and deletes it if unlocked.
bool RemoveMiss(int id=0); //Removes a cache miss. bool RemoveMiss(int id=0); //Removes a cache miss.
bool RemoveOldest(); //Remove oldest unlocked item. bool RemoveOldest(); //Remove oldest unlocked item.
bool Cleanup(); //Repeats RemoveOldest() until cache fits in size limits bool Cleanup(); //Repeats RemoveOldest() until cache fits in size limits
@@ -102,18 +108,63 @@ public:
void Refresh(); //Refreshes all cache items. void Refresh(); //Refreshes all cache items.
unsigned int Flatten(); //Ensures that the times don't loop. Returns new lastTime. unsigned int Flatten(); //Ensures that the times don't loop. Returns new lastTime.
void Resize(unsigned long size, int items); //Sets new limits, then enforces them. Lock safe, so not a "hard limit". void Resize(unsigned long size, int items); //Sets new limits, then enforces them. Lock safe, so not a "hard limit".
protected: protected:
bool RemoveItem(cacheItem * item, bool force = true); //Removes an item, deleting it. if(force), ignores locks / permanent
bool UnlinkCache(cacheItem * item); //Removes an item from our cache, does not delete it. Use with care. cacheItem* LoadIntoCache(int id, const string& filename, int submode, int style = RETRIEVE_NORMAL);
bool Delete(cacheItem * item); //SAFE_DELETE and garbage collect. If maxCached == 0, nullify first. (This means you have to free that cacheActual later!)
/*
** Attempts a new cache item, progressively clearing cache if it fails.
*/
cacheItem* AttemptNew(const string& filename, int submode);
bool RemoveItem(cacheItem* item, bool force = true); //Removes an item, deleting it. if(force), ignores locks / permanent
bool UnlinkCache(cacheItem* item); //Removes an item from our cache, does not delete it. Use with care.
bool Delete(cacheItem* item); //SAFE_DELETE and garbage collect. If maxCached == 0, nullify first. (This means you have to free that cacheActual later!)
cacheItem* Get(int id, const string& filename, int style = RETRIEVE_NORMAL, int submode = CACHE_NORMAL); //Subordinate to Retrieve. cacheItem* Get(int id, const string& filename, int style = RETRIEVE_NORMAL, int submode = CACHE_NORMAL); //Subordinate to Retrieve.
cacheItem* AttemptNew(const string& filename, int submode); //Attempts a new cache item, progressively clearing cache if it fails.
int makeID(int id, const string& filename, int submode); //Makes an ID appropriate to the submode. int makeID(int id, const string& filename, int submode); //Makes an ID appropriate to the submode.
map<string, int> ids; inline bool RequiresMissCleanup()
map<int, cacheItem*> cache; {
map<int, cacheItem*> managed; //Cache can be arbitrarily large, so managed items are seperate. return (cacheItems < cache.size() /*&& cache.size() - cacheItems > MAX_CACHE_MISSES*/);
}
inline bool RequiresOldItemCleanup()
{
if (cacheItems > MAX_CACHE_OBJECTS || cacheItems > maxCached || cacheSize > maxCacheSize)
{
std::ostringstream stream;
if (cacheItems > MAX_CACHE_OBJECTS)
{
stream << "CacheItems exceeded 300, cacheItems = " << cacheItems;
}
else if (cacheItems > maxCached)
{
stream << "CacheItems (" << cacheItems << ") exceeded arbitrary limit of " << maxCached;
}
else
{
stream << "Cache size (" << cacheSize << ") exceeded max value of " << maxCacheSize;
}
LOG(stream.str().c_str());
return true;
}
#if PSPENV
if (ramAvailableLineareMax() < MIN_LINEAR_RAM)
{
DebugTrace("Memory below minimum threshold!!");
return true;
}
#endif
return false;
}
map<string,int> ids;
map<int,cacheItem*> cache;
map<int,cacheItem*> managed; //Cache can be arbitrarily large, so managed items are separate.
unsigned long totalSize; unsigned long totalSize;
unsigned long cacheSize; unsigned long cacheSize;
@@ -123,6 +174,11 @@ protected:
unsigned int cacheItems; unsigned int cacheItems;
int mError; int mError;
// mutex meant for the cache map
boost::mutex mCacheMutex;
// mutex meant to protect against unthread-safe calls into JFileSystem, etc.
boost::mutex mLoadFunctionMutex;
}; };
struct WManagedQuad struct WManagedQuad
@@ -152,6 +208,8 @@ public:
virtual ~WResourceManager(); virtual ~WResourceManager();
bool IsThreaded();
void Unmiss(string filename); void Unmiss(string filename);
JQuadPtr RetrieveCard(MTGCard * card, int style = RETRIEVE_NORMAL,int submode = CACHE_NORMAL); JQuadPtr RetrieveCard(MTGCard * card, int style = RETRIEVE_NORMAL,int submode = CACHE_NORMAL);
JSample * RetrieveSample(const string& filename, int style = RETRIEVE_NORMAL, int submode = CACHE_NORMAL); JSample * RetrieveSample(const string& filename, int style = RETRIEVE_NORMAL, int submode = CACHE_NORMAL);
@@ -224,8 +282,8 @@ private:
*/ */
WResourceManager(); WResourceManager();
bool bThemedCards; //Does the theme have a "sets" directory for overwriting cards? bool bThemedCards; //Does the theme have a "sets" directory for overwriting cards?
void FlattenTimes(); //To prevent bad cache timing on int overflow void FlattenTimes(); //To prevent bad cache timing on int overflow
//For cached stuff //For cached stuff
WCache<WCachedTexture,JTexture> textureWCache; WCache<WCachedTexture,JTexture> textureWCache;
+1 -9
View File
@@ -776,15 +776,7 @@ void CardGui::RenderBig(MTGCard* card, const Pos& pos)
return; return;
} }
//No card found, attempt to render the thumbnail instead (better than nothing, even if it gets super stretched) DebugTrace("Unable to fetch image: " << card->getImageName());
JQuadPtr q;
if ((q = WResourceManager::Instance()->RetrieveCard(card, CACHE_THUMB)))
{
float scale = pos.actZ * 250 / q->mHeight;
q->SetColor(ARGB(255,255,255,255));
renderer->RenderQuad(q.get(), x, pos.actY, pos.actT, scale, scale);
return;
}
// If we come here, we do not have the picture. // If we come here, we do not have the picture.
AlternateRender(card, pos); AlternateRender(card, pos);
+1
View File
@@ -91,6 +91,7 @@ void GameApp::Create()
//_CrtSetBreakAlloc(368); //_CrtSetBreakAlloc(368);
LOG("starting Game"); LOG("starting Game");
WResourceManager::Instance()->ResetCacheLimits();
//Find the Res folder //Find the Res folder
wagic::ifstream mfile("Res.txt"); wagic::ifstream mfile("Res.txt");
string resPath; string resPath;
+54 -41
View File
@@ -21,8 +21,7 @@
#include "SimpleMenu.h" #include "SimpleMenu.h"
#include "utils.h" #include "utils.h"
// This is pending a change by Wil regarding graphics threads
#define GRAPHICS_NO_THREADING
//!! helper function; this is probably handled somewhere in the code already. //!! helper function; this is probably handled somewhere in the code already.
// If not, should be placed in general library // If not, should be placed in general library
@@ -1314,63 +1313,64 @@ void GameStateDeckViewer::renderCard(int id, float rotation)
int alpha = (int) (255 * (scale + 1.0 - max_scale)); int alpha = (int) (255 * (scale + 1.0 - max_scale));
if (!card) return; if (!card) return;
#ifdef GRAPHICS_NO_THREADING if (!WResourceManager::Instance()->IsThreaded())
JQuadPtr backQuad = WResourceManager::Instance()->GetQuad("back");
JQuadPtr quad;
int cacheError = CACHE_ERROR_NONE;
if (!options[Options::DISABLECARDS].number)
{ {
quad = WResourceManager::Instance()->RetrieveCard(card, RETRIEVE_EXISTING); JQuadPtr backQuad = WResourceManager::Instance()->GetQuad("back");
cacheError = WResourceManager::Instance()->RetrieveError(); JQuadPtr quad;
if (!quad.get() && cacheError != CACHE_ERROR_404)
int cacheError = CACHE_ERROR_NONE;
if (!options[Options::DISABLECARDS].number)
{ {
if (last_user_activity > (abs(2 - id) + 1) * NO_USER_ACTIVITY_SHOWCARD_DELAY) quad = WResourceManager::Instance()->RetrieveCard(card, RETRIEVE_EXISTING);
quad = WResourceManager::Instance()->RetrieveCard(card); cacheError = WResourceManager::Instance()->RetrieveError();
else if (!quad.get() && cacheError != CACHE_ERROR_404)
{ {
quad = backQuad; if (last_user_activity > (abs(2 - id) + 1) * NO_USER_ACTIVITY_SHOWCARD_DELAY)
quad = WResourceManager::Instance()->RetrieveCard(card);
else
{
quad = backQuad;
}
} }
} }
}
if (quad.get()) if (quad.get())
{
if (quad == backQuad)
{ {
quad->SetColor(ARGB(255,255,255,255)); if (quad == backQuad)
float _scale = scale * (285 / quad->mHeight); {
JRenderer::GetInstance()->RenderQuad(quad.get(), x, y, 0.0f, _scale, _scale); quad->SetColor(ARGB(255,255,255,255));
float _scale = scale * (285 / quad->mHeight);
JRenderer::GetInstance()->RenderQuad(quad.get(), x, y, 0.0f, _scale, _scale);
}
else
{
Pos pos = Pos(x, y, scale * 285 / 250, 0.0, 255);
CardGui::DrawCard(card, pos);
}
} }
else else
{ {
Pos pos = Pos(x, y, scale * 285 / 250, 0.0, 255); Pos pos = Pos(x, y, scale * 285 / 250, 0.0, 255);
CardGui::DrawCard(card, pos); CardGui::DrawCard(card, pos, DrawMode::kText);
if (!options[Options::DISABLECARDS].number) quad = WResourceManager::Instance()->RetrieveCard(card, CACHE_THUMB);
if (quad.get())
{
float _scale = 285 * scale / quad->mHeight;
quad->SetColor(ARGB(40,255,255,255));
JRenderer::GetInstance()->RenderQuad(quad.get(), x, y, 0, _scale, _scale);
}
} }
} }
else else
{ {
int mode = !options[Options::DISABLECARDS].number ? DrawMode::kNormal : DrawMode::kText;
Pos pos = Pos(x, y, scale * 285 / 250, 0.0, 255); Pos pos = Pos(x, y, scale * 285 / 250, 0.0, 255);
CardGui::DrawCard(card, pos, DrawMode::kText); CardGui::DrawCard(card, pos, mode);
if (!options[Options::DISABLECARDS].number) quad = WResourceManager::Instance()->RetrieveCard(card, CACHE_THUMB);
if (quad.get())
{
float _scale = 285 * scale / quad->mHeight;
quad->SetColor(ARGB(40,255,255,255));
JRenderer::GetInstance()->RenderQuad(quad.get(), x, y, 0, _scale, _scale);
}
} }
#else
int mode = !options[Options::DISABLECARDS].number ? DrawMode::kNormal : DrawMode::kText;
Pos pos = Pos(x, y, scale * 285 / 250, 0.0, 255);
CardGui::DrawCard(card, pos, mode);
#endif
int quadAlpha = alpha; int quadAlpha = alpha;
if (!displayed_deck->count(card)) quadAlpha /= 2; if (!displayed_deck->count(card)) quadAlpha /= 2;
quadAlpha = 255 - quadAlpha; quadAlpha = 255 - quadAlpha;
@@ -1421,6 +1421,19 @@ void GameStateDeckViewer::Render()
order[2] = 1; order[2] = 1;
} }
// even though we want to draw the cards in a particular z order for layering, we want to prefetch them
// in a different order, ie the center card should appear first, then the adjacent ones
if (WResourceManager::Instance()->IsThreaded())
{
WResourceManager::Instance()->RetrieveCard(cardIndex[0]);
WResourceManager::Instance()->RetrieveCard(cardIndex[3]);
WResourceManager::Instance()->RetrieveCard(cardIndex[4]);
WResourceManager::Instance()->RetrieveCard(cardIndex[2]);
WResourceManager::Instance()->RetrieveCard(cardIndex[5]);
WResourceManager::Instance()->RetrieveCard(cardIndex[1]);
WResourceManager::Instance()->RetrieveCard(cardIndex[6]);
}
renderCard(6, mRotation); renderCard(6, mRotation);
renderCard(5, mRotation); renderCard(5, mRotation);
renderCard(4, mRotation); renderCard(4, mRotation);
+8 -7
View File
@@ -56,13 +56,14 @@ WSrcCards::WSrcCards(float delay)
JQuadPtr WSrcCards::getImage(int offset) JQuadPtr WSrcCards::getImage(int offset)
{ {
#if defined WIN32 || defined LINUX //Loading delay only on PSP. if (!WResourceManager::Instance()->IsThreaded())
#else {
if (mDelay && mLastInput < mDelay) if (mDelay && mLastInput < mDelay)
{ {
return WResourceManager::Instance()->RetrieveCard(getCard(offset), RETRIEVE_EXISTING); return WResourceManager::Instance()->RetrieveCard(getCard(offset), RETRIEVE_EXISTING);
} }
#endif }
return WResourceManager::Instance()->RetrieveCard(getCard(offset)); return WResourceManager::Instance()->RetrieveCard(getCard(offset));
} }
+147 -114
View File
@@ -1,9 +1,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include <JFileSystem.h>
#include "utils.h"
#include "GameOptions.h" #include "GameOptions.h"
#include "CacheEngine.h"
#include "WResourceManager.h" #include "WResourceManager.h"
#include "StyleManager.h" #include "StyleManager.h"
@@ -12,9 +10,8 @@
#include <sys/stat.h> #include <sys/stat.h>
#endif #endif
#include "WFont.h" #include "WFont.h"
#include <JLogger.h>
//#define FORCE_LOW_CACHE_MEMORY #define FORCE_LOW_CACHE_MEMORY
const unsigned int kConstrainedCacheLimit = 8 * 1024 * 1024; const unsigned int kConstrainedCacheLimit = 8 * 1024 * 1024;
extern bool neofont; extern bool neofont;
@@ -25,6 +22,9 @@ namespace
const std::string kExtension_png(".png"); const std::string kExtension_png(".png");
const std::string kExtension_gbk(".gbk"); const std::string kExtension_gbk(".gbk");
const std::string kExtension_font(".font"); const std::string kExtension_font(".font");
const std::string kGenericCard("back.jpg");
const std::string kGenericThumbCard("back_thumb.jpg");
} }
WResourceManager* WResourceManager::sInstance = NULL; WResourceManager* WResourceManager::sInstance = NULL;
@@ -48,10 +48,9 @@ void WResourceManager::DebugRender()
{ {
JRenderer* renderer = JRenderer::GetInstance(); JRenderer* renderer = JRenderer::GetInstance();
WFont * font = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT); WFont * font = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT);
font->SetColor(ARGB(255,255,255,255));
if (!font || !renderer) return; if (!font || !renderer) return;
font->SetColor(ARGB(255,255,255,255));
font->SetScale(DEFAULT_MAIN_FONT_SCALE); font->SetScale(DEFAULT_MAIN_FONT_SCALE);
renderer->FillRect(0, 0, SCREEN_WIDTH, 40, ARGB(128,155,0,0)); renderer->FillRect(0, 0, SCREEN_WIDTH, 40, ARGB(128,155,0,0));
@@ -70,11 +69,11 @@ void WResourceManager::DebugRender()
textureWCache.cacheSize, textureWCache.maxCacheSize, man); textureWCache.cacheSize, textureWCache.maxCacheSize, man);
font->DrawString(buf, 10, 5); font->DrawString(buf, 10, 5);
#if defined (WIN32) || defined (LINUX) || defined (IOS) #if PSPENV
#else //int maxLinear = ramAvailableLineareMax();
int maxLinear = ramAvailableLineareMax(); //int ram = ramAvailable();
int ram = ramAvailable();
sprintf(buf, "Ram : linear max: %i - total : %i\n", maxLinear, ram); //sprintf(buf, "Ram : linear max: %i - total : %i sceSize : %i\n", maxLinear, ram, sceSize);
font->DrawString(buf, 10, 20); font->DrawString(buf, 10, 20);
#endif #endif
@@ -180,6 +179,14 @@ WResourceManager::WResourceManager()
lastError = CACHE_ERROR_NONE; lastError = CACHE_ERROR_NONE;
bThemedCards = false; bThemedCards = false;
LOG("Calling CacheEngine::Create");
#ifdef PSPENV
CacheEngine::Create<UnthreadedCardRetriever>(textureWCache);
#else
CacheEngine::Create<ThreadedCardRetriever>(textureWCache);
#endif
} }
WResourceManager::~WResourceManager() WResourceManager::~WResourceManager()
@@ -187,9 +194,15 @@ WResourceManager::~WResourceManager()
LOG("==Destroying WResourceManager=="); LOG("==Destroying WResourceManager==");
RemoveWFonts(); RemoveWFonts();
CacheEngine::Terminate();
LOG("==Successfully Destroyed WResourceManager=="); LOG("==Successfully Destroyed WResourceManager==");
} }
bool WResourceManager::IsThreaded()
{
return CacheEngine::IsThreaded();
}
JQuadPtr WResourceManager::RetrieveCard(MTGCard * card, int style, int submode) JQuadPtr WResourceManager::RetrieveCard(MTGCard * card, int style, int submode)
{ {
//Cards are never, ever resource managed, so just check cache. //Cards are never, ever resource managed, so just check cache.
@@ -197,9 +210,7 @@ JQuadPtr WResourceManager::RetrieveCard(MTGCard * card, int style, int submode)
submode = submode | TEXTURE_SUB_CARD; submode = submode | TEXTURE_SUB_CARD;
string filename = setlist[card->setId]; string filename = setlist[card->setId] + "/" + card->getImageName();
filename += "/";
string filename1 = filename + card->getImageName();
int id = card->getMTGId(); int id = card->getMTGId();
//Aliases. //Aliases.
@@ -209,40 +220,8 @@ JQuadPtr WResourceManager::RetrieveCard(MTGCard * card, int style, int submode)
style = RETRIEVE_NORMAL; style = RETRIEVE_NORMAL;
} }
//Hack to allow either ID or card name as a filename for a given card. JQuadPtr jq = RetrieveQuad(filename, 0, 0, 0, 0, "", style, submode | TEXTURE_SUB_5551, id);
// When missing the first attempt (for example [id].jpg), the cache assigns a "404" to the card's image cache,
// Preventing us to try a second time with [name].jpg.
//To bypass this, we first check if the card was ever marked as "null". If not, it means it's the first time we're looking for it
// In that case, we "unmiss" it after trying the [id].jpg, in order to give a chance to the [name.jpg]
bool canUnmiss = false;
{
JQuadPtr tempQuad = RetrieveQuad(filename1, 0, 0, 0, 0, "", RETRIEVE_EXISTING, submode | TEXTURE_SUB_5551, id);
lastError = textureWCache.mError;
if (!tempQuad && lastError != CACHE_ERROR_404)
{
canUnmiss = true;
}
}
JQuadPtr jq = RetrieveQuad(filename1, 0, 0, 0, 0, "", style, submode | TEXTURE_SUB_5551, id);
if (!jq)
{
if (canUnmiss)
{
int mId = id;
//To differentiate between cached thumbnails and the real thing.
if (submode & TEXTURE_SUB_THUMB)
{
if (mId < 0)
mId -= THUMBNAILS_OFFSET;
else
mId += THUMBNAILS_OFFSET;
}
textureWCache.RemoveMiss(mId);
}
filename1 = filename + card->data->getName() + ".jpg";
jq = RetrieveQuad(filename1, 0, 0, 0, 0, "", style, submode | TEXTURE_SUB_5551, id);
}
lastError = textureWCache.mError; lastError = textureWCache.mError;
if (jq) if (jq)
{ {
@@ -353,11 +332,7 @@ JQuadPtr WResourceManager::RetrieveQuad(const string& filename, float offX, floa
if (!resname.size()) resname = filename; if (!resname.size()) resname = filename;
//No quad, but we have a managed texture for this! //No quad, but we have a managed texture for this!
WCachedTexture * jtex = NULL; WCachedTexture* jtex = textureWCache.Retrieve(id, filename, style, submode);
if (style == RETRIEVE_MANAGE || style == RETRIEVE_EXISTING)
jtex = textureWCache.Retrieve(id, filename, style, submode);
else
jtex = textureWCache.Retrieve(id, filename, RETRIEVE_NORMAL, submode);
lastError = textureWCache.mError; lastError = textureWCache.mError;
@@ -939,13 +914,15 @@ void WResourceManager::ResetCacheLimits()
textureWCache.Resize(HUGE_CACHE_LIMIT,MAX_CACHE_OBJECTS); textureWCache.Resize(HUGE_CACHE_LIMIT,MAX_CACHE_OBJECTS);
#endif #endif
#else #else
unsigned int ram = ramAvailable(); static unsigned int ram(ramAvailable() / 2);
unsigned int myNewSize = ram - OPERATIONAL_SIZE + textureWCache.totalSize; unsigned int myNewSize = ram - OPERATIONAL_SIZE;
if (myNewSize < TEXTURES_CACHE_MINSIZE) if (myNewSize < TEXTURES_CACHE_MINSIZE)
{ {
fprintf(stderr, "Error, Not enough RAM for Cache: %i - total Ram: %i\n", myNewSize, ram); DebugTrace( "Error, Not enough RAM for Cache: " << myNewSize << " - total Ram: " << ram);
} }
textureWCache.Resize(myNewSize, MAX_CACHE_OBJECTS); textureWCache.Resize(MIN(myNewSize, HUGE_CACHE_LIMIT), MAX_CACHE_OBJECTS);
DebugTrace("Texture cache resized to " << myNewSize);
#endif #endif
return; return;
} }
@@ -990,6 +967,11 @@ bool WCache<cacheItem, cacheActual>::RemoveOldest()
if (oldest != cache.end() && oldest->second && !oldest->second->isLocked()) if (oldest != cache.end() && oldest->second && !oldest->second->isLocked())
{ {
#ifdef DEBUG_CACHE
std::ostringstream stream;
stream << "erasing from cache: " << oldest->second->mFilename << " " << oldest->first;
LOG(stream.str().c_str());
#endif
Delete(oldest->second); Delete(oldest->second);
cache.erase(oldest); cache.erase(oldest);
return true; return true;
@@ -1024,6 +1006,12 @@ void WCache<cacheItem, cacheActual>::Resize(unsigned long size, int items)
{ {
maxCacheSize = size; maxCacheSize = size;
#ifdef DEBUG_CACHE
std::ostringstream stream;
stream << "Max cache limit resized to " << size << ", items limit reset to " << items;
LOG(stream.str().c_str());
#endif
if (items > MAX_CACHE_OBJECTS || items < 1) if (items > MAX_CACHE_OBJECTS || items < 1)
maxCached = MAX_CACHE_OBJECTS; maxCached = MAX_CACHE_OBJECTS;
else else
@@ -1033,12 +1021,6 @@ void WCache<cacheItem, cacheActual>::Resize(unsigned long size, int items)
template<class cacheItem, class cacheActual> template<class cacheItem, class cacheActual>
cacheItem* WCache<cacheItem, cacheActual>::AttemptNew(const string& filename, int submode) cacheItem* WCache<cacheItem, cacheActual>::AttemptNew(const string& filename, int submode)
{ {
if (submode & CACHE_EXISTING)
{ //Should never get this far.
mError = CACHE_ERROR_NOT_CACHED;
return NULL;
}
cacheItem* item = NEW cacheItem; cacheItem* item = NEW cacheItem;
if (!item) if (!item)
{ {
@@ -1053,25 +1035,22 @@ cacheItem* WCache<cacheItem, cacheActual>::AttemptNew(const string& filename, in
//No such file. Fail //No such file. Fail
if (mError == CACHE_ERROR_404) if (mError == CACHE_ERROR_404)
{ {
DebugTrace("AttemptNew failed to load. Deleting cache item " << ToHex(item));
SAFE_DELETE(item); SAFE_DELETE(item);
return NULL; return NULL;
} }
//Probably not enough memory: cleanup and try again else
Cleanup();
mError = CACHE_ERROR_NONE;
if (!item->Attempt(filename, submode, mError) || !item->isGood())
{ {
DebugTrace("AttemptNew failed to load (not a 404 error). Deleting cache item " << ToHex(item));
SAFE_DELETE(item); SAFE_DELETE(item);
mError = CACHE_ERROR_BAD; mError = CACHE_ERROR_BAD;
return NULL; return NULL;
} }
} }
//Success! Enforce cache limits, then return. //Success! Enforce cache limits, then return.
mError = CACHE_ERROR_NONE; mError = CACHE_ERROR_NONE;
item->lock();
Cleanup();
item->unlock();
return item; return item;
} }
@@ -1079,13 +1058,8 @@ template<class cacheItem, class cacheActual>
cacheItem* WCache<cacheItem, cacheActual>::Retrieve(int id, const string& filename, int style, int submode) cacheItem* WCache<cacheItem, cacheActual>::Retrieve(int id, const string& filename, int style, int submode)
{ {
//Check cache. //Check cache.
cacheItem * tc = NULL;
mError = CACHE_ERROR_NONE; //Reset error status. mError = CACHE_ERROR_NONE; //Reset error status.
cacheItem* tc = Get(id, filename, style, submode);
if (style == RETRIEVE_EXISTING || style == RETRIEVE_RESOURCE)
tc = Get(id, filename, style, submode | CACHE_EXISTING);
else
tc = Get(id, filename, style, submode);
//Retrieve resource only works on permanent items. //Retrieve resource only works on permanent items.
if (style == RETRIEVE_RESOURCE && tc && !tc->isPermanent()) if (style == RETRIEVE_RESOURCE && tc && !tc->isPermanent())
@@ -1153,14 +1127,9 @@ int WCache<cacheItem, cacheActual>::makeID(int id, const string& filename, int s
} }
} }
//To differentiate between cached thumbnails and the real thing.
if (submode & TEXTURE_SUB_THUMB) if (submode & TEXTURE_SUB_THUMB)
{ mId += THUMBNAILS_OFFSET;
if (mId < 0)
mId -= THUMBNAILS_OFFSET;
else
mId += THUMBNAILS_OFFSET;
}
return mId; return mId;
} }
@@ -1179,38 +1148,73 @@ cacheItem* WCache<cacheItem, cacheActual>::Get(int id, const string& filename, i
return it->second; //A hit. return it->second; //A hit.
} }
//Failed to find managed resource and won't create one. Record a miss.
if (style == RETRIEVE_RESOURCE)
{
managed[lookup] = NULL;
return NULL;
}
//Not managed, so look in cache. //Not managed, so look in cache.
if (style != RETRIEVE_MANAGE) if (style != RETRIEVE_MANAGE)
{ {
boost::mutex::scoped_lock lock(mCacheMutex);
//DebugTrace("Cache lock acquired, looking up index " << lookup);
it = cache.find(lookup); it = cache.find(lookup);
//Well, we've found something... //Well, we've found something...
if (it != cache.end()) if (it != cache.end())
{ {
if (!it->second) mError = CACHE_ERROR_404; if (!it->second)
{
mError = CACHE_ERROR_404;
DebugTrace("cache hit, no item??");
//assert(false);
}
return it->second; //A hit, or maybe a miss. return it->second; //A hit, or maybe a miss.
} }
} }
//Didn't exist in cache. // not hit in the cache, respect the RETRIEVE_EXISTING flag if present
if (submode & CACHE_EXISTING) if (style == RETRIEVE_EXISTING)
{ {
mError = CACHE_ERROR_NOT_CACHED;
return NULL; return NULL;
} }
//Space in cache, make new texture // no hit in cache, clear space before attempting to load a new one
cacheItem * item = AttemptNew(filename, submode); // note: Cleanup() should ONLY be ever called on the main (UI) thread!
Cleanup();
// check if we're doing a card lookup
if (submode & TEXTURE_SUB_CARD)
{
// processing a cache miss, return a generic card & queue up an async read
// side note: using a string reference here to a global predefined string, as basic_string is not thread safe for allocations!
const std::string& cardPath = (submode & TEXTURE_SUB_THUMB) ? kGenericThumbCard : kGenericCard;
int genericCardId = makeID(0, cardPath, CACHE_NORMAL);
it = managed.find(genericCardId);
assert(it != managed.end());
CacheEngine::Instance()->QueueRequest(filename, submode, lookup);
return it->second;
}
//Space in cache, make new texture
return LoadIntoCache(lookup, filename, submode, style);
}
template<class cacheItem, class cacheActual>
cacheItem* WCache<cacheItem, cacheActual>::LoadIntoCache(int id, const string& filename, int submode, int style)
{
// note: my original implementation only had one lock (the cache mutex lock) - I eventually
// added this second one, as locking at the Get() call means that the main thread is blocked on doing a simple cache
// check. If you're hitting the system hard (like, paging up in the deck editor which forces 7 cards to load simultaneously),
// we'd block the UI thread for a long period at this point. So I moved the cache mutex to lock specifically only attempts to touch
// the shared cache container, and this separate lock was added to insulate us against thread safety issues in JGE. In particular,
// JFileSystem is particularly unsafe, as it assumes that we have only one zip loaded at a time... rather than add mutexes in JGE,
// I've kept it local to here.
boost::mutex::scoped_lock functionLock(mLoadFunctionMutex);
cacheItem* item = AttemptNew(filename, submode);
//assert(item);
if (style == RETRIEVE_MANAGE) if (style == RETRIEVE_MANAGE)
{ {
if (mError == CACHE_ERROR_404 || item) managed[lookup] = item; //Record a hit or miss. if (mError == CACHE_ERROR_404 || item)
{
managed[id] = item; //Record a hit or miss.
}
if (item) if (item)
{ {
item->deadbolt(); //Make permanent. item->deadbolt(); //Make permanent.
@@ -1218,10 +1222,25 @@ cacheItem* WCache<cacheItem, cacheActual>::Get(int id, const string& filename, i
} }
else else
{ {
if (mError == CACHE_ERROR_404 || item) cache[lookup] = item; if (mError == CACHE_ERROR_404 || item)
{
boost::mutex::scoped_lock lock(mCacheMutex);
cache[id] = item;
DebugTrace("inserted item ptr " << ToHex(item) << " at index " << id);
}
} }
if (!item) return NULL; //Failure if (item == NULL)
{
DebugTrace("Can't locate ");
if (submode & TEXTURE_SUB_THUMB)
{
DebugTrace("thumbnail ");
}
DebugTrace(filename);
return NULL; //Failure
}
//Succeeded in making a new item. //Succeeded in making a new item.
unsigned long isize = item->size(); unsigned long isize = item->size();
@@ -1234,6 +1253,12 @@ cacheItem* WCache<cacheItem, cacheActual>::Get(int id, const string& filename, i
cacheSize += isize; cacheSize += isize;
} }
#ifdef DEBUG_CACHE
std::ostringstream stream;
stream << "Cache insert: " << filename << " " << id << ", cacheItem count: " << cacheItems << ", cacheSize is now: " << cacheSize;
LOG(stream.str().c_str());
#endif
return item; return item;
} }
@@ -1292,24 +1317,31 @@ WCache<cacheItem, cacheActual>::~WCache()
template<class cacheItem, class cacheActual> template<class cacheItem, class cacheActual>
bool WCache<cacheItem, cacheActual>::Cleanup() bool WCache<cacheItem, cacheActual>::Cleanup()
{ {
while (cacheItems < cache.size() && cache.size() - cacheItems > MAX_CACHE_MISSES) bool result = true;
// this looks redundant, but the idea is, don't grab the mutex if there's no work to do
if (RequiresMissCleanup())
{ {
RemoveMiss(); boost::mutex::scoped_lock lock(mCacheMutex);
} while (RequiresMissCleanup())
while (cacheItems > MAX_CACHE_OBJECTS || cacheItems > maxCached || cacheSize > maxCacheSize
#if defined WIN32 || defined LINUX || defined (IOS)
#else
|| ramAvailableLineareMax() < MIN_LINEAR_RAM
#endif
)
{
if (!RemoveOldest())
{ {
return false; RemoveMiss();
} }
} }
return true;
if (RequiresOldItemCleanup())
{
boost::mutex::scoped_lock lock(mCacheMutex);
while (RequiresOldItemCleanup())
{
if (!RemoveOldest())
{
result = false;
break;
}
}
}
return result;
} }
bool WCacheSort::operator()(const WResource * l, const WResource * r) bool WCacheSort::operator()(const WResource * l, const WResource * r)
@@ -1425,6 +1457,7 @@ bool WCache<cacheItem, cacheActual>::Delete(cacheItem * item)
cacheItems--; cacheItems--;
DebugTrace("Deleting cache item " << ToHex(item));
SAFE_DELETE(item); SAFE_DELETE(item);
return true; return true;
} }
+19
View File
@@ -7,6 +7,10 @@
#include "WFont.h" #include "WFont.h"
#include <sys/stat.h> #include <sys/stat.h>
#ifdef PSPENV
#include "pspsdk.h"
#endif
namespace wagic namespace wagic
{ {
#ifdef TRACK_FILE_USAGE_STATS #ifdef TRACK_FILE_USAGE_STATS
@@ -162,6 +166,10 @@ u32 ramAvailableLineareMax(void)
size = 0; size = 0;
sizeblock = RAM_BLOCK; sizeblock = RAM_BLOCK;
#ifdef PSPENV
int disableInterrupts = pspSdkDisableInterrupts();
#endif
// Check loop // Check loop
while (sizeblock) while (sizeblock)
{ {
@@ -184,6 +192,10 @@ u32 ramAvailableLineareMax(void)
free(ram); free(ram);
} }
#ifdef PSPENV
pspSdkEnableInterrupts(disableInterrupts);
#endif
return size; return size;
} }
@@ -197,6 +209,10 @@ u32 ramAvailable(void)
size = 0; size = 0;
count = 0; count = 0;
#ifdef PSPENV
int disableInterrupts = pspSdkDisableInterrupts();
#endif
// Check loop // Check loop
for (;;) for (;;)
{ {
@@ -233,6 +249,9 @@ u32 ramAvailable(void)
free(ram); free(ram);
} }
#ifdef PSPENV
pspSdkEnableInterrupts(disableInterrupts);
#endif
return size; return size;
} }