mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 22:09:40 +00:00
Store preloaded terrain view in the main thread
This commit is contained in:
parent
63ab7345be
commit
489e5c6cce
7 changed files with 88 additions and 49 deletions
|
@ -167,6 +167,44 @@ namespace MWWorld
|
||||||
std::vector<osg::ref_ptr<const osg::Object> > mPreloadedObjects;
|
std::vector<osg::ref_ptr<const osg::Object> > mPreloadedObjects;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TerrainPreloadItem : public SceneUtil::WorkItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TerrainPreloadItem(const std::vector<osg::ref_ptr<Terrain::View> >& views, Terrain::World* world, const std::vector<osg::Vec3f>& preloadPositions)
|
||||||
|
: mAbort(false)
|
||||||
|
, mTerrainViews(views)
|
||||||
|
, mWorld(world)
|
||||||
|
, mPreloadPositions(preloadPositions)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void storeViews(double referenceTime)
|
||||||
|
{
|
||||||
|
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size(); ++i)
|
||||||
|
mWorld->storeView(mTerrainViews[i], referenceTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void doWork()
|
||||||
|
{
|
||||||
|
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
|
||||||
|
{
|
||||||
|
mTerrainViews[i]->reset();
|
||||||
|
mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void abort()
|
||||||
|
{
|
||||||
|
mAbort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<bool> mAbort;
|
||||||
|
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
||||||
|
Terrain::World* mWorld;
|
||||||
|
std::vector<osg::Vec3f> mPreloadPositions;
|
||||||
|
};
|
||||||
|
|
||||||
/// Worker thread item: update the resource system's cache, effectively deleting unused entries.
|
/// Worker thread item: update the resource system's cache, effectively deleting unused entries.
|
||||||
class UpdateCacheItem : public SceneUtil::WorkItem
|
class UpdateCacheItem : public SceneUtil::WorkItem
|
||||||
{
|
{
|
||||||
|
@ -288,6 +326,9 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
mPreloadCells.erase(found);
|
mPreloadCells.erase(found);
|
||||||
|
|
||||||
|
if (cell->isExterior() && mTerrainPreloadItem && mTerrainPreloadItem->isDone())
|
||||||
|
mTerrainPreloadItem->storeViews(0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,6 +370,12 @@ namespace MWWorld
|
||||||
mWorkQueue->addWorkItem(mUpdateCacheItem, true);
|
mWorkQueue->addWorkItem(mUpdateCacheItem, true);
|
||||||
mLastResourceCacheUpdate = timestamp;
|
mLastResourceCacheUpdate = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mTerrainPreloadItem && mTerrainPreloadItem->isDone())
|
||||||
|
{
|
||||||
|
mTerrainPreloadItem->storeViews(timestamp);
|
||||||
|
mTerrainPreloadItem = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellPreloader::setExpiryDelay(double expiryDelay)
|
void CellPreloader::setExpiryDelay(double expiryDelay)
|
||||||
|
@ -366,38 +413,6 @@ namespace MWWorld
|
||||||
mUnrefQueue = unrefQueue;
|
mUnrefQueue = unrefQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
class TerrainPreloadItem : public SceneUtil::WorkItem
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TerrainPreloadItem(const std::vector<osg::ref_ptr<Terrain::View> >& views, Terrain::World* world, const std::vector<osg::Vec3f>& preloadPositions)
|
|
||||||
: mAbort(false)
|
|
||||||
, mTerrainViews(views)
|
|
||||||
, mWorld(world)
|
|
||||||
, mPreloadPositions(preloadPositions)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void doWork()
|
|
||||||
{
|
|
||||||
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
|
|
||||||
{
|
|
||||||
mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort);
|
|
||||||
mTerrainViews[i]->reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void abort()
|
|
||||||
{
|
|
||||||
mAbort = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic<bool> mAbort;
|
|
||||||
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
|
||||||
Terrain::World* mWorld;
|
|
||||||
std::vector<osg::Vec3f> mPreloadPositions;
|
|
||||||
};
|
|
||||||
|
|
||||||
void CellPreloader::setTerrainPreloadPositions(const std::vector<osg::Vec3f> &positions)
|
void CellPreloader::setTerrainPreloadPositions(const std::vector<osg::Vec3f> &positions)
|
||||||
{
|
{
|
||||||
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
|
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
|
||||||
|
@ -418,8 +433,6 @@ namespace MWWorld
|
||||||
mTerrainViews.push_back(mTerrain->createView());
|
mTerrainViews.push_back(mTerrain->createView());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: provide some way of giving the preloaded view to the main thread when we enter the cell
|
|
||||||
// right now, we just use it to make sure the resources are preloaded
|
|
||||||
mTerrainPreloadPositions = positions;
|
mTerrainPreloadPositions = positions;
|
||||||
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);
|
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);
|
||||||
mWorkQueue->addWorkItem(mTerrainPreloadItem);
|
mWorkQueue->addWorkItem(mTerrainPreloadItem);
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace MWRender
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class CellStore;
|
class CellStore;
|
||||||
|
class TerrainPreloadItem;
|
||||||
|
|
||||||
class CellPreloader
|
class CellPreloader
|
||||||
{
|
{
|
||||||
|
@ -105,7 +106,7 @@ namespace MWWorld
|
||||||
|
|
||||||
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
||||||
std::vector<osg::Vec3f> mTerrainPreloadPositions;
|
std::vector<osg::Vec3f> mTerrainPreloadPositions;
|
||||||
osg::ref_ptr<SceneUtil::WorkItem> mTerrainPreloadItem;
|
osg::ref_ptr<TerrainPreloadItem> mTerrainPreloadItem;
|
||||||
osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem;
|
osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -440,8 +440,14 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
|
||||||
if (!isCullVisitor)
|
if (!isCullVisitor)
|
||||||
vd->reset(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray.
|
vd->reset(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray.
|
||||||
|
|
||||||
vd->finishFrame(nv.getTraversalNumber());
|
vd->markUnchanged();
|
||||||
mRootNode->getViewDataMap()->clearUnusedViews(nv.getTraversalNumber());
|
|
||||||
|
double referenceTime = nv.getFrameStamp() ? nv.getFrameStamp()->getReferenceTime() : 0.0;
|
||||||
|
if (referenceTime != 0.0)
|
||||||
|
{
|
||||||
|
vd->setLastUsageTimeStamp(referenceTime);
|
||||||
|
mViewDataMap->clearUnusedViews(referenceTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeWorld::ensureQuadTreeBuilt()
|
void QuadTreeWorld::ensureQuadTreeBuilt()
|
||||||
|
@ -504,6 +510,17 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic
|
||||||
ViewData::Entry& entry = vd->getEntry(i);
|
ViewData::Entry& entry = vd->getEntry(i);
|
||||||
loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get());
|
loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get());
|
||||||
}
|
}
|
||||||
|
vd->markUnchanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuadTreeWorld::storeView(const View* view, double referenceTime)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Object> dummy = new osg::DummyObject;
|
||||||
|
const ViewData* vd = static_cast<const ViewData*>(view);
|
||||||
|
bool needsUpdate = false;
|
||||||
|
ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), needsUpdate);
|
||||||
|
stored->copyFrom(*vd);
|
||||||
|
stored->setLastUsageTimeStamp(referenceTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats)
|
void QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats)
|
||||||
|
|
|
@ -37,8 +37,8 @@ namespace Terrain
|
||||||
virtual void unloadCell(int x, int y);
|
virtual void unloadCell(int x, int y);
|
||||||
|
|
||||||
View* createView();
|
View* createView();
|
||||||
|
void preload(View* view, const osg::Vec3f& eyePoint, std::atomic<bool>& abort);
|
||||||
void preload(View* view, const osg::Vec3f& viewPoint, std::atomic<bool>& abort);
|
void storeView(const View* view, double referenceTime);
|
||||||
|
|
||||||
void reportStats(unsigned int frameNumber, osg::Stats* stats);
|
void reportStats(unsigned int frameNumber, osg::Stats* stats);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace Terrain
|
||||||
|
|
||||||
ViewData::ViewData()
|
ViewData::ViewData()
|
||||||
: mNumEntries(0)
|
: mNumEntries(0)
|
||||||
, mFrameLastUsed(0)
|
, mLastUsageTimeStamp(0.0)
|
||||||
, mChanged(false)
|
, mChanged(false)
|
||||||
, mHasViewPoint(false)
|
, mHasViewPoint(false)
|
||||||
{
|
{
|
||||||
|
@ -85,7 +85,7 @@ void ViewData::clear()
|
||||||
for (unsigned int i=0; i<mEntries.size(); ++i)
|
for (unsigned int i=0; i<mEntries.size(); ++i)
|
||||||
mEntries[i].set(nullptr, false);
|
mEntries[i].set(nullptr, false);
|
||||||
mNumEntries = 0;
|
mNumEntries = 0;
|
||||||
mFrameLastUsed = 0;
|
mLastUsageTimeStamp = 0;
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
mHasViewPoint = false;
|
mHasViewPoint = false;
|
||||||
}
|
}
|
||||||
|
@ -173,12 +173,12 @@ ViewData *ViewDataMap::createOrReuseView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewDataMap::clearUnusedViews(unsigned int frame)
|
void ViewDataMap::clearUnusedViews(double referenceTime)
|
||||||
{
|
{
|
||||||
for (Map::iterator it = mViews.begin(); it != mViews.end(); )
|
for (Map::iterator it = mViews.begin(); it != mViews.end(); )
|
||||||
{
|
{
|
||||||
ViewData* vd = it->second;
|
ViewData* vd = it->second;
|
||||||
if (vd->getFrameLastUsed() + 2 < frame)
|
if (vd->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
|
||||||
{
|
{
|
||||||
vd->clear();
|
vd->clear();
|
||||||
mUnusedViews.push_back(vd);
|
mUnusedViews.push_back(vd);
|
||||||
|
|
|
@ -46,11 +46,12 @@ namespace Terrain
|
||||||
|
|
||||||
Entry& getEntry(unsigned int i);
|
Entry& getEntry(unsigned int i);
|
||||||
|
|
||||||
unsigned int getFrameLastUsed() const { return mFrameLastUsed; }
|
double getLastUsageTimeStamp() const { return mLastUsageTimeStamp; }
|
||||||
void finishFrame(unsigned int frame) { mFrameLastUsed = frame; mChanged = false; }
|
void setLastUsageTimeStamp(double timeStamp) { mLastUsageTimeStamp = timeStamp; }
|
||||||
|
|
||||||
/// @return Have any nodes changed since the last frame
|
/// @return Have any nodes changed since the last frame
|
||||||
bool hasChanged() const;
|
bool hasChanged() const;
|
||||||
|
void markUnchanged() { mChanged = false; }
|
||||||
|
|
||||||
bool hasViewPoint() const;
|
bool hasViewPoint() const;
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ namespace Terrain
|
||||||
private:
|
private:
|
||||||
std::vector<Entry> mEntries;
|
std::vector<Entry> mEntries;
|
||||||
unsigned int mNumEntries;
|
unsigned int mNumEntries;
|
||||||
unsigned int mFrameLastUsed;
|
double mLastUsageTimeStamp;
|
||||||
bool mChanged;
|
bool mChanged;
|
||||||
osg::Vec3f mViewPoint;
|
osg::Vec3f mViewPoint;
|
||||||
bool mHasViewPoint;
|
bool mHasViewPoint;
|
||||||
|
@ -71,14 +72,16 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ViewDataMap()
|
ViewDataMap()
|
||||||
: mReuseDistance(100) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused.
|
: mReuseDistance(300) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused.
|
||||||
|
// this value also serves as a threshold for when a newly loaded LOD gets unloaded again so that if you hover around an LOD transition point the LODs won't keep loading and unloading all the time.
|
||||||
|
, mExpiryDelay(1.f)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, bool& needsUpdate);
|
ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, bool& needsUpdate);
|
||||||
|
|
||||||
ViewData* createOrReuseView();
|
ViewData* createOrReuseView();
|
||||||
|
|
||||||
void clearUnusedViews(unsigned int frame);
|
void clearUnusedViews(double referenceTime);
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
@ -93,6 +96,7 @@ namespace Terrain
|
||||||
Map mViews;
|
Map mViews;
|
||||||
|
|
||||||
float mReuseDistance;
|
float mReuseDistance;
|
||||||
|
float mExpiryDelay; // time in seconds for unused view to be removed
|
||||||
|
|
||||||
std::deque<ViewData*> mUnusedViews;
|
std::deque<ViewData*> mUnusedViews;
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace Terrain
|
||||||
/// @note Thread safe.
|
/// @note Thread safe.
|
||||||
virtual void clearAssociatedCaches();
|
virtual void clearAssociatedCaches();
|
||||||
|
|
||||||
/// Load a terrain cell at maximum LOD and store it in the View for later use.
|
/// Load a terrain cell and store it in the View for later use.
|
||||||
/// @note Thread safe.
|
/// @note Thread safe.
|
||||||
virtual void cacheCell(View* view, int x, int y) {}
|
virtual void cacheCell(View* view, int x, int y) {}
|
||||||
|
|
||||||
|
@ -106,6 +106,10 @@ namespace Terrain
|
||||||
|
|
||||||
virtual void preload(View* view, const osg::Vec3f& viewPoint, std::atomic<bool>& abort) {}
|
virtual void preload(View* view, const osg::Vec3f& viewPoint, std::atomic<bool>& abort) {}
|
||||||
|
|
||||||
|
/// Store a preloaded view into the cache with the intent that the next rendering traversal can use it.
|
||||||
|
/// @note Not thread safe.
|
||||||
|
virtual void storeView(const View* view, double referenceTime) {}
|
||||||
|
|
||||||
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {}
|
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {}
|
||||||
|
|
||||||
/// Set the default viewer (usually a Camera), used as viewpoint for any viewers that don't use their own viewpoint.
|
/// Set the default viewer (usually a Camera), used as viewpoint for any viewers that don't use their own viewpoint.
|
||||||
|
|
Loading…
Reference in a new issue