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; 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…
Cancel
Save