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.
pull/16/head
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)
{
Nif::NIFFile::CacheLock cachelock;
std::string renderSystem = settings.getString("render system", "Video");
if (renderSystem == "")
{

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

@ -34,10 +34,159 @@
#include "controller.hpp"
#include <iostream>
//TODO: when threading is needed, enable these
//#include <boost/mutex.hpp>
//#include <boost/thread/locks.hpp>
using namespace std;
using namespace Nif;
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
where we stash all the functions we couldn't add as inline
definitions in the record types.

@ -37,6 +37,11 @@
#include <vector>
#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 "record.hpp"
@ -93,6 +98,14 @@ class NIFFile
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:
/// Used for error handling
void fail(const std::string &msg)
@ -108,19 +121,21 @@ public:
<< "File: "<<filename <<std::endl;
}
typedef boost::shared_ptr <NIFFile> ptr;
/// Open a NIF stream. The name is used for error messages.
NIFFile(const std::string &name)
: filename(name)
{
inp = Ogre::ResourceGroupManager::getSingleton().openResource(name);
parse();
}
NIFFile(const std::string &name, psudo_private_modifier);
~NIFFile();
static ptr create (const std::string &name);
static void lockCache ();
static void unlockCache ();
~NIFFile()
struct CacheLock
{
for(std::size_t i=0; i<records.size(); i++)
delete records[i];
}
CacheLock () { lockCache (); }
~CacheLock () { unlockCache (); }
};
/// Get a given record
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
// every error as early and intrusively as possible, as it's most
// 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)
{
warn("Found no records in NIF.");

@ -252,7 +252,8 @@ void loadResource(Ogre::Resource *resource)
Ogre::Skeleton *skel = dynamic_cast<Ogre::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));
std::vector<Nif::NiKeyframeController const*> ctrls;
@ -956,8 +957,8 @@ public:
return;
}
Nif::NIFFile nif(mName);
Nif::Node const *node = dynamic_cast<Nif::Node const *>(nif.getRecord(0));
Nif::NIFFile::ptr nif = Nif::NIFFile::create (mName);
Nif::Node const *node = dynamic_cast<Nif::Node const *>(nif->getRecord(0));
findTriShape(mesh, node);
}
@ -1054,7 +1055,8 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::
return meshiter->second;
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)
{
nif.warn("Found no records in NIF.");

Loading…
Cancel
Save