1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-02-27 08:34:02 +00:00

Rework texture loading, part 1

Major code/logic cleanups + it has a multithreaded batch mode now.
The VFS code doesn't like multiple threads at all,
so for now there's a big lock in front of where it matters.
Changed precacher, map/tileset, and worldmap loading to batched mode.

Still TODO:
- fix broken mod preview images
- reloading resources on debug key
- make mod recache entirely unnecessary
- actually drop resources when no longer needed
This commit is contained in:
fgenesis 2023-05-31 00:55:16 +02:00
parent 1100e286a6
commit 70b8dcdc3a
22 changed files with 758 additions and 817 deletions

View file

@ -1153,7 +1153,7 @@ void AnimationEditor::clearRot()
if (editingBone) if (editingBone)
{ {
if(core->getCtrlState()) if(core->getCtrlState())
editingBone->texture->reload(); core->texmgr.load(editingBone->texture->name, TextureMgr::OVERWRITE);
else if(splinegrid) else if(splinegrid)
splinegrid->resetControlPoints(); splinegrid->resetControlPoints();
else else

View file

@ -441,27 +441,27 @@ void DSQ::loadFonts()
font.load(file, 1, false); font.load(file, 1, false);
font.fontTopColor = Vector(0.9f,0.9f,1); font.fontTopColor = Vector(0.9f,0.9f,1);
font.fontBtmColor = Vector(0.5f,0.8f,1); font.fontBtmColor = Vector(0.5f,0.8f,1);
font.overrideTexture = core->addTexture("font"); font.overrideTexture = this->getTexture("font");
smallFont.load(file, 0.6f, false); smallFont.load(file, 0.6f, false);
smallFont.fontTopColor = Vector(0.9f,0.9f,1); smallFont.fontTopColor = Vector(0.9f,0.9f,1);
smallFont.fontBtmColor = Vector(0.5f,0.8f,1); smallFont.fontBtmColor = Vector(0.5f,0.8f,1);
smallFont.overrideTexture = core->addTexture("font"); smallFont.overrideTexture = this->getTexture("font");
smallFontRed.load(file, 0.6f, false); smallFontRed.load(file, 0.6f, false);
smallFontRed.fontTopColor = Vector(1,0.9f,0.9f); smallFontRed.fontTopColor = Vector(1,0.9f,0.9f);
smallFontRed.fontBtmColor = Vector(1,0.8f,0.5f); smallFontRed.fontBtmColor = Vector(1,0.8f,0.5f);
smallFontRed.overrideTexture = core->addTexture("font"); smallFontRed.overrideTexture = this->getTexture("font");
subsFont.load(file, 0.5f, false); subsFont.load(file, 0.5f, false);
subsFont.fontTopColor = Vector(1,1,1); subsFont.fontTopColor = Vector(1,1,1);
subsFont.fontBtmColor = Vector(0.5f,0.8f,1); subsFont.fontBtmColor = Vector(0.5f,0.8f,1);
subsFont.overrideTexture = core->addTexture("font"); subsFont.overrideTexture = this->getTexture("font");
goldFont.load(file, 1, false); goldFont.load(file, 1, false);
goldFont.fontTopColor = Vector(1,0.9f,0.5f); goldFont.fontTopColor = Vector(1,0.9f,0.5f);
goldFont.fontBtmColor = Vector(0.6f,0.5f,0.25f); goldFont.fontBtmColor = Vector(0.6f,0.5f,0.25f);
goldFont.overrideTexture = core->addTexture("font"); goldFont.overrideTexture = this->getTexture("font");
file = localisePath("data/font.ttf"); file = localisePath("data/font.ttf");
@ -844,11 +844,10 @@ void DSQ::init()
addStateInstance(new Nag); addStateInstance(new Nag);
this->setBaseTextureDirectory("gfx/");
voiceOversEnabled = true; voiceOversEnabled = true;
this->setExtraTexturePath(NULL);
if(!user.load(false)) if(!user.load(false))
@ -1255,6 +1254,7 @@ void DSQ::init()
} }
addRenderObject(fpsText, LR_DEBUG_TEXT); addRenderObject(fpsText, LR_DEBUG_TEXT);
precacher.setBaseDir(this->getBaseTexturePath());
precacher.precacheList("data/precache.txt", loadBitForTexPrecache); precacher.precacheList("data/precache.txt", loadBitForTexPrecache);
setTexturePointers(); setTexturePointers();
@ -1899,20 +1899,10 @@ void DSQ::refreshResourcesForPatch(const std::string& name)
int reloaded = 0; int reloaded = 0;
if(files.size()) if(files.size())
{ {
for(size_t i = 0; i < dsq->resources.size(); ++i) texmgr.loadBatch(NULL, &files[0], files.size(), TextureMgr::OVERWRITE);
{
Texture *r = dsq->resources[i];
for(size_t i = 0; i < files.size(); ++i)
if(files[i] == r->name)
{
++reloaded;
r->reload();
break;
}
}
} }
os.str(""); os.str("");
os << "refreshResourcesForPatch - " << reloaded << " textures reloaded"; os << "refreshResourcesForPatch - " << files.size() << " textures reloaded";
debugLog(os.str()); debugLog(os.str());
} }
#else #else
@ -1995,7 +1985,7 @@ void DSQ::shutdown()
Network::shutdown(); Network::shutdown();
scriptInterface.shutdown(); scriptInterface.shutdown();
precacher.clean(); precacher.clear();
core->particleManager->clearParticleBank(); core->particleManager->clearParticleBank();
@ -2086,11 +2076,11 @@ void DSQ::shutdown()
void DSQ::setTexturePointers() void DSQ::setTexturePointers()
{ {
texCursor = core->addTexture("cursor"); texCursor = this->getTexture("cursor");
texCursorLook = core->addTexture("cursor-look"); texCursorLook = this->getTexture("cursor-look");
texCursorBurst = core->addTexture("cursor-burst"); texCursorBurst = this->getTexture("cursor-burst");
texCursorSwim = core->addTexture("cursor-swim"); texCursorSwim = this->getTexture("cursor-swim");
texCursorSing = core->addTexture("cursor-sing"); texCursorSing = this->getTexture("cursor-sing");
if (cursor) if (cursor)
cursor->setTexturePointer(texCursor); cursor->setTexturePointer(texCursor);
@ -3928,7 +3918,7 @@ void DSQ::onUpdate(float dt)
if (isDeveloperKeys() && fpsText && cmDebug && cmDebug->alpha == 1) if (isDeveloperKeys() && fpsText && cmDebug && cmDebug->alpha == 1)
{ {
std::ostringstream os; std::ostringstream os;
os << "FPS: " << core->fps << " | ROC: " << core->renderObjectCount << " | RC: " << g_dbg_numRenderCalls << " | RES: " << core->resources.size(); os << "FPS: " << core->fps << " | ROC: " << core->renderObjectCount << " | RC: " << g_dbg_numRenderCalls << " | RES: " << texmgr.getNumLoaded();
os << " | p: " << core->processedRenderObjectCount << " | t: " << core->totalRenderObjectCount; os << " | p: " << core->processedRenderObjectCount << " | t: " << core->totalRenderObjectCount;
os << " | s: " << dsq->continuity.seconds; os << " | s: " << dsq->continuity.seconds;
os << " | sndQ: " << core->dbg_numThreadDecoders; os << " | sndQ: " << core->dbg_numThreadDecoders;

View file

@ -231,7 +231,6 @@ Game::Game() : StateObject()
Game::~Game() Game::~Game()
{ {
delete themenu; delete themenu;
tileCache.clean();
game = 0; game = 0;
} }
@ -410,7 +409,7 @@ void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim)
tpos.y -= h2; tpos.y -= h2;
int w = 0, h = 0; int w = 0, h = 0;
unsigned int size = 0; size_t size = 0;
unsigned char *data = q->texture->getBufferAndSize(&w, &h, &size); unsigned char *data = q->texture->getBufferAndSize(&w, &h, &size);
if (!data) if (!data)
{ {
@ -1082,7 +1081,7 @@ void Game::generateCollisionMask(Bone *q, float overrideCollideRadius /* = 0 */)
tpos.y -= h2; tpos.y -= h2;
int w = 0, h = 0; int w = 0, h = 0;
unsigned int size = 0; size_t size = 0;
unsigned char *data = q->texture->getBufferAndSize(&w, &h, &size); unsigned char *data = q->texture->getBufferAndSize(&w, &h, &size);
if (!data) if (!data)
{ {
@ -1285,7 +1284,6 @@ bool Game::loadSceneXML(std::string scene)
{ {
bgSfxLoop = ""; bgSfxLoop = "";
airSfxLoop = ""; airSfxLoop = "";
elementTemplatePack = "Main";
entitySaveData.clear(); entitySaveData.clear();
std::string fn = getSceneFilename(scene); std::string fn = getSceneFilename(scene);
if (!exists(fn)) if (!exists(fn))
@ -1317,17 +1315,13 @@ bool Game::loadSceneXML(std::string scene)
if (level) if (level)
{ {
XMLElement *levelSF = saveFile->NewElement("Level"); XMLElement *levelSF = saveFile->NewElement("Level");
if (level->Attribute("tileset")) const char *tileset = level->Attribute("tileset");
if(!tileset)
tileset = level->Attribute("elementTemplatePack"); // legacy, still present in some very old maps
if (tileset)
{ {
elementTemplatePack = level->Attribute("tileset"); loadElementTemplates(tileset);
loadElementTemplates(elementTemplatePack); levelSF->SetAttribute("tileset", tileset);
levelSF->SetAttribute("tileset", elementTemplatePack.c_str());
}
else if (level->Attribute("elementTemplatePack"))
{
elementTemplatePack = level->Attribute("elementTemplatePack");
loadElementTemplates(elementTemplatePack);
levelSF->SetAttribute("tileset", elementTemplatePack.c_str());
} }
else else
return false; return false;
@ -1729,206 +1723,195 @@ bool Game::loadSceneXML(std::string scene)
saveFile->InsertEndChild(newSF); saveFile->InsertEndChild(newSF);
} }
std::vector<Element*> loadedElements; struct ElementDef
loadedElements.reserve(200); {
ElementDef(int lr)
: layer(lr), idx(0), x(0), y(0), rot(0), fh(0), fv(0), flags(0), efxIdx(0), repeat(0)
, tag(0), sx(1), sy(1), rsx(1), rsy(1)
{}
int layer, idx, x, y, rot, fh, fv, flags, efxIdx, repeat, tag;
float sx, sy, rsx, rsy;
};
std::vector<ElementDef> elemsDefs;
elemsDefs.reserve(256);
XMLElement *simpleElements = doc.FirstChildElement("SE"); XMLElement *simpleElements = doc.FirstChildElement("SE");
while (simpleElements) while (simpleElements)
{ {
int idx, x, y, rot; const size_t defsBeginIdx = elemsDefs.size();
float sz,sz2; const int layer = atoi(simpleElements->Attribute("l"));
loadedElements.clear();
if (simpleElements->Attribute("d"))
{
SimpleIStringStream is(simpleElements->Attribute("d"));
while (is >> idx)
{
is >> x >> y >> rot;
Element *e = createElement(idx, Vector(x,y), 4);
e->rotation.z = rot;
loadedElements.push_back(e);
}
}
if (simpleElements->Attribute("e"))
{
SimpleIStringStream is2(simpleElements->Attribute("e"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
{
is2 >> x >> y >> rot;
Element *e = createElement(idx, Vector(x,y), l);
e->rotation.z = rot;
loadedElements.push_back(e);
}
}
if (simpleElements->Attribute("f"))
{
SimpleIStringStream is2(simpleElements->Attribute("f"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
{
is2 >> x >> y >> rot >> sz;
Element *e = createElement(idx, Vector(x,y), l);
e->scale = Vector(sz,sz);
e->rotation.z = rot;
loadedElements.push_back(e);
}
}
if (simpleElements->Attribute("g"))
{
SimpleIStringStream is2(simpleElements->Attribute("g"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
{
int fh, fv;
is2 >> x >> y >> rot >> sz >> fh >> fv;
Element *e = createElement(idx, Vector(x,y), l);
if (fh)
e->flipHorizontal();
if (fv)
e->flipVertical();
e->scale = Vector(sz,sz);
e->rotation.z = rot;
loadedElements.push_back(e);
}
}
if (simpleElements->Attribute("h"))
{
SimpleIStringStream is2(simpleElements->Attribute("h"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
{
int fh, fv;
int flags;
is2 >> x >> y >> rot >> sz >> fh >> fv >> flags;
Element *e = createElement(idx, Vector(x,y), l);
e->elementFlag = (ElementFlag)flags;
if (e->elementFlag >= EF_MAX || e->elementFlag < EF_NONE)
e->elementFlag = EF_NONE;
if (fh)
e->flipHorizontal();
if (fv)
e->flipVertical();
e->scale = Vector(sz,sz);
e->rotation.z = rot;
loadedElements.push_back(e);
}
}
if (simpleElements->Attribute("i"))
{
SimpleIStringStream is2(simpleElements->Attribute("i"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
{
int fh, fv;
int flags;
int efxIdx;
is2 >> x >> y >> rot >> sz >> fh >> fv >> flags >> efxIdx;
if (sz < MIN_SIZE)
sz = MIN_SIZE;
Element *e = createElement(idx, Vector(x,y), l);
e->elementFlag = (ElementFlag)flags;
if (fh)
e->flipHorizontal();
if (fv)
e->flipVertical();
e->scale = Vector(sz,sz); if (const char *attr = simpleElements->Attribute("d"))
e->rotation.z = rot; {
e->setElementEffectByIndex(efxIdx); SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
loadedElements.push_back(e); ElementDef d(4); // legacy crap
while (is >> d.idx)
{
is >> d.x >> d.y >> d.rot;
elemsDefs.push_back(d);
} }
} }
if (simpleElements->Attribute("j")) if (const char *attr = simpleElements->Attribute("e"))
{ {
SimpleIStringStream is2(simpleElements->Attribute("j")); SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
int l = atoi(simpleElements->Attribute("l")); ElementDef d(layer);
while(is2 >> idx) while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot;
elemsDefs.push_back(d);
}
}
if (const char *attr = simpleElements->Attribute("f"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot >> d.sx;
d.sy = d.sx;
elemsDefs.push_back(d);
}
}
if (const char *attr = simpleElements->Attribute("g"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv;
d.sy = d.sx;
elemsDefs.push_back(d);
}
}
if (const char *attr = simpleElements->Attribute("h"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.flags;
d.sy = d.sx;
elemsDefs.push_back(d);
}
}
if (const char *attr = simpleElements->Attribute("i"))
{ {
int fh, fv;
int flags;
int efxIdx;
int repeat;
is2 >> x >> y >> rot >> sz >> fh >> fv >> flags >> efxIdx >> repeat;
if (sz < MIN_SIZE)
sz = MIN_SIZE;
Element *e = createElement(idx, Vector(x,y), l);
e->elementFlag = (ElementFlag)flags;
if (fh)
e->flipHorizontal();
if (fv)
e->flipVertical();
e->scale = Vector(sz,sz); SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
e->rotation.z = rot; ElementDef d(layer);
e->setElementEffectByIndex(efxIdx); while(is >> d.idx)
if (repeat) {
e->repeatTextureToFill(true); is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.flags >> d.efxIdx;
loadedElements.push_back(e); d.sy = d.sx;
elemsDefs.push_back(d);
} }
} }
if (simpleElements->Attribute("k")) if (const char *attr = simpleElements->Attribute("j"))
{ {
SimpleIStringStream is2(simpleElements->Attribute("k")); SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
int l = atoi(simpleElements->Attribute("l")); ElementDef d(layer);
while(is2 >> idx) while(is >> d.idx)
{ {
int fh, fv; is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.flags >> d.efxIdx >> d.repeat;
int flags; d.sy = d.sx;
int efxIdx; elemsDefs.push_back(d);
int repeat; }
is2 >> x >> y >> rot >> sz >> sz2 >> fh >> fv >> flags >> efxIdx >> repeat; }
if (sz < MIN_SIZE) if (const char *attr = simpleElements->Attribute("k"))
sz = MIN_SIZE; {
if (sz2 < MIN_SIZE) SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
sz2 = MIN_SIZE; ElementDef d(layer);
Element *e = createElement(idx, Vector(x,y), l); while(is >> d.idx)
e->elementFlag = (ElementFlag)flags; {
if (fh) is >> d.x >> d.y >> d.rot >> d.sx >> d.sy >> d.fh >> d.fv >> d.flags >> d.efxIdx >> d.repeat;
e->flipHorizontal(); elemsDefs.push_back(d);
if (fv) }
e->flipVertical(); }
e->scale = Vector(sz,sz2); // done loading raw data, now for some possible extensions added later
e->rotation.z = rot;
e->setElementEffectByIndex(efxIdx);
if (repeat)
e->repeatTextureToFill(true);
loadedElements.push_back(e); if (const char *attr = simpleElements->Attribute("repeatScale"))
}
}
if (simpleElements->Attribute("repeatScale"))
{ {
SimpleIStringStream is2(simpleElements->Attribute("repeatScale"), SimpleIStringStream::REUSE); SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
for(size_t i = 0; i < loadedElements.size(); ++i) for(size_t i = defsBeginIdx; i < elemsDefs.size(); ++i)
{ {
Element *e = loadedElements[i]; ElementDef& d = elemsDefs[i];
if(e->isRepeatingTextureToFill()) if(d.repeat)
{ {
float repeatScaleX = 1, repeatScaleY = 1; if(!(is >> d.rsx >> d.rsx))
if(!(is2 >> repeatScaleX >> repeatScaleY))
break; break;
e->repeatToFillScale.x = repeatScaleX;
e->repeatToFillScale.y = repeatScaleY;
e->refreshRepeatTextureToFill();
} }
} }
} }
if (simpleElements->Attribute("tag")) if (const char *attr = simpleElements->Attribute("tag"))
{ {
SimpleIStringStream is2(simpleElements->Attribute("tag"), SimpleIStringStream::REUSE); SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
for(size_t i = 0; i < loadedElements.size(); ++i) for(size_t i = defsBeginIdx; i < elemsDefs.size(); ++i)
{ {
Element *e = loadedElements[i]; ElementDef& d = elemsDefs[i];
int tag = 0; if(!(is >> d.tag))
if(!(is2 >> tag))
break; break;
e->setTag(tag);
} }
} }
simpleElements = simpleElements->NextSiblingElement("SE"); simpleElements = simpleElements->NextSiblingElement("SE");
} }
// figure out which textures in the tileset are used and preload those that are actually used
{
unsigned char usedIdx[1024] = {0};
for(size_t i = 0; i < elemsDefs.size(); ++i)
{
unsigned idx = elemsDefs[i].idx;
if(idx < Countof(usedIdx))
usedIdx[idx] = 1;
}
std::vector<std::string> usedTex;
usedTex.reserve(elementTemplates.size()); // optimistically assume all textures in the tileset are used
for (size_t i = 0; i < elementTemplates.size(); i++)
{
unsigned idx = elementTemplates[i].idx;
if (idx < Countof(usedIdx) && usedIdx[idx])
usedTex.push_back(elementTemplates[i].gfx);
}
std::sort(usedTex.begin(), usedTex.end());
// drop duplicates
usedTex.resize(std::distance(usedTex.begin(), std::unique(usedTex.begin(), usedTex.end())));
std::ostringstream os;
os << "Scene has " << elemsDefs.size() << " elements that use " << usedTex.size()
<< " distinct textures out of the " << elementTemplates.size() << " entries in the tileset";
debugLog(os.str());
// preload all used textures
if(usedTex.size())
dsq->texmgr.loadBatch(NULL, &usedTex[0], usedTex.size());
}
// Now that all SE tags have been processed, spawn them
for(size_t i = 0; i < elemsDefs.size(); ++i)
{
const ElementDef& d = elemsDefs[i];
Element *e = createElement(d.idx, Vector(d.x,d.y), d.layer);
e->elementFlag = (ElementFlag)d.flags;
if (d.fh)
e->flipHorizontal();
if (d.fv)
e->flipVertical();
e->scale = Vector(d.sx, d.sy);
e->rotation.z = d.rot;
e->repeatToFillScale.x = d.rsx;
e->repeatToFillScale.y = d.rsy;
if(d.efxIdx)
e->setElementEffectByIndex(d.efxIdx);
if (d.repeat)
e->repeatTextureToFill(true); // also applies repeatToFillScale
e->setTag(d.tag);
}
XMLElement *element = doc.FirstChildElement("Element"); XMLElement *element = doc.FirstChildElement("Element");
while (element) while (element)
{ {
@ -2783,7 +2766,6 @@ void Game::applyState()
//sceneColor = Vector(0.75, 0.75, 0.8); //sceneColor = Vector(0.75, 0.75, 0.8);
sceneColor = Vector(1,1,1); sceneColor = Vector(1,1,1);
sceneName = ""; sceneName = "";
elementTemplatePack ="";
clearGrid(); clearGrid();
bg = 0; bg = 0;
bg2 = 0; bg2 = 0;
@ -2950,9 +2932,9 @@ void Game::applyState()
bindInput(); bindInput();
if (verbose) debugLog("Loading Scene"); if (verbose) debugLog("Loading Scene");
if (!loadScene(sceneToLoad)) loadScene(sceneToLoad);
{ {
loadElementTemplates(elementTemplatePack); debugLog("Failed to load scene [" + sceneToLoad + "]");
} }
if (verbose) debugLog("...Done"); if (verbose) debugLog("...Done");
@ -4934,33 +4916,18 @@ void Game::loadElementTemplates(std::string pack)
elementTemplates.clear(); elementTemplates.clear();
// HACK: need to uncache things! causes memory leak currently
bool doPrecache=false;
std::string fn; std::string fn;
if (dsq->mod.isActive()) if (dsq->mod.isActive())
fn = dsq->mod.getPath() + "tilesets/" + pack + ".txt"; fn = dsq->mod.getPath() + "tilesets/" + pack + ".txt";
else else
fn = "data/tilesets/" + pack + ".txt"; fn = "data/tilesets/" + pack + ".txt";
if (lastTileset == fn)
{
doPrecache=false;
}
lastTileset = fn;
if (!exists(fn)) if (!exists(fn))
{ {
errorLog ("Could not open element template pack [" + fn + "]"); errorLog ("Could not open tileset [" + fn + "]");
return; return;
} }
if (doPrecache)
{
tileCache.clean();
}
InStream in(fn.c_str()); InStream in(fn.c_str());
std::string line; std::string line;
while (std::getline(in, line)) while (std::getline(in, line))
@ -4977,21 +4944,12 @@ void Game::loadElementTemplates(std::string pack)
t.w = w; t.w = w;
t.h = h; t.h = h;
elementTemplates.push_back(t); elementTemplates.push_back(t);
if (doPrecache)
tileCache.precacheTex(gfx);
} }
in.close(); in.close();
for (size_t i = 0; i < elementTemplates.size(); i++)
{ std::sort(elementTemplates.begin(), elementTemplates.end());
for (size_t j = i; j < elementTemplates.size(); j++)
{
if (elementTemplates[i].idx > elementTemplates[j].idx)
{
std::swap(elementTemplates[i], elementTemplates[j]);
}
}
}
for (int i = 0; i < 27; i++) for (int i = 0; i < 27; i++)
{ {
elementTemplates.push_back(getElementTemplateForLetter(i)); elementTemplates.push_back(getElementTemplateForLetter(i));

View file

@ -88,6 +88,7 @@ class ElementTemplate
{ {
public: public:
ElementTemplate() { alpha = 1; cull = true; w=-1; h=-1; idx=-1; tu1=tu2=tv1=tv2=0; } ElementTemplate() { alpha = 1; cull = true; w=-1; h=-1; idx=-1; tu1=tu2=tv1=tv2=0; }
inline bool operator<(const ElementTemplate& o) const { return idx < o.idx; }
std::string gfx; std::string gfx;
std::vector <TileVector> grid; std::vector <TileVector> grid;
int w,h; int w,h;
@ -293,8 +294,6 @@ public:
std::vector<EntitySaveData> entitySaveData; std::vector<EntitySaveData> entitySaveData;
int getIdxForEntityType(std::string type); int getIdxForEntityType(std::string type);
Precacher tileCache;
void setCameraFollow(Vector *position); void setCameraFollow(Vector *position);
Shot *fireShot(const std::string &bankShot, Entity *firer, Entity *target=0, const Vector &pos=Vector(0,0,0), const Vector &aim=Vector(0,0,0), bool playSfx=true); Shot *fireShot(const std::string &bankShot, Entity *firer, Entity *target=0, const Vector &pos=Vector(0,0,0), const Vector &aim=Vector(0,0,0), bool playSfx=true);
void playBurstSound(bool wallJump=false); void playBurstSound(bool wallJump=false);
@ -474,9 +473,6 @@ protected:
bool controlHint_ignoreClear; bool controlHint_ignoreClear;
BitmapText *controlHint_text; BitmapText *controlHint_text;
std::string lastTileset;
void createLi(); void createLi();
void createPets(); void createPets();
Quad *backdropQuad; Quad *backdropQuad;
@ -498,9 +494,6 @@ protected:
Vector getClosestPointOnTriangle(Vector a, Vector b, Vector c, Vector p); Vector getClosestPointOnTriangle(Vector a, Vector b, Vector c, Vector p);
Vector getClosestPointOnLine(Vector a, Vector b, Vector p); Vector getClosestPointOnLine(Vector a, Vector b, Vector p);
std::string elementTemplatePack;
Vector *cameraFollow; Vector *cameraFollow;
RenderObject *cameraFollowObject; RenderObject *cameraFollowObject;
Entity *cameraFollowEntity; Entity *cameraFollowEntity;

View file

@ -77,7 +77,7 @@ void Intro::endIntro()
dsq->sound->clearLocalSounds(); dsq->sound->clearLocalSounds();
cachy.clean(); cachy.clear();
dsq->toggleBlackBars(0); dsq->toggleBlackBars(0);

View file

@ -106,19 +106,14 @@ void MinimapIcon::update(float dt)
// pretty much copied from RenderObject::setTexture() // pretty much copied from RenderObject::setTexture()
static bool _setTex(CountedPtr<Texture> &tex, std::string name) static bool _setTex(CountedPtr<Texture> &tex, std::string name)
{ {
stringToLowerUserData(name);
if (name.empty()) if (name.empty())
{ {
tex = NULL; tex = NULL;
return false; return false;
} }
if(tex && tex->getLoadResult() == TEX_SUCCESS && name == tex->name) tex = core->getTexture(name);
return true; // no texture change return tex->success;
tex = core->addTexture(name);
return tex && tex->getLoadResult() == TEX_SUCCESS;
} }
bool MinimapIcon::setTexture(std::string name) bool MinimapIcon::setTexture(std::string name)
@ -168,12 +163,12 @@ MiniMapRender::MiniMapRender() : RenderObject()
cull = false; cull = false;
lightLevel = 1.0; lightLevel = 1.0;
texWaterBit = core->addTexture("gui/minimap/waterbit"); texWaterBit = core->getTexture("gui/minimap/waterbit");
texMinimapBtm = core->addTexture("gui/minimap/btm"); texMinimapBtm = core->getTexture("gui/minimap/btm");
texMinimapTop = core->addTexture("gui/minimap/top"); texMinimapTop = core->getTexture("gui/minimap/top");
texNaija = core->addTexture("gems/naija-token"); texNaija = core->getTexture("gems/naija-token");
texHealthBar = core->addTexture("particles/glow-masked"); texHealthBar = core->getTexture("particles/glow-masked");
texMarker = core->addTexture("gui/minimap/marker"); texMarker = core->getTexture("gui/minimap/marker");
buttons.clear(); buttons.clear();

View file

@ -43,7 +43,7 @@ Mod::Mod()
Mod::~Mod() Mod::~Mod()
{ {
modcache.clean(); modcache.clear();
} }
/* /*
@ -152,7 +152,7 @@ void Mod::load(const std::string &p)
} }
} }
dsq->secondaryTexturePath = path + "graphics/"; dsq->setExtraTexturePath((path + "graphics/").c_str());
dsq->sound->audioPath2 = path + "audio/"; dsq->sound->audioPath2 = path + "audio/";
dsq->sound->setVoicePath2(path + "audio/"); dsq->sound->setVoicePath2(path + "audio/");
@ -180,13 +180,13 @@ void Mod::recache()
{ {
if(doRecache) if(doRecache)
{ {
dsq->precacher.clean(); core->texmgr.reloadAll(TextureMgr::KEEP_IF_SAME);
dsq->unloadResources(); dsq->unloadResources();
} }
if(active) if(active)
{ {
modcache.setBaseDir(dsq->secondaryTexturePath); modcache.setBaseDir(dsq->getExtraTexturePath());
std::string fname = path; std::string fname = path;
if(fname[fname.length() - 1] != '/') if(fname[fname.length() - 1] != '/')
fname += '/'; fname += '/';
@ -200,7 +200,7 @@ void Mod::recache()
} }
else else
{ {
modcache.clean(); modcache.clear();
} }
if(doRecache) if(doRecache)
@ -292,7 +292,7 @@ void Mod::setActive(bool a)
mapRevealMethod = REVEAL_UNSPECIFIED; mapRevealMethod = REVEAL_UNSPECIFIED;
setLocalisationModPath(""); setLocalisationModPath("");
name = path = ""; name = path = "";
dsq->secondaryTexturePath = ""; dsq->setExtraTexturePath(NULL);
dsq->sound->audioPath2 = ""; dsq->sound->audioPath2 = "";
dsq->sound->setVoicePath2(""); dsq->sound->setVoicePath2("");
SkeletalSprite::secondaryAnimationPath = ""; SkeletalSprite::secondaryAnimationPath = "";

View file

@ -494,7 +494,7 @@ static unsigned char *tileDataToAlpha(WorldMapTile *tile)
const unsigned int scaleY = texHeight / MAPVIS_SUBDIV; const unsigned int scaleY = texHeight / MAPVIS_SUBDIV;
unsigned char *savedTexData = new unsigned char[texWidth * texHeight * 4]; unsigned char *savedTexData = new unsigned char[texWidth * texHeight * 4];
tile->q->texture->read(0, 0, texWidth, texHeight, savedTexData); tile->q->texture->readRGBA(savedTexData);
unsigned char *texData = new unsigned char[texWidth * texHeight * 4]; unsigned char *texData = new unsigned char[texWidth * texHeight * 4];
memcpy(texData, savedTexData, texWidth * texHeight * 4); memcpy(texData, savedTexData, texWidth * texHeight * 4);
@ -538,7 +538,7 @@ static unsigned char *tileDataToAlpha(WorldMapTile *tile)
} }
} }
tile->q->texture->write(0, 0, texWidth, texHeight, texData); tile->q->texture->writeRGBA(0, 0, texWidth, texHeight, texData);
delete[] texData; delete[] texData;
return savedTexData; return savedTexData;
@ -546,7 +546,7 @@ static unsigned char *tileDataToAlpha(WorldMapTile *tile)
static void resetTileAlpha(WorldMapTile *tile, const unsigned char *savedTexData) static void resetTileAlpha(WorldMapTile *tile, const unsigned char *savedTexData)
{ {
tile->q->texture->write(0, 0, tile->q->texture->width, tile->q->texture->height, savedTexData); tile->q->texture->writeRGBA(0, 0, tile->q->texture->width, tile->q->texture->height, savedTexData);
} }
@ -629,25 +629,29 @@ WorldMapRender::WorldMapRender() : RenderObject(), ActionMapper()
int num = dsq->continuity.worldMap.getNumWorldMapTiles(); const size_t num = dsq->continuity.worldMap.getNumWorldMapTiles();
std::string n = dsq->game->sceneName; std::string n = dsq->game->sceneName;
stringToUpper(n); stringToUpper(n);
for (int i = 0; i < num; i++) std::vector<std::string> textodo(num);
std::vector<Texture*> texs(num, NULL);
textodo.reserve(num);
for (size_t i = 0; i < num; i++)
{ {
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
if (tile) if (tile)
{ {
if (tile->name == n) if (tile->name == n)
{
activeTile = tile; activeTile = tile;
break; textodo[i] = "gui/worldmap/" + tile->name;
}
} }
} }
tiles.clear(); tiles.clear();
for (int i = 0; i < num; i++) if(num)
dsq->texmgr.loadBatch(&texs[0], &textodo[0], num);
for (size_t i = 0; i < num; i++)
{ {
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
if (tile) if (tile)
@ -655,8 +659,7 @@ WorldMapRender::WorldMapRender() : RenderObject(), ActionMapper()
Vector pos(tile->gridPos.x, tile->gridPos.y); Vector pos(tile->gridPos.x, tile->gridPos.y);
Quad *q = new Quad; Quad *q = new Quad;
std::string tn = "Gui/WorldMap/" + tile->name; q->setTexturePointer(texs[i]);
q->setTexture(tn);
q->position = pos; q->position = pos;
q->alphaMod = 0; q->alphaMod = 0;
q->drawOrder = Quad::GRID_DRAW_WORLDMAP; q->drawOrder = Quad::GRID_DRAW_WORLDMAP;

View file

@ -232,10 +232,14 @@ bool exists(const std::string &f, bool makeFatal, bool skipVFS)
#ifdef BBGE_BUILD_VFS #ifdef BBGE_BUILD_VFS
if (!skipVFS) if (!skipVFS)
{ {
e = !!vfs.GetFile(f.c_str()); e = !!vfgetfile(f.c_str());
} }
else else
#endif {
std::string tmp = adjustFilenameCase(f);
e = ttvfs::FileExists(tmp.c_str());
}
#else
if (!e) if (!e)
{ {
std::string tmp = adjustFilenameCase(f); std::string tmp = adjustFilenameCase(f);
@ -246,6 +250,7 @@ bool exists(const std::string &f, bool makeFatal, bool skipVFS)
fclose(file); fclose(file);
} }
} }
#endif
if (makeFatal && !e) if (makeFatal && !e)
{ {

View file

@ -105,6 +105,8 @@ set(BBGE_SRCS
StringBank.h StringBank.h
Texture.cpp Texture.cpp
Texture.h Texture.h
TextureMgr.cpp
TextureMgr.h
TTFFont.cpp TTFFont.cpp
TTFFont.h TTFFont.h
Vector.cpp Vector.cpp

View file

@ -350,8 +350,6 @@ Core::Core(const std::string &filesystem, const std::string& extraDataDir, int n
debugLogActive = true; debugLogActive = true;
debugOutputActive = false; debugOutputActive = false;
debugLogTextures = true;
grabInput = false; grabInput = false;
srand(time(NULL)); srand(time(NULL));
@ -398,6 +396,8 @@ Core::Core(const std::string &filesystem, const std::string& extraDataDir, int n
initRenderObjectLayers(numRenderLayers); initRenderObjectLayers(numRenderLayers);
initPlatform(filesystem); initPlatform(filesystem);
texmgr.spawnThreads(3);
} }
void Core::initPlatform(const std::string &filesystem) void Core::initPlatform(const std::string &filesystem)
@ -1824,20 +1824,6 @@ void Core::showBuffer()
window->present(); window->present();
} }
// WARNING: only for use during shutdown
// otherwise, textures will try to remove themselves
// when destroy is called on them
void Core::clearResources()
{
if(resources.size())
{
debugLog("Warning: The following resources were not cleared:");
for(size_t i = 0; i < resources.size(); ++i)
debugLog(resources[i]->name);
resources.clear(); // nothing we can do; refcounting is messed up
}
}
void Core::shutdownInputLibrary() void Core::shutdownInputLibrary()
{ {
} }
@ -1897,7 +1883,7 @@ void Core::shutdown()
debugLog("OK"); debugLog("OK");
debugLog("Clear All Resources..."); debugLog("Clear All Resources...");
clearResources(); texmgr.shutdown();
debugLog("OK"); debugLog("OK");
@ -1956,118 +1942,9 @@ bool Core::exists(const std::string &filename)
return ::exists(filename, false); // defined in Base.cpp return ::exists(filename, false); // defined in Base.cpp
} }
CountedPtr<Texture> Core::findTexture(const std::string &name) CountedPtr<Texture> Core::getTexture(const std::string &name)
{ {
int sz = resources.size(); return texmgr.getOrLoad(name);
for (int i = 0; i < sz; i++)
{
//out << resources[i]->name << " is " << name << " ?" << std::endl;
//NOTE: ensure all names are lowercase before this point
if (resources[i]->name == name)
{
return resources[i];
}
}
return 0;
}
// This handles unix/win32 relative paths: ./rel/path
// Unix abs paths: /home/user/...
// Win32 abs paths: C:/Stuff/.. and also C:\Stuff\...
#define ISPATHROOT(x) (x[0] == '.' || x[0] == '/' || ((x).length() > 1 && x[1] == ':'))
std::string Core::getTextureLoadName(const std::string &texture)
{
std::string loadName = texture;
if (texture.empty() || !ISPATHROOT(texture))
{
if (texture.find(baseTextureDirectory) == std::string::npos)
loadName = baseTextureDirectory + texture;
}
return loadName;
}
CountedPtr<Texture> Core::doTextureAdd(const std::string &texture, const std::string &loadName, std::string internalTextureName)
{
if (texture.empty() || !ISPATHROOT(texture))
{
if (texture.find(baseTextureDirectory) != std::string::npos)
internalTextureName = internalTextureName.substr(baseTextureDirectory.size(), internalTextureName.size());
}
if (internalTextureName.size() > 4)
{
if (internalTextureName[internalTextureName.size()-4] == '.')
{
internalTextureName = internalTextureName.substr(0, internalTextureName.size()-4);
}
}
stringToLowerUserData(internalTextureName);
CountedPtr<Texture> t = core->findTexture(internalTextureName);
if (t)
return t;
t = new Texture;
t->name = internalTextureName;
if(t->load(loadName, true))
{
std::ostringstream os;
os << "LOADED TEXTURE FROM DISK: [" << internalTextureName << "] idx: " << resources.size()-1;
debugLog(os.str());
}
else
{
t->width = 64;
t->height = 64;
}
return t;
}
CountedPtr<Texture> Core::addTexture(const std::string &textureName)
{
if (textureName.empty())
return NULL;
CountedPtr<Texture> ptex;
std::string texture = textureName;
stringToLowerUserData(texture);
std::string internalTextureName = texture;
std::string loadName = getTextureLoadName(texture);
if (!texture.empty() && texture[0] == '@')
{
texture = secondaryTexturePath + texture.substr(1, texture.size());
loadName = texture;
}
else if (!secondaryTexturePath.empty() && texture[0] != '.' && texture[0] != '/')
{
std::string t = texture;
std::string ln = loadName;
texture = secondaryTexturePath + texture;
loadName = texture;
ptex = doTextureAdd(texture, loadName, internalTextureName);
if (!ptex || ptex->getLoadResult() == TEX_FAILED)
ptex = doTextureAdd(t, ln, internalTextureName);
}
else
ptex = doTextureAdd(texture, loadName, internalTextureName);
addTexture(ptex.content());
if(debugLogTextures)
{
if(!ptex || ptex->getLoadResult() != TEX_SUCCESS)
{
std::ostringstream os;
os << "FAILED TO LOAD TEXTURE: [" << internalTextureName << "] idx: " << resources.size()-1;
debugLog(os.str());
}
}
return ptex;
} }
void Core::addRenderObject(RenderObject *o, unsigned layer) void Core::addRenderObject(RenderObject *o, unsigned layer)
@ -2089,10 +1966,10 @@ void Core::switchRenderObjectLayer(RenderObject *o, unsigned toLayer)
void Core::unloadResources() void Core::unloadResources()
{ {
for (size_t i = 0; i < resources.size(); i++) /*for (size_t i = 0; i < resources.size(); i++)
{ {
resources[i]->unload(); resources[i]->unload();
} }*/
} }
void Core::onReloadResources() void Core::onReloadResources()
@ -2101,43 +1978,30 @@ void Core::onReloadResources()
void Core::reloadResources() void Core::reloadResources()
{ {
for (size_t i = 0; i < resources.size(); i++) /*for (size_t i = 0; i < resources.size(); i++)
{ {
resources[i]->reload(); resources[i]->reload();
} }
onReloadResources(); onReloadResources();*/
} }
void Core::addTexture(Texture *r) const std::string & Core::getBaseTexturePath() const
{ {
for(size_t i = 0; i < resources.size(); ++i) return texmgr.loadFromPaths.back();
if(resources[i] == r)
return;
resources.push_back(r);
if (r->name.empty())
{
debugLog("Empty name resource added");
}
} }
void Core::removeTexture(Texture *res) void Core::setExtraTexturePath(const char * dir)
{ {
std::vector<Texture*> copy; texmgr.loadFromPaths.resize(size_t(1) + !!dir);
copy.swap(resources); size_t w = 0;
if(dir)
for (size_t i = 0; i < copy.size(); ++i) texmgr.loadFromPaths[w++] = dir;
{ texmgr.loadFromPaths[w] = "gfx/";
if (copy[i] == res)
{
copy[i]->destroy();
copy[i] = copy.back();
copy.pop_back();
break;
}
} }
resources.swap(copy); const char *Core::getExtraTexturePath() const
{
return texmgr.loadFromPaths.size() > 1 ? texmgr.loadFromPaths[0].c_str() : NULL;
} }
void Core::removeRenderObject(RenderObject *r, RemoveRenderObjectFlag flag) void Core::removeRenderObject(RenderObject *r, RemoveRenderObjectFlag flag)

View file

@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "StateManager.h" #include "StateManager.h"
#include "Localization.h" #include "Localization.h"
#include "Window.h" #include "Window.h"
#include "TextureMgr.h"
#include "DarkLayer.h" #include "DarkLayer.h"
@ -231,8 +232,6 @@ public:
// state functions // state functions
std::string getTextureLoadName(const std::string &texture);
void setMousePosition(const Vector &p); void setMousePosition(const Vector &p);
void setFullscreen(bool full); void setFullscreen(bool full);
@ -240,12 +239,7 @@ public:
void enable2D(int pixelScaleX, int pixelScaleY); void enable2D(int pixelScaleX, int pixelScaleY);
void addRenderObject(RenderObject *o, unsigned layer); void addRenderObject(RenderObject *o, unsigned layer);
void switchRenderObjectLayer(RenderObject *o, unsigned toLayer); void switchRenderObjectLayer(RenderObject *o, unsigned toLayer);
void addTexture(Texture *r); CountedPtr<Texture> getTexture(const std::string &name);
CountedPtr<Texture> findTexture(const std::string &name);
void removeTexture(Texture *res);
void clearResources();
CountedPtr<Texture> addTexture(const std::string &texture);
enum RemoveRenderObjectFlag { DESTROY_RENDER_OBJECT=0, DO_NOT_DESTROY_RENDER_OBJECT }; enum RemoveRenderObjectFlag { DESTROY_RENDER_OBJECT=0, DO_NOT_DESTROY_RENDER_OBJECT };
void removeRenderObject(RenderObject *r, RemoveRenderObjectFlag flag = DESTROY_RENDER_OBJECT); void removeRenderObject(RenderObject *r, RemoveRenderObjectFlag flag = DESTROY_RENDER_OBJECT);
@ -279,8 +273,6 @@ public:
void print(int x, int y, const char *str, float sz=1); void print(int x, int y, const char *str, float sz=1);
std::vector<Texture*> resources;
RenderObjectLayer *getRenderObjectLayer(int i); RenderObjectLayer *getRenderObjectLayer(int i);
std::vector <int> renderObjectLayerOrder; std::vector <int> renderObjectLayerOrder;
@ -311,15 +303,9 @@ public:
ParticleManager *particleManager; ParticleManager *particleManager;
void setExtraTexturePath(const char *dir); // pass NULL to disable secondary
const char *getExtraTexturePath() const; // NULL when no secondary
void setBaseTextureDirectory(const std::string &newBaseTextureDirectory) const std::string& getBaseTexturePath() const;
{ this->baseTextureDirectory = newBaseTextureDirectory; }
std::string getBaseTextureDirectory()
{
return baseTextureDirectory;
}
virtual bool canChangeState(); virtual bool canChangeState();
void resetTimer(); void resetTimer();
@ -376,8 +362,6 @@ public:
bool joystickEnabled; bool joystickEnabled;
bool debugLogTextures;
void setup_opengl(); void setup_opengl();
void setClearColor(const Vector &c); void setClearColor(const Vector &c);
int flipMouseButtons; int flipMouseButtons;
@ -388,8 +372,6 @@ public:
ParticleEffect* createParticleEffect(const std::string &name, const Vector &position, int layer, float rotz=0); ParticleEffect* createParticleEffect(const std::string &name, const Vector &position, int layer, float rotz=0);
std::string secondaryTexturePath;
float get_old_dt() { return old_dt; } float get_old_dt() { return old_dt; }
float get_current_dt() { return current_dt; } float get_current_dt() { return current_dt; }
@ -431,6 +413,8 @@ public:
void initLocalization(); void initLocalization();
TextureMgr texmgr;
protected: protected:
CoreWindow *window; CoreWindow *window;
@ -450,8 +434,6 @@ protected:
virtual void onReloadResources(); virtual void onReloadResources();
CountedPtr<Texture> doTextureAdd(const std::string &texture, const std::string &name, std::string internalTextureName);
bool lib_graphics, lib_sound, lib_input; bool lib_graphics, lib_sound, lib_input;
Vector clearColor; Vector clearColor;
virtual void unloadDevice(); virtual void unloadDevice();
@ -495,7 +477,6 @@ protected:
bool shuttingDown; bool shuttingDown;
bool quitNestedMainFlag; bool quitNestedMainFlag;
int nestedMains; int nestedMains;
std::string baseTextureDirectory;
int nowTicks, thenTicks; int nowTicks, thenTicks;

View file

@ -25,129 +25,104 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Precacher::Precacher() Precacher::Precacher()
{ {
loadProgressCallback = NULL;
cleaned = true;
} }
Precacher::~Precacher() Precacher::~Precacher()
{ {
if (!cleaned)
errorLog ("Precacher shutdown unclean");
} }
void Precacher::setBaseDir(const std::string& dir) void Precacher::setBaseDir(const std::string& dir)
{ {
basedirOverride = dir; basedir = dir;
} }
void Precacher::clean() void Precacher::clear()
{ {
for (unsigned int i = 0; i < renderObjects.size(); i++) texkeep.clear();
{
RenderObject *r = renderObjects[i];
r->destroy();
delete r;
}
renderObjects.clear();
cleaned = true;
} }
void Precacher::loadTextureRange(const std::string &file, const std::string &type, int start, int end) void Precacher::_Callback(const std::string &file, void *param)
{
for (int t = start; t < end; t++)
{
std::ostringstream num_os;
num_os << t;
std::ostringstream os;
os << file;
if(num_os.str().size() <= 4) {
for (size_t j = 0; j < 4 - num_os.str().size(); j++) {
os << "0";
}
}
os << t;
os << type;
precacheTex(os.str());
}
}
void precacherCallback(const std::string &file, void *param)
{ {
Precacher *p = (Precacher*)param; Precacher *p = (Precacher*)param;
p->precacheTex(file); p->_precacheTex(file);
} }
// precacheTex // precacheTex
// caches one texture // caches one texture
// also support simple wildcard to cache multiple textures // also support simple wildcard to cache multiple textures
// e.g. naija/*.png // e.g. naija/*.png
void Precacher::precacheTex(const std::string &tex) void Precacher::_precacheTex(const std::string &tex)
{ {
if (tex.find("txt") != std::string::npos) assert(!basedir.empty());
{
errorLog("Call precacheList to precache a text file of gfx names, not precacheTex!");
}
if (tex.empty()) return; if (tex.empty()) return;
std::string basedir = basedirOverride.empty() ? core->getBaseTextureDirectory() : basedirOverride;
if (core->debugLogTextures)
debugLog("PRECACHING: " + tex);
if (tex.find('*')!=std::string::npos) if (tex.find('*')!=std::string::npos)
{ {
if (core->debugLogTextures) size_t loc = tex.find('*');
debugLog("searching directory");
int loc = tex.find('*');
std::string path = tex.substr(0, loc); std::string path = tex.substr(0, loc);
std::string type = tex.substr(loc+1, tex.size()); std::string type = tex.substr(loc+1, tex.size());
path = basedir + path; path = basedir + path;
forEachFile(path, type, precacherCallback, this); forEachFile(path, type, _Callback, this);
return;
} }
else else
{ {
if (loadProgressCallback)
loadProgressCallback();
std::string t = tex; std::string t = tex;
if (tex.find(basedir) != std::string::npos) if (tex.find(basedir) != std::string::npos)
{ {
t = tex.substr(basedir.size(), tex.size()); t = tex.substr(basedir.size(), tex.size());
} }
Quad *q = new Quad; todo.push_back(t);
q->setTexture(t);
q->alpha = 0;
renderObjects.push_back(q);
cleaned = false;
} }
} }
void Precacher::precacheList(const std::string &list, void progressCallback()) static void texLoadProgressCallback(size_t done, void *ud)
{ {
loadProgressCallback = progressCallback; Precacher::ProgressCallback cb = (Precacher::ProgressCallback)(ud);
cb();
}
void Precacher::doCache(ProgressCallback progress)
{
if(!todo.empty())
{
std::ostringstream os;
os << "Precacher: Batch-loading " << todo.size() << " textures...";
debugLog(os.str());
std::vector<Texture*> tmp(todo.size());
core->texmgr.loadBatch(&tmp[0], &todo[0], todo.size(), TextureMgr::KEEP,
progress ? texLoadProgressCallback : NULL, progress);
todo.clear();
texkeep.reserve(texkeep.size() + tmp.size());
for(size_t i = 0; i < tmp.size(); ++i)
texkeep.push_back(tmp[i]);
}
debugLog("Precacher: done");
}
void Precacher::precacheList(const std::string &list, ProgressCallback progress)
{
assert(todo.empty());
InStream in(list.c_str()); InStream in(list.c_str());
std::string t; std::string t;
while (std::getline(in, t)) while (std::getline(in, t))
{ {
if (!t.empty()) while (!t.empty())
{ {
#if defined(BBGE_BUILD_UNIX) if(t.back() == '\r' || t.back() == '\n') // linux doesn't like CRLF, make sure to trim that off
t.pop_back();
t = t.substr(0,t.size()-1); else
debugLog("precache["+t+"]"); break;
#endif
stringToLower(t);
precacheTex(t);
} }
if(!t.empty())
_precacheTex(t);
} }
in.close(); in.close();
loadProgressCallback = NULL; doCache(progress);
} }
void Precacher::precacheTex(const std::string& tex, ProgressCallback progress)
{
_precacheTex(tex);
doCache(progress);
}

View file

@ -23,25 +23,28 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <vector> #include <vector>
#include <string> #include <string>
#include "Texture.h"
class RenderObject; class RenderObject;
class Precacher class Precacher
{ {
public: public:
typedef void (*ProgressCallback)(void);
Precacher(); Precacher();
~Precacher(); ~Precacher();
void precacheTex(const std::string &tex); void precacheList(const std::string &list, ProgressCallback progress = NULL);
void precacheList(const std::string &list, void progressCallback() = NULL); void precacheTex(const std::string &tex, ProgressCallback progress = NULL);
void clean(); void clear();
void loadTextureRange(const std::string &file, const std::string &type, int start, int end);
void setBaseDir(const std::string& dir); void setBaseDir(const std::string& dir);
std::vector<RenderObject*> renderObjects;
private: private:
bool cleaned; static void _Callback(const std::string &file, void *param);
void (*loadProgressCallback)(); void _precacheTex(const std::string &tex);
std::string basedirOverride; std::string basedir;
std::vector<CountedPtr<Texture> > texkeep;
std::vector<std::string> todo;
void doCache(ProgressCallback progress = NULL);
}; };
#endif #endif

View file

@ -349,8 +349,7 @@ void Quad::renderGrid(const RenderState& rs) const
} }
} }
glEnd(); glEnd();
if (texture) RenderObject::lastTextureApplied = 0;
glBindTexture(GL_TEXTURE_2D, texture->textures[0]);
} }
} }

View file

@ -491,11 +491,11 @@ nofollow:
{ {
if (texture) if (texture)
{ {
if (texture->textures[0] != lastTextureApplied || repeatTexture != lastTextureRepeat) if (texture->gltexid != lastTextureApplied || repeatTexture != lastTextureRepeat)
{ {
texture->apply(repeatTexture); texture->apply(repeatTexture);
lastTextureRepeat = repeatTexture; lastTextureRepeat = repeatTexture;
lastTextureApplied = texture->textures[0]; lastTextureApplied = texture->gltexid;
} }
} }
else else
@ -866,21 +866,9 @@ void RenderObject::freeMotionBlur()
bool RenderObject::setTexture(const std::string &n) bool RenderObject::setTexture(const std::string &n)
{ {
std::string name = n; CountedPtr<Texture> tex = core->getTexture(n);
stringToLowerUserData(name);
if (name.empty())
{
setTexturePointer(NULL);
return false;
}
if(texture && texture->getLoadResult() == TEX_SUCCESS && name == texture->name)
return true; // no texture change
CountedPtr<Texture> tex = core->addTexture(name);
setTexturePointer(tex); setTexturePointer(tex);
return tex && tex->getLoadResult() == TEX_SUCCESS; return tex->success;
} }
void RenderObject::addChild(RenderObject *r, ParentManaged pm, RenderBeforeParent rbp, ChildOrder order) void RenderObject::addChild(RenderObject *r, ParentManaged pm, RenderBeforeParent rbp, ChildOrder order)

View file

@ -18,8 +18,10 @@ You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ */
#include <sstream>
#include "Base.h"
#include "Texture.h" #include "Texture.h"
#include "Core.h"
#include "Image.h" #include "Image.h"
#include "ByteBuffer.h" #include "ByteBuffer.h"
#include "RenderBase.h" #include "RenderBase.h"
@ -31,41 +33,31 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Texture::Texture() Texture::Texture()
{ {
textures[0] = 0; gltexid = 0;
width = height = 0; width = height = 0;
_repeating = false; _repeating = false;
ow = oh = -1; ow = oh = -1;
loadResult = TEX_FAILED;
_mipmap = false; _mipmap = false;
success = false;
} }
Texture::~Texture() Texture::~Texture()
{ {
destroy(); unload();
} }
void Texture::read(int tx, int ty, int w, int h, unsigned char *pixels) void Texture::readRGBA(unsigned char *pixels)
{ {
if (tx == 0 && ty == 0 && w == this->width && h == this->height) glPixelStorei(GL_PACK_ALIGNMENT, 1);
{ glBindTexture(GL_TEXTURE_2D, gltexid);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
else
{
std::ostringstream os;
os << "Unable to read a texture subimage (size = "
<< this->width << "x" << this->height << ", requested = "
<< tx << "," << ty << "+" << w << "x" << h << ")";
debugLog(os.str());
}
}
void Texture::write(int tx, int ty, int w, int h, const unsigned char *pixels) void Texture::writeRGBA(int tx, int ty, int w, int h, const unsigned char *pixels)
{ {
glBindTexture(GL_TEXTURE_2D, textures[0]); glBindTexture(GL_TEXTURE_2D, gltexid);
glTexSubImage2D(GL_TEXTURE_2D, 0, glTexSubImage2D(GL_TEXTURE_2D, 0,
tx, tx,
@ -78,158 +70,24 @@ void Texture::write(int tx, int ty, int w, int h, const unsigned char *pixels)
); );
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
/*
target Specifies the target texture. Must be
GL_TEXTURE_2D.
level Specifies the level-of-detail number. Level 0 is
the base image level. Level n is the nth mipmap
reduction image.
xoffset Specifies a texel offset in the x direction within
the texture array.
yoffset Specifies a texel offset in the y direction within
the texture array.
width Specifies the width of the texture subimage.
height Specifies the height of the texture subimage.
format Specifies the format of the pixel data. The
following symbolic values are accepted:
GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE,
GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, and
GL_LUMINANCE_ALPHA.
type Specifies the data type of the pixel data. The
following symbolic values are accepted:
GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP,
GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT,
GL_INT, and GL_FLOAT.
pixels Specifies a pointer to the image data in memory.
*/
} }
void Texture::unload() void Texture::unload()
{ {
if (textures[0]) if (gltexid)
{ {
ow = width; ow = width;
oh = height; oh = height;
if (core->debugLogTextures) glDeleteTextures(1, &gltexid);
{ gltexid = 0;
debugLog("UNLOADING TEXTURE: " + name);
} }
glDeleteTextures(1, &textures[0]);
textures[0] = 0;
}
}
void Texture::destroy()
{
unload();
core->removeTexture(this);
}
void Texture::reload()
{
debugLog("RELOADING TEXTURE: " + name + " with loadName " + loadName + "...");
unload();
load(loadName, _mipmap);
debugLog("DONE");
}
bool Texture::load(std::string file, bool mipmap)
{
loadResult = TEX_FAILED;
if (file.size()<4)
{
errorLog("Texture Name is Empty or Too Short");
return false;
}
stringToLowerUserData(file);
file = adjustFilenameCase(file);
loadName = file;
_mipmap = mipmap;
size_t pos = file.find_last_of('.');
if (pos != std::string::npos)
{
// make sure this didn't catch the '.' in /home/username/.Aquaria/* --ryan.
const std::string userdata = core->getUserDataFolder();
const size_t len = userdata.length();
if (pos < len)
pos = std::string::npos;
}
bool found = exists(file);
if(!found && exists(file + ".png"))
{
found = true;
file += ".png";
}
// .tga/.zga are never used as game graphics anywhere except save slot thumbnails.
// if so, their file names are passed exact, not with a missing extension
bool ok = false;
if (found)
{
file = localisePathInternalModpath(file);
file = adjustFilenameCase(file);
std::string post = file.substr(file.size()-3, 3);
stringToLower(post);
ImageData img = {};
if (post == "zga")
{
img = imageLoadZGA(file.c_str());
if(img.pixels)
mipmap = false;
else
debugLog("Can't load ZGA File: " + file);
}
else
{
img = imageLoadGeneric(file.c_str(), false);
if(!img.pixels)
debugLog("unknown image file type: " + file);
}
if(img.pixels)
{
ok = loadInternal(img, mipmap);
free(img.pixels);
}
}
else
{
// load default image / leave white
if (core->debugLogTextures)
debugLog("***Could not find texture: " + file);
}
return ok;
} }
static const GLenum repeatLUT[] = { GL_CLAMP_TO_EDGE, GL_REPEAT }; static const GLenum repeatLUT[] = { GL_CLAMP_TO_EDGE, GL_REPEAT };
void Texture::apply(bool repeat) const void Texture::apply(bool repeat) const
{ {
glBindTexture(GL_TEXTURE_2D, textures[0]); glBindTexture(GL_TEXTURE_2D, gltexid);
if(repeat != _repeating) if(repeat != _repeating)
{ {
_repeating = repeat; _repeating = repeat;
@ -252,7 +110,7 @@ static const GlTexFormat formatLUT[] =
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 3 } { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 3 }
}; };
bool Texture::loadInternal(const ImageData& img, bool mipmap) bool Texture::upload(const ImageData& img, bool mipmap)
{ {
if(!img.pixels || !img.channels || img.channels > 4 || !img.w || !img.h) if(!img.pixels || !img.channels || img.channels > 4 || !img.w || !img.h)
return false; return false;
@ -266,8 +124,9 @@ bool Texture::loadInternal(const ImageData& img, bool mipmap)
// no padding // no padding
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &textures[0]); if(!gltexid)
glBindTexture(GL_TEXTURE_2D, textures[0]); glGenTextures(1, &gltexid);
glBindTexture(GL_TEXTURE_2D, gltexid);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
_repeating = false; _repeating = false;
@ -345,79 +204,24 @@ bool Texture::loadInternal(const ImageData& img, bool mipmap)
width = img.w; width = img.w;
height = img.h; height = img.h;
loadResult = TEX_SUCCESS;
return true; return true;
} }
unsigned char * Texture::getBufferAndSize(int *wparam, int *hparam, unsigned int *sizeparam) unsigned char * Texture::getBufferAndSize(int *wparam, int *hparam, size_t *sizeparam)
{ {
unsigned char *data = NULL; const size_t bytes = size_t(width) * size_t(height) * 4;
unsigned int size = 0; unsigned char *data = (unsigned char*)malloc(bytes);
int tw = 0, th = 0;
int w = 0, h = 0;
// This can't happen. If it does we're doomed.
if(width <= 0 || height <= 0)
goto fail;
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, textures[0]);
// As returned by graphics driver
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
// As we know it - but round to nearest power of 2 - OpenGL does this internally anyways.
tw = bithacks::clp2(width); // known to be > 0.
th = bithacks::clp2(height);
if (w != tw || h != th)
{
std::ostringstream os;
os << "Texture::getBufferAndSize() WARNING: width/height disagree: ";
os << "Driver says (" << w << ", " << h << "); ";
os << "Texture says (" << width << ", " << height << "); ";
os << "Rounded to (" << tw << ", " << th << ")";
debugLog(os.str());
// choose max. for size calculation
w = w > tw ? w : tw;
h = h > th ? h : th;
}
size = w * h * 4;
if (!size)
goto fail;
data = (unsigned char*)malloc(size + 32);
if (!data) if (!data)
{ {
std::ostringstream os; std::ostringstream os;
os << "Game::fillGridFromQuad allocation failure, size = " << size; os << "Game::getBufferAndSize allocation failure, bytes = " << bytes;
errorLog(os.str()); errorLog(os.str());
goto fail;
}
memcpy(data + size, "SAFE", 5);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glBindTexture(GL_TEXTURE_2D, 0);
// Not sure but this might be the case with nouveau drivers on linux... still investigating. -- fg
if(memcmp(data + size, "SAFE", 5))
{
errorLog("Texture::getBufferAndSize(): Broken graphics driver! Wrote past end of buffer!");
free(data); // in case we are here, this will most likely cause a crash.
goto fail;
}
*wparam = w;
*hparam = h;
*sizeparam = size;
return data;
fail:
*wparam = 0;
*hparam = 0;
*sizeparam = 0;
return NULL; return NULL;
} }
this->readRGBA(data);
*wparam = width;
*hparam = height;
*sizeparam = bytes;
return data;
}

View file

@ -39,39 +39,30 @@ public:
Texture(); Texture();
~Texture(); ~Texture();
bool load(std::string file, bool mipmap);
void apply(bool repeat = false) const; void apply(bool repeat = false) const;
void unload(); void unload();
void destroy(); unsigned gltexid;
int width, height; int width, height;
unsigned textures[1]; void writeRGBA(int tx, int ty, int w, int h, const unsigned char *pixels);
void readRGBA(unsigned char *pixels);
void reload(); unsigned char *getBufferAndSize(int *w, int *h, size_t *size); // returned memory must be free()'d
void write(int tx, int ty, int w, int h, const unsigned char *pixels); std::string name, filename;
void read(int tx, int ty, int w, int h, unsigned char *pixels); bool upload(const ImageData& img, bool mipmap);
unsigned char *getBufferAndSize(int *w, int *h, unsigned int *size); // returned memory must be free()'d bool success;
std::string name;
TextureLoadResult getLoadResult() const { return loadResult; }
protected: protected:
std::string loadName;
bool loadInternal(const ImageData& img, bool mipmap);
int ow, oh; int ow, oh;
TextureLoadResult loadResult;
bool _mipmap; bool _mipmap;
private: private:
mutable bool _repeating; // modified during rendering mutable bool _repeating; // modified during rendering
}; };
#define UNREFTEX(x) if (x) {x = NULL;} #define UNREFTEX(x) {x = NULL;}
#endif #endif

318
BBGE/TextureMgr.cpp Normal file
View file

@ -0,0 +1,318 @@
#include "TextureMgr.h"
#include <sstream>
#include <SDL.h>
#include "MT.h"
#include "Image.h"
#include "Base.h"
#include "Localization.h"
struct TexLoadTmp
{
TexLoadTmp() : curTex(NULL), success(false) { img.pixels = NULL; }
std::string name, filename;
ImageData img;
TextureMgr::LoadMode loadmode;
Texture *curTex; // immutable
bool success; // if this is true and img.pixels is NULL, don't change anything
//bool mipmap;
};
typedef ImageData (*ImageLoadFunc)(const char *fn);
struct TexLoader
{
TexLoader(ImageLoadFunc f, const std::string& s, const std::string& name = std::string())
: loader(f), fn(s), name(name) {}
ImageLoadFunc loader;
std::string fn;
std::string name; // usually empty, but may specify tex name in case cleanups had to be done. always lowercase.
ImageData load() const { return loader(fn.c_str()); }
};
static ImageData loadGeneric(const char *fn)
{
return imageLoadGeneric(fn, false);
}
static const std::string fixup(const std::string& fn)
{
std::string file = localisePathInternalModpath(fn);
file = adjustFilenameCase(file);
return file;
}
struct ExtAndLoader
{
const char *ext;
ImageLoadFunc func;
};
static const ExtAndLoader s_extAndLoader[] =
{
{ "png", loadGeneric },
{ "jpg", loadGeneric },
{ "zga", imageLoadZGA },
{ "tga", loadGeneric },
{ NULL, NULL }
};
static TexLoader getFullnameAndLoader(const std::string& name, const std::string& basedir)
{
// TODO: use localisePath()
// TODO: assert that we don't start with . or /
if(exists(name))
{
// check first if name exists and has a known extension, if so, use correct loader
size_t lastdot = name.rfind('.');
size_t lastslash = name.rfind('/');
if(lastdot != std::string::npos && (lastslash == std::string::npos || lastslash < lastdot))
{
std::string ext = name.substr(lastdot + 1);
for(const ExtAndLoader *exl = &s_extAndLoader[0]; exl->func; ++exl)
{
if(ext == exl->ext)
{
// remove basedir and extension
std::string texname = name.substr(basedir.length(), name.length() - (basedir.length() + ext.length() + 1)); // strip extension
return TexLoader(exl->func, fixup(name), texname );
}
}
}
}
std::string fn = name;
for(const ExtAndLoader *exl = &s_extAndLoader[0]; exl->func; ++exl)
{
fn.resize(name.length());
fn += '.';
fn += exl->ext;
if(exists(fn))
return TexLoader(exl->func, fixup(fn));
}
return TexLoader(NULL, std::string());
}
void TextureMgr::th_loadFromFile(TexLoadTmp& tt) const
{
std::string withoutExt;
for(size_t i = 0; i < loadFromPaths.size(); ++i)
{
withoutExt = loadFromPaths[i] + tt.name;
TexLoader ldr = getFullnameAndLoader(withoutExt, loadFromPaths[i]);
if(!ldr.name.empty()) // name was cleaned up, use the updated one
tt.name = ldr.name;
if(ldr.loader)
{
if(tt.loadmode < OVERWRITE && tt.curTex && tt.curTex->success && tt.curTex->filename == ldr.fn)
{
tt.success = true;
break;
}
tt.filename = ldr.fn;
tt.img = ldr.load();
tt.success = !!tt.img.pixels;
return;
}
}
tt.img.pixels = NULL;
}
TextureMgr::TextureMgr()
: sem(SDL_CreateSemaphore(0))
{
}
TextureMgr::~TextureMgr()
{
for(size_t i = 0; i < threads.size(); ++i)
worktodo.push(NULL); // signal all threads to exit
for(size_t i = 0; i < threads.size(); ++i)
SDL_WaitThread((SDL_Thread*)threads[i], NULL);
SDL_DestroySemaphore((SDL_sem*)sem);
}
size_t TextureMgr::spawnThreads(size_t n)
{
for(size_t i = 0; i < n; ++i)
{
SDL_Thread *worker;
#if SDL_VERSION_ATLEAST(2,0,0)
worker = SDL_CreateThread(Th_Main, "texldr", this);
#else
worker = SDL_CreateThread(Th_Main, this);
#endif
if(!worker)
return i;
threads.push_back(worker);
}
return n;
}
size_t TextureMgr::getNumLoaded() const
{
return cache.size();
}
Texture* TextureMgr::getOrLoad(const std::string& name)
{
return load(name, KEEP);
}
void TextureMgr::shutdown()
{
for(TexCache::iterator it = cache.begin(); it != cache.end(); ++it)
it->second->unload();
cache.clear();
}
int TextureMgr::Th_Main(void* ud)
{
TextureMgr *self = (TextureMgr*)ud;
self->thMain();
return 0;
}
void TextureMgr::thMain()
{
for(;;)
{
void *p;
worktodo.pop(p);
if(!p) // a pushed NULL is the signal to exit
break;
TexLoadTmp *tt = (TexLoadTmp*)p;
th_loadFromFile(*tt);
workdone.push(p);
}
}
Texture *TextureMgr::finalize(const TexLoadTmp& tt)
{
Texture *tex = tt.curTex;
if(!tex)
{
tex = new Texture; // always make sure a valid Texture object comes out
tex->name = tt.name; // this doesn't ever change
cache[tt.name] = tex; // didn't exist, cache now
}
tex->filename = tt.filename;
tex->success = tt.success;
if(!tt.success)
{
debugLog("FAILED TO LOAD TEXTURE: [" + tt.name + "]");
tex->unload();
tex->width = 64;
tex->height = 64;
}
if(tt.img.pixels)
{
//debugLog("LOADED TEXTURE FROM DISK: [" + tt.name + "]");
tex->upload(tt.img, /*tt.mipmap*/ true);
free(tt.img.pixels);
}
return tex;
}
void TextureMgr::loadBatch(Texture * pdst[], const std::string texnames[], size_t n, LoadMode mode, ProgressCallback cb, void *cbUD)
{
if(threads.empty())
{
for(size_t i = 0; i < n; ++i)
{
Texture *tex = load(texnames[i], mode);
if(pdst)
pdst[i] = tex;
}
return;
}
// Important that this is pre-allocated. We store pointers to elements and
// send them to threads, so this must never reallocate.
std::vector<TexLoadTmp> tmp(n);
size_t inprogress = 0, doneCB = 0;
for(size_t i = 0; i < n; ++i)
{
TexLoadTmp& tt = tmp[i];
tt.name = texnames[i];
stringToLower(tt.name);
TexCache::iterator it = cache.find(tt.name);
tt.curTex = it != cache.end() ? it->second.content() : NULL;
if(mode == KEEP && tt.curTex && tt.curTex->success)
{
if(pdst)
pdst[i] = tt.curTex;
if(cb)
cb(++doneCB, cbUD);
continue;
}
tt.loadmode = mode;
worktodo.push(&tt);
++inprogress;
}
for(size_t i = 0; i < inprogress; ++i)
{
void *p;
workdone.pop(p);
const TexLoadTmp& tt = *(const TexLoadTmp*)p;
Texture *tex = finalize(tt);
if(pdst)
pdst[i] = tex;
if(cb)
cb(++doneCB, cbUD);
}
}
Texture* TextureMgr::load(const std::string& texname, LoadMode mode)
{
TexLoadTmp tt;
tt.name = texname;
stringToLower(tt.name);
TexCache::iterator it = cache.find(tt.name);
tt.curTex = it != cache.end() ? it->second.content() : NULL;
// texname "" will never load, so don't even try once we have a valid object
if(tt.curTex && (texname.empty() || (mode == KEEP && tt.curTex->success)))
return tt.curTex;
tt.loadmode = mode;
th_loadFromFile(tt);
return finalize(tt);
}
void TextureMgr::reloadAll(LoadMode mode)
{
std::vector<std::string> todo;
for(TexCache::iterator it = cache.begin(); it != cache.end(); ++it)
{
Texture *tex = it->second.content();
if(mode == KEEP && tex->success)
continue;
todo.push_back(tex->name);
}
std::ostringstream os;
os << "TextureMgr: Potentially reloading up to " << todo.size() << " textures...";
debugLog(os.str());
if(!todo.empty())
loadBatch(NULL, &todo[0],todo.size(), mode);
}

53
BBGE/TextureMgr.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef BBGE_TEXTUREMGR_H
#define BBGE_TEXTUREMGR_H
#include <map>
#include "Texture.h"
#include "MT.h"
struct TexLoadTmp;
class TextureMgr
{
public:
TextureMgr();
~TextureMgr();
typedef void (*ProgressCallback)(size_t done, void*);
std::vector<std::string> loadFromPaths;
size_t spawnThreads(size_t n);
size_t getNumLoaded() const;
Texture *getOrLoad(const std::string& name);
void clearUnused(); // clear everything whose refcount is 1
void shutdown();
enum LoadMode
{
KEEP, // if already exists, keep unchanged
KEEP_IF_SAME, // load if we resolve to a different file than the texture that's already there, if any.
OVERWRITE, // always overwrite
};
void loadBatch(Texture *pdst[], const std::string texnames[], size_t n, LoadMode mode = KEEP, ProgressCallback cb = 0, void *cbUD = 0);
Texture *load(const std::string& texname, LoadMode mode);
void reloadAll(LoadMode mode);
private:
typedef std::map<std::string, CountedPtr<Texture> > TexCache;
TexCache cache;
BlockingQueue<void*> worktodo;
BlockingQueue<void*> workdone;
std::vector<void*> threads;
void *sem; // SDL_sem*
static int Th_Main(void *self);
void thMain(); // for int
void th_loadFromFile(TexLoadTmp& tt) const;
Texture *finalize(const TexLoadTmp& tt);
};
#endif

View file

@ -6,6 +6,10 @@
#include <sstream> #include <sstream>
#include <stdio.h> #include <stdio.h>
// HACK: add a big lock to make this thing not crash when multiple threads are active
#include "../../BBGE/MT.h"
static Lockable lock;
static ttvfs::Root *vfs = NULL; static ttvfs::Root *vfs = NULL;
@ -14,8 +18,15 @@ void ttvfs_setroot(ttvfs::Root *root)
vfs = root; vfs = root;
} }
VFILE* vfgetfile(const char* fn)
{
MTGuard g(lock);
return vfs->GetFile(fn);
}
VFILE *vfopen(const char *fn, const char *mode) VFILE *vfopen(const char *fn, const char *mode)
{ {
MTGuard g(lock);
VFILE *vf = vfs->GetFile(fn); VFILE *vf = vfs->GetFile(fn);
if (!vf || !vf->open(mode)) if (!vf || !vf->open(mode))
return NULL; return NULL;
@ -30,6 +41,7 @@ size_t vfread(void *ptr, size_t size, size_t count, VFILE *vf)
int vfclose(VFILE *vf) int vfclose(VFILE *vf)
{ {
MTGuard g(lock);
vf->close(); vf->close();
vf->decref(); vf->decref();
return 0; return 0;
@ -105,12 +117,16 @@ InStream::InStream(const char *fn)
bool InStream::open(const char *fn) bool InStream::open(const char *fn)
{ {
ttvfs::File *vf = vfs->GetFile(fn); ttvfs::File *vf = NULL;
{
MTGuard g(lock);
vf = vfs->GetFile(fn);
if(!vf || !vf->open("r")) if(!vf || !vf->open("r"))
{ {
setstate(std::ios::failbit); setstate(std::ios::failbit);
return false; return false;
} }
}
size_t sz = (size_t)vf->size(); size_t sz = (size_t)vf->size();
std::string s; std::string s;
s.resize(sz); s.resize(sz);

View file

@ -36,6 +36,9 @@ typedef ttvfs::File VFILE;
void ttvfs_setroot(ttvfs::Root *root); void ttvfs_setroot(ttvfs::Root *root);
// HACK
VFILE *vfgetfile(const char *fn);
// Note that vfopen() returns the same pointer for the same file name, // Note that vfopen() returns the same pointer for the same file name,
// so effectively a file is a singleton object. // so effectively a file is a singleton object.
VFILE *vfopen(const char *fn, const char *mode); VFILE *vfopen(const char *fn, const char *mode);