diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 0792f4eaf4..b1c5bcfdec 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -67,6 +67,140 @@ namespace if (ret != 0) Log(Debug::Error) << "SDL error: " << SDL_GetError(); } + + struct UserStats + { + const std::string mLabel; + const std::string mBegin; + const std::string mEnd; + const std::string mTaken; + + UserStats(const std::string& label, const std::string& prefix) + : mLabel(label), + mBegin(prefix + "_time_begin"), + mEnd(prefix + "_time_end"), + mTaken(prefix + "_time_taken") + {} + }; + + enum class UserStatsType : std::size_t + { + Input, + Sound, + State, + Script, + Mechanics, + Physics, + World, + Gui, + + Number, + }; + + template + struct UserStatsValue + { + static const UserStats sValue; + }; + + template <> + const UserStats UserStatsValue::sValue {"Input", "input"}; + + template <> + const UserStats UserStatsValue::sValue {"Sound", "sound"}; + + template <> + const UserStats UserStatsValue::sValue {"State", "state"}; + + template <> + const UserStats UserStatsValue::sValue {"Script", "script"}; + + template <> + const UserStats UserStatsValue::sValue {"Mech", "mechanics"}; + + template <> + const UserStats UserStatsValue::sValue {"Phys", "physics"}; + + template <> + const UserStats UserStatsValue::sValue {"World", "world"}; + + template <> + const UserStats UserStatsValue::sValue {"Gui", "gui"}; + + template + struct ForEachUserStatsValue + { + template + static void apply(F&& f) + { + f(UserStatsValue::sValue); + using Next = ForEachUserStatsValue(static_cast(type) + 1)>; + Next::apply(std::forward(f)); + } + }; + + template <> + struct ForEachUserStatsValue + { + template + static void apply(F&&) {} + }; + + template + void forEachUserStatsValue(F&& f) + { + ForEachUserStatsValue(0)>::apply(std::forward(f)); + } + + template + class ScopedProfile + { + public: + ScopedProfile(osg::Timer_t frameStart, unsigned int frameNumber, const osg::Timer& timer, osg::Stats& stats) + : mScopeStart(timer.tick()), + mFrameStart(frameStart), + mFrameNumber(frameNumber), + mTimer(timer), + mStats(stats) + { + } + + ScopedProfile(const ScopedProfile&) = delete; + ScopedProfile& operator=(const ScopedProfile&) = delete; + + ~ScopedProfile() + { + const osg::Timer_t end = mTimer.tick(); + const UserStats& stats = UserStatsValue::sValue; + + mStats.setAttribute(mFrameNumber, stats.mBegin, mTimer.delta_s(mFrameStart, mScopeStart)); + mStats.setAttribute(mFrameNumber, stats.mTaken, mTimer.delta_s(mScopeStart, end)); + mStats.setAttribute(mFrameNumber, stats.mEnd, mTimer.delta_s(mFrameStart, end)); + } + + private: + const osg::Timer_t mScopeStart; + const osg::Timer_t mFrameStart; + const unsigned int mFrameNumber; + const osg::Timer& mTimer; + osg::Stats& mStats; + }; + + void initStatsHandler(Resource::Profiler& profiler) + { + const osg::Vec4f textColor(1.f, 1.f, 1.f, 1.f); + const osg::Vec4f barColor(1.f, 1.f, 1.f, 1.f); + const float multiplier = 1000; + const bool average = true; + const bool averageInInverseSpace = false; + const float maxValue = 10000; + + forEachUserStatsValue([&] (const UserStats& v) + { + profiler.addUserStatsLine(v.mLabel, textColor, barColor, v.mTaken, multiplier, + average, averageInInverseSpace, v.mBegin, v.mEnd, maxValue); + }); + } } void OMW::Engine::executeLocalScripts() @@ -87,119 +221,119 @@ bool OMW::Engine::frame(float frametime) { try { - mStartTick = mViewer->getStartTick(); + const osg::Timer_t frameStart = mViewer->getStartTick(); + const unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); + const osg::Timer* const timer = osg::Timer::instance(); + osg::Stats* const stats = mViewer->getViewerStats(); mEnvironment.setFrameDuration(frametime); // update input - mEnvironment.getInputManager()->update(frametime, false); + { + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + mEnvironment.getInputManager()->update(frametime, false); + } // When the window is minimized, pause the game. Currently this *has* to be here to work around a MyGUI bug. // If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2), // and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21) - if (!mEnvironment.getWindowManager()->isWindowVisible()) { - mEnvironment.getSoundManager()->pausePlayback(); - return false; - } - else - mEnvironment.getSoundManager()->resumePlayback(); + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + + if (!mEnvironment.getWindowManager()->isWindowVisible()) + { + mEnvironment.getSoundManager()->pausePlayback(); + return false; + } + else + mEnvironment.getSoundManager()->resumePlayback(); - // sound - if (mUseSound) - mEnvironment.getSoundManager()->update(frametime); + // sound + if (mUseSound) + mEnvironment.getSoundManager()->update(frametime); + } // Main menu opened? Then scripts are also paused. bool paused = mEnvironment.getWindowManager()->containsMode(MWGui::GM_MainMenu); // update game state - mEnvironment.getStateManager()->update (frametime); + { + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + mEnvironment.getStateManager()->update (frametime); + } bool guiActive = mEnvironment.getWindowManager()->isGuiMode(); - osg::Timer_t beforeScriptTick = osg::Timer::instance()->tick(); - if (mEnvironment.getStateManager()->getState()!= - MWBase::StateManager::State_NoGame) { - if (!paused) + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + + if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame) { - if (mEnvironment.getWorld()->getScriptsEnabled()) + if (!paused) { - // local scripts - executeLocalScripts(); + if (mEnvironment.getWorld()->getScriptsEnabled()) + { + // local scripts + executeLocalScripts(); - // global scripts - mEnvironment.getScriptManager()->getGlobalScripts().run(); - } + // global scripts + mEnvironment.getScriptManager()->getGlobalScripts().run(); + } - mEnvironment.getWorld()->markCellAsUnchanged(); - } + mEnvironment.getWorld()->markCellAsUnchanged(); + } - if (!guiActive) - { - double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0; - mEnvironment.getWorld()->advanceTime(hours, true); - mEnvironment.getWorld()->rechargeItems(frametime, true); + if (!guiActive) + { + double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0; + mEnvironment.getWorld()->advanceTime(hours, true); + mEnvironment.getWorld()->rechargeItems(frametime, true); + } } } - osg::Timer_t afterScriptTick = osg::Timer::instance()->tick(); - // update actors - osg::Timer_t beforeMechanicsTick = osg::Timer::instance()->tick(); - if (mEnvironment.getStateManager()->getState()!= - MWBase::StateManager::State_NoGame) + // update mechanics { - mEnvironment.getMechanicsManager()->update(frametime, - guiActive); - } - osg::Timer_t afterMechanicsTick = osg::Timer::instance()->tick(); + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); - if (mEnvironment.getStateManager()->getState()== - MWBase::StateManager::State_Running) - { - MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr(); - if(!guiActive && player.getClass().getCreatureStats(player).isDead()) - mEnvironment.getStateManager()->endGame(); + if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame) + { + mEnvironment.getMechanicsManager()->update(frametime, guiActive); + } + + if (mEnvironment.getStateManager()->getState() == MWBase::StateManager::State_Running) + { + MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr(); + if(!guiActive && player.getClass().getCreatureStats(player).isDead()) + mEnvironment.getStateManager()->endGame(); + } } // update physics - osg::Timer_t beforePhysicsTick = osg::Timer::instance()->tick(); - if (mEnvironment.getStateManager()->getState()!= - MWBase::StateManager::State_NoGame) { - mEnvironment.getWorld()->updatePhysics(frametime, guiActive); + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + + if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame) + { + mEnvironment.getWorld()->updatePhysics(frametime, guiActive); + } } - osg::Timer_t afterPhysicsTick = osg::Timer::instance()->tick(); // update world - osg::Timer_t beforeWorldTick = osg::Timer::instance()->tick(); - if (mEnvironment.getStateManager()->getState()!= - MWBase::StateManager::State_NoGame) { - mEnvironment.getWorld()->update(frametime, guiActive); + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + + if (mEnvironment.getStateManager()->getState() != MWBase::StateManager::State_NoGame) + { + mEnvironment.getWorld()->update(frametime, guiActive); + } } - osg::Timer_t afterWorldTick = osg::Timer::instance()->tick(); // update GUI - mEnvironment.getWindowManager()->update(frametime); - - unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); - osg::Stats* stats = mViewer->getViewerStats(); - stats->setAttribute(frameNumber, "script_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeScriptTick)); - stats->setAttribute(frameNumber, "script_time_taken", osg::Timer::instance()->delta_s(beforeScriptTick, afterScriptTick)); - stats->setAttribute(frameNumber, "script_time_end", osg::Timer::instance()->delta_s(mStartTick, afterScriptTick)); - - stats->setAttribute(frameNumber, "mechanics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeMechanicsTick)); - stats->setAttribute(frameNumber, "mechanics_time_taken", osg::Timer::instance()->delta_s(beforeMechanicsTick, afterMechanicsTick)); - stats->setAttribute(frameNumber, "mechanics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterMechanicsTick)); - - stats->setAttribute(frameNumber, "physics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforePhysicsTick)); - stats->setAttribute(frameNumber, "physics_time_taken", osg::Timer::instance()->delta_s(beforePhysicsTick, afterPhysicsTick)); - stats->setAttribute(frameNumber, "physics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterPhysicsTick)); - - stats->setAttribute(frameNumber, "world_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeWorldTick)); - stats->setAttribute(frameNumber, "world_time_taken", osg::Timer::instance()->delta_s(beforeWorldTick, afterWorldTick)); - stats->setAttribute(frameNumber, "world_time_end", osg::Timer::instance()->delta_s(mStartTick, afterWorldTick)); + { + ScopedProfile profile(frameStart, frameNumber, *timer, *stats); + mEnvironment.getWindowManager()->update(frametime); + } if (stats->collectStats("resource")) { @@ -212,7 +346,6 @@ bool OMW::Engine::frame(float frametime) mEnvironment.reportStats(frameNumber, *stats); } - } catch (const std::exception& e) { @@ -255,8 +388,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) throw std::runtime_error("Could not initialize SDL! " + std::string(SDL_GetError())); } } - - mStartTick = osg::Timer::instance()->tick(); } OMW::Engine::~Engine() @@ -703,14 +834,7 @@ void OMW::Engine::go() // Setup profiler osg::ref_ptr statshandler = new Resource::Profiler; - statshandler->addUserStatsLine("Script", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), - "script_time_taken", 1000.0, true, false, "script_time_begin", "script_time_end", 10000); - statshandler->addUserStatsLine("Mech", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), - "mechanics_time_taken", 1000.0, true, false, "mechanics_time_begin", "mechanics_time_end", 10000); - statshandler->addUserStatsLine("Phys", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), - "physics_time_taken", 1000.0, true, false, "physics_time_begin", "physics_time_end", 10000); - statshandler->addUserStatsLine("World", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f), - "world_time_taken", 1000.0, true, false, "world_time_begin", "world_time_end", 10000); + initStatsHandler(*statshandler); mViewer->addEventHandler(statshandler); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index a00cf474a9..3dd1a69b27 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -111,8 +111,6 @@ namespace OMW bool mScriptBlacklistUse; bool mNewGame; - osg::Timer_t mStartTick; - // not implemented Engine (const Engine&); Engine& operator= (const Engine&);