1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-03 17:54:06 +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);
}
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))
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 pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled);
bool pagingEnableObject(int type, const MWWorld::ConstPtr& ptr, bool enabled);
private:
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)
mWorld->storeView(mTerrainViews[i], referenceTime);
if (!mWorld->storeView(mTerrainViews[i], referenceTime))
return false;
return true;
}
virtual void doWork()
@ -244,6 +246,7 @@ namespace MWWorld
, mMaxCacheSize(0)
, mPreloadInstances(true)
, mLastResourceCacheUpdate(0.0)
, mStoreViewsFailCount(0)
{
}
@ -379,7 +382,17 @@ namespace MWWorld
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;
}
}
@ -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)
{
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
if (positions.empty())
mTerrainPreloadPositions.clear();
else if (contains(mTerrainPreloadPositions, positions))
return;
else if (positions == mTerrainPreloadPositions)
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
return;
else
{
@ -472,7 +505,6 @@ namespace MWWorld
}
mTerrainPreloadPositions = positions;
if (!positions.empty())
{
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);

View File

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

View File

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

View File

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

View File

@ -817,7 +817,8 @@ namespace MWWorld
if (reference.getCellRef().getRefNum().hasContentFile())
{
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())
{
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())

View File

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

View File

@ -253,7 +253,6 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour
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.
@ -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
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;
for (unsigned int i=0; i<4; ++i)
@ -506,7 +505,7 @@ void QuadTreeWorld::enable(bool enabled)
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)
@ -533,14 +532,9 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::
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;
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);
return mViewDataMap->storeView(static_cast<const ViewData*>(view), referenceTime);
}
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());
}
void QuadTreeWorld::clearCachedViews(const osg::Vec3f &pos)
void QuadTreeWorld::rebuildViews()
{
//mViewDataMap->clear();
osg::Vec3 pos_ = pos / mStorage->getCellWorldSize();
mViewDataMap->clearCachedViews(pos_);
mViewDataMap->rebuildViews();
}
}

View File

@ -39,8 +39,8 @@ namespace Terrain
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 storeView(const View* view, double referenceTime);
void clearCachedViews(const osg::Vec3f& pos) override;
bool storeView(const View* view, double referenceTime);
void rebuildViews() override;
void reportStats(unsigned int frameNumber, osg::Stats* stats);

View File

@ -10,6 +10,7 @@ ViewData::ViewData()
, mLastUsageTimeStamp(0.0)
, mChanged(false)
, mHasViewPoint(false)
, mWorldUpdateRevision(0)
{
}
@ -27,6 +28,7 @@ void ViewData::copyFrom(const ViewData& other)
mHasViewPoint = other.mHasViewPoint;
mViewPoint = other.mViewPoint;
mActiveGrid = other.mActiveGrid;
mWorldUpdateRevision = other.mWorldUpdateRevision;
}
void ViewData::add(QuadTreeNode *node)
@ -93,7 +95,12 @@ void ViewData::clear()
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)
if (mEntries[i].mNode == node)
@ -101,18 +108,6 @@ bool ViewData::contains(QuadTreeNode *node)
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()
: mNode(nullptr)
, 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)
{
Map::const_iterator found = mViews.find(viewer);
ViewerMap::const_iterator found = mViewers.find(viewer);
ViewData* vd = nullptr;
if (found == mViews.end())
if (found == mViewers.end())
{
vd = createOrReuseView();
mViews[viewer] = vd;
mViewers[viewer] = vd;
}
else
vd = found->second;
needsUpdate = false;
if (!suitable(vd, viewPoint, mReuseDistance, activeGrid))
if (!(vd->suitableToUse(activeGrid) && (vd->getViewPoint()-viewPoint).length2() < mReuseDistance*mReuseDistance && vd->getWorldUpdateRevision() >= mWorldUpdateRevision))
{
for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other)
float shortestDist = std::numeric_limits<float>::max();
const ViewData* mostSuitableView = nullptr;
for (const ViewData& other : mViewVector)
{
if (suitable(other->second, viewPoint, mReuseDistance, activeGrid) && other->second->getNumEntries())
if (other.suitableToUse(activeGrid) && other.getWorldUpdateRevision() >= mWorldUpdateRevision)
{
vd->copyFrom(*other->second);
needsUpdate = false;
return vd;
float dist = (viewPoint-other.getViewPoint()).length2();
if (dist < shortestDist)
{
shortestDist = dist;
mostSuitableView = &other;
}
}
}
if (mostSuitableView && mostSuitableView != vd)
{
vd->copyFrom(*mostSuitableView);
return vd;
}
}
if (!vd->suitableToUse(activeGrid))
{
vd->setViewPoint(viewPoint);
vd->setActiveGrid(activeGrid);
needsUpdate = true;
}
else
needsUpdate = false;
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* vd = nullptr;
if (mUnusedViews.size())
{
ViewData* vd = mUnusedViews.front();
vd = mUnusedViews.front();
mUnusedViews.pop_front();
return vd;
}
else
{
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)
{
for (Map::iterator it = mViews.begin(); it != mViews.end(); )
for (ViewerMap::iterator it = mViewers.begin(); it != mViewers.end(); )
{
ViewData* vd = it->second;
if (vd->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
{
vd->clear();
mUnusedViews.push_back(vd);
mViews.erase(it++);
}
if (it->second->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
mViewers.erase(it++);
else
++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)
pair.second->clearCache(cellPos);
}
void ViewDataMap::clear()
{
mViews.clear();
mUnusedViews.clear();
mViewVector.clear();
++mWorldUpdateRevision;
}
}

View File

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