1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-13 12:40:04 +00:00

avoid blocking on pagerebuild

Signed-off-by: Bret Curtis <psi29a@gmail.com>
This commit is contained in:
bzzt lost a hitlab login 2020-05-08 13:37:00 +00:00 committed by Bret Curtis
parent 17637c6575
commit b4af2ac672
13 changed files with 151 additions and 90 deletions

View File

@ -1481,9 +1481,15 @@ namespace MWRender
{ {
mTerrain->setActiveGrid(grid); mTerrain->setActiveGrid(grid);
} }
void RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled) bool RenderingManager::pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled)
{ {
if (!ptr.isInCell() || !ptr.getCell()->isExterior())
return false;
if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled)) if (mObjectPaging && mObjectPaging->enableObject(type, ptr.getCellRef().getRefNum(), ptr.getRefData().getPosition().asVec3(), enabled))
mTerrain->clearCachedViews(ptr.getRefData().getPosition().asVec3()); {
mTerrain->rebuildViews();
return true;
}
return false;
} }
} }

View File

@ -241,7 +241,7 @@ namespace MWRender
void setActiveGrid(const osg::Vec4i &grid); void setActiveGrid(const osg::Vec4i &grid);
void pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled); bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled);
private: private:
void updateProjectionMatrix(); void updateProjectionMatrix();

View File

@ -182,10 +182,12 @@ namespace MWWorld
{ {
} }
void storeViews(double referenceTime) bool storeViews(double referenceTime)
{ {
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size(); ++i) for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size(); ++i)
mWorld->storeView(mTerrainViews[i], referenceTime); if (!mWorld->storeView(mTerrainViews[i], referenceTime))
return false;
return true;
} }
virtual void doWork() virtual void doWork()
@ -244,6 +246,7 @@ namespace MWWorld
, mMaxCacheSize(0) , mMaxCacheSize(0)
, mPreloadInstances(true) , mPreloadInstances(true)
, mLastResourceCacheUpdate(0.0) , mLastResourceCacheUpdate(0.0)
, mStoreViewsFailCount(0)
{ {
} }
@ -379,7 +382,17 @@ namespace MWWorld
if (mTerrainPreloadItem && mTerrainPreloadItem->isDone()) if (mTerrainPreloadItem && mTerrainPreloadItem->isDone())
{ {
mTerrainPreloadItem->storeViews(timestamp); if (!mTerrainPreloadItem->storeViews(timestamp))
{
if (++mStoreViewsFailCount > 100)
{
OSG_ALWAYS << "paging views are rebuilt every frame, please check for faulty enable/disable scripts." << std::endl;
mStoreViewsFailCount = 0;
}
setTerrainPreloadPositions(std::vector<PositionCellGrid>());
}
else
mStoreViewsFailCount = 0;
mTerrainPreloadItem = nullptr; mTerrainPreloadItem = nullptr;
} }
} }
@ -451,11 +464,31 @@ namespace MWWorld
} }
} }
bool contains(const std::vector<CellPreloader::PositionCellGrid>& container, const std::vector<CellPreloader::PositionCellGrid>& contained)
{
for (auto pos : contained)
{
bool found = false;
for (auto pos2 : container)
{
if ((pos.first-pos2.first).length2() < 1 && pos.second == pos2.second)
{
found = true;
break;
}
}
if (!found) return false;
}
return true;
}
void CellPreloader::setTerrainPreloadPositions(const std::vector<CellPreloader::PositionCellGrid> &positions) void CellPreloader::setTerrainPreloadPositions(const std::vector<CellPreloader::PositionCellGrid> &positions)
{ {
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) if (positions.empty())
mTerrainPreloadPositions.clear();
else if (contains(mTerrainPreloadPositions, positions))
return; return;
else if (positions == mTerrainPreloadPositions) if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
return; return;
else else
{ {
@ -472,7 +505,6 @@ namespace MWWorld
} }
mTerrainPreloadPositions = positions; mTerrainPreloadPositions = positions;
if (!positions.empty()) if (!positions.empty())
{ {
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);

View File

@ -88,6 +88,7 @@ namespace MWWorld
bool mPreloadInstances; bool mPreloadInstances;
double mLastResourceCacheUpdate; double mLastResourceCacheUpdate;
int mStoreViewsFailCount;
struct PreloadEntry struct PreloadEntry
{ {

View File

@ -1135,6 +1135,11 @@ namespace MWWorld
mPreloader->setTerrainPreloadPositions(vec); mPreloader->setTerrainPreloadPositions(vec);
} }
void Scene::reloadTerrain()
{
mPreloader->setTerrainPreloadPositions(std::vector<PositionCellGrid>());
}
struct ListFastTravelDestinationsVisitor struct ListFastTravelDestinationsVisitor
{ {
ListFastTravelDestinationsVisitor(float preloadDist, const osg::Vec3f& playerPos) ListFastTravelDestinationsVisitor(float preloadDist, const osg::Vec3f& playerPos)

View File

@ -115,6 +115,7 @@ namespace MWWorld
void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false);
void preloadTerrain(const osg::Vec3f& pos); void preloadTerrain(const osg::Vec3f& pos);
void reloadTerrain();
void unloadCell (CellStoreCollection::iterator iter, bool test = false); void unloadCell (CellStoreCollection::iterator iter, bool test = false);

View File

@ -817,7 +817,8 @@ namespace MWWorld
if (reference.getCellRef().getRefNum().hasContentFile()) if (reference.getCellRef().getRefNum().hasContentFile())
{ {
int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId()));
mRendering->pagingEnableObject(type, reference, true); if (mRendering->pagingEnableObject(type, reference, true))
mWorldScene->reloadTerrain();
} }
} }
} }
@ -858,7 +859,8 @@ namespace MWWorld
if (reference.getCellRef().getRefNum().hasContentFile()) if (reference.getCellRef().getRefNum().hasContentFile())
{ {
int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId())); int type = mStore.find(Misc::StringUtils::lowerCase(reference.getCellRef().getRefId()));
mRendering->pagingEnableObject(type, reference, false); if (mRendering->pagingEnableObject(type, reference, false))
mWorldScene->reloadTerrain();
} }
if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount())

View File

@ -53,7 +53,6 @@ namespace Terrain
virtual bool isSufficientDetail(QuadTreeNode *node, float dist) = 0; virtual bool isSufficientDetail(QuadTreeNode *node, float dist) = 0;
}; };
class ViewDataMap;
class ViewData; class ViewData;
class QuadTreeNode : public osg::Group class QuadTreeNode : public osg::Group

View File

@ -253,7 +253,6 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour
QuadTreeWorld::~QuadTreeWorld() QuadTreeWorld::~QuadTreeWorld()
{ {
mViewDataMap->clear();
} }
/// get the level of vertex detail to render this node at, expressed relative to the native resolution of the data set. /// get the level of vertex detail to render this node at, expressed relative to the native resolution of the data set.
@ -279,7 +278,7 @@ unsigned int getVertexLod(QuadTreeNode* node, int vertexLodMod)
} }
/// get the flags to use for stitching in the index buffer so that chunks of different LOD connect seamlessly /// get the flags to use for stitching in the index buffer so that chunks of different LOD connect seamlessly
unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewData* vd) unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, const ViewData* vd)
{ {
unsigned int lodFlags = 0; unsigned int lodFlags = 0;
for (unsigned int i=0; i<4; ++i) for (unsigned int i=0; i<4; ++i)
@ -506,7 +505,7 @@ void QuadTreeWorld::enable(bool enabled)
View* QuadTreeWorld::createView() View* QuadTreeWorld::createView()
{ {
return new ViewData; return mViewDataMap->createIndependentView();
} }
void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic<bool> &abort, std::atomic<int> &progress, int& progressTotal) void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic<bool> &abort, std::atomic<int> &progress, int& progressTotal)
@ -533,14 +532,9 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::
vd->markUnchanged(); vd->markUnchanged();
} }
void QuadTreeWorld::storeView(const View* view, double referenceTime) bool QuadTreeWorld::storeView(const View* view, double referenceTime)
{ {
osg::ref_ptr<osg::Object> dummy = new osg::DummyObject; return mViewDataMap->storeView(static_cast<const ViewData*>(view), referenceTime);
const ViewData* vd = static_cast<const ViewData*>(view);
bool needsUpdate = false;
ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), vd->getActiveGrid(), 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)
@ -574,11 +568,9 @@ void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m)
mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask()); mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask());
} }
void QuadTreeWorld::clearCachedViews(const osg::Vec3f &pos) void QuadTreeWorld::rebuildViews()
{ {
//mViewDataMap->clear(); mViewDataMap->rebuildViews();
osg::Vec3 pos_ = pos / mStorage->getCellWorldSize();
mViewDataMap->clearCachedViews(pos_);
} }
} }

View File

@ -39,8 +39,8 @@ namespace Terrain
View* createView(); View* createView();
void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic<bool>& abort, std::atomic<int>& progress, int& progressRange); void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic<bool>& abort, std::atomic<int>& progress, int& progressRange);
void storeView(const View* view, double referenceTime); bool storeView(const View* view, double referenceTime);
void clearCachedViews(const osg::Vec3f& pos) override; void rebuildViews() override;
void reportStats(unsigned int frameNumber, osg::Stats* stats); void reportStats(unsigned int frameNumber, osg::Stats* stats);

View File

@ -10,6 +10,7 @@ ViewData::ViewData()
, mLastUsageTimeStamp(0.0) , mLastUsageTimeStamp(0.0)
, mChanged(false) , mChanged(false)
, mHasViewPoint(false) , mHasViewPoint(false)
, mWorldUpdateRevision(0)
{ {
} }
@ -27,6 +28,7 @@ void ViewData::copyFrom(const ViewData& other)
mHasViewPoint = other.mHasViewPoint; mHasViewPoint = other.mHasViewPoint;
mViewPoint = other.mViewPoint; mViewPoint = other.mViewPoint;
mActiveGrid = other.mActiveGrid; mActiveGrid = other.mActiveGrid;
mWorldUpdateRevision = other.mWorldUpdateRevision;
} }
void ViewData::add(QuadTreeNode *node) void ViewData::add(QuadTreeNode *node)
@ -93,7 +95,12 @@ void ViewData::clear()
mHasViewPoint = false; mHasViewPoint = false;
} }
bool ViewData::contains(QuadTreeNode *node) bool ViewData::suitableToUse(const osg::Vec4i &activeGrid) const
{
return hasViewPoint() && activeGrid == mActiveGrid && getNumEntries();
}
bool ViewData::contains(QuadTreeNode *node) const
{ {
for (unsigned int i=0; i<mNumEntries; ++i) for (unsigned int i=0; i<mNumEntries; ++i)
if (mEntries[i].mNode == node) if (mEntries[i].mNode == node)
@ -101,18 +108,6 @@ bool ViewData::contains(QuadTreeNode *node)
return false; return false;
} }
bool intersects(const osg::Vec2f& center, float halfSize, osg::Vec3f pos)
{
return (pos.x() >= center.x()-halfSize && pos.y() >= center.y()-halfSize && pos.x() <= center.x()+halfSize && pos.y() <= center.y()+halfSize);
}
void ViewData::clearCache(const osg::Vec3f &cellPos)
{
for (Entry& entry : mEntries)
if (entry.mNode && intersects(entry.mNode->getCenter(), entry.mNode->getSize()/2.f, cellPos))
entry.mRenderingNode = nullptr;
}
ViewData::Entry::Entry() ViewData::Entry::Entry()
: mNode(nullptr) : mNode(nullptr)
, mLodFlags(0) , mLodFlags(0)
@ -133,86 +128,106 @@ bool ViewData::Entry::set(QuadTreeNode *node)
} }
} }
bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist, const osg::Vec4i& activeGrid)
{
return vd->hasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist && vd->getActiveGrid() == activeGrid;
}
ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate) ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate)
{ {
Map::const_iterator found = mViews.find(viewer); ViewerMap::const_iterator found = mViewers.find(viewer);
ViewData* vd = nullptr; ViewData* vd = nullptr;
if (found == mViews.end()) if (found == mViewers.end())
{ {
vd = createOrReuseView(); vd = createOrReuseView();
mViews[viewer] = vd; mViewers[viewer] = vd;
} }
else else
vd = found->second; vd = found->second;
if (!suitable(vd, viewPoint, mReuseDistance, activeGrid))
{
for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other)
{
if (suitable(other->second, viewPoint, mReuseDistance, activeGrid) && other->second->getNumEntries())
{
vd->copyFrom(*other->second);
needsUpdate = false; needsUpdate = false;
if (!(vd->suitableToUse(activeGrid) && (vd->getViewPoint()-viewPoint).length2() < mReuseDistance*mReuseDistance && vd->getWorldUpdateRevision() >= mWorldUpdateRevision))
{
float shortestDist = std::numeric_limits<float>::max();
const ViewData* mostSuitableView = nullptr;
for (const ViewData& other : mViewVector)
{
if (other.suitableToUse(activeGrid) && other.getWorldUpdateRevision() >= mWorldUpdateRevision)
{
float dist = (viewPoint-other.getViewPoint()).length2();
if (dist < shortestDist)
{
shortestDist = dist;
mostSuitableView = &other;
}
}
}
if (mostSuitableView && mostSuitableView != vd)
{
vd->copyFrom(*mostSuitableView);
return vd; return vd;
} }
} }
if (!vd->suitableToUse(activeGrid))
{
vd->setViewPoint(viewPoint); vd->setViewPoint(viewPoint);
vd->setActiveGrid(activeGrid); vd->setActiveGrid(activeGrid);
needsUpdate = true; needsUpdate = true;
} }
else
needsUpdate = false;
return vd; return vd;
} }
bool ViewDataMap::storeView(const ViewData* view, double referenceTime)
{
if (view->getWorldUpdateRevision() < mWorldUpdateRevision)
return false;
ViewData* store = createOrReuseView();
store->copyFrom(*view);
store->setLastUsageTimeStamp(referenceTime);
return true;
}
ViewData *ViewDataMap::createOrReuseView() ViewData *ViewDataMap::createOrReuseView()
{ {
ViewData* vd = nullptr;
if (mUnusedViews.size()) if (mUnusedViews.size())
{ {
ViewData* vd = mUnusedViews.front(); vd = mUnusedViews.front();
mUnusedViews.pop_front(); mUnusedViews.pop_front();
return vd;
} }
else else
{ {
mViewVector.push_back(ViewData()); mViewVector.push_back(ViewData());
return &mViewVector.back(); vd = &mViewVector.back();
} }
vd->setWorldUpdateRevision(mWorldUpdateRevision);
return vd;
}
ViewData *ViewDataMap::createIndependentView() const
{
ViewData* vd = new ViewData;
vd->setWorldUpdateRevision(mWorldUpdateRevision);
return vd;
} }
void ViewDataMap::clearUnusedViews(double referenceTime) void ViewDataMap::clearUnusedViews(double referenceTime)
{ {
for (Map::iterator it = mViews.begin(); it != mViews.end(); ) for (ViewerMap::iterator it = mViewers.begin(); it != mViewers.end(); )
{ {
ViewData* vd = it->second; if (it->second->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
if (vd->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) mViewers.erase(it++);
{
vd->clear();
mUnusedViews.push_back(vd);
mViews.erase(it++);
}
else else
++it; ++it;
} }
for (ViewData& vd : mViewVector)
{
if (vd.getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
{
vd.clear();
mUnusedViews.push_back(&vd);
}
}
} }
void ViewDataMap::clearCachedViews(const osg::Vec3f &cellPos) void ViewDataMap::rebuildViews()
{ {
for (auto pair : mViews) ++mWorldUpdateRevision;
pair.second->clearCache(cellPos);
}
void ViewDataMap::clear()
{
mViews.clear();
mUnusedViews.clear();
mViewVector.clear();
} }
} }

View File

@ -23,10 +23,11 @@ namespace Terrain
void reset(); void reset();
void clear(); bool suitableToUse(const osg::Vec4i& activeGrid) const;
void clearCache(const osg::Vec3f &cellPos);
bool contains(QuadTreeNode* node); void clear();
bool contains(QuadTreeNode* node) const;
void copyFrom(const ViewData& other); void copyFrom(const ViewData& other);
@ -61,6 +62,9 @@ namespace Terrain
void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} } void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} }
const osg::Vec4i &getActiveGrid() const { return mActiveGrid;} const osg::Vec4i &getActiveGrid() const { return mActiveGrid;}
unsigned int getWorldUpdateRevision() const { return mWorldUpdateRevision; }
void setWorldUpdateRevision(int updateRevision) { mWorldUpdateRevision = updateRevision; }
private: private:
std::vector<Entry> mEntries; std::vector<Entry> mEntries;
unsigned int mNumEntries; unsigned int mNumEntries;
@ -69,35 +73,39 @@ namespace Terrain
osg::Vec3f mViewPoint; osg::Vec3f mViewPoint;
bool mHasViewPoint; bool mHasViewPoint;
osg::Vec4i mActiveGrid; osg::Vec4i mActiveGrid;
unsigned int mWorldUpdateRevision;
}; };
class ViewDataMap : public osg::Referenced class ViewDataMap : public osg::Referenced
{ {
public: public:
ViewDataMap() ViewDataMap()
: 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. : mReuseDistance(150) // 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. // 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) , mExpiryDelay(1.f)
, mWorldUpdateRevision(0)
{} {}
ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate); ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate);
ViewData* createOrReuseView(); ViewData* createOrReuseView();
ViewData* createIndependentView() const;
void clearUnusedViews(double referenceTime); void clearUnusedViews(double referenceTime);
void clearCachedViews(const osg::Vec3f &cellPos); void rebuildViews();
bool storeView(const ViewData* view, double referenceTime);
void clear();
private: private:
std::list<ViewData> mViewVector; std::list<ViewData> mViewVector;
typedef std::map<osg::ref_ptr<osg::Object>, ViewData*> Map; typedef std::map<osg::ref_ptr<osg::Object>, ViewData*> ViewerMap;
Map mViews; ViewerMap mViewers;
float mReuseDistance; float mReuseDistance;
float mExpiryDelay; // time in seconds for unused view to be removed float mExpiryDelay; // time in seconds for unused view to be removed
unsigned int mWorldUpdateRevision;
std::deque<ViewData*> mUnusedViews; std::deque<ViewData*> mUnusedViews;
}; };

View File

@ -151,9 +151,9 @@ namespace Terrain
/// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it.
/// @note Not thread safe. /// @note Not thread safe.
virtual void storeView(const View* view, double referenceTime) {} virtual bool storeView(const View* view, double referenceTime) {return true;}
virtual void clearCachedViews(const osg::Vec3f& pos) {} virtual void rebuildViews() {}
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {}