Merge branch 'cpu_idle_sleep' of git://github.com/GoaLitiuM/FlaxEngine into GoaLitiuM-cpu_idle_sleep
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -70,6 +70,7 @@ void Time::TickData::OnBeforeRun(float targetFps, double currentTime)
|
||||
LastLength = static_cast<double>(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)
|
||||
|
||||
@@ -48,6 +48,11 @@ public:
|
||||
/// </summary>
|
||||
double LastLength;
|
||||
|
||||
/// <summary>
|
||||
/// The next tick start time.
|
||||
/// </summary>
|
||||
double NextBegin;
|
||||
|
||||
/// <summary>
|
||||
/// The delta time.
|
||||
/// </summary>
|
||||
@@ -167,11 +172,10 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="fps">The FPS rate of the highest frequency ticking group.</param>
|
||||
/// <returns>The tick data.</returns>
|
||||
static TickData* GetHighestFrequency(float& fps);
|
||||
/// <returns>The time of next tick.</returns>
|
||||
static double GetNextTick();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value indicating whenever game logic is paused (physics, script updates, etc.).
|
||||
@@ -185,7 +189,7 @@ public:
|
||||
/// <summary>
|
||||
/// Sets the value indicating whenever game logic is paused (physics, script updates, etc.).
|
||||
/// </summary>
|
||||
/// <param name="value">>True if pause game logic, otherwise false.</param>
|
||||
/// <param name="value">True if pause game logic, otherwise false.</param>
|
||||
API_PROPERTY() static void SetGamePaused(bool value);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -456,7 +456,21 @@ void Win32Platform::SetThreadAffinityMask(uint64 affinityMask)
|
||||
|
||||
void Win32Platform::Sleep(int32 milliseconds)
|
||||
{
|
||||
::Sleep(static_cast<DWORD>(milliseconds));
|
||||
static thread_local HANDLE timer = NULL;
|
||||
if (timer == NULL)
|
||||
{
|
||||
// Attempt to create high-resolution timer for each thread (Windows 10 build 17134 or later)
|
||||
timer = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
|
||||
if (timer == NULL) // fallback for older versions of Windows
|
||||
timer = CreateWaitableTimer(NULL, TRUE, NULL);
|
||||
}
|
||||
|
||||
// Negative value is relative to current time, minimum waitable time is 10 microseconds
|
||||
LARGE_INTEGER dueTime;
|
||||
dueTime.QuadPart = -int64_t(milliseconds) * 10000;
|
||||
|
||||
SetWaitableTimerEx(timer, &dueTime, 0, NULL, NULL, NULL, 0);
|
||||
WaitForSingleObject(timer, INFINITE);
|
||||
}
|
||||
|
||||
double Win32Platform::GetTimeSeconds()
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "../Win32/IncludeWindowsHeaders.h"
|
||||
#include <VersionHelpers.h>
|
||||
#include <ShellAPI.h>
|
||||
#include <timeapi.h>
|
||||
#include <Psapi.h>
|
||||
#include <objbase.h>
|
||||
#if CRASH_LOG_ENABLE
|
||||
@@ -30,11 +31,12 @@ void* WindowsPlatform::Instance = nullptr;
|
||||
|
||||
namespace
|
||||
{
|
||||
String UserLocale, ComputerName, UserName;
|
||||
String UserLocale, ComputerName, UserName, WindowsName;
|
||||
HANDLE EngineMutex = nullptr;
|
||||
Rectangle VirtualScreenBounds = Rectangle(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
int32 VersionMajor = 0;
|
||||
int32 VersionMinor = 0;
|
||||
int32 VersionBuild = 0;
|
||||
int32 SystemDpi = 96;
|
||||
#if CRASH_LOG_ENABLE
|
||||
CriticalSection SymLocker;
|
||||
@@ -126,6 +128,102 @@ LONG GetDWORDRegKey(HKEY hKey, const Char* strValueName, DWORD& nValue, DWORD nD
|
||||
return nError;
|
||||
}
|
||||
|
||||
void GetWindowsVersion(String& windowsName, int32& versionMajor, int32& versionMinor, int32& versionBuild)
|
||||
{
|
||||
// Get OS version
|
||||
|
||||
HKEY hKey;
|
||||
LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), 0, KEY_READ, &hKey);
|
||||
if (lRes == ERROR_SUCCESS)
|
||||
{
|
||||
GetStringRegKey(hKey, TEXT("ProductName"), windowsName, TEXT("Windows"));
|
||||
|
||||
DWORD currentMajorVersionNumber;
|
||||
DWORD currentMinorVersionNumber;
|
||||
String currentBuildNumber;
|
||||
GetDWORDRegKey(hKey, TEXT("CurrentMajorVersionNumber"), currentMajorVersionNumber, 0);
|
||||
GetDWORDRegKey(hKey, TEXT("CurrentMinorVersionNumber"), currentMinorVersionNumber, 0);
|
||||
GetStringRegKey(hKey, TEXT("CurrentBuildNumber"), currentBuildNumber, TEXT("0"));
|
||||
VersionMajor = currentMajorVersionNumber;
|
||||
VersionMinor = currentMinorVersionNumber;
|
||||
StringUtils::Parse(currentBuildNumber.Get(), &VersionBuild);
|
||||
|
||||
if (StringUtils::Compare(windowsName.Get(), TEXT("Windows 7"), 9) == 0)
|
||||
{
|
||||
VersionMajor = 6;
|
||||
VersionMinor = 2;
|
||||
}
|
||||
|
||||
if (VersionMajor == 0 && VersionMinor == 0)
|
||||
{
|
||||
String windowsVersion;
|
||||
GetStringRegKey(hKey, TEXT("CurrentVersion"), windowsVersion, TEXT(""));
|
||||
|
||||
if (windowsVersion.HasChars())
|
||||
{
|
||||
const int32 dot = windowsVersion.Find('.');
|
||||
if (dot != -1)
|
||||
{
|
||||
StringUtils::Parse(windowsVersion.Substring(0, dot).Get(), &VersionMajor);
|
||||
StringUtils::Parse(windowsVersion.Substring(dot + 1).Get(), &VersionMinor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsWindowsServer())
|
||||
{
|
||||
windowsName = TEXT("Windows Server");
|
||||
versionMajor = 6;
|
||||
versionMinor = 3;
|
||||
}
|
||||
else if (IsWindows8Point1OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows 8.1");
|
||||
versionMajor = 6;
|
||||
versionMinor = 3;
|
||||
}
|
||||
else if (IsWindows8OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows 8");
|
||||
versionMajor = 6;
|
||||
versionMinor = 2;
|
||||
}
|
||||
else if (IsWindows7SP1OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows 7 SP1");
|
||||
versionMajor = 6;
|
||||
versionMinor = 2;
|
||||
}
|
||||
else if (IsWindows7OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows 7");
|
||||
versionMajor = 6;
|
||||
versionMinor = 1;
|
||||
}
|
||||
else if (IsWindowsVistaSP2OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows Vista SP2");
|
||||
versionMajor = 6;
|
||||
versionMinor = 1;
|
||||
}
|
||||
else if (IsWindowsVistaSP1OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows Vista SP1");
|
||||
versionMajor = 6;
|
||||
versionMinor = 1;
|
||||
}
|
||||
else if (IsWindowsVistaOrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows Vista");
|
||||
versionMajor = 6;
|
||||
versionMinor = 0;
|
||||
}
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
// Find window to process that message
|
||||
@@ -414,6 +512,15 @@ void WindowsPlatform::PreInit(void* hInstance)
|
||||
OnSymbolsPathModified();
|
||||
SymLocker.Unlock();
|
||||
#endif
|
||||
|
||||
GetWindowsVersion(WindowsName, VersionMajor, VersionMinor, VersionBuild);
|
||||
|
||||
// Validate platform
|
||||
if (VersionMajor < 6)
|
||||
{
|
||||
Error(TEXT("Not supported operating system version."));
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowsPlatform::IsWindows10()
|
||||
@@ -473,6 +580,12 @@ bool WindowsPlatform::Init()
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set lowest possible timer resolution for previous Windows versions
|
||||
if (VersionMajor < 10 || (VersionMajor == 10 && VersionBuild < 17134))
|
||||
{
|
||||
timeBeginPeriod(1);
|
||||
}
|
||||
|
||||
DWORD tmp;
|
||||
Char buffer[256];
|
||||
|
||||
@@ -503,105 +616,7 @@ void WindowsPlatform::LogInfo()
|
||||
{
|
||||
Win32Platform::LogInfo();
|
||||
|
||||
// Get OS version
|
||||
{
|
||||
String windowsName;
|
||||
HKEY hKey;
|
||||
LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), 0, KEY_READ, &hKey);
|
||||
if (lRes == ERROR_SUCCESS)
|
||||
{
|
||||
GetStringRegKey(hKey, TEXT("ProductName"), windowsName, TEXT("Windows"));
|
||||
|
||||
DWORD currentMajorVersionNumber;
|
||||
DWORD currentMinorVersionNumber;
|
||||
GetDWORDRegKey(hKey, TEXT("CurrentMajorVersionNumber"), currentMajorVersionNumber, 0);
|
||||
GetDWORDRegKey(hKey, TEXT("CurrentMinorVersionNumber"), currentMinorVersionNumber, 0);
|
||||
VersionMajor = currentMajorVersionNumber;
|
||||
VersionMinor = currentMinorVersionNumber;
|
||||
|
||||
if (StringUtils::Compare(windowsName.Get(), TEXT("Windows 7"), 9) == 0)
|
||||
{
|
||||
VersionMajor = 6;
|
||||
VersionMinor = 2;
|
||||
}
|
||||
|
||||
if (VersionMajor == 0 && VersionMinor == 0)
|
||||
{
|
||||
String windowsVersion;
|
||||
GetStringRegKey(hKey, TEXT("CurrentVersion"), windowsVersion, TEXT(""));
|
||||
|
||||
if (windowsVersion.HasChars())
|
||||
{
|
||||
const int32 dot = windowsVersion.Find('.');
|
||||
if (dot != -1)
|
||||
{
|
||||
StringUtils::Parse(windowsVersion.Substring(0, dot).Get(), &VersionMajor);
|
||||
StringUtils::Parse(windowsVersion.Substring(dot + 1).Get(), &VersionMinor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsWindowsServer())
|
||||
{
|
||||
windowsName = TEXT("Windows Server");
|
||||
VersionMajor = 6;
|
||||
VersionMinor = 3;
|
||||
}
|
||||
else if (IsWindows8Point1OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows 8.1");
|
||||
VersionMajor = 6;
|
||||
VersionMinor = 3;
|
||||
}
|
||||
else if (IsWindows8OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows 8");
|
||||
VersionMajor = 6;
|
||||
VersionMinor = 2;
|
||||
}
|
||||
else if (IsWindows7SP1OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows 7 SP1");
|
||||
VersionMajor = 6;
|
||||
VersionMinor = 2;
|
||||
}
|
||||
else if (IsWindows7OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows 7");
|
||||
VersionMajor = 6;
|
||||
VersionMinor = 1;
|
||||
}
|
||||
else if (IsWindowsVistaSP2OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows Vista SP2");
|
||||
VersionMajor = 6;
|
||||
VersionMinor = 1;
|
||||
}
|
||||
else if (IsWindowsVistaSP1OrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows Vista SP1");
|
||||
VersionMajor = 6;
|
||||
VersionMinor = 1;
|
||||
}
|
||||
else if (IsWindowsVistaOrGreater())
|
||||
{
|
||||
windowsName = TEXT("Windows Vista");
|
||||
VersionMajor = 6;
|
||||
VersionMinor = 0;
|
||||
}
|
||||
}
|
||||
RegCloseKey(hKey);
|
||||
LOG(Info, "Microsoft {0} {1}-bit ({2}.{3})", windowsName, Platform::Is64BitPlatform() ? TEXT("64") : TEXT("32"), VersionMajor, VersionMinor);
|
||||
}
|
||||
|
||||
// Validate platform
|
||||
if (VersionMajor < 6)
|
||||
{
|
||||
LOG(Error, "Not supported operating system version.");
|
||||
exit(0);
|
||||
}
|
||||
LOG(Info, "Microsoft {0} {1}-bit ({2}.{3}.{4})", WindowsName, Platform::Is64BitPlatform() ? TEXT("64") : TEXT("32"), VersionMajor, VersionMinor, VersionBuild);
|
||||
|
||||
// Check minimum amount of RAM
|
||||
auto memStats = Platform::GetMemoryStats();
|
||||
|
||||
Reference in New Issue
Block a user