Add basic cell preloader class

Not properly in use yet, but seems to be working.
coverity_scan
scrawl 9 years ago
parent e055ae094a
commit 6f9ca0f68f

@ -67,6 +67,7 @@ add_openmw_dir (mwworld
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager
cellpreloader
) )
add_openmw_dir (mwphysics add_openmw_dir (mwphysics

@ -690,6 +690,11 @@ namespace MWPhysics
delete mBroadphase; delete mBroadphase;
} }
Resource::BulletShapeManager *PhysicsSystem::getShapeManager()
{
return mShapeManager.get();
}
bool PhysicsSystem::toggleDebugRendering() bool PhysicsSystem::toggleDebugRendering()
{ {
mDebugDrawEnabled = !mDebugDrawEnabled; mDebugDrawEnabled = !mDebugDrawEnabled;

@ -53,6 +53,8 @@ namespace MWPhysics
PhysicsSystem (Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode); PhysicsSystem (Resource::ResourceSystem* resourceSystem, osg::ref_ptr<osg::Group> parentNode);
~PhysicsSystem (); ~PhysicsSystem ();
Resource::BulletShapeManager* getShapeManager();
void enableWater(float height); void enableWater(float height);
void setWaterHeight(float height); void setWaterHeight(float height);
void disableWater(); void disableWater();

@ -0,0 +1,148 @@
#include "cellpreloader.hpp"
#include <iostream>
#include <components/resource/scenemanager.hpp>
#include <components/resource/bulletshapemanager.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "cellstore.hpp"
#include "manualref.hpp"
#include "class.hpp"
namespace MWWorld
{
struct ListModelsVisitor
{
ListModelsVisitor(std::vector<std::string>& out)
: mOut(out)
{
}
virtual bool operator()(const MWWorld::ConstPtr& ptr)
{
std::string model = ptr.getClass().getModel(ptr);
if (!model.empty())
mOut.push_back(model);
return true;
}
std::vector<std::string>& mOut;
};
class PreloadItem : public SceneUtil::WorkItem
{
public:
/// Constructor to be called from the main thread.
PreloadItem(const MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager)
: mSceneManager(sceneManager)
, mBulletShapeManager(bulletShapeManager)
{
if (cell->getState() == MWWorld::CellStore::State_Loaded)
{
ListModelsVisitor visitor (mMeshes);
cell->forEachConst(visitor);
}
else
{
const std::vector<std::string>& objectIds = cell->getPreloadedIds();
// could possibly build the model list in the worker thread if we manage to make the Store thread safe
for (std::vector<std::string>::const_iterator it = objectIds.begin(); it != objectIds.end(); ++it)
{
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), *it);
std::string model = ref.getPtr().getClass().getModel(ref.getPtr());
if (!model.empty())
mMeshes.push_back(model);
}
}
}
/// Preload work to be called from the worker thread.
virtual void doWork()
{
for (MeshList::const_iterator it = mMeshes.begin(); it != mMeshes.end(); ++it)
{
try
{
mPreloadedNodes.push_back(mSceneManager->getTemplate(*it));
mPreloadedShapes.push_back(mBulletShapeManager->getShape(*it));
// TODO: do a createInstance() and hold on to it since we can make use of it when the cell goes active
}
catch (std::exception& e)
{
// ignore error for now, would spam the log too much
// error will be shown when visiting the cell
}
}
}
private:
typedef std::vector<std::string> MeshList;
MeshList mMeshes;
Resource::SceneManager* mSceneManager;
Resource::BulletShapeManager* mBulletShapeManager;
// keep a ref to the loaded object to make sure it stays loaded as long as this cell is in the preloaded state
std::vector<osg::ref_ptr<const osg::Node> > mPreloadedNodes;
std::vector<osg::ref_ptr<const Resource::BulletShape> > mPreloadedShapes;
};
CellPreloader::CellPreloader(Resource::SceneManager *sceneManager, Resource::BulletShapeManager* bulletShapeManager)
: mSceneManager(sceneManager)
, mBulletShapeManager(bulletShapeManager)
{
mWorkQueue = new SceneUtil::WorkQueue;
}
void CellPreloader::preload(const CellStore *cell, double timestamp)
{
if (!mWorkQueue)
{
std::cerr << "can't preload, no work queue set " << std::endl;
return;
}
if (cell->getState() == CellStore::State_Unloaded)
{
std::cerr << "can't preload objects for unloaded cell" << std::endl;
return;
}
PreloadMap::iterator found = mPreloadCells.find(cell);
if (found != mPreloadCells.end())
{
// already preloaded, nothing to do other than updating the timestamp
found->second.mTimeStamp = timestamp;
return;
}
osg::ref_ptr<PreloadItem> item (new PreloadItem(cell, mSceneManager, mBulletShapeManager));
mWorkQueue->addWorkItem(item);
mPreloadCells[cell] = PreloadEntry(timestamp, item);
}
void CellPreloader::updateCache(double timestamp)
{
// time (in seconds) to keep a preloaded cell in cache after it's no longer needed
const double expiryTime = 60.0;
for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();)
{
if (it->second.mTimeStamp < timestamp - expiryTime)
mPreloadCells.erase(it++);
else
++it;
}
}
void CellPreloader::setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue)
{
mWorkQueue = workQueue;
}
}

@ -0,0 +1,60 @@
#ifndef OPENMW_MWWORLD_CELLPRELOADER_H
#define OPENMW_MWWORLD_CELLPRELOADER_H
#include <map>
#include <osg/ref_ptr>
#include <components/sceneutil/workqueue.hpp>
namespace Resource
{
class SceneManager;
class BulletShapeManager;
}
namespace MWWorld
{
class CellStore;
class CellPreloader
{
public:
CellPreloader(Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager);
/// Ask a background thread to preload rendering meshes and collision shapes for objects in this cell.
/// @note The cell itself must be in State_Loaded or State_Preloaded.
void preload(const MWWorld::CellStore* cell, double timestamp);
/// Removes preloaded cells that have not had a preload request for a while.
void updateCache(double timestamp);
void setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue);
private:
Resource::SceneManager* mSceneManager;
Resource::BulletShapeManager* mBulletShapeManager;
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
struct PreloadEntry
{
PreloadEntry(double timestamp, osg::ref_ptr<SceneUtil::WorkItem> workItem)
: mTimeStamp(timestamp)
, mWorkItem(workItem)
{
}
PreloadEntry()
: mTimeStamp(0.0)
{
}
double mTimeStamp;
osg::ref_ptr<SceneUtil::WorkItem> mWorkItem;
};
typedef std::map<const MWWorld::CellStore*, PreloadEntry> PreloadMap;
// Cells that are currently being preloaded, or have already finished preloading
PreloadMap mPreloadCells;
};
}
#endif

@ -333,6 +333,11 @@ namespace MWWorld
return mState; return mState;
} }
const std::vector<std::string> &CellStore::getPreloadedIds() const
{
return mIds;
}
bool CellStore::hasState() const bool CellStore::hasState() const
{ {
return mHasState; return mHasState;

@ -204,6 +204,9 @@ namespace MWWorld
State getState() const; State getState() const;
const std::vector<std::string>& getPreloadedIds() const;
///< Get Ids of objects in this cell, only valid in State_Preloaded
bool hasState() const; bool hasState() const;
///< Does this cell have state that needs to be stored in a saved game file? ///< Does this cell have state that needs to be stored in a saved game file?

@ -46,9 +46,7 @@ add_component_dir (resource
add_component_dir (sceneutil add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller
lightmanager lightutil positionattitudetransform lightmanager lightutil positionattitudetransform workqueue
# not used yet
#workqueue
) )
add_component_dir (nif add_component_dir (nif

@ -54,6 +54,9 @@ namespace Resource
void ResourceSystem::updateCache(double referenceTime) void ResourceSystem::updateCache(double referenceTime)
{ {
// TODO: call updateCache from the worker thread so the main thread isn't held up by the delete operations
// change ObjectCache to not hold lock while the unref happens
osg::Timer timer; osg::Timer timer;
for (std::vector<ResourceManager*>::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) for (std::vector<ResourceManager*>::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it)
(*it)->updateCache(referenceTime); (*it)->updateCache(referenceTime);

Loading…
Cancel
Save