diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index 24d691c56..0bc79573e 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -146,26 +146,22 @@ int32 Engine::Main(const Char* cmdLine) EngineImpl::IsReady = true; // Main engine loop - bool canDraw = true; // Prevent drawing 2 or more frames in a row without update or fixed update (nothing will change) + const bool useSleep = true; // TODO: this should probably be a platform setting while (!ShouldExit()) { -#if 0 - // TODO: test it more and maybe use in future to reduce CPU usage - // Reduce CPU usage by introducing idle time if the engine is running very fast and has enough time to spend - { - float tickFps; - auto tick = Time->GetHighestFrequency(tickFps); - double tickTargetStepTime = 1.0 / tickFps; - double nextTick = tick->LastEnd + tickTargetStepTime; - double timeToTick = nextTick - Platform::GetTimeSeconds(); - int32 sleepTimeMs = Math::Min(4, Math::FloorToInt(timeToTick * (1000.0 * 0.8))); // Convert seconds to milliseconds and apply adjustment with limit - if (!Device->WasVSyncUsed() && sleepTimeMs > 0) - { - PROFILE_CPU_NAMED("Idle"); - Platform::Sleep(sleepTimeMs); - } - } -#endif + // Reduce CPU usage by introducing idle time if the engine is running very fast and has enough time to spend + if ((useSleep && Time::UpdateFPS > 0) || !Platform::GetHasFocus()) + { + double nextTick = Time::GetNextTick(); + double timeToTick = nextTick - Platform::GetTimeSeconds(); + + // Sleep less than needed, some platforms may sleep slightly more than requested + if (timeToTick > 0.002) + { + PROFILE_CPU_NAMED("Idle"); + Platform::Sleep(1); + } + } // App paused logic if (Platform::GetIsPaused()) @@ -187,7 +183,6 @@ int32 Engine::Main(const Char* cmdLine) OnUpdate(); OnLateUpdate(); Time::OnEndUpdate(); - canDraw = true; } // Start physics simulation @@ -195,16 +190,14 @@ int32 Engine::Main(const Char* cmdLine) { OnFixedUpdate(); Time::OnEndPhysics(); - canDraw = true; } // Draw frame - if (canDraw && Time::OnBeginDraw()) + if (Time::OnBeginDraw()) { OnDraw(); Time::OnEndDraw(); FrameMark; - canDraw = false; } // Collect physics simulation results (does nothing if Simulate hasn't been called in the previous loop step) diff --git a/Source/Engine/Engine/Time.cpp b/Source/Engine/Engine/Time.cpp index 2dd348207..36db6a236 100644 --- a/Source/Engine/Engine/Time.cpp +++ b/Source/Engine/Engine/Time.cpp @@ -70,6 +70,7 @@ void Time::TickData::OnBeforeRun(float targetFps, double currentTime) LastLength = static_cast(DeltaTime.Ticks) / Constants::TicksPerSecond; LastBegin = currentTime - LastLength; LastEnd = currentTime; + NextBegin = targetFps > ZeroTolerance ? LastBegin + (1.0f / targetFps) : 0.0; } void Time::TickData::OnReset(float targetFps, double currentTime) @@ -91,10 +92,18 @@ bool Time::TickData::OnTickBegin(float targetFps, float maxDeltaTime) } else { - deltaTime = Math::Clamp(time - LastBegin, 0.0, (double)maxDeltaTime); - const double minDeltaTime = targetFps > ZeroTolerance ? 1.0 / (double)targetFps : 0.0; - if (deltaTime < minDeltaTime) + if (time < NextBegin) return false; + + deltaTime = Math::Max((time - LastBegin), 0.0); + if (deltaTime > maxDeltaTime) + { + deltaTime = (double)maxDeltaTime; + NextBegin = time; + } + + if (targetFps > ZeroTolerance) + NextBegin += (1.0 / targetFps); } // Update data @@ -135,10 +144,19 @@ bool Time::FixedStepTickData::OnTickBegin(float targetFps, float maxDeltaTime) } else { - deltaTime = Math::Clamp(time - LastBegin, 0.0, (double)maxDeltaTime); - minDeltaTime = targetFps > ZeroTolerance ? 1.0 / (double)targetFps : 0.0; - if (deltaTime < minDeltaTime) + if (time < NextBegin) return false; + + minDeltaTime = targetFps > ZeroTolerance ? 1.0 / targetFps : 0.0; + deltaTime = Math::Max((time - LastBegin), 0.0); + if (deltaTime > maxDeltaTime) + { + deltaTime = (double)maxDeltaTime; + NextBegin = time; + } + + if (targetFps > ZeroTolerance) + NextBegin += (1.0 / targetFps); } Samples.Add(deltaTime); @@ -158,26 +176,23 @@ bool Time::FixedStepTickData::OnTickBegin(float targetFps, float maxDeltaTime) return true; } -Time::TickData* Time::GetHighestFrequency(float& fps) +double Time::GetNextTick() { - const auto updateFps = UpdateFPS; - const auto drawFps = DrawFPS; - const auto physicsFps = PhysicsFPS; + const double nextUpdate = Time::Update.NextBegin; + const double nextPhysics = Time::Physics.NextBegin; + const double nextDraw = Time::Draw.NextBegin; - if (physicsFps >= drawFps && physicsFps >= updateFps) - { - fps = physicsFps; - return &Physics; - } + double nextTick = MAX_double; + if (UpdateFPS > 0 && nextUpdate < nextTick) + nextTick = nextUpdate; + if (PhysicsFPS > 0 && nextPhysics < nextTick) + nextTick = nextPhysics; + if (DrawFPS > 0 && nextDraw < nextTick) + nextTick = nextDraw; - if (drawFps >= physicsFps && drawFps >= updateFps) - { - fps = drawFps; - return &Draw; - } - - fps = updateFps; - return &Update; + if (nextTick == MAX_double) + return 0.0; + return nextTick; } void Time::SetGamePaused(bool value) diff --git a/Source/Engine/Engine/Time.h b/Source/Engine/Engine/Time.h index af0545184..d4cf673e9 100644 --- a/Source/Engine/Engine/Time.h +++ b/Source/Engine/Engine/Time.h @@ -48,6 +48,11 @@ public: /// double LastLength; + /// + /// The next tick start time. + /// + double NextBegin; + /// /// The delta time. /// @@ -167,11 +172,10 @@ public: } /// - /// Gets the tick data that uses the highest frequency for the ticking. + /// Gets the time of next upcoming tick data of ticking group with defined update frequency. /// - /// The FPS rate of the highest frequency ticking group. - /// The tick data. - static TickData* GetHighestFrequency(float& fps); + /// The time of next tick. + static double GetNextTick(); /// /// Gets the value indicating whenever game logic is paused (physics, script updates, etc.). @@ -185,7 +189,7 @@ public: /// /// Sets the value indicating whenever game logic is paused (physics, script updates, etc.). /// - /// >True if pause game logic, otherwise false. + /// True if pause game logic, otherwise false. API_PROPERTY() static void SetGamePaused(bool value); ///