cache loaded NIF files to eliminate reloads

Created a NIF file caching mechanism to prevent the system from
reloading a NIF during a startup and cell changes.
actorid
Nathan Jeffords 12 years ago
parent 0989b44b41
commit d5ebd6654d

@ -277,6 +277,8 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
void OMW::Engine::prepareEngine (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings)
{ {
Nif::NIFFile::CacheLock cachelock;
std::string renderSystem = settings.getString("render system", "Video"); std::string renderSystem = settings.getString("render system", "Video");
if (renderSystem == "") if (renderSystem == "")
{ {

@ -172,6 +172,8 @@ namespace MWWorld
void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos)
{ {
Nif::NIFFile::CacheLock cachelock;
mRendering.preCellChange(mCurrentCell); mRendering.preCellChange(mCurrentCell);
// remove active // remove active

@ -34,10 +34,159 @@
#include "controller.hpp" #include "controller.hpp"
#include <iostream> #include <iostream>
//TODO: when threading is needed, enable these
//#include <boost/mutex.hpp>
//#include <boost/thread/locks.hpp>
using namespace std; using namespace std;
using namespace Nif; using namespace Nif;
using namespace Misc; using namespace Misc;
class NIFFile::LoadedCache
{
//TODO: enable this to make cache thread safe...
//typedef boost::mutex mutex;
struct mutex
{
void lock () {};
void unlock () {}
};
typedef boost::lock_guard <mutex> lock_guard;
typedef std::map < std::string, boost::weak_ptr <NIFFile> > loaded_map;
typedef std::vector < boost::shared_ptr <NIFFile> > locked_files;
static int sLockLevel;
static mutex sProtector;
static loaded_map sLoadedMap;
static locked_files sLockedFiles;
public:
static ptr create (const std::string &name)
{
lock_guard _ (sProtector);
ptr result;
// lookup the resource
loaded_map::iterator i = sLoadedMap.find (name);
if (i == sLoadedMap.end ()) // it doesn't existing currently,
{ // or hasn't in the very near past
// create it now, for smoother threading if needed, the
// loading should be performed outside of the sLoaderMap
// lock and an alternate mechanism should be used to
// synchronize threads competing to load the same resource
result = boost::make_shared <NIFFile> (name, psudo_private_modifier());
// if we are locking the cache add an extra reference
// to keep the file in memory
if (sLockLevel > 0)
sLockedFiles.push_back (result);
// stash a reference to the resource so that future
// calls can benefit
sLoadedMap [name] = boost::weak_ptr <NIFFile> (result);
}
else // it may (probably) still exists
{
// attempt to get the reference
result = i->second.lock ();
if (!result) // resource is in the process of being destroyed
{
// create a new instance, to replace the one that has
// begun the irreversible process of being destroyed
result = boost::make_shared <NIFFile> (name, psudo_private_modifier());
// respect the cache lock...
if (sLockLevel > 0)
sLockedFiles.push_back (result);
// we potentially overwrite an expired pointer here
// but the other thread performing the delete on
// the previous copy of this resource will detect it
// and make sure not to erase the new reference
sLoadedMap [name] = boost::weak_ptr <NIFFile> (result);
}
}
// we made it!
return result;
}
static void release (NIFFile * file)
{
lock_guard _ (sProtector);
loaded_map::iterator i = sLoadedMap.find (file->filename);
// its got to be in here, it just might not be us...
assert (i != sLoadedMap.end ());
// if weak_ptr is still expired, this resource hasn't been recreated
// between the initiation of the final release due to destruction
// of the last shared pointer and this thread acquiring the lock on
// the loader map
if (i->second.expired ())
sLoadedMap.erase (i);
}
static void lockCache ()
{
lock_guard _ (sProtector);
sLockLevel++;
}
static void unlockCache ()
{
locked_files resetList;
{
lock_guard _ (sProtector);
if (--sLockLevel)
sLockedFiles.swap(resetList);
}
// this not necessary, but makes it clear that the
// deletion of the locked cache entries is being done
// outside the protection of sProtector
resetList.clear ();
}
};
int NIFFile::LoadedCache::sLockLevel = 0;
NIFFile::LoadedCache::mutex NIFFile::LoadedCache::sProtector;
NIFFile::LoadedCache::loaded_map NIFFile::LoadedCache::sLoadedMap;
NIFFile::LoadedCache::locked_files NIFFile::LoadedCache::sLockedFiles;
// these three calls are forwarded to the cache implementation...
void NIFFile::lockCache () { LoadedCache::lockCache (); }
void NIFFile::unlockCache () { LoadedCache::unlockCache (); }
NIFFile::ptr NIFFile::create (const std::string &name) { return LoadedCache::create (name); }
/// Open a NIF stream. The name is used for error messages.
NIFFile::NIFFile(const std::string &name, psudo_private_modifier)
: filename(name)
{
inp = Ogre::ResourceGroupManager::getSingleton().openResource(name);
parse();
}
NIFFile::~NIFFile()
{
LoadedCache::release (this);
for(std::size_t i=0; i<records.size(); i++)
delete records[i];
}
/* This file implements functions from the NIFFile class. It is also /* This file implements functions from the NIFFile class. It is also
where we stash all the functions we couldn't add as inline where we stash all the functions we couldn't add as inline
definitions in the record types. definitions in the record types.

@ -37,6 +37,11 @@
#include <vector> #include <vector>
#include <cassert> #include <cassert>
#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/detail/endian.hpp>
#include <libs/platform/stdint.h> #include <libs/platform/stdint.h>
#include "record.hpp" #include "record.hpp"
@ -93,6 +98,14 @@ class NIFFile
return u.f; return u.f;
} }
class LoadedCache;
friend class LoadedCache;
// attempt to protect NIFFile from misuse...
struct psudo_private_modifier {}; // this dirty little trick should optimize out
NIFFile (NIFFile const &);
void operator = (NIFFile const &);
public: public:
/// Used for error handling /// Used for error handling
void fail(const std::string &msg) void fail(const std::string &msg)
@ -108,19 +121,21 @@ public:
<< "File: "<<filename <<std::endl; << "File: "<<filename <<std::endl;
} }
typedef boost::shared_ptr <NIFFile> ptr;
/// Open a NIF stream. The name is used for error messages. /// Open a NIF stream. The name is used for error messages.
NIFFile(const std::string &name) NIFFile(const std::string &name, psudo_private_modifier);
: filename(name) ~NIFFile();
{
inp = Ogre::ResourceGroupManager::getSingleton().openResource(name); static ptr create (const std::string &name);
parse(); static void lockCache ();
} static void unlockCache ();
~NIFFile() struct CacheLock
{ {
for(std::size_t i=0; i<records.size(); i++) CacheLock () { lockCache (); }
delete records[i]; ~CacheLock () { unlockCache (); }
} };
/// Get a given record /// Get a given record
Record *getRecord(size_t index) Record *getRecord(size_t index)

@ -82,7 +82,8 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource)
// of the early stages of development. Right now we WANT to catch // of the early stages of development. Right now we WANT to catch
// every error as early and intrusively as possible, as it's most // every error as early and intrusively as possible, as it's most
// likely a sign of incomplete code rather than faulty input. // likely a sign of incomplete code rather than faulty input.
Nif::NIFFile nif(resourceName.substr(0, resourceName.length()-7)); Nif::NIFFile::ptr pnif (Nif::NIFFile::create (resourceName.substr(0, resourceName.length()-7)));
Nif::NIFFile & nif = *pnif.get ();
if (nif.numRecords() < 1) if (nif.numRecords() < 1)
{ {
warn("Found no records in NIF."); warn("Found no records in NIF.");

@ -252,7 +252,8 @@ void loadResource(Ogre::Resource *resource)
Ogre::Skeleton *skel = dynamic_cast<Ogre::Skeleton*>(resource); Ogre::Skeleton *skel = dynamic_cast<Ogre::Skeleton*>(resource);
OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!");
Nif::NIFFile nif(skel->getName()); Nif::NIFFile::ptr pnif(Nif::NIFFile::create (skel->getName()));
Nif::NIFFile & nif = *pnif.get ();
const Nif::Node *node = dynamic_cast<const Nif::Node*>(nif.getRecord(0)); const Nif::Node *node = dynamic_cast<const Nif::Node*>(nif.getRecord(0));
std::vector<Nif::NiKeyframeController const*> ctrls; std::vector<Nif::NiKeyframeController const*> ctrls;
@ -956,8 +957,8 @@ public:
return; return;
} }
Nif::NIFFile nif(mName); Nif::NIFFile::ptr nif = Nif::NIFFile::create (mName);
Nif::Node const *node = dynamic_cast<Nif::Node const *>(nif.getRecord(0)); Nif::Node const *node = dynamic_cast<Nif::Node const *>(nif->getRecord(0));
findTriShape(mesh, node); findTriShape(mesh, node);
} }
@ -1054,7 +1055,8 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::
return meshiter->second; return meshiter->second;
MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName]; MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName];
Nif::NIFFile nif(name); Nif::NIFFile::ptr pnif = Nif::NIFFile::create (name);
Nif::NIFFile &nif = *pnif.get ();
if (nif.numRecords() < 1) if (nif.numRecords() < 1)
{ {
nif.warn("Found no records in NIF."); nif.warn("Found no records in NIF.");

Loading…
Cancel
Save