From 86c25f5dba5c384df911fef539bd36af5f94d7fd Mon Sep 17 00:00:00 2001 From: uramer Date: Fri, 9 Feb 2018 01:53:52 +0100 Subject: [PATCH 1/4] Removed NIF flag handling to replicate vanilla engine behaviour --- components/nifbullet/bulletnifloader.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 9e9fe3759..598b0f0f5 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -245,11 +245,6 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, { assert(shape != NULL); - // Interpret flags - bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0; - bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0; - bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0; - // If the object was marked "NCO" earlier, it shouldn't collide with // anything. So don't do anything. if ((flags & 0x800)) @@ -257,10 +252,17 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, return; } + + /* this is an improper way to handle NIF flags, see https://bugs.openmw.org/issues/4319#change-22066 for more details + // Interpret flags + bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0; + bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0; + bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0; + if (!collide && !bbcollide && hidden) // This mesh apparently isn't being used for anything, so don't // bother setting it up. - return; + return;*/ if (!shape->skin.empty()) isAnimated = false; From 5502790ed91bb985f6fcc7d541644b6e6838afa0 Mon Sep 17 00:00:00 2001 From: uramer Date: Fri, 9 Feb 2018 16:34:55 +0100 Subject: [PATCH 2/4] removed the unnecessary comment --- components/nifbullet/bulletnifloader.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 598b0f0f5..8f827e4e2 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -252,18 +252,6 @@ void BulletNifLoader::handleNiTriShape(const Nif::NiTriShape *shape, int flags, return; } - - /* this is an improper way to handle NIF flags, see https://bugs.openmw.org/issues/4319#change-22066 for more details - // Interpret flags - bool hidden = (flags&Nif::NiNode::Flag_Hidden) != 0; - bool collide = (flags&Nif::NiNode::Flag_MeshCollision) != 0; - bool bbcollide = (flags&Nif::NiNode::Flag_BBoxCollision) != 0; - - if (!collide && !bbcollide && hidden) - // This mesh apparently isn't being used for anything, so don't - // bother setting it up. - return;*/ - if (!shape->skin.empty()) isAnimated = false; From a708ac488e34c85a783243f462e4301093e62db8 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Tue, 13 Feb 2018 00:38:55 +0000 Subject: [PATCH 3/4] Don't call Store::setUp() unnecessarily Fixes a threading issue with ESM::Land store caused by calling setUp() while it's being used. --- apps/openmw/mwworld/store.cpp | 5 +++++ apps/openmw/mwworld/store.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 1 - 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 704dbf970..9279e3fe7 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -607,6 +607,11 @@ namespace MWWorld } return ptr; } + void Store::clearDynamic() + { + setUp(); + } + void Store::setUp() { typedef DynamicExt::iterator ExtIterator; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 3babea86a..00ea6f66f 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -293,6 +293,7 @@ namespace MWWorld const ESM::Cell *find(const std::string &id) const; const ESM::Cell *find(int x, int y) const; + virtual void clearDynamic(); void setUp(); RecordId load(ESM::ESMReader &esm); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 02e0737ee..7786b6823 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -295,7 +295,6 @@ namespace MWWorld mWorldScene->clear(); mStore.clearDynamic(); - mStore.setUp(); if (mPlayer) { From 123f7b83d59800f869a7454dcd3bcbe8b1ad51a3 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Tue, 13 Feb 2018 00:40:41 +0000 Subject: [PATCH 4/4] Make the CompositeMapRenderer use available time and add related setting --- apps/openmw/mwgui/loadingscreen.cpp | 1 - apps/openmw/mwrender/renderingmanager.cpp | 4 ++++ components/terrain/compositemaprenderer.cpp | 22 +++++++++++++++---- components/terrain/compositemaprenderer.hpp | 9 ++++++-- components/terrain/world.cpp | 5 +++++ components/terrain/world.hpp | 3 +++ .../reference/modding/settings/cells.rst | 8 +++++++ files/settings-default.cfg | 3 +++ 8 files changed, 48 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 4753c39f2..2c12547fd 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -163,7 +163,6 @@ namespace MWGui if (mViewer->getIncrementalCompileOperation()) { mViewer->getIncrementalCompileOperation()->setMaximumNumOfObjectsToCompilePerFrame(100); - mViewer->getIncrementalCompileOperation()->setTargetFrameRate(getTargetFrameRate()); } // Assign dummy bounding sphere callback to avoid the bounding sphere of the entire scene being recomputed after each frame of loading diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 4fbcdc4d5..3281a787b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -205,7 +205,10 @@ namespace MWRender mObjects.reset(new Objects(mResourceSystem, sceneRoot, mUnrefQueue.get())); if (getenv("OPENMW_DONT_PRECOMPILE") == NULL) + { mViewer->setIncrementalCompileOperation(new osgUtil::IncrementalCompileOperation); + mViewer->getIncrementalCompileOperation()->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); + } mResourceSystem->getSceneManager()->setIncrementalCompileOperation(mViewer->getIncrementalCompileOperation()); @@ -223,6 +226,7 @@ namespace MWRender else mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); mTerrain->setDefaultViewer(mViewer->getCamera()); + mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); mCamera.reset(new Camera(mViewer->getCamera())); diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index 653ac53ec..6626a5713 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -10,7 +10,8 @@ namespace Terrain { CompositeMapRenderer::CompositeMapRenderer() - : mTimeAvailable(0.0005) + : mTargetFrameRate(120) + , mMinimumTimeAvailable(0.0025) { setSupportsDisplayList(false); setCullingActive(false); @@ -22,6 +23,14 @@ CompositeMapRenderer::CompositeMapRenderer() void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const { + double dt = mTimer.time_s(); + dt = std::min(dt, 0.2); + mTimer.setStartTick(); + double targetFrameTime = 1.0/static_cast(mTargetFrameRate); + double conservativeTimeRatio(0.75); + double availableTime = std::max((targetFrameTime - dt)*conservativeTimeRatio, + mMinimumTimeAvailable); + mCompiled.clear(); OpenThreads::ScopedLock lock(mMutex); @@ -39,7 +48,7 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const mImmediateCompileSet.erase(mImmediateCompileSet.begin()); } - double timeLeft = mTimeAvailable; + double timeLeft = availableTime; while (!mCompileSet.empty() && timeLeft > 0) { @@ -126,9 +135,14 @@ void CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo & ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId); } -void CompositeMapRenderer::setTimeAvailableForCompile(double time) +void CompositeMapRenderer::setMinimumTimeAvailableForCompile(double time) +{ + mMinimumTimeAvailable = time; +} + +void CompositeMapRenderer::setTargetFrameRate(float framerate) { - mTimeAvailable = time; + mTargetFrameRate = framerate; } void CompositeMapRenderer::addCompositeMap(CompositeMap* compositeMap, bool immediate) diff --git a/components/terrain/compositemaprenderer.hpp b/components/terrain/compositemaprenderer.hpp index 15e939389..54e158ed4 100644 --- a/components/terrain/compositemaprenderer.hpp +++ b/components/terrain/compositemaprenderer.hpp @@ -40,7 +40,10 @@ namespace Terrain void compile(CompositeMap& compositeMap, osg::RenderInfo& renderInfo, double* timeLeft) const; /// Set the available time in seconds for compiling (non-immediate) composite maps each frame - void setTimeAvailableForCompile(double time); + void setMinimumTimeAvailableForCompile(double time); + + /// If current frame rate is higher than this, the extra time will be set aside to do more compiling + void setTargetFrameRate(float framerate); /// Add a composite map to be rendered void addCompositeMap(CompositeMap* map, bool immediate=false); @@ -51,7 +54,9 @@ namespace Terrain unsigned int getCompileSetSize() const; private: - double mTimeAvailable; + float mTargetFrameRate; + double mMinimumTimeAvailable; + mutable osg::Timer mTimer; typedef std::set > CompileSet; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 335bb496b..213d5c8a7 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -65,6 +65,11 @@ World::~World() delete mStorage; } +void World::setTargetFrameRate(float rate) +{ + mCompositeMapRenderer->setTargetFrameRate(rate); +} + float World::getHeightAt(const osg::Vec3f &worldPos) { return mStorage->getHeightAt(worldPos); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index e1c3828fc..688ed84d5 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -57,6 +57,9 @@ namespace Terrain World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask); virtual ~World(); + /// See CompositeMapRenderer::setTargetFrameRate + void setTargetFrameRate(float rate); + /// Apply the scene manager's texture filtering settings to all cached textures. /// @note Thread safe. void updateTextureFiltering(); diff --git a/docs/source/reference/modding/settings/cells.rst b/docs/source/reference/modding/settings/cells.rst index d2e7cf5be..aa5d43231 100644 --- a/docs/source/reference/modding/settings/cells.rst +++ b/docs/source/reference/modding/settings/cells.rst @@ -178,6 +178,14 @@ cache expiry delay The amount of time (in seconds) that a preloaded texture or object will stay in cache after it is no longer referenced or required, for example, when all cells containing this texture have been unloaded. +target framerate +---------------- +:Type: floating point +:Range: >0 +:Default: 60 + +Affects the time to be set aside each frame for graphics preloading operations. The game will distribute the preloading over several frames so as to not go under the specified framerate. For best results, set this value to the monitor's refresh rate. If you still experience stutters on turning around, you can try a lower value, although the framerate during loading will suffer a bit in that case. + pointers cache size ------------------ diff --git a/files/settings-default.cfg b/files/settings-default.cfg index c694d4db2..8a28c42e4 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -79,6 +79,9 @@ prediction time = 1 # How long to keep models/textures/collision shapes in cache after they're no longer referenced/required (in seconds) cache expiry delay = 5 +# Affects the time to be set aside each frame for graphics preloading operations +target framerate = 60 + # The count of pointers, that will be saved for a faster search by object ID. pointers cache size = 40