diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index e8d7f93c4..77f522ea6 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -167,6 +167,44 @@ namespace MWWorld std::vector > mPreloadedObjects; }; + class TerrainPreloadItem : public SceneUtil::WorkItem + { + public: + TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) + : mAbort(false) + , mTerrainViews(views) + , mWorld(world) + , mPreloadPositions(preloadPositions) + { + } + + void storeViews(double referenceTime) + { + for (unsigned int i=0; istoreView(mTerrainViews[i], referenceTime); + } + + virtual void doWork() + { + for (unsigned int i=0; ireset(); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort); + } + } + + virtual void abort() + { + mAbort = true; + } + + private: + std::atomic mAbort; + std::vector > mTerrainViews; + Terrain::World* mWorld; + std::vector 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 >& views, Terrain::World* world, const std::vector& preloadPositions) - : mAbort(false) - , mTerrainViews(views) - , mWorld(world) - , mPreloadPositions(preloadPositions) - { - } - - virtual void doWork() - { - for (unsigned int i=0; ipreload(mTerrainViews[i], mPreloadPositions[i], mAbort); - mTerrainViews[i]->reset(); - } - } - - virtual void abort() - { - mAbort = true; - } - - private: - std::atomic mAbort; - std::vector > mTerrainViews; - Terrain::World* mWorld; - std::vector mPreloadPositions; - }; - void CellPreloader::setTerrainPreloadPositions(const std::vector &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); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index df878a55d..0501a4f9b 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -31,6 +31,7 @@ namespace MWRender namespace MWWorld { class CellStore; + class TerrainPreloadItem; class CellPreloader { @@ -105,7 +106,7 @@ namespace MWWorld std::vector > mTerrainViews; std::vector mTerrainPreloadPositions; - osg::ref_ptr mTerrainPreloadItem; + osg::ref_ptr mTerrainPreloadItem; osg::ref_ptr mUpdateCacheItem; }; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 367c82503..b27334881 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -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 dummy = new osg::DummyObject; + const ViewData* vd = static_cast(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) diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 0ee17b35b..0c2296b24 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -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& abort); + void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort); + void storeView(const View* view, double referenceTime); void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 1f15bcc7f..10a28ca06 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -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; isecond; - if (vd->getFrameLastUsed() + 2 < frame) + if (vd->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) { vd->clear(); mUnusedViews.push_back(vd); diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 1d02ace34..abbf5ff7f 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -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 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 mUnusedViews; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 290626069..e0de69eee 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -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& 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.