*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

View File

@@ -90,6 +90,7 @@ public:
UnthreadedCardRetriever(WCache<WCachedTexture,JTexture>& inCache)
: CardRetrieverBase(inCache)
{
DebugTrace("Unthreaded version");
}
virtual ~UnthreadedCardRetriever()
@@ -115,6 +116,7 @@ public:
ThreadedCardRetriever(WCache<WCachedTexture,JTexture>& inCache)
: CardRetrieverBase(inCache), mProcessing(true)
{
DebugTrace("Threaded Version");
mWorkerThread = boost::thread(ThreadProc, this);
}
@@ -186,7 +188,9 @@ protected:
// 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
#ifdef PSPENV
boost::this_thread::sleep(boost::posix_time::milliseconds(10));
#endif
}
boost::this_thread::sleep(kIdleTime);

View File

@@ -9,6 +9,10 @@
#include "WCachedResource.h"
#include "WFont.h"
#include <sstream>
#include "Threading.h"
#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 PSI_CACHE_SIZE 500000 // Size in bytes of the cached particles
@@ -56,7 +60,7 @@ enum ENUM_RETRIEVE_STYLE
enum ENUM_CACHE_SUBTYPE
{
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,
//We can use them over and over for each resource type.
@@ -89,12 +93,14 @@ class WCache
{
public:
friend class WResourceManager;
friend class ThreadedCardRetriever;
friend class UnthreadedCardRetriever;
WCache();
~WCache();
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 RemoveOldest(); //Remove oldest unlocked item.
bool Cleanup(); //Repeats RemoveOldest() until cache fits in size limits
@@ -102,18 +108,63 @@ public:
void Refresh(); //Refreshes all cache items.
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".
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.
bool Delete(cacheItem * item); //SAFE_DELETE and garbage collect. If maxCached == 0, nullify first. (This means you have to free that cacheActual later!)
cacheItem* LoadIntoCache(int id, const string& filename, int submode, int style = RETRIEVE_NORMAL);
/*
** 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* 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;
map<int, cacheItem*> cache;
map<int, cacheItem*> managed; //Cache can be arbitrarily large, so managed items are seperate.
inline bool RequiresMissCleanup()
{
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 cacheSize;
@@ -123,6 +174,11 @@ protected:
unsigned int cacheItems;
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
@@ -152,6 +208,8 @@ public:
virtual ~WResourceManager();
bool IsThreaded();
void Unmiss(string filename);
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);
@@ -224,8 +282,8 @@ private:
*/
WResourceManager();
bool bThemedCards; //Does the theme have a "sets" directory for overwriting cards?
void FlattenTimes(); //To prevent bad cache timing on int overflow
bool bThemedCards; //Does the theme have a "sets" directory for overwriting cards?
void FlattenTimes(); //To prevent bad cache timing on int overflow
//For cached stuff
WCache<WCachedTexture,JTexture> textureWCache;

View File

@@ -776,15 +776,7 @@ void CardGui::RenderBig(MTGCard* card, const Pos& pos)
return;
}
//No card found, attempt to render the thumbnail instead (better than nothing, even if it gets super stretched)
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;
}
DebugTrace("Unable to fetch image: " << card->getImageName());
// If we come here, we do not have the picture.
AlternateRender(card, pos);

View File

@@ -91,6 +91,7 @@ void GameApp::Create()
//_CrtSetBreakAlloc(368);
LOG("starting Game");
WResourceManager::Instance()->ResetCacheLimits();
//Find the Res folder
wagic::ifstream mfile("Res.txt");
string resPath;

View File

@@ -21,8 +21,7 @@
#include "SimpleMenu.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.
// 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));
if (!card) return;
#ifdef GRAPHICS_NO_THREADING
JQuadPtr backQuad = WResourceManager::Instance()->GetQuad("back");
JQuadPtr quad;
int cacheError = CACHE_ERROR_NONE;
if (!options[Options::DISABLECARDS].number)
if (!WResourceManager::Instance()->IsThreaded())
{
quad = WResourceManager::Instance()->RetrieveCard(card, RETRIEVE_EXISTING);
cacheError = WResourceManager::Instance()->RetrieveError();
if (!quad.get() && cacheError != CACHE_ERROR_404)
JQuadPtr backQuad = WResourceManager::Instance()->GetQuad("back");
JQuadPtr quad;
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);
else
quad = WResourceManager::Instance()->RetrieveCard(card, RETRIEVE_EXISTING);
cacheError = WResourceManager::Instance()->RetrieveError();
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 == backQuad)
if (quad.get())
{
quad->SetColor(ARGB(255,255,255,255));
float _scale = scale * (285 / quad->mHeight);
JRenderer::GetInstance()->RenderQuad(quad.get(), x, y, 0.0f, _scale, _scale);
if (quad == backQuad)
{
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
{
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
{
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, 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);
}
CardGui::DrawCard(card, pos, mode);
}
#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;
if (!displayed_deck->count(card)) quadAlpha /= 2;
quadAlpha = 255 - quadAlpha;
@@ -1421,6 +1421,19 @@ void GameStateDeckViewer::Render()
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(5, mRotation);
renderCard(4, mRotation);

View File

@@ -56,13 +56,14 @@ WSrcCards::WSrcCards(float delay)
JQuadPtr WSrcCards::getImage(int offset)
{
#if defined WIN32 || defined LINUX //Loading delay only on PSP.
#else
if (mDelay && mLastInput < mDelay)
{
return WResourceManager::Instance()->RetrieveCard(getCard(offset), RETRIEVE_EXISTING);
}
#endif
if (!WResourceManager::Instance()->IsThreaded())
{
if (mDelay && mLastInput < mDelay)
{
return WResourceManager::Instance()->RetrieveCard(getCard(offset), RETRIEVE_EXISTING);
}
}
return WResourceManager::Instance()->RetrieveCard(getCard(offset));
}

View File

@@ -1,9 +1,7 @@
#include "PrecompiledHeader.h"
#include <JFileSystem.h>
#include "utils.h"
#include "GameOptions.h"
#include "CacheEngine.h"
#include "WResourceManager.h"
#include "StyleManager.h"
@@ -12,9 +10,8 @@
#include <sys/stat.h>
#endif
#include "WFont.h"
#include <JLogger.h>
//#define FORCE_LOW_CACHE_MEMORY
#define FORCE_LOW_CACHE_MEMORY
const unsigned int kConstrainedCacheLimit = 8 * 1024 * 1024;
extern bool neofont;
@@ -25,6 +22,9 @@ namespace
const std::string kExtension_png(".png");
const std::string kExtension_gbk(".gbk");
const std::string kExtension_font(".font");
const std::string kGenericCard("back.jpg");
const std::string kGenericThumbCard("back_thumb.jpg");
}
WResourceManager* WResourceManager::sInstance = NULL;
@@ -48,10 +48,9 @@ void WResourceManager::DebugRender()
{
JRenderer* renderer = JRenderer::GetInstance();
WFont * font = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT);
font->SetColor(ARGB(255,255,255,255));
if (!font || !renderer) return;
font->SetColor(ARGB(255,255,255,255));
font->SetScale(DEFAULT_MAIN_FONT_SCALE);
renderer->FillRect(0, 0, SCREEN_WIDTH, 40, ARGB(128,155,0,0));
@@ -70,11 +69,11 @@ void WResourceManager::DebugRender()
textureWCache.cacheSize, textureWCache.maxCacheSize, man);
font->DrawString(buf, 10, 5);
#if defined (WIN32) || defined (LINUX) || defined (IOS)
#else
int maxLinear = ramAvailableLineareMax();
int ram = ramAvailable();
sprintf(buf, "Ram : linear max: %i - total : %i\n", maxLinear, ram);
#if PSPENV
//int maxLinear = ramAvailableLineareMax();
//int ram = ramAvailable();
//sprintf(buf, "Ram : linear max: %i - total : %i sceSize : %i\n", maxLinear, ram, sceSize);
font->DrawString(buf, 10, 20);
#endif
@@ -180,6 +179,14 @@ WResourceManager::WResourceManager()
lastError = CACHE_ERROR_NONE;
bThemedCards = false;
LOG("Calling CacheEngine::Create");
#ifdef PSPENV
CacheEngine::Create<UnthreadedCardRetriever>(textureWCache);
#else
CacheEngine::Create<ThreadedCardRetriever>(textureWCache);
#endif
}
WResourceManager::~WResourceManager()
@@ -187,9 +194,15 @@ WResourceManager::~WResourceManager()
LOG("==Destroying WResourceManager==");
RemoveWFonts();
CacheEngine::Terminate();
LOG("==Successfully Destroyed WResourceManager==");
}
bool WResourceManager::IsThreaded()
{
return CacheEngine::IsThreaded();
}
JQuadPtr WResourceManager::RetrieveCard(MTGCard * card, int style, int submode)
{
//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;
string filename = setlist[card->setId];
filename += "/";
string filename1 = filename + card->getImageName();
string filename = setlist[card->setId] + "/" + card->getImageName();
int id = card->getMTGId();
//Aliases.
@@ -209,40 +220,8 @@ JQuadPtr WResourceManager::RetrieveCard(MTGCard * card, int style, int submode)
style = RETRIEVE_NORMAL;
}
//Hack to allow either ID or card name as a filename for a given card.
// 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);
JQuadPtr jq = RetrieveQuad(filename, 0, 0, 0, 0, "", style, submode | TEXTURE_SUB_5551, id);
}
lastError = textureWCache.mError;
if (jq)
{
@@ -353,11 +332,7 @@ JQuadPtr WResourceManager::RetrieveQuad(const string& filename, float offX, floa
if (!resname.size()) resname = filename;
//No quad, but we have a managed texture for this!
WCachedTexture * jtex = NULL;
if (style == RETRIEVE_MANAGE || style == RETRIEVE_EXISTING)
jtex = textureWCache.Retrieve(id, filename, style, submode);
else
jtex = textureWCache.Retrieve(id, filename, RETRIEVE_NORMAL, submode);
WCachedTexture* jtex = textureWCache.Retrieve(id, filename, style, submode);
lastError = textureWCache.mError;
@@ -939,13 +914,15 @@ void WResourceManager::ResetCacheLimits()
textureWCache.Resize(HUGE_CACHE_LIMIT,MAX_CACHE_OBJECTS);
#endif
#else
unsigned int ram = ramAvailable();
unsigned int myNewSize = ram - OPERATIONAL_SIZE + textureWCache.totalSize;
static unsigned int ram(ramAvailable() / 2);
unsigned int myNewSize = ram - OPERATIONAL_SIZE;
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
return;
}
@@ -990,6 +967,11 @@ bool WCache<cacheItem, cacheActual>::RemoveOldest()
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);
cache.erase(oldest);
return true;
@@ -1024,6 +1006,12 @@ void WCache<cacheItem, cacheActual>::Resize(unsigned long size, int items)
{
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)
maxCached = MAX_CACHE_OBJECTS;
else
@@ -1033,12 +1021,6 @@ void WCache<cacheItem, cacheActual>::Resize(unsigned long size, int items)
template<class cacheItem, class cacheActual>
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;
if (!item)
{
@@ -1053,25 +1035,22 @@ cacheItem* WCache<cacheItem, cacheActual>::AttemptNew(const string& filename, in
//No such file. Fail
if (mError == CACHE_ERROR_404)
{
DebugTrace("AttemptNew failed to load. Deleting cache item " << ToHex(item));
SAFE_DELETE(item);
return NULL;
}
//Probably not enough memory: cleanup and try again
Cleanup();
mError = CACHE_ERROR_NONE;
if (!item->Attempt(filename, submode, mError) || !item->isGood())
else
{
DebugTrace("AttemptNew failed to load (not a 404 error). Deleting cache item " << ToHex(item));
SAFE_DELETE(item);
mError = CACHE_ERROR_BAD;
return NULL;
return NULL;
}
}
//Success! Enforce cache limits, then return.
mError = CACHE_ERROR_NONE;
item->lock();
Cleanup();
item->unlock();
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)
{
//Check cache.
cacheItem * tc = NULL;
mError = CACHE_ERROR_NONE; //Reset error status.
if (style == RETRIEVE_EXISTING || style == RETRIEVE_RESOURCE)
tc = Get(id, filename, style, submode | CACHE_EXISTING);
else
tc = Get(id, filename, style, submode);
cacheItem* tc = Get(id, filename, style, submode);
//Retrieve resource only works on permanent items.
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 (mId < 0)
mId -= THUMBNAILS_OFFSET;
else
mId += THUMBNAILS_OFFSET;
}
mId += THUMBNAILS_OFFSET;
return mId;
}
@@ -1179,38 +1148,73 @@ cacheItem* WCache<cacheItem, cacheActual>::Get(int id, const string& filename, i
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.
if (style != RETRIEVE_MANAGE)
{
boost::mutex::scoped_lock lock(mCacheMutex);
//DebugTrace("Cache lock acquired, looking up index " << lookup);
it = cache.find(lookup);
//Well, we've found something...
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.
}
}
//Didn't exist in cache.
if (submode & CACHE_EXISTING)
// not hit in the cache, respect the RETRIEVE_EXISTING flag if present
if (style == RETRIEVE_EXISTING)
{
mError = CACHE_ERROR_NOT_CACHED;
return NULL;
}
//Space in cache, make new texture
cacheItem * item = AttemptNew(filename, submode);
// no hit in cache, clear space before attempting to load a new one
// 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 (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)
{
item->deadbolt(); //Make permanent.
@@ -1218,10 +1222,25 @@ cacheItem* WCache<cacheItem, cacheActual>::Get(int id, const string& filename, i
}
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.
unsigned long isize = item->size();
@@ -1234,6 +1253,12 @@ cacheItem* WCache<cacheItem, cacheActual>::Get(int id, const string& filename, i
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;
}
@@ -1292,24 +1317,31 @@ WCache<cacheItem, cacheActual>::~WCache()
template<class cacheItem, class cacheActual>
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();
}
while (cacheItems > MAX_CACHE_OBJECTS || cacheItems > maxCached || cacheSize > maxCacheSize
#if defined WIN32 || defined LINUX || defined (IOS)
#else
|| ramAvailableLineareMax() < MIN_LINEAR_RAM
#endif
)
{
if (!RemoveOldest())
boost::mutex::scoped_lock lock(mCacheMutex);
while (RequiresMissCleanup())
{
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)
@@ -1425,6 +1457,7 @@ bool WCache<cacheItem, cacheActual>::Delete(cacheItem * item)
cacheItems--;
DebugTrace("Deleting cache item " << ToHex(item));
SAFE_DELETE(item);
return true;
}

View File

@@ -7,6 +7,10 @@
#include "WFont.h"
#include <sys/stat.h>
#ifdef PSPENV
#include "pspsdk.h"
#endif
namespace wagic
{
#ifdef TRACK_FILE_USAGE_STATS
@@ -162,6 +166,10 @@ u32 ramAvailableLineareMax(void)
size = 0;
sizeblock = RAM_BLOCK;
#ifdef PSPENV
int disableInterrupts = pspSdkDisableInterrupts();
#endif
// Check loop
while (sizeblock)
{
@@ -184,6 +192,10 @@ u32 ramAvailableLineareMax(void)
free(ram);
}
#ifdef PSPENV
pspSdkEnableInterrupts(disableInterrupts);
#endif
return size;
}
@@ -197,6 +209,10 @@ u32 ramAvailable(void)
size = 0;
count = 0;
#ifdef PSPENV
int disableInterrupts = pspSdkDisableInterrupts();
#endif
// Check loop
for (;;)
{
@@ -233,6 +249,9 @@ u32 ramAvailable(void)
free(ram);
}
#ifdef PSPENV
pspSdkEnableInterrupts(disableInterrupts);
#endif
return size;
}