forked from teamnwah/openmw-tes3coop
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.
This commit is contained in:
parent
0989b44b41
commit
d5ebd6654d
6 changed files with 187 additions and 16 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
/// 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();
|
||||
}
|
||||
typedef boost::shared_ptr <NIFFile> ptr;
|
||||
|
||||
~NIFFile()
|
||||
/// Open a NIF stream. The name is used for error messages.
|
||||
NIFFile(const std::string &name, psudo_private_modifier);
|
||||
~NIFFile();
|
||||
|
||||
static ptr create (const std::string &name);
|
||||
static void lockCache ();
|
||||
static void unlockCache ();
|
||||
|
||||
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…
Reference in a new issue