From 63ab7345be753c25c9988f7a63c1a5579e6d41be Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 1/4] Reuse traversal result for different traversal with same view point Rename eyePoint to viewPoint to match OSG conventions (eyePoint is the camera position, viewPoint is for LOD handling) --- apps/openmw/mwworld/cellpreloader.cpp | 2 +- components/terrain/quadtreenode.cpp | 25 +++++---- components/terrain/quadtreenode.hpp | 2 +- components/terrain/quadtreeworld.cpp | 61 ++++++++++++++-------- components/terrain/quadtreeworld.hpp | 3 +- components/terrain/terraingrid.cpp | 2 +- components/terrain/viewdata.cpp | 74 +++++++++++++++++++++------ components/terrain/viewdata.hpp | 26 +++++++--- components/terrain/world.hpp | 6 ++- 9 files changed, 136 insertions(+), 65 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 98e9c7368..e8d7f93c4 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -382,7 +382,7 @@ namespace MWWorld for (unsigned int i=0; ipreload(mTerrainViews[i], mPreloadPositions[i], mAbort); - mTerrainViews[i]->reset(0); + mTerrainViews[i]->reset(); } } diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 0c157cd48..d9fd4cd45 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -114,9 +114,10 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv) if (!hasValidBounds()) return; - ViewData* vd = getView(nv); + bool needsUpdate = true; + ViewData* vd = getView(nv, needsUpdate); - if ((mLodCallback && mLodCallback->isSufficientDetail(this, distance(vd->getEyePoint()))) || !getNumChildren()) + if ((mLodCallback && mLodCallback->isSufficientDetail(this, distance(vd->getViewPoint()))) || !getNumChildren()) vd->add(this, true); else osg::Group::traverse(nv); @@ -142,26 +143,24 @@ ViewDataMap *QuadTreeNode::getViewDataMap() return mViewDataMap; } -ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv) +ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv, bool& needsUpdate) { + ViewData* vd = NULL; if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) { osgUtil::CullVisitor* cv = static_cast(&nv); - ViewData* vd = mViewDataMap->getViewData(cv->getCurrentCamera()); - vd->setEyePoint(nv.getViewPoint()); - return vd; + vd = mViewDataMap->getViewData(cv->getCurrentCamera(), nv.getViewPoint(), needsUpdate); } else // INTERSECTION_VISITOR { + osg::Vec3f viewPoint = nv.getViewPoint(); + mViewDataMap->getDefaultViewPoint(viewPoint); + static osg::ref_ptr dummyObj = new osg::DummyObject; - ViewData* vd = mViewDataMap->getViewData(dummyObj.get()); - ViewData* defaultView = mViewDataMap->getDefaultView(); - if (defaultView->hasEyePoint()) - vd->setEyePoint(defaultView->getEyePoint()); - else - vd->setEyePoint(nv.getEyePoint()); - return vd; + vd = mViewDataMap->getViewData(dummyObj.get(), viewPoint, needsUpdate); + needsUpdate = true; } + return vd; } void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 336a257fb..9f7c7bbb7 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -85,7 +85,7 @@ namespace Terrain ViewDataMap* getViewDataMap(); /// Create or retrieve a view for the given traversal. - ViewData* getView(osg::NodeVisitor& nv); + ViewData* getView(osg::NodeVisitor& nv, bool& needsUpdate); private: QuadTreeNode* mParent; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index fcb0f51a7..367c82503 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -247,7 +247,7 @@ QuadTreeWorld::~QuadTreeWorld() } -void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& eyePoint, bool visible, float maxDist) +void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& viewPoint, bool visible, float maxDist) { if (!node->hasValidBounds()) return; @@ -255,7 +255,7 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) visible = visible && !static_cast(nv)->isCulled(node->getBoundingBox()); - float dist = node->distance(eyePoint); + float dist = node->distance(viewPoint); if (dist > maxDist) return; @@ -266,7 +266,7 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac else { for (unsigned int i=0; igetNumChildren(); ++i) - traverse(node->getChild(i), vd, nv, lodCallback, eyePoint, visible, maxDist); + traverse(node->getChild(i), vd, nv, lodCallback, viewPoint, visible, maxDist); } } @@ -367,7 +367,8 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, C void QuadTreeWorld::accept(osg::NodeVisitor &nv) { - if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) + bool isCullVisitor = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR; + if (!isCullVisitor && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) { if (nv.getName().find("AcceptedByComponentsTerrainQuadTreeWorld") != std::string::npos) { @@ -382,26 +383,40 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) return; } - ViewData* vd = mRootNode->getView(nv); + bool needsUpdate = false; + ViewData* vd = mRootNode->getView(nv, needsUpdate); - if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + if (needsUpdate) { - osgUtil::CullVisitor* cv = static_cast(&nv); - - osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer(); - if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod") + vd->reset(); + if (isCullVisitor) { - std::istringstream stream(udc->getDescriptions()[1]); - int x,y; - stream >> x; - stream >> y; - traverseToCell(mRootNode.get(), vd, x,y); + osgUtil::CullVisitor* cv = static_cast(&nv); + + osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer(); + if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod") + { + std::istringstream stream(udc->getDescriptions()[1]); + int x,y; + stream >> x; + stream >> y; + traverseToCell(mRootNode.get(), vd, x,y); + } + else + traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true, mViewDistance); } else - traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true, mViewDistance); + mRootNode->traverse(nv); + } + else if (isCullVisitor) + { + // view point is the same, but must still update visible status in case the camera has rotated + for (unsigned int i=0; igetNumEntries(); ++i) + { + ViewData::Entry& entry = vd->getEntry(i); + entry.set(entry.mNode, !static_cast(&nv)->isCulled(entry.mNode->getBoundingBox())); + } } - else - mRootNode->traverse(nv); for (unsigned int i=0; igetNumEntries(); ++i) { @@ -416,13 +431,16 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) { mCompositeMapRenderer->setImmediate(static_cast(udc->getUserData())); udc->setUserData(nullptr); + } entry.mRenderingNode->accept(nv); } } - vd->reset(nv.getTraversalNumber()); + 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()); } @@ -473,12 +491,13 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint, std::atomic &abort) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic &abort) { ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false, mViewDistance); + vd->setViewPoint(viewPoint); + traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), viewPoint, false, mViewDistance); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 0595096e3..0ee17b35b 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -37,7 +37,8 @@ namespace Terrain virtual void unloadCell(int x, int y); View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort); + + void preload(View* view, const osg::Vec3f& viewPoint, std::atomic& abort); void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index a7d43f673..f8ce2019c 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -15,7 +15,7 @@ class MyView : public View public: osg::ref_ptr mLoaded; - virtual void reset(unsigned int frame) {} + virtual void reset() {} }; TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask) diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 3d09e7ba0..1f15bcc7f 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -7,7 +7,7 @@ ViewData::ViewData() : mNumEntries(0) , mFrameLastUsed(0) , mChanged(false) - , mHasEyePoint(false) + , mHasViewPoint(false) { } @@ -17,6 +17,15 @@ ViewData::~ViewData() } +void ViewData::copyFrom(const ViewData& other) +{ + mNumEntries = other.mNumEntries; + mEntries = other.mEntries; + mChanged = other.mChanged; + mHasViewPoint = other.mHasViewPoint; + mViewPoint = other.mViewPoint; +} + void ViewData::add(QuadTreeNode *node, bool visible) { unsigned int index = mNumEntries++; @@ -44,23 +53,23 @@ bool ViewData::hasChanged() const return mChanged; } -bool ViewData::hasEyePoint() const +bool ViewData::hasViewPoint() const { - return mHasEyePoint; + return mHasViewPoint; } -void ViewData::setEyePoint(const osg::Vec3f &eye) +void ViewData::setViewPoint(const osg::Vec3f &viewPoint) { - mEyePoint = eye; - mHasEyePoint = true; + mViewPoint = viewPoint; + mHasViewPoint = true; } -const osg::Vec3f& ViewData::getEyePoint() const +const osg::Vec3f& ViewData::getViewPoint() const { - return mEyePoint; + return mViewPoint; } -void ViewData::reset(unsigned int frame) +void ViewData::reset() { // clear any unused entries for (unsigned int i=mNumEntries; ihasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist; +} + +ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, bool& needsUpdate) { Map::const_iterator found = mViews.find(viewer); + ViewData* vd = nullptr; if (found == mViews.end()) { - ViewData* vd = createOrReuseView(); + vd = createOrReuseView(); + vd->setViewer(viewer); mViews[viewer] = vd; - return vd; } else - return found->second; + vd = found->second; + + if (!suitable(vd, viewPoint, mReuseDistance)) + { + for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other) + { + if (suitable(other->second, viewPoint, mReuseDistance) && other->second->getNumEntries()) + { + vd->copyFrom(*other->second); + needsUpdate = false; + return vd; + } + } + vd->setViewPoint(viewPoint); + needsUpdate = true; + } + else + needsUpdate = false; + + return vd; } ViewData *ViewDataMap::createOrReuseView() @@ -168,9 +201,16 @@ void ViewDataMap::setDefaultViewer(osg::Object *viewer) mDefaultViewer = viewer; } -ViewData* ViewDataMap::getDefaultView() +bool ViewDataMap::getDefaultViewPoint(osg::Vec3f& viewPoint) { - return getViewData(mDefaultViewer); + Map::const_iterator found = mViews.find(mDefaultViewer); + if (found != mViews.end() && found->second->hasViewPoint()) + { + viewPoint = found->second->getViewPoint(); + return true; + } + else + return false; } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 53bcf42c0..1d02ace34 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -21,12 +21,14 @@ namespace Terrain void add(QuadTreeNode* node, bool visible); - void reset(unsigned int frame); + void reset(); void clear(); bool contains(QuadTreeNode* node); + void copyFrom(const ViewData& other); + struct Entry { Entry(); @@ -45,28 +47,34 @@ namespace Terrain Entry& getEntry(unsigned int i); unsigned int getFrameLastUsed() const { return mFrameLastUsed; } + void finishFrame(unsigned int frame) { mFrameLastUsed = frame; mChanged = false; } /// @return Have any nodes changed since the last frame bool hasChanged() const; - bool hasEyePoint() const; + bool hasViewPoint() const; - void setEyePoint(const osg::Vec3f& eye); - const osg::Vec3f& getEyePoint() const; + void setViewPoint(const osg::Vec3f& viewPoint); + const osg::Vec3f& getViewPoint() const; private: std::vector mEntries; unsigned int mNumEntries; unsigned int mFrameLastUsed; bool mChanged; - osg::Vec3f mEyePoint; - bool mHasEyePoint; + osg::Vec3f mViewPoint; + bool mHasViewPoint; + float mReuseDistance; }; class ViewDataMap : public osg::Referenced { public: - ViewData* getViewData(osg::Object* viewer); + 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. + {} + + ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, bool& needsUpdate); ViewData* createOrReuseView(); @@ -76,7 +84,7 @@ namespace Terrain void setDefaultViewer(osg::Object* viewer); - ViewData* getDefaultView(); + bool getDefaultViewPoint(osg::Vec3f& viewPoint); private: std::list mViewVector; @@ -84,6 +92,8 @@ namespace Terrain typedef std::map, ViewData*> Map; Map mViews; + float mReuseDistance; + std::deque mUnusedViews; osg::ref_ptr mDefaultViewer; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index cfb7edda6..290626069 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "defs.hpp" #include "cellborder.hpp" @@ -48,7 +49,7 @@ namespace Terrain virtual ~View() {} /// Reset internal structure so that the next addition to the view will override the previous frame's contents. - virtual void reset(unsigned int frame) = 0; + virtual void reset() = 0; }; /** @@ -102,7 +103,8 @@ namespace Terrain virtual View* createView() { return nullptr; } /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. - virtual void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort) {} + + virtual void preload(View* view, const osg::Vec3f& viewPoint, std::atomic& abort) {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} From 489e5c6cce1edd911cefebbcdfd612e17ecf5e92 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 21 Mar 2019 17:50:49 +0400 Subject: [PATCH 2/4] Store preloaded terrain view in the main thread --- apps/openmw/mwworld/cellpreloader.cpp | 81 ++++++++++++++++----------- apps/openmw/mwworld/cellpreloader.hpp | 3 +- components/terrain/quadtreeworld.cpp | 21 ++++++- components/terrain/quadtreeworld.hpp | 4 +- components/terrain/viewdata.cpp | 8 +-- components/terrain/viewdata.hpp | 14 +++-- components/terrain/world.hpp | 6 +- 7 files changed, 88 insertions(+), 49 deletions(-) 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. From e9087905845d1509cc7dc7b83fae8986a4866d0a Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 3/4] Inherit the view point from main camera for water RTT cameras --- apps/openmw/mwrender/water.cpp | 19 +++++++++++++++++++ components/terrain/viewdata.cpp | 1 - 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index d047130d4..56b76427f 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -161,6 +161,23 @@ private: osg::Plane mPlane; }; +/// This callback on the Camera has the effect of a RELATIVE_RF_INHERIT_VIEWPOINT transform mode (which does not exist in OSG). +/// We want to keep the View Point of the parent camera so we will not have to recreate LODs. +class InheritViewPointCallback : public osg::NodeCallback +{ +public: + InheritViewPointCallback() {} + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osgUtil::CullVisitor* cv = static_cast(nv); + osg::ref_ptr modelViewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix()); + cv->popModelViewMatrix(); + cv->pushModelViewMatrix(modelViewMatrix, osg::Transform::ABSOLUTE_RF_INHERIT_VIEWPOINT); + traverse(node, nv); + } +}; + /// Moves water mesh away from the camera slightly if the camera gets too close on the Z axis. /// The offset works around graphics artifacts that occurred with the GL_DEPTH_CLAMP when the camera gets extremely close to the mesh (seen on NVIDIA at least). /// Must be added as a Cull callback. @@ -224,6 +241,7 @@ public: setReferenceFrame(osg::Camera::RELATIVE_RF); setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); setName("RefractionCamera"); + setCullCallback(new InheritViewPointCallback); setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Static|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting); setNodeMask(Mask_RenderToTexture); @@ -315,6 +333,7 @@ public: setReferenceFrame(osg::Camera::RELATIVE_RF); setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); setName("ReflectionCamera"); + setCullCallback(new InheritViewPointCallback); int reflectionDetail = Settings::Manager::getInt("reflection detail", "Water"); reflectionDetail = std::min(4, std::max(isInterior ? 2 : 0, reflectionDetail)); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 10a28ca06..3185d840d 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -132,7 +132,6 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo if (found == mViews.end()) { vd = createOrReuseView(); - vd->setViewer(viewer); mViews[viewer] = vd; } else From 391f6faffb76060fce3c05ca1eb301b7e8cbbc9e Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH 4/4] Remove unused defaultViewer / defaultViewPoint --- apps/openmw/mwrender/renderingmanager.cpp | 1 - components/terrain/quadtreenode.cpp | 2 -- components/terrain/quadtreeworld.cpp | 5 ----- components/terrain/quadtreeworld.hpp | 2 -- components/terrain/viewdata.cpp | 18 ------------------ components/terrain/viewdata.hpp | 6 ------ components/terrain/world.hpp | 3 --- 7 files changed, 37 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bdefe4c5c..976635cb4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -312,7 +312,6 @@ namespace MWRender else mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); - mTerrain->setDefaultViewer(mViewer->getCamera()); mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); mTerrain->setWorkQueue(mWorkQueue.get()); diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index d9fd4cd45..141803cf7 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -154,8 +154,6 @@ ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv, bool& needsUpdate) else // INTERSECTION_VISITOR { osg::Vec3f viewPoint = nv.getViewPoint(); - mViewDataMap->getDefaultViewPoint(viewPoint); - static osg::ref_ptr dummyObj = new osg::DummyObject; vd = mViewDataMap->getViewData(dummyObj.get(), viewPoint, needsUpdate); needsUpdate = true; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index b27334881..499e268dc 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -528,11 +528,6 @@ void QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats) stats->setAttribute(frameNumber, "Composite", mCompositeMapRenderer->getCompileSetSize()); } -void QuadTreeWorld::setDefaultViewer(osg::Object *obj) -{ - mViewDataMap->setDefaultViewer(obj); -} - void QuadTreeWorld::loadCell(int x, int y) { // fallback behavior only for undefined cells (every other is already handled in quadtree) diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 0c2296b24..349f27b64 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -42,8 +42,6 @@ namespace Terrain void reportStats(unsigned int frameNumber, osg::Stats* stats); - virtual void setDefaultViewer(osg::Object* obj); - private: void ensureQuadTreeBuilt(); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 3185d840d..d1870abaa 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -195,22 +195,4 @@ void ViewDataMap::clear() mViewVector.clear(); } -void ViewDataMap::setDefaultViewer(osg::Object *viewer) -{ - mDefaultViewer = viewer; -} - -bool ViewDataMap::getDefaultViewPoint(osg::Vec3f& viewPoint) -{ - Map::const_iterator found = mViews.find(mDefaultViewer); - if (found != mViews.end() && found->second->hasViewPoint()) - { - viewPoint = found->second->getViewPoint(); - return true; - } - else - return false; -} - - } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index abbf5ff7f..001627640 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -85,10 +85,6 @@ namespace Terrain void clear(); - void setDefaultViewer(osg::Object* viewer); - - bool getDefaultViewPoint(osg::Vec3f& viewPoint); - private: std::list mViewVector; @@ -99,8 +95,6 @@ namespace Terrain float mExpiryDelay; // time in seconds for unused view to be removed std::deque mUnusedViews; - - osg::ref_ptr mDefaultViewer; }; } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index e0de69eee..0402b8197 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -112,9 +112,6 @@ namespace Terrain 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. - virtual void setDefaultViewer(osg::Object* obj) {} - virtual void setViewDistance(float distance) {} Storage* getStorage() { return mStorage; }