*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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user