Store preloaded terrain view in the main thread

pull/541/head
Andrei Kortunov 6 years ago
parent 63ab7345be
commit 489e5c6cce

@ -167,6 +167,44 @@ namespace MWWorld
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.
class UpdateCacheItem : public SceneUtil::WorkItem
{
@ -288,6 +326,9 @@ namespace MWWorld
}
mPreloadCells.erase(found);
if (cell->isExterior() && mTerrainPreloadItem && mTerrainPreloadItem->isDone())
mTerrainPreloadItem->storeViews(0.0);
}
}
@ -329,6 +370,12 @@ namespace MWWorld
mWorkQueue->addWorkItem(mUpdateCacheItem, true);
mLastResourceCacheUpdate = timestamp;
}
if (mTerrainPreloadItem && mTerrainPreloadItem->isDone())
{
mTerrainPreloadItem->storeViews(timestamp);
mTerrainPreloadItem = nullptr;
}
}
void CellPreloader::setExpiryDelay(double expiryDelay)
@ -366,38 +413,6 @@ namespace MWWorld
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)
{
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
@ -418,8 +433,6 @@ namespace MWWorld
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;
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);
mWorkQueue->addWorkItem(mTerrainPreloadItem);

@ -31,6 +31,7 @@ namespace MWRender
namespace MWWorld
{
class CellStore;
class TerrainPreloadItem;
class CellPreloader
{
@ -105,7 +106,7 @@ namespace MWWorld
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
std::vector<osg::Vec3f> mTerrainPreloadPositions;
osg::ref_ptr<SceneUtil::WorkItem> mTerrainPreloadItem;
osg::ref_ptr<TerrainPreloadItem> mTerrainPreloadItem;
osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem;
};

@ -440,8 +440,14 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
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->finishFrame(nv.getTraversalNumber());
mRootNode->getViewDataMap()->clearUnusedViews(nv.getTraversalNumber());
vd->markUnchanged();
double referenceTime = nv.getFrameStamp() ? nv.getFrameStamp()->getReferenceTime() : 0.0;
if (referenceTime != 0.0)
{
vd->setLastUsageTimeStamp(referenceTime);
mViewDataMap->clearUnusedViews(referenceTime);
}
}
void QuadTreeWorld::ensureQuadTreeBuilt()
@ -504,6 +510,17 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic
ViewData::Entry& entry = vd->getEntry(i);
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)

@ -37,8 +37,8 @@ namespace Terrain
virtual void unloadCell(int x, int y);
View* createView();
void preload(View* view, const osg::Vec3f& viewPoint, std::atomic<bool>& abort);
void preload(View* view, const osg::Vec3f& eyePoint, std::atomic<bool>& abort);
void storeView(const View* view, double referenceTime);
void reportStats(unsigned int frameNumber, osg::Stats* stats);

@ -5,7 +5,7 @@ namespace Terrain
ViewData::ViewData()
: mNumEntries(0)
, mFrameLastUsed(0)
, mLastUsageTimeStamp(0.0)
, mChanged(false)
, mHasViewPoint(false)
{
@ -85,7 +85,7 @@ void ViewData::clear()
for (unsigned int i=0; i<mEntries.size(); ++i)
mEntries[i].set(nullptr, false);
mNumEntries = 0;
mFrameLastUsed = 0;
mLastUsageTimeStamp = 0;
mChanged = 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(); )
{
ViewData* vd = it->second;
if (vd->getFrameLastUsed() + 2 < frame)
if (vd->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
{
vd->clear();
mUnusedViews.push_back(vd);

@ -46,11 +46,12 @@ namespace Terrain
Entry& getEntry(unsigned int i);
unsigned int getFrameLastUsed() const { return mFrameLastUsed; }
void finishFrame(unsigned int frame) { mFrameLastUsed = frame; mChanged = false; }
double getLastUsageTimeStamp() const { return mLastUsageTimeStamp; }
void setLastUsageTimeStamp(double timeStamp) { mLastUsageTimeStamp = timeStamp; }
/// @return Have any nodes changed since the last frame
bool hasChanged() const;
void markUnchanged() { mChanged = false; }
bool hasViewPoint() const;
@ -60,7 +61,7 @@ namespace Terrain
private:
std::vector<Entry> mEntries;
unsigned int mNumEntries;
unsigned int mFrameLastUsed;
double mLastUsageTimeStamp;
bool mChanged;
osg::Vec3f mViewPoint;
bool mHasViewPoint;
@ -71,14 +72,16 @@ namespace Terrain
{
public:
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* createOrReuseView();
void clearUnusedViews(unsigned int frame);
void clearUnusedViews(double referenceTime);
void clear();
@ -93,6 +96,7 @@ namespace Terrain
Map mViews;
float mReuseDistance;
float mExpiryDelay; // time in seconds for unused view to be removed
std::deque<ViewData*> mUnusedViews;

@ -82,7 +82,7 @@ namespace Terrain
/// @note Thread safe.
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.
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) {}
/// 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) {}
/// Set the default viewer (usually a Camera), used as viewpoint for any viewers that don't use their own viewpoint.

Loading…
Cancel
Save