From 05ffd56dba81893099081eeabed79bddcdfe012b Mon Sep 17 00:00:00 2001
From: xezon <4720891+xezon@users.noreply.github.com>
Date: Tue, 14 Oct 2025 18:25:14 +0200
Subject: [PATCH 1/2] refactor(gameengine): Move game time related code into
new FramePacer class (#1688)
---
Core/GameEngine/CMakeLists.txt | 2 +
Core/GameEngine/Include/Common/FramePacer.h | 73 +++++++
Core/GameEngine/Source/Common/FramePacer.cpp | 182 ++++++++++++++++++
.../GameEngine/Include/Common/GameEngine.h | 32 ---
.../GameEngine/Source/Common/GameEngine.cpp | 168 ++--------------
.../GameEngine/Source/Common/GameMain.cpp | 4 +
.../GameEngine/Source/GameClient/Drawable.cpp | 4 +-
.../GUI/GUICallbacks/Menus/QuitMenu.cpp | 3 +-
.../GameEngine/Source/GameClient/InGameUI.cpp | 7 +-
.../GameClient/MessageStream/CommandXlat.cpp | 30 +--
.../GameClient/MessageStream/LookAtXlat.cpp | 4 +-
.../GameLogic/ScriptEngine/ScriptActions.cpp | 6 +-
.../GameLogic/ScriptEngine/ScriptEngine.cpp | 6 +-
.../Source/GameLogic/System/GameLogic.cpp | 7 +-
.../GameLogic/System/GameLogicDispatch.cpp | 3 +-
.../W3DDevice/GameClient/W3DDisplay.cpp | 8 +-
.../Source/W3DDevice/GameClient/W3DView.cpp | 8 +-
.../W3DDevice/GameClient/Water/W3DWater.cpp | 4 +-
.../GameClient/Water/W3DWaterTracks.cpp | 4 +-
.../GameEngine/Include/Common/GameEngine.h | 32 ---
.../GameEngine/Source/Common/GameEngine.cpp | 168 ++--------------
.../GameEngine/Source/Common/GameMain.cpp | 4 +
.../GameEngine/Source/GameClient/Drawable.cpp | 4 +-
.../GUI/GUICallbacks/Menus/QuitMenu.cpp | 3 +-
.../GameEngine/Source/GameClient/InGameUI.cpp | 7 +-
.../GameClient/MessageStream/CommandXlat.cpp | 30 +--
.../GameClient/MessageStream/LookAtXlat.cpp | 4 +-
.../GameLogic/ScriptEngine/ScriptActions.cpp | 6 +-
.../GameLogic/ScriptEngine/ScriptEngine.cpp | 6 +-
.../Source/GameLogic/System/GameLogic.cpp | 7 +-
.../GameLogic/System/GameLogicDispatch.cpp | 3 +-
.../W3DDevice/GameClient/W3DDisplay.cpp | 8 +-
.../Source/W3DDevice/GameClient/W3DView.cpp | 10 +-
.../W3DDevice/GameClient/Water/W3DWater.cpp | 4 +-
.../GameClient/Water/W3DWaterTracks.cpp | 4 +-
35 files changed, 390 insertions(+), 465 deletions(-)
create mode 100644 Core/GameEngine/Include/Common/FramePacer.h
create mode 100644 Core/GameEngine/Source/Common/FramePacer.cpp
diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt
index 43460b8ecf..0a58dfc431 100644
--- a/Core/GameEngine/CMakeLists.txt
+++ b/Core/GameEngine/CMakeLists.txt
@@ -39,6 +39,7 @@ set(GAMEENGINE_SRC
# Include/Common/Errors.h
Include/Common/file.h
Include/Common/FileSystem.h
+ Include/Common/FramePacer.h
Include/Common/FrameRateLimit.h
# Include/Common/FunctionLexicon.h
Include/Common/GameAudio.h
@@ -570,6 +571,7 @@ set(GAMEENGINE_SRC
# Source/Common/DamageFX.cpp
# Source/Common/Dict.cpp
# Source/Common/DiscreteCircle.cpp
+ Source/Common/FramePacer.cpp
Source/Common/FrameRateLimit.cpp
# Source/Common/GameEngine.cpp
# Source/Common/GameLOD.cpp
diff --git a/Core/GameEngine/Include/Common/FramePacer.h b/Core/GameEngine/Include/Common/FramePacer.h
new file mode 100644
index 0000000000..95c7652736
--- /dev/null
+++ b/Core/GameEngine/Include/Common/FramePacer.h
@@ -0,0 +1,73 @@
+/*
+** Command & Conquer Generals Zero Hour(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 3 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program. If not, see .
+*/
+#pragma once
+
+#include "Common/FrameRateLimit.h"
+
+
+class FramePacer
+{
+public:
+
+ typedef UnsignedInt LogicTimeQueryFlags;
+ enum LogicTimeQueryFlags_ CPP_11(: LogicTimeQueryFlags)
+ {
+ IgnoreFrozenTime = 1<<0, ///< Ignore frozen time for the query
+ IgnoreHaltedGame = 1<<1, ///< Ignore halted game for the query
+ };
+
+ FramePacer();
+ ~FramePacer();
+
+ void update(); ///< Signal that the app/render update is done and wait for the fps limit if applicable.
+
+ void setFramesPerSecondLimit( Int fps ); ///< Set the max update fps.
+ Int getFramesPerSecondLimit() const; ///< Get the max update fps.
+ Real getUpdateTime() const; ///< Get the last update delta time in seconds.
+ Real getUpdateFps() const; ///< Get the last update fps.
+
+ void setTimeFrozen(Bool frozen); ///< Set time frozen. Allows scripted camera movement.
+ void setGameHalted(Bool halted); ///< Set game halted. Does not allow scripted camera movement.
+ Bool isTimeFrozen() const;
+ Bool isGameHalted() const;
+
+ void setLogicTimeScaleFps( Int fps ); ///< Set the logic time scale fps and therefore scale the simulation time. Is capped by the max render fps and does not apply to network matches.
+ Int getLogicTimeScaleFps() const; ///< Get the raw logic time scale fps value.
+ void enableLogicTimeScale( Bool enable ); ///< Enable the logic time scale setup. If disabled, the simulation time scale is bound to the render frame time or network update time.
+ Bool isLogicTimeScaleEnabled() const; ///< Check whether the logic time scale setup is enabled.
+ Int getActualLogicTimeScaleFps(LogicTimeQueryFlags flags = 0) const; ///< Get the real logic time scale fps, depending on the max render fps, network state and enabled state.
+ Real getActualLogicTimeScaleRatio(LogicTimeQueryFlags flags = 0) const; ///< Get the real logic time scale ratio, depending on the max render fps, network state and enabled state.
+ Real getActualLogicTimeScaleOverFpsRatio(LogicTimeQueryFlags flags = 0) const; ///< Get the real logic time scale over render fps ratio, used to scale down steps in render updates to match logic updates.
+ Real getLogicTimeStepSeconds(LogicTimeQueryFlags flags = 0) const; ///< Get the logic time step in seconds
+ Real getLogicTimeStepMilliseconds(LogicTimeQueryFlags flags = 0) const; ///< Get the logic time step in milliseconds
+
+protected:
+
+ FrameRateLimit m_frameRateLimit;
+
+ Int m_maxFPS; ///< Maximum frames per second for rendering
+ Int m_logicTimeScaleFPS; ///< Maximum frames per second for logic time scale
+
+ Real m_updateTime; ///< Last update delta time in seconds
+
+ Bool m_enableLogicTimeScale;
+ Bool m_isTimeFrozen;
+ Bool m_isGameHalted;
+};
+
+extern FramePacer* TheFramePacer;
diff --git a/Core/GameEngine/Source/Common/FramePacer.cpp b/Core/GameEngine/Source/Common/FramePacer.cpp
new file mode 100644
index 0000000000..8bbc9e914f
--- /dev/null
+++ b/Core/GameEngine/Source/Common/FramePacer.cpp
@@ -0,0 +1,182 @@
+/*
+** Command & Conquer Generals Zero Hour(tm)
+** Copyright 2025 Electronic Arts Inc.
+**
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation, either version 3 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program. If not, see .
+*/
+#include "PreRTS.h"
+
+#include "Common/FramePacer.h"
+
+#include "GameClient/View.h"
+
+#include "GameLogic/GameLogic.h"
+#include "GameLogic/ScriptEngine.h"
+
+#include "GameNetwork/NetworkDefs.h"
+#include "GameNetwork/NetworkInterface.h"
+
+
+FramePacer* TheFramePacer = NULL;
+
+FramePacer::FramePacer()
+{
+ // Set the time slice size to 1 ms.
+ timeBeginPeriod(1);
+
+ m_maxFPS = BaseFps;
+ m_logicTimeScaleFPS = LOGICFRAMES_PER_SECOND;
+ m_updateTime = 1.0f / BaseFps; // initialized to something to avoid division by zero on first use
+ m_enableLogicTimeScale = FALSE;
+ m_isTimeFrozen = FALSE;
+ m_isGameHalted = FALSE;
+}
+
+FramePacer::~FramePacer()
+{
+ // Restore the previous time slice for Windows.
+ timeEndPeriod(1);
+}
+
+void FramePacer::update()
+{
+ Bool allowFpsLimit = TheTacticalView->getTimeMultiplier()<=1 && !TheScriptEngine->isTimeFast();
+
+ // I'm disabling this in debug because many people need alt-tab capability. If you happen to be
+ // doing performance tuning, please just change this on your local system. -MDC
+#if defined(RTS_DEBUG)
+ if (allowFpsLimit)
+ ::Sleep(1); // give everyone else a tiny time slice.
+#endif
+
+#if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
+ allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode);
+#else //always allow this cheat key if we're in a replay game.
+ allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode && TheGameLogic->isInReplayGame());
+#endif
+
+ // TheSuperHackers @bugfix xezon 05/08/2025 Re-implements the frame rate limiter
+ // with higher resolution counters to cap the frame rate more accurately to the desired limit.
+ allowFpsLimit &= TheGlobalData->m_useFpsLimit;
+ const UnsignedInt maxFps = allowFpsLimit ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue;
+ m_updateTime = m_frameRateLimit.wait(maxFps);
+}
+
+void FramePacer::setFramesPerSecondLimit( Int fps )
+{
+ DEBUG_LOG(("FramePacer::setFramesPerSecondLimit() - setting max fps to %d (TheGlobalData->m_useFpsLimit == %d)", fps, TheGlobalData->m_useFpsLimit));
+ m_maxFPS = fps;
+}
+
+Int FramePacer::getFramesPerSecondLimit() const
+{
+ return m_maxFPS;
+}
+
+Real FramePacer::getUpdateTime() const
+{
+ return m_updateTime;
+}
+
+Real FramePacer::getUpdateFps() const
+{
+ return 1.0f / m_updateTime;
+}
+
+void FramePacer::setTimeFrozen(Bool frozen)
+{
+ m_isTimeFrozen = frozen;
+}
+
+void FramePacer::setGameHalted(Bool halted)
+{
+ m_isGameHalted = halted;
+}
+
+Bool FramePacer::isTimeFrozen() const
+{
+ return m_isTimeFrozen;
+}
+
+Bool FramePacer::isGameHalted() const
+{
+ return m_isGameHalted;
+}
+
+void FramePacer::setLogicTimeScaleFps( Int fps )
+{
+ m_logicTimeScaleFPS = fps;
+}
+
+Int FramePacer::getLogicTimeScaleFps() const
+{
+ return m_logicTimeScaleFPS;
+}
+
+void FramePacer::enableLogicTimeScale( Bool enable )
+{
+ m_enableLogicTimeScale = enable;
+}
+
+Bool FramePacer::isLogicTimeScaleEnabled() const
+{
+ return m_enableLogicTimeScale;
+}
+
+Int FramePacer::getActualLogicTimeScaleFps(LogicTimeQueryFlags flags) const
+{
+ if (m_isTimeFrozen && (flags & IgnoreFrozenTime) == 0)
+ {
+ return 0;
+ }
+
+ if (m_isGameHalted && (flags & IgnoreHaltedGame) == 0)
+ {
+ return 0;
+ }
+
+ if (TheNetwork != NULL)
+ {
+ return TheNetwork->getFrameRate();
+ }
+
+ if (isLogicTimeScaleEnabled())
+ {
+ return min(getLogicTimeScaleFps(), getFramesPerSecondLimit());
+ }
+
+ return getFramesPerSecondLimit();
+}
+
+Real FramePacer::getActualLogicTimeScaleRatio(LogicTimeQueryFlags flags) const
+{
+ return (Real)getActualLogicTimeScaleFps(flags) / LOGICFRAMES_PER_SECONDS_REAL;
+}
+
+Real FramePacer::getActualLogicTimeScaleOverFpsRatio(LogicTimeQueryFlags flags) const
+{
+ // TheSuperHackers @info Clamps ratio to min 1, because the logic
+ // frame rate is currently capped by the render frame rate.
+ return min(1.0f, (Real)getActualLogicTimeScaleFps(flags) / getUpdateFps());
+}
+
+Real FramePacer::getLogicTimeStepSeconds(LogicTimeQueryFlags flags) const
+{
+ return SECONDS_PER_LOGICFRAME_REAL * getActualLogicTimeScaleOverFpsRatio(flags);
+}
+
+Real FramePacer::getLogicTimeStepMilliseconds(LogicTimeQueryFlags flags) const
+{
+ return MSEC_PER_LOGICFRAME_REAL * getActualLogicTimeScaleOverFpsRatio(flags);
+}
diff --git a/Generals/Code/GameEngine/Include/Common/GameEngine.h b/Generals/Code/GameEngine/Include/Common/GameEngine.h
index 90be51a76c..8dc44e396e 100644
--- a/Generals/Code/GameEngine/Include/Common/GameEngine.h
+++ b/Generals/Code/GameEngine/Include/Common/GameEngine.h
@@ -55,15 +55,6 @@ class ParticleSystemManager;
class GameEngine : public SubsystemInterface
{
-public:
-
- typedef UnsignedInt LogicTimeQueryFlags;
- enum LogicTimeQueryFlags_ CPP_11(: LogicTimeQueryFlags)
- {
- IgnoreFrozenTime = 1<<0, // Ignore frozen time for the query
- IgnoreHaltedGame = 1<<1, // Ignore halted game for the query
- };
-
public:
GameEngine( void );
@@ -76,24 +67,9 @@ class GameEngine : public SubsystemInterface
virtual void execute( void ); /**< The "main loop" of the game engine.
It will not return until the game exits. */
- virtual void setFramesPerSecondLimit( Int fps ); ///< Set the max render and engine update fps.
- virtual Int getFramesPerSecondLimit( void ); ///< Get the max render and engine update fps.
- Real getUpdateTime(); ///< Get the last engine update delta time in seconds.
- Real getUpdateFps(); ///< Get the last engine update fps.
-
static Bool isTimeFrozen(); ///< Returns true if a script has frozen time.
static Bool isGameHalted(); ///< Returns true if the game is paused or the network is stalling.
- virtual void setLogicTimeScaleFps( Int fps ); ///< Set the logic time scale fps and therefore scale the simulation time. Is capped by the max render fps and does not apply to network matches.
- virtual Int getLogicTimeScaleFps(); ///< Get the raw logic time scale fps value.
- virtual void enableLogicTimeScale( Bool enable ); ///< Enable the logic time scale setup. If disabled, the simulation time scale is bound to the render frame time or network update time.
- virtual Bool isLogicTimeScaleEnabled(); ///< Check whether the logic time scale setup is enabled.
- Int getActualLogicTimeScaleFps(LogicTimeQueryFlags flags = 0); ///< Get the real logic time scale fps, depending on the max render fps, network state and enabled state.
- Real getActualLogicTimeScaleRatio(LogicTimeQueryFlags flags = 0); ///< Get the real logic time scale ratio, depending on the max render fps, network state and enabled state.
- Real getActualLogicTimeScaleOverFpsRatio(LogicTimeQueryFlags flags = 0); ///< Get the real logic time scale over render fps ratio, used to scale down steps in render updates to match logic updates.
- Real getLogicTimeStepSeconds(LogicTimeQueryFlags flags = 0); ///< Get the logic time step in seconds
- Real getLogicTimeStepMilliseconds(LogicTimeQueryFlags flags = 0); ///< Get the logic time step in milliseconds
-
virtual void setQuitting( Bool quitting ); ///< set quitting status
virtual Bool getQuitting(void); ///< is app getting ready to quit.
@@ -125,18 +101,10 @@ class GameEngine : public SubsystemInterface
virtual ParticleSystemManager* createParticleSystemManager( void ) = 0;
virtual AudioManager *createAudioManager( void ) = 0; ///< Factory for Audio Manager
- Int m_maxFPS; ///< Maximum frames per second for rendering
- Int m_logicTimeScaleFPS; ///< Maximum frames per second for logic time scale
-
- Real m_updateTime; ///< Last engine update delta time in seconds
Real m_logicTimeAccumulator; ///< Frame time accumulated towards submitting a new logic frame
Bool m_quitting; ///< true when we need to quit the game
Bool m_isActive; ///< app has OS focus.
- Bool m_enableLogicTimeScale;
- Bool m_isTimeFrozen;
- Bool m_isGameHalted;
-
};
inline void GameEngine::setQuitting( Bool quitting ) { m_quitting = quitting; }
diff --git a/Generals/Code/GameEngine/Source/Common/GameEngine.cpp b/Generals/Code/GameEngine/Source/Common/GameEngine.cpp
index 58a1419c12..7943cab329 100644
--- a/Generals/Code/GameEngine/Source/Common/GameEngine.cpp
+++ b/Generals/Code/GameEngine/Source/Common/GameEngine.cpp
@@ -32,6 +32,7 @@
#include "Common/AudioAffect.h"
#include "Common/BuildAssistant.h"
#include "Common/CRCDebug.h"
+#include "Common/FramePacer.h"
#include "Common/Radar.h"
#include "Common/PlayerTemplate.h"
#include "Common/Team.h"
@@ -44,7 +45,6 @@
#include "Common/ThingFactory.h"
#include "Common/file.h"
#include "Common/FileSystem.h"
-#include "Common/FrameRateLimit.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/LocalFileSystem.h"
#include "Common/CDManager.h"
@@ -249,19 +249,10 @@ static void updateWindowTitle()
//-------------------------------------------------------------------------------------------------
GameEngine::GameEngine( void )
{
- // Set the time slice size to 1 ms.
- timeBeginPeriod(1);
-
// initialize to non garbage values
- m_maxFPS = BaseFps;
- m_logicTimeScaleFPS = LOGICFRAMES_PER_SECOND;
- m_updateTime = 1.0f / BaseFps; // initialized to something to avoid division by zero on first use
m_logicTimeAccumulator = 0.0f;
m_quitting = FALSE;
m_isActive = FALSE;
- m_enableLogicTimeScale = FALSE;
- m_isTimeFrozen = FALSE;
- m_isGameHalted = FALSE;
_Module.Init(NULL, ApplicationHInstance, NULL);
}
@@ -307,34 +298,6 @@ GameEngine::~GameEngine()
#ifdef PERF_TIMERS
PerfGather::termPerfDump();
#endif
-
- // Restore the previous time slice for Windows.
- timeEndPeriod(1);
-}
-
-//-------------------------------------------------------------------------------------------------
-void GameEngine::setFramesPerSecondLimit( Int fps )
-{
- DEBUG_LOG(("GameEngine::setFramesPerSecondLimit() - setting max fps to %d (TheGlobalData->m_useFpsLimit == %d)", fps, TheGlobalData->m_useFpsLimit));
- m_maxFPS = fps;
-}
-
-//-------------------------------------------------------------------------------------------------
-Int GameEngine::getFramesPerSecondLimit( void )
-{
- return m_maxFPS;
-}
-
-//-------------------------------------------------------------------------------------------------
-Real GameEngine::getUpdateTime()
-{
- return m_updateTime;
-}
-
-//-------------------------------------------------------------------------------------------------
-Real GameEngine::getUpdateFps()
-{
- return 1.0f / m_updateTime;
}
//-------------------------------------------------------------------------------------------------
@@ -376,80 +339,6 @@ Bool GameEngine::isGameHalted()
return false;
}
-//-------------------------------------------------------------------------------------------------
-void GameEngine::setLogicTimeScaleFps( Int fps )
-{
- m_logicTimeScaleFPS = fps;
-}
-
-//-------------------------------------------------------------------------------------------------
-Int GameEngine::getLogicTimeScaleFps()
-{
- return m_logicTimeScaleFPS;
-}
-
-//-------------------------------------------------------------------------------------------------
-void GameEngine::enableLogicTimeScale( Bool enable )
-{
- m_enableLogicTimeScale = enable;
-}
-
-//-------------------------------------------------------------------------------------------------
-Bool GameEngine::isLogicTimeScaleEnabled()
-{
- return m_enableLogicTimeScale;
-}
-
-//-------------------------------------------------------------------------------------------------
-Int GameEngine::getActualLogicTimeScaleFps(LogicTimeQueryFlags flags)
-{
- if (m_isTimeFrozen && (flags & IgnoreFrozenTime) == 0)
- {
- return 0;
- }
-
- if (m_isGameHalted && (flags & IgnoreHaltedGame) == 0)
- {
- return 0;
- }
-
- if (TheNetwork != NULL)
- {
- return TheNetwork->getFrameRate();
- }
-
- if (isLogicTimeScaleEnabled())
- {
- return min(getLogicTimeScaleFps(), getFramesPerSecondLimit());
- }
-
- return getFramesPerSecondLimit();
-}
-
-//-------------------------------------------------------------------------------------------------
-Real GameEngine::getActualLogicTimeScaleRatio(LogicTimeQueryFlags flags)
-{
- return (Real)getActualLogicTimeScaleFps(flags) / LOGICFRAMES_PER_SECONDS_REAL;
-}
-
-//-------------------------------------------------------------------------------------------------
-Real GameEngine::getActualLogicTimeScaleOverFpsRatio(LogicTimeQueryFlags flags)
-{
- // TheSuperHackers @info Clamps ratio to min 1, because the logic
- // frame rate is currently capped by the render frame rate.
- return min(1.0f, (Real)getActualLogicTimeScaleFps(flags) / getUpdateFps());
-}
-
-Real GameEngine::getLogicTimeStepSeconds(LogicTimeQueryFlags flags)
-{
- return SECONDS_PER_LOGICFRAME_REAL * getActualLogicTimeScaleOverFpsRatio(flags);
-}
-
-Real GameEngine::getLogicTimeStepMilliseconds(LogicTimeQueryFlags flags)
-{
- return MSEC_PER_LOGICFRAME_REAL * getActualLogicTimeScaleOverFpsRatio(flags);
-}
-
/** -----------------------------------------------------------------------------------------------
* Initialize the game engine by initializing the GameLogic and GameClient.
*/
@@ -601,7 +490,7 @@ void GameEngine::init()
TheSubsystemList->postProcessLoadAll();
- setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
+ TheFramePacer->setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_musicOn, AudioAffect_Music);
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_soundsOn, AudioAffect_Sound);
@@ -754,8 +643,8 @@ Bool GameEngine::canUpdateGameLogic()
// Must be first.
TheGameLogic->preUpdate();
- m_isTimeFrozen = isTimeFrozen();
- m_isGameHalted = isGameHalted();
+ TheFramePacer->setTimeFrozen(isTimeFrozen());
+ TheFramePacer->setGameHalted(isGameHalted());
if (TheNetwork != NULL)
{
@@ -767,6 +656,7 @@ Bool GameEngine::canUpdateGameLogic()
}
}
+/// -----------------------------------------------------------------------------------------------
Bool GameEngine::canUpdateNetworkGameLogic()
{
DEBUG_ASSERTCRASH(TheNetwork != NULL, ("TheNetwork is NULL"));
@@ -774,7 +664,7 @@ Bool GameEngine::canUpdateNetworkGameLogic()
if (TheNetwork->isFrameDataReady())
{
// Important: The Network is definitely no longer stalling.
- m_isGameHalted = false;
+ TheFramePacer->setGameHalted(false);
return true;
}
@@ -782,11 +672,12 @@ Bool GameEngine::canUpdateNetworkGameLogic()
return false;
}
+/// -----------------------------------------------------------------------------------------------
Bool GameEngine::canUpdateRegularGameLogic()
{
- const Bool enabled = isLogicTimeScaleEnabled();
- const Int logicTimeScaleFps = getLogicTimeScaleFps();
- const Int maxRenderFps = getFramesPerSecondLimit();
+ const Bool enabled = TheFramePacer->isLogicTimeScaleEnabled();
+ const Int logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps();
+ const Int maxRenderFps = TheFramePacer->getFramesPerSecondLimit();
#if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
const Bool useFastMode = TheGlobalData->m_TiVOFastMode;
@@ -804,7 +695,7 @@ Bool GameEngine::canUpdateRegularGameLogic()
// TheSuperHackers @tweak xezon 06/08/2025
// The logic time step is now decoupled from the render update.
const Real targetFrameTime = 1.0f / logicTimeScaleFps;
- m_logicTimeAccumulator += min(m_updateTime, targetFrameTime);
+ m_logicTimeAccumulator += min(TheFramePacer->getUpdateTime(), targetFrameTime);
if (m_logicTimeAccumulator >= targetFrameTime)
{
@@ -847,8 +738,8 @@ void GameEngine::update( void )
}
const Bool canUpdate = canUpdateGameLogic();
- const Bool canUpdateLogic = canUpdate && !m_isGameHalted && !m_isTimeFrozen;
- const Bool canUpdateScript = canUpdate && !m_isGameHalted;
+ const Bool canUpdateLogic = canUpdate && !TheFramePacer->isGameHalted() && !TheFramePacer->isTimeFrozen();
+ const Bool canUpdateScript = canUpdate && !TheFramePacer->isGameHalted();
if (canUpdateLogic)
{
@@ -873,8 +764,6 @@ extern HWND ApplicationHWnd;
*/
void GameEngine::execute( void )
{
- FrameRateLimit* frameRateLimit = new FrameRateLimit();
-
#if defined(RTS_DEBUG)
DWORD startTime = timeGetTime() / 1000;
#endif
@@ -943,34 +832,7 @@ void GameEngine::execute( void )
}
}
- {
- {
- Bool allowFpsLimit = TheTacticalView->getTimeMultiplier()<=1 && !TheScriptEngine->isTimeFast();
-
- // I'm disabling this in debug because many people need alt-tab capability. If you happen to be
- // doing performance tuning, please just change this on your local system. -MDC
- #if defined(RTS_DEBUG)
- if (allowFpsLimit)
- ::Sleep(1); // give everyone else a tiny time slice.
- #endif
-
-
- #if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
- allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode);
- #else //always allow this cheat key if we're in a replay game.
- allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode && TheGameLogic->isInReplayGame());
- #endif
- {
- // TheSuperHackers @bugfix xezon 05/08/2025 Re-implements the frame rate limiter
- // with higher resolution counters to cap the frame rate more accurately to the desired limit.
- allowFpsLimit &= TheGlobalData->m_useFpsLimit;
- const UnsignedInt maxFps = allowFpsLimit ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue;
- m_updateTime = frameRateLimit->wait(maxFps);
- }
-
- }
- }
-
+ TheFramePacer->update();
}
#ifdef PERF_TIMERS
@@ -983,8 +845,6 @@ void GameEngine::execute( void )
#endif
}
-
- delete frameRateLimit;
}
/** -----------------------------------------------------------------------------------------------
diff --git a/Generals/Code/GameEngine/Source/Common/GameMain.cpp b/Generals/Code/GameEngine/Source/Common/GameMain.cpp
index cf5f2e3be8..6255d6384c 100644
--- a/Generals/Code/GameEngine/Source/Common/GameMain.cpp
+++ b/Generals/Code/GameEngine/Source/Common/GameMain.cpp
@@ -28,6 +28,7 @@
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
+#include "Common/FramePacer.h"
#include "Common/GameEngine.h"
#include "Common/ReplaySimulation.h"
@@ -39,6 +40,7 @@ Int GameMain()
{
int exitcode = 0;
// initialize the game engine using factory function
+ TheFramePacer = new FramePacer();
TheGameEngine = CreateGameEngine();
TheGameEngine->init();
@@ -53,6 +55,8 @@ Int GameMain()
}
// since execute() returned, we are exiting the game
+ delete TheFramePacer;
+ TheFramePacer = NULL;
delete TheGameEngine;
TheGameEngine = NULL;
diff --git a/Generals/Code/GameEngine/Source/GameClient/Drawable.cpp b/Generals/Code/GameEngine/Source/GameClient/Drawable.cpp
index 30232b73e0..f8c9bbe9a5 100644
--- a/Generals/Code/GameEngine/Source/GameClient/Drawable.cpp
+++ b/Generals/Code/GameEngine/Source/GameClient/Drawable.cpp
@@ -35,8 +35,8 @@
#include "Common/BuildAssistant.h"
#include "Common/ClientUpdateModule.h"
#include "Common/DrawModule.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
-#include "Common/GameEngine.h"
#include "Common/GameLOD.h"
#include "Common/GameState.h"
#include "Common/GlobalData.h"
@@ -4785,7 +4785,7 @@ void TintEnvelope::setDecayFrames( UnsignedInt frames )
void TintEnvelope::update(void)
{
// TheSuperHackers @tweak The tint time step is now decoupled from the render update.
- const Real timeScale = TheGameEngine->getActualLogicTimeScaleOverFpsRatio();
+ const Real timeScale = TheFramePacer->getActualLogicTimeScaleOverFpsRatio();
switch ( m_envState )
{
diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
index eb51606ebb..37fab26756 100644
--- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
+++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
@@ -30,6 +30,7 @@
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
+#include "Common/FramePacer.h"
#include "Common/GameEngine.h"
#include "Common/GameState.h"
#include "Common/MessageStream.h"
@@ -215,7 +216,7 @@ static void restartMissionMenu()
Int rankPointsStartedWith = TheGameLogic->getRankPointsToAddAtGameStart();// must write down before reset
GameDifficulty diff = TheScriptEngine->getGlobalDifficulty();
- Int fps = TheGameEngine->getFramesPerSecondLimit();
+ Int fps = TheFramePacer->getFramesPerSecondLimit();
TheGameLogic->clearGameData(FALSE);
TheGameEngine->setQuitting(FALSE);
diff --git a/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp b/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp
index ed5de55706..0cd5764c2a 100644
--- a/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp
+++ b/Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp
@@ -32,8 +32,8 @@
#define DEFINE_SHADOW_NAMES
#include "Common/ActionManager.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
-#include "Common/GameEngine.h"
#include "Common/GameType.h"
#include "Common/MessageStream.h"
#include "Common/PerfTimer.h"
@@ -46,7 +46,6 @@
#include "Common/BuildAssistant.h"
#include "Common/Recorder.h"
#include "Common/SpecialPower.h"
-#include "Common/FrameRateLimit.h"
#include "GameClient/Anim2D.h"
#include "GameClient/ControlBar.h"
@@ -1889,7 +1888,7 @@ void InGameUI::update( void )
if (m_cameraRotatingLeft || m_cameraRotatingRight || m_cameraZoomingIn || m_cameraZoomingOut)
{
// TheSuperHackers @tweak The camera rotation and zoom are now decoupled from the render update.
- const Real fpsRatio = (Real)BaseFps / TheGameEngine->getUpdateFps();
+ const Real fpsRatio = (Real)BaseFps / TheFramePacer->getUpdateFps();
const Real rotateAngle = TheGlobalData->m_keyboardCameraRotateSpeed * fpsRatio;
const Real zoomHeight = (Real)View::ZoomHeightPerSecond * fpsRatio;
@@ -5871,7 +5870,7 @@ void InGameUI::drawRenderFps(Int &x, Int &y)
UnsignedInt renderFpsLimit = 0u;
if (TheGlobalData->m_useFpsLimit)
{
- renderFpsLimit = (UnsignedInt)TheGameEngine->getFramesPerSecondLimit();
+ renderFpsLimit = (UnsignedInt)TheFramePacer->getFramesPerSecondLimit();
if (renderFpsLimit == RenderFpsPreset::UncappedFpsValue)
{
renderFpsLimit = 0u;
diff --git a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp
index 37241be0b2..678cd66653 100644
--- a/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp
+++ b/Generals/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp
@@ -32,7 +32,7 @@
#include "Common/AudioAffect.h"
#include "Common/ActionManager.h"
-#include "Common/FrameRateLimit.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
#include "Common/GameEngine.h"
#include "Common/GameType.h"
@@ -187,10 +187,10 @@ Bool hasThingsInProduction(PlayerType playerType)
bool changeMaxRenderFps(FpsValueChange change)
{
- UnsignedInt maxRenderFps = TheGameEngine->getFramesPerSecondLimit();
+ UnsignedInt maxRenderFps = TheFramePacer->getFramesPerSecondLimit();
maxRenderFps = RenderFpsPreset::changeFpsValue(maxRenderFps, change);
- TheGameEngine->setFramesPerSecondLimit(maxRenderFps);
+ TheFramePacer->setFramesPerSecondLimit(maxRenderFps);
TheWritableGlobalData->m_useFpsLimit = (maxRenderFps != RenderFpsPreset::UncappedFpsValue);
UnicodeString message;
@@ -214,16 +214,16 @@ bool changeLogicTimeScale(FpsValueChange change)
if (TheNetwork != NULL)
return false;
- const UnsignedInt maxRenderFps = TheGameEngine->getFramesPerSecondLimit();
+ const UnsignedInt maxRenderFps = TheFramePacer->getFramesPerSecondLimit();
UnsignedInt maxRenderRemainder = LogicTimeScaleFpsPreset::StepFpsValue;
maxRenderRemainder -= maxRenderFps % LogicTimeScaleFpsPreset::StepFpsValue;
maxRenderRemainder %= LogicTimeScaleFpsPreset::StepFpsValue;
- UnsignedInt logicTimeScaleFps = TheGameEngine->getLogicTimeScaleFps();
+ UnsignedInt logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps();
// Set the value to the max render fps value plus a bit when time scale is
// disabled. This ensures that the time scale does not re-enable with a
// 'surprise' value.
- if (!TheGameEngine->isLogicTimeScaleEnabled())
+ if (!TheFramePacer->isLogicTimeScaleEnabled())
{
logicTimeScaleFps = maxRenderFps + maxRenderRemainder;
}
@@ -234,26 +234,26 @@ bool changeLogicTimeScale(FpsValueChange change)
logicTimeScaleFps = LogicTimeScaleFpsPreset::changeFpsValue(logicTimeScaleFps, change);
// Set value before potentially disabling it.
- if (TheGameEngine->isLogicTimeScaleEnabled())
+ if (TheFramePacer->isLogicTimeScaleEnabled())
{
- TheGameEngine->setLogicTimeScaleFps(logicTimeScaleFps);
+ TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps);
}
- TheGameEngine->enableLogicTimeScale(logicTimeScaleFps < maxRenderFps);
+ TheFramePacer->enableLogicTimeScale(logicTimeScaleFps < maxRenderFps);
// Set value after potentially enabling it.
- if (TheGameEngine->isLogicTimeScaleEnabled())
+ if (TheFramePacer->isLogicTimeScaleEnabled())
{
- TheGameEngine->setLogicTimeScaleFps(logicTimeScaleFps);
+ TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps);
}
- logicTimeScaleFps = TheGameEngine->getLogicTimeScaleFps();
- const UnsignedInt actualLogicTimeScaleFps = TheGameEngine->getActualLogicTimeScaleFps();
- const Real actualLogicTimeScaleRatio = TheGameEngine->getActualLogicTimeScaleRatio();
+ logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps();
+ const UnsignedInt actualLogicTimeScaleFps = TheFramePacer->getActualLogicTimeScaleFps();
+ const Real actualLogicTimeScaleRatio = TheFramePacer->getActualLogicTimeScaleRatio();
UnicodeString message;
- if (TheGameEngine->isLogicTimeScaleEnabled())
+ if (TheFramePacer->isLogicTimeScaleEnabled())
{
message = TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:SetLogicTimeScaleFps", L"Logic Time Scale FPS is %u (actual %u, ratio %.02f)",
logicTimeScaleFps, actualLogicTimeScaleFps, actualLogicTimeScaleRatio);
diff --git a/Generals/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp b/Generals/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp
index 34907e5b62..ad6242db93 100644
--- a/Generals/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp
+++ b/Generals/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp
@@ -28,8 +28,8 @@
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
+#include "Common/FramePacer.h"
#include "Common/GameType.h"
-#include "Common/GameEngine.h"
#include "Common/MessageStream.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
@@ -440,7 +440,7 @@ GameMessageDisposition LookAtTranslator::translateGameMessage(const GameMessage
{
// TheSuperHackers @bugfix Mauller 07/06/2025 The camera scrolling is now decoupled from the render update.
- const Real fpsRatio = (Real)BaseFps / TheGameEngine->getUpdateFps();
+ const Real fpsRatio = (Real)BaseFps / TheFramePacer->getUpdateFps();
switch (m_scrollType)
{
diff --git a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp
index 479f5bd42c..a46d193b26 100644
--- a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp
+++ b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp
@@ -31,8 +31,8 @@
#include "Common/AudioAffect.h"
#include "Common/AudioHandleSpecialValues.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
-#include "Common/GameEngine.h"
#include "Common/MapObject.h" // For MAP_XY_FACTOR
#include "Common/PartitionSolver.h"
#include "Common/Player.h"
@@ -6583,11 +6583,11 @@ void ScriptActions::executeAction( ScriptAction *pAction )
case ScriptAction::SET_FPS_LIMIT:
if (!pAction->getParameter(0)->getInt())
{
- TheGameEngine->setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
+ TheFramePacer->setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
}
else
{
- TheGameEngine->setFramesPerSecondLimit(pAction->getParameter(0)->getInt());
+ TheFramePacer->setFramesPerSecondLimit(pAction->getParameter(0)->getInt());
}
// Setting the fps limit doesn't do much good if we don't use it. jba.
TheWritableGlobalData->m_useFpsLimit = true;
diff --git a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp
index 7ee2c1ec16..b9678cbefe 100644
--- a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp
+++ b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp
@@ -31,7 +31,7 @@
#include "Common/DataChunk.h"
#include "Common/file.h"
#include "Common/FileSystem.h"
-#include "Common/GameEngine.h"
+#include "Common/FramePacer.h"
#include "Common/GameState.h"
#include "Common/LatchRestore.h"
#include "Common/MessageStream.h"
@@ -4530,8 +4530,8 @@ void ScriptEngine::init( void )
void ScriptEngine::reset( void )
{
// setting FPS limit in case a script had changed it
- if (TheGameEngine && TheGlobalData)
- TheGameEngine->setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
+ if (TheFramePacer && TheGlobalData)
+ TheFramePacer->setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
if (TheScriptActions) {
TheScriptActions->reset();
diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
index 92bf469c28..fec5363934 100644
--- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
+++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
@@ -33,6 +33,7 @@
#include "Common/AudioHandleSpecialValues.h"
#include "Common/BuildAssistant.h"
#include "Common/CRCDebug.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
#include "Common/GameEngine.h"
#include "Common/GameState.h"
@@ -3718,8 +3719,8 @@ void GameLogic::setGamePausedInFrame( UnsignedInt frame, Bool disableLogicTimeSc
if (disableLogicTimeScale)
{
- m_logicTimeScaleEnabledMemory = TheGameEngine->isLogicTimeScaleEnabled();
- TheGameEngine->enableLogicTimeScale(FALSE);
+ m_logicTimeScaleEnabledMemory = TheFramePacer->isLogicTimeScaleEnabled();
+ TheFramePacer->enableLogicTimeScale(FALSE);
}
}
}
@@ -3758,7 +3759,7 @@ void GameLogic::pauseGameLogic(Bool paused)
if (!paused && m_logicTimeScaleEnabledMemory)
{
m_logicTimeScaleEnabledMemory = FALSE;
- TheGameEngine->enableLogicTimeScale(TRUE);
+ TheFramePacer->enableLogicTimeScale(TRUE);
}
}
diff --git a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
index 3ffbd73acd..1726d02196 100644
--- a/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
+++ b/Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
@@ -31,6 +31,7 @@
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/CRCDebug.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
#include "Common/GameEngine.h"
#include "Common/GlobalData.h"
@@ -427,7 +428,7 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )
if (maxFPS < 1 || maxFPS > 1000)
maxFPS = TheGlobalData->m_framesPerSecondLimit;
DEBUG_LOG(("Setting max FPS limit to %d FPS", maxFPS));
- TheGameEngine->setFramesPerSecondLimit(maxFPS);
+ TheFramePacer->setFramesPerSecondLimit(maxFPS);
TheWritableGlobalData->m_useFpsLimit = true;
}
diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp
index e6209f3ef5..ed0dd771aa 100644
--- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp
+++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp
@@ -41,8 +41,8 @@ static void drawFramerateBar(void);
#include
// USER INCLUDES //////////////////////////////////////////////////////////////
+#include "Common/FramePacer.h"
#include "Common/ThingFactory.h"
-#include "Common/GameEngine.h"
#include "Common/GlobalData.h"
#include "Common/PerfTimer.h"
#include "Common/FileSystem.h"
@@ -975,7 +975,7 @@ void W3DDisplay::gatherDebugStats( void )
//Int LOD = TheGlobalData->m_terrainLOD;
//unibuffer.format( L"FPS: %.2f, %.2fms mapLOD=%d [cumu FPS=%.2f] draws: %.2f sort: %.2f", fps, ms, LOD, cumuFPS, drawsPerFrame,sortPolysPerFrame);
if (TheGlobalData->m_useFpsLimit)
- unibuffer.format( L"%.2f/%d FPS, ", fps, TheGameEngine->getFramesPerSecondLimit());
+ unibuffer.format( L"%.2f/%d FPS, ", fps, TheFramePacer->getFramesPerSecondLimit());
else
unibuffer.format( L"%.2f FPS, ", fps);
@@ -1673,7 +1673,7 @@ void W3DDisplay::draw( void )
//
//PredictiveLODOptimizerClass::Optimize_LODs( 5000 );
- Bool freezeTime = TheGameEngine->isTimeFrozen() || TheGameEngine->isGameHalted();
+ Bool freezeTime = TheFramePacer->isTimeFrozen() || TheFramePacer->isGameHalted();
/// @todo: I'm assuming the first view is our main 3D view.
W3DView *primaryW3DView=(W3DView *)getFirstView();
@@ -1709,7 +1709,7 @@ void W3DDisplay::draw( void )
}
}
- WW3D::Update_Logic_Frame_Time(TheGameEngine->getLogicTimeStepMilliseconds());
+ WW3D::Update_Logic_Frame_Time(TheFramePacer->getLogicTimeStepMilliseconds());
// TheSuperHackers @info This binds the WW3D update to the logic update.
WW3D::Sync(TheGameLogic->hasUpdated());
diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
index 31e10ef8f5..538368efac 100644
--- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
+++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
@@ -38,7 +38,7 @@
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "Common/BuildAssistant.h"
-#include "Common/GameEngine.h"
+#include "Common/FramePacer.h"
#include "Common/GlobalData.h"
#include "Common/Module.h"
#include "Common/RandomValue.h"
@@ -905,7 +905,7 @@ Bool W3DView::updateCameraMovements()
m_previousLookAtPosition = *getPosition();
// TheSuperHackers @tweak The scripted camera movement is now decoupled from the render update.
// The scripted camera will still move when the time is frozen, but not when the game is halted.
- moveAlongWaypointPath(TheGameEngine->getLogicTimeStepMilliseconds(GameEngine::IgnoreFrozenTime));
+ moveAlongWaypointPath(TheFramePacer->getLogicTimeStepMilliseconds(FramePacer::IgnoreFrozenTime));
didUpdate = true;
}
if (m_doingScriptedCameraLock)
@@ -1206,7 +1206,7 @@ void W3DView::update(void)
// if scrolling, only adjust if we're too close or too far
if (m_scrollAmount.length() < m_scrollAmountCutoff || (m_currentHeightAboveGround < m_minHeightAboveGround) || (TheGlobalData->m_enforceMaxCameraHeight && m_currentHeightAboveGround > m_maxHeightAboveGround))
{
- const Real fpsRatio = (Real)BaseFps / TheGameEngine->getUpdateFps();
+ const Real fpsRatio = (Real)BaseFps / TheFramePacer->getUpdateFps();
const Real zoomAdj = (desiredZoom - m_zoom) * TheGlobalData->m_cameraAdjustSpeed * fpsRatio;
if (fabs(zoomAdj) >= 0.0001f) // only do positive
{
@@ -1218,7 +1218,7 @@ void W3DView::update(void)
else if (!didScriptedMovement)
{
// we're not scrolling; settle toward desired height above ground
- const Real fpsRatio = (Real)BaseFps / TheGameEngine->getUpdateFps();
+ const Real fpsRatio = (Real)BaseFps / TheFramePacer->getUpdateFps();
const Real zoomAdj = (m_zoom - desiredZoom) * TheGlobalData->m_cameraAdjustSpeed * fpsRatio;
if (fabs(zoomAdj) >= 0.0001f)
{
diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp
index 784851861e..2119e618d7 100644
--- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp
+++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp
@@ -49,7 +49,7 @@
#include "mesh.h"
#include "matinfo.h"
-#include "Common/GameEngine.h"
+#include "Common/FramePacer.h"
#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/PerfTimer.h"
@@ -1187,7 +1187,7 @@ void WaterRenderObjClass::enableWaterGrid(Bool state)
void WaterRenderObjClass::update( void )
{
// TheSuperHackers @tweak The water movement time step is now decoupled from the render update.
- const Real timeScale = TheGameEngine->getActualLogicTimeScaleOverFpsRatio();
+ const Real timeScale = TheFramePacer->getActualLogicTimeScaleOverFpsRatio();
{
constexpr const Real MagicOffset = 0.0125f * 33 / 5000; ///< the work of top Munkees; do not question it
diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWaterTracks.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWaterTracks.cpp
index 61f71d2de7..4edfd72bde 100644
--- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWaterTracks.cpp
+++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWaterTracks.cpp
@@ -47,7 +47,7 @@
#include "W3DDevice/GameClient/W3DWaterTracks.h"
#include "GameClient/InGameUI.h"
#include "GameLogic/TerrainLogic.h"
-#include "Common/GameEngine.h"
+#include "Common/FramePacer.h"
#include "Common/GlobalData.h"
#include "Common/UnicodeString.h"
#include "Common/file.h"
@@ -293,7 +293,7 @@ Int WaterTracksObj::update(Int msElapsed)
Int WaterTracksObj::render(DX8VertexBufferClass *vertexBuffer, Int batchStart)
{
// TheSuperHackers @tweak The wave movement time step is now decoupled from the render update.
- m_elapsedMs += TheGameEngine->getLogicTimeStepMilliseconds();
+ m_elapsedMs += TheFramePacer->getLogicTimeStepMilliseconds();
VertexFormatXYZDUV1 *vb;
Vector2 waveTailOrigin,waveFrontOrigin;
diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GameEngine.h b/GeneralsMD/Code/GameEngine/Include/Common/GameEngine.h
index d952b45f58..5ce1fed5a7 100644
--- a/GeneralsMD/Code/GameEngine/Include/Common/GameEngine.h
+++ b/GeneralsMD/Code/GameEngine/Include/Common/GameEngine.h
@@ -55,15 +55,6 @@ class ParticleSystemManager;
class GameEngine : public SubsystemInterface
{
-public:
-
- typedef UnsignedInt LogicTimeQueryFlags;
- enum LogicTimeQueryFlags_ CPP_11(: LogicTimeQueryFlags)
- {
- IgnoreFrozenTime = 1<<0, // Ignore frozen time for the query
- IgnoreHaltedGame = 1<<1, // Ignore halted game for the query
- };
-
public:
GameEngine( void );
@@ -76,24 +67,9 @@ class GameEngine : public SubsystemInterface
virtual void execute( void ); /**< The "main loop" of the game engine.
It will not return until the game exits. */
- virtual void setFramesPerSecondLimit( Int fps ); ///< Set the max render and engine update fps.
- virtual Int getFramesPerSecondLimit( void ); ///< Get the max render and engine update fps.
- Real getUpdateTime(); ///< Get the last engine update delta time in seconds.
- Real getUpdateFps(); ///< Get the last engine update fps.
-
static Bool isTimeFrozen(); ///< Returns true if a script has frozen time.
static Bool isGameHalted(); ///< Returns true if the game is paused or the network is stalling.
- virtual void setLogicTimeScaleFps( Int fps ); ///< Set the logic time scale fps and therefore scale the simulation time. Is capped by the max render fps and does not apply to network matches.
- virtual Int getLogicTimeScaleFps(); ///< Get the raw logic time scale fps value.
- virtual void enableLogicTimeScale( Bool enable ); ///< Enable the logic time scale setup. If disabled, the simulation time scale is bound to the render frame time or network update time.
- virtual Bool isLogicTimeScaleEnabled(); ///< Check whether the logic time scale setup is enabled.
- Int getActualLogicTimeScaleFps(LogicTimeQueryFlags flags = 0); ///< Get the real logic time scale fps, depending on the max render fps, network state and enabled state.
- Real getActualLogicTimeScaleRatio(LogicTimeQueryFlags flags = 0); ///< Get the real logic time scale ratio, depending on the max render fps, network state and enabled state.
- Real getActualLogicTimeScaleOverFpsRatio(LogicTimeQueryFlags flags = 0); ///< Get the real logic time scale over render fps ratio, used to scale down steps in render updates to match logic updates.
- Real getLogicTimeStepSeconds(LogicTimeQueryFlags flags = 0); ///< Get the logic time step in seconds
- Real getLogicTimeStepMilliseconds(LogicTimeQueryFlags flags = 0); ///< Get the logic time step in milliseconds
-
virtual void setQuitting( Bool quitting ); ///< set quitting status
virtual Bool getQuitting(void); ///< is app getting ready to quit.
@@ -124,18 +100,10 @@ class GameEngine : public SubsystemInterface
virtual ParticleSystemManager* createParticleSystemManager( void ) = 0;
virtual AudioManager *createAudioManager( void ) = 0; ///< Factory for Audio Manager
- Int m_maxFPS; ///< Maximum frames per second for rendering
- Int m_logicTimeScaleFPS; ///< Maximum frames per second for logic time scale
-
- Real m_updateTime; ///< Last engine update delta time in seconds
Real m_logicTimeAccumulator; ///< Frame time accumulated towards submitting a new logic frame
Bool m_quitting; ///< true when we need to quit the game
Bool m_isActive; ///< app has OS focus.
- Bool m_enableLogicTimeScale;
- Bool m_isTimeFrozen;
- Bool m_isGameHalted;
-
};
inline void GameEngine::setQuitting( Bool quitting ) { m_quitting = quitting; }
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp
index 02d786b4e5..2b6bcad36d 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp
@@ -32,6 +32,7 @@
#include "Common/AudioAffect.h"
#include "Common/BuildAssistant.h"
#include "Common/CRCDebug.h"
+#include "Common/FramePacer.h"
#include "Common/Radar.h"
#include "Common/PlayerTemplate.h"
#include "Common/Team.h"
@@ -44,7 +45,6 @@
#include "Common/ThingFactory.h"
#include "Common/file.h"
#include "Common/FileSystem.h"
-#include "Common/FrameRateLimit.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/LocalFileSystem.h"
#include "Common/CDManager.h"
@@ -247,19 +247,10 @@ static void updateWindowTitle()
//-------------------------------------------------------------------------------------------------
GameEngine::GameEngine( void )
{
- // Set the time slice size to 1 ms.
- timeBeginPeriod(1);
-
// initialize to non garbage values
- m_maxFPS = BaseFps;
- m_logicTimeScaleFPS = LOGICFRAMES_PER_SECOND;
- m_updateTime = 1.0f / BaseFps; // initialized to something to avoid division by zero on first use
m_logicTimeAccumulator = 0.0f;
m_quitting = FALSE;
m_isActive = FALSE;
- m_enableLogicTimeScale = FALSE;
- m_isTimeFrozen = FALSE;
- m_isGameHalted = FALSE;
_Module.Init(NULL, ApplicationHInstance, NULL);
}
@@ -308,34 +299,6 @@ GameEngine::~GameEngine()
#ifdef PERF_TIMERS
PerfGather::termPerfDump();
#endif
-
- // Restore the previous time slice for Windows.
- timeEndPeriod(1);
-}
-
-//-------------------------------------------------------------------------------------------------
-void GameEngine::setFramesPerSecondLimit( Int fps )
-{
- DEBUG_LOG(("GameEngine::setFramesPerSecondLimit() - setting max fps to %d (TheGlobalData->m_useFpsLimit == %d)", fps, TheGlobalData->m_useFpsLimit));
- m_maxFPS = fps;
-}
-
-//-------------------------------------------------------------------------------------------------
-Int GameEngine::getFramesPerSecondLimit( void )
-{
- return m_maxFPS;
-}
-
-//-------------------------------------------------------------------------------------------------
-Real GameEngine::getUpdateTime()
-{
- return m_updateTime;
-}
-
-//-------------------------------------------------------------------------------------------------
-Real GameEngine::getUpdateFps()
-{
- return 1.0f / m_updateTime;
}
//-------------------------------------------------------------------------------------------------
@@ -377,80 +340,6 @@ Bool GameEngine::isGameHalted()
return false;
}
-//-------------------------------------------------------------------------------------------------
-void GameEngine::setLogicTimeScaleFps( Int fps )
-{
- m_logicTimeScaleFPS = fps;
-}
-
-//-------------------------------------------------------------------------------------------------
-Int GameEngine::getLogicTimeScaleFps()
-{
- return m_logicTimeScaleFPS;
-}
-
-//-------------------------------------------------------------------------------------------------
-void GameEngine::enableLogicTimeScale( Bool enable )
-{
- m_enableLogicTimeScale = enable;
-}
-
-//-------------------------------------------------------------------------------------------------
-Bool GameEngine::isLogicTimeScaleEnabled()
-{
- return m_enableLogicTimeScale;
-}
-
-//-------------------------------------------------------------------------------------------------
-Int GameEngine::getActualLogicTimeScaleFps(LogicTimeQueryFlags flags)
-{
- if (m_isTimeFrozen && (flags & IgnoreFrozenTime) == 0)
- {
- return 0;
- }
-
- if (m_isGameHalted && (flags & IgnoreHaltedGame) == 0)
- {
- return 0;
- }
-
- if (TheNetwork != NULL)
- {
- return TheNetwork->getFrameRate();
- }
-
- if (isLogicTimeScaleEnabled())
- {
- return min(getLogicTimeScaleFps(), getFramesPerSecondLimit());
- }
-
- return getFramesPerSecondLimit();
-}
-
-//-------------------------------------------------------------------------------------------------
-Real GameEngine::getActualLogicTimeScaleRatio(LogicTimeQueryFlags flags)
-{
- return (Real)getActualLogicTimeScaleFps(flags) / LOGICFRAMES_PER_SECONDS_REAL;
-}
-
-//-------------------------------------------------------------------------------------------------
-Real GameEngine::getActualLogicTimeScaleOverFpsRatio(LogicTimeQueryFlags flags)
-{
- // TheSuperHackers @info Clamps ratio to min 1, because the logic
- // frame rate is currently capped by the render frame rate.
- return min(1.0f, (Real)getActualLogicTimeScaleFps(flags) / getUpdateFps());
-}
-
-Real GameEngine::getLogicTimeStepSeconds(LogicTimeQueryFlags flags)
-{
- return SECONDS_PER_LOGICFRAME_REAL * getActualLogicTimeScaleOverFpsRatio(flags);
-}
-
-Real GameEngine::getLogicTimeStepMilliseconds(LogicTimeQueryFlags flags)
-{
- return MSEC_PER_LOGICFRAME_REAL * getActualLogicTimeScaleOverFpsRatio(flags);
-}
-
/** -----------------------------------------------------------------------------------------------
* Initialize the game engine by initializing the GameLogic and GameClient.
*/
@@ -765,7 +654,7 @@ void GameEngine::init()
TheSubsystemList->postProcessLoadAll();
- setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
+ TheFramePacer->setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_musicOn, AudioAffect_Music);
TheAudio->setOn(TheGlobalData->m_audioOn && TheGlobalData->m_soundsOn, AudioAffect_Sound);
@@ -929,8 +818,8 @@ Bool GameEngine::canUpdateGameLogic()
// Must be first.
TheGameLogic->preUpdate();
- m_isTimeFrozen = isTimeFrozen();
- m_isGameHalted = isGameHalted();
+ TheFramePacer->setTimeFrozen(isTimeFrozen());
+ TheFramePacer->setGameHalted(isGameHalted());
if (TheNetwork != NULL)
{
@@ -942,6 +831,7 @@ Bool GameEngine::canUpdateGameLogic()
}
}
+/// -----------------------------------------------------------------------------------------------
Bool GameEngine::canUpdateNetworkGameLogic()
{
DEBUG_ASSERTCRASH(TheNetwork != NULL, ("TheNetwork is NULL"));
@@ -949,7 +839,7 @@ Bool GameEngine::canUpdateNetworkGameLogic()
if (TheNetwork->isFrameDataReady())
{
// Important: The Network is definitely no longer stalling.
- m_isGameHalted = false;
+ TheFramePacer->setGameHalted(false);
return true;
}
@@ -957,11 +847,12 @@ Bool GameEngine::canUpdateNetworkGameLogic()
return false;
}
+/// -----------------------------------------------------------------------------------------------
Bool GameEngine::canUpdateRegularGameLogic()
{
- const Bool enabled = isLogicTimeScaleEnabled();
- const Int logicTimeScaleFps = getLogicTimeScaleFps();
- const Int maxRenderFps = getFramesPerSecondLimit();
+ const Bool enabled = TheFramePacer->isLogicTimeScaleEnabled();
+ const Int logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps();
+ const Int maxRenderFps = TheFramePacer->getFramesPerSecondLimit();
#if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
const Bool useFastMode = TheGlobalData->m_TiVOFastMode;
@@ -979,7 +870,7 @@ Bool GameEngine::canUpdateRegularGameLogic()
// TheSuperHackers @tweak xezon 06/08/2025
// The logic time step is now decoupled from the render update.
const Real targetFrameTime = 1.0f / logicTimeScaleFps;
- m_logicTimeAccumulator += min(m_updateTime, targetFrameTime);
+ m_logicTimeAccumulator += min(TheFramePacer->getUpdateTime(), targetFrameTime);
if (m_logicTimeAccumulator >= targetFrameTime)
{
@@ -1022,8 +913,8 @@ void GameEngine::update( void )
}
const Bool canUpdate = canUpdateGameLogic();
- const Bool canUpdateLogic = canUpdate && !m_isGameHalted && !m_isTimeFrozen;
- const Bool canUpdateScript = canUpdate && !m_isGameHalted;
+ const Bool canUpdateLogic = canUpdate && !TheFramePacer->isGameHalted() && !TheFramePacer->isTimeFrozen();
+ const Bool canUpdateScript = canUpdate && !TheFramePacer->isGameHalted();
if (canUpdateLogic)
{
@@ -1048,8 +939,6 @@ extern HWND ApplicationHWnd;
*/
void GameEngine::execute( void )
{
- FrameRateLimit* frameRateLimit = new FrameRateLimit();
-
#if defined(RTS_DEBUG)
DWORD startTime = timeGetTime() / 1000;
#endif
@@ -1118,34 +1007,7 @@ void GameEngine::execute( void )
}
}
- {
- {
- Bool allowFpsLimit = TheTacticalView->getTimeMultiplier()<=1 && !TheScriptEngine->isTimeFast();
-
- // I'm disabling this in debug because many people need alt-tab capability. If you happen to be
- // doing performance tuning, please just change this on your local system. -MDC
- #if defined(RTS_DEBUG)
- if (allowFpsLimit)
- ::Sleep(1); // give everyone else a tiny time slice.
- #endif
-
-
- #if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
- allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode);
- #else //always allow this cheat key if we're in a replay game.
- allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode && TheGameLogic->isInReplayGame());
- #endif
- {
- // TheSuperHackers @bugfix xezon 05/08/2025 Re-implements the frame rate limiter
- // with higher resolution counters to cap the frame rate more accurately to the desired limit.
- allowFpsLimit &= TheGlobalData->m_useFpsLimit;
- const UnsignedInt maxFps = allowFpsLimit ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue;
- m_updateTime = frameRateLimit->wait(maxFps);
- }
-
- }
- }
-
+ TheFramePacer->update();
}
#ifdef PERF_TIMERS
@@ -1158,8 +1020,6 @@ void GameEngine::execute( void )
#endif
}
-
- delete frameRateLimit;
}
/** -----------------------------------------------------------------------------------------------
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp
index 7ee88428c0..76cfecef5d 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp
@@ -28,6 +28,7 @@
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
+#include "Common/FramePacer.h"
#include "Common/GameEngine.h"
#include "Common/ReplaySimulation.h"
@@ -39,6 +40,7 @@ Int GameMain()
{
int exitcode = 0;
// initialize the game engine using factory function
+ TheFramePacer = new FramePacer();
TheGameEngine = CreateGameEngine();
TheGameEngine->init();
@@ -53,6 +55,8 @@ Int GameMain()
}
// since execute() returned, we are exiting the game
+ delete TheFramePacer;
+ TheFramePacer = NULL;
delete TheGameEngine;
TheGameEngine = NULL;
diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp
index 416da00b5b..ed091e87c8 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp
@@ -36,8 +36,8 @@
#include "Common/BuildAssistant.h"
#include "Common/ClientUpdateModule.h"
#include "Common/DrawModule.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
-#include "Common/GameEngine.h"
#include "Common/GameLOD.h"
#include "Common/GameState.h"
#include "Common/GlobalData.h"
@@ -5542,7 +5542,7 @@ void TintEnvelope::setDecayFrames( UnsignedInt frames )
void TintEnvelope::update(void)
{
// TheSuperHackers @tweak The tint time step is now decoupled from the render update.
- const Real timeScale = TheGameEngine->getActualLogicTimeScaleOverFpsRatio();
+ const Real timeScale = TheFramePacer->getActualLogicTimeScaleOverFpsRatio();
switch ( m_envState )
{
diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
index d247d84086..81f65ec654 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/QuitMenu.cpp
@@ -30,6 +30,7 @@
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
+#include "Common/FramePacer.h"
#include "Common/GameEngine.h"
#include "Common/GameState.h"
#include "Common/MessageStream.h"
@@ -215,7 +216,7 @@ static void restartMissionMenu()
Int rankPointsStartedWith = TheGameLogic->getRankPointsToAddAtGameStart();// must write down before reset
GameDifficulty diff = TheScriptEngine->getGlobalDifficulty();
- Int fps = TheGameEngine->getFramesPerSecondLimit();
+ Int fps = TheFramePacer->getFramesPerSecondLimit();
TheGameLogic->clearGameData(FALSE);
TheGameEngine->setQuitting(FALSE);
diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp
index 70f367d029..4c331e3f88 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp
@@ -32,8 +32,8 @@
#define DEFINE_SHADOW_NAMES
#include "Common/ActionManager.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
-#include "Common/GameEngine.h"
#include "Common/GameType.h"
#include "Common/MessageStream.h"
#include "Common/PerfTimer.h"
@@ -46,7 +46,6 @@
#include "Common/BuildAssistant.h"
#include "Common/Recorder.h"
#include "Common/SpecialPower.h"
-#include "Common/FrameRateLimit.h"
#include "GameClient/Anim2D.h"
#include "GameClient/ControlBar.h"
@@ -1945,7 +1944,7 @@ void InGameUI::update( void )
if (m_cameraRotatingLeft || m_cameraRotatingRight || m_cameraZoomingIn || m_cameraZoomingOut)
{
// TheSuperHackers @tweak The camera rotation and zoom are now decoupled from the render update.
- const Real fpsRatio = (Real)BaseFps / TheGameEngine->getUpdateFps();
+ const Real fpsRatio = (Real)BaseFps / TheFramePacer->getUpdateFps();
const Real rotateAngle = TheGlobalData->m_keyboardCameraRotateSpeed * fpsRatio;
const Real zoomHeight = (Real)View::ZoomHeightPerSecond * fpsRatio;
@@ -6044,7 +6043,7 @@ void InGameUI::drawRenderFps(Int &x, Int &y)
UnsignedInt renderFpsLimit = 0u;
if (TheGlobalData->m_useFpsLimit)
{
- renderFpsLimit = (UnsignedInt)TheGameEngine->getFramesPerSecondLimit();
+ renderFpsLimit = (UnsignedInt)TheFramePacer->getFramesPerSecondLimit();
if (renderFpsLimit == RenderFpsPreset::UncappedFpsValue)
{
renderFpsLimit = 0u;
diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp
index 7dd70033b9..fadbc5f232 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp
@@ -32,7 +32,7 @@
#include "Common/AudioAffect.h"
#include "Common/ActionManager.h"
-#include "Common/FrameRateLimit.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
#include "Common/GameEngine.h"
#include "Common/GameType.h"
@@ -190,10 +190,10 @@ Bool hasThingsInProduction(PlayerType playerType)
bool changeMaxRenderFps(FpsValueChange change)
{
- UnsignedInt maxRenderFps = TheGameEngine->getFramesPerSecondLimit();
+ UnsignedInt maxRenderFps = TheFramePacer->getFramesPerSecondLimit();
maxRenderFps = RenderFpsPreset::changeFpsValue(maxRenderFps, change);
- TheGameEngine->setFramesPerSecondLimit(maxRenderFps);
+ TheFramePacer->setFramesPerSecondLimit(maxRenderFps);
TheWritableGlobalData->m_useFpsLimit = (maxRenderFps != RenderFpsPreset::UncappedFpsValue);
UnicodeString message;
@@ -217,16 +217,16 @@ bool changeLogicTimeScale(FpsValueChange change)
if (TheNetwork != NULL)
return false;
- const UnsignedInt maxRenderFps = TheGameEngine->getFramesPerSecondLimit();
+ const UnsignedInt maxRenderFps = TheFramePacer->getFramesPerSecondLimit();
UnsignedInt maxRenderRemainder = LogicTimeScaleFpsPreset::StepFpsValue;
maxRenderRemainder -= maxRenderFps % LogicTimeScaleFpsPreset::StepFpsValue;
maxRenderRemainder %= LogicTimeScaleFpsPreset::StepFpsValue;
- UnsignedInt logicTimeScaleFps = TheGameEngine->getLogicTimeScaleFps();
+ UnsignedInt logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps();
// Set the value to the max render fps value plus a bit when time scale is
// disabled. This ensures that the time scale does not re-enable with a
// 'surprise' value.
- if (!TheGameEngine->isLogicTimeScaleEnabled())
+ if (!TheFramePacer->isLogicTimeScaleEnabled())
{
logicTimeScaleFps = maxRenderFps + maxRenderRemainder;
}
@@ -237,26 +237,26 @@ bool changeLogicTimeScale(FpsValueChange change)
logicTimeScaleFps = LogicTimeScaleFpsPreset::changeFpsValue(logicTimeScaleFps, change);
// Set value before potentially disabling it.
- if (TheGameEngine->isLogicTimeScaleEnabled())
+ if (TheFramePacer->isLogicTimeScaleEnabled())
{
- TheGameEngine->setLogicTimeScaleFps(logicTimeScaleFps);
+ TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps);
}
- TheGameEngine->enableLogicTimeScale(logicTimeScaleFps < maxRenderFps);
+ TheFramePacer->enableLogicTimeScale(logicTimeScaleFps < maxRenderFps);
// Set value after potentially enabling it.
- if (TheGameEngine->isLogicTimeScaleEnabled())
+ if (TheFramePacer->isLogicTimeScaleEnabled())
{
- TheGameEngine->setLogicTimeScaleFps(logicTimeScaleFps);
+ TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps);
}
- logicTimeScaleFps = TheGameEngine->getLogicTimeScaleFps();
- const UnsignedInt actualLogicTimeScaleFps = TheGameEngine->getActualLogicTimeScaleFps();
- const Real actualLogicTimeScaleRatio = TheGameEngine->getActualLogicTimeScaleRatio();
+ logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps();
+ const UnsignedInt actualLogicTimeScaleFps = TheFramePacer->getActualLogicTimeScaleFps();
+ const Real actualLogicTimeScaleRatio = TheFramePacer->getActualLogicTimeScaleRatio();
UnicodeString message;
- if (TheGameEngine->isLogicTimeScaleEnabled())
+ if (TheFramePacer->isLogicTimeScaleEnabled())
{
message = TheGameText->FETCH_OR_SUBSTITUTE_FORMAT("GUI:SetLogicTimeScaleFps", L"Logic Time Scale FPS is %u (actual %u, ratio %.02f)",
logicTimeScaleFps, actualLogicTimeScaleFps, actualLogicTimeScaleRatio);
diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp
index e753b25869..fc20aa0185 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp
@@ -28,8 +28,8 @@
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
+#include "Common/FramePacer.h"
#include "Common/GameType.h"
-#include "Common/GameEngine.h"
#include "Common/MessageStream.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
@@ -439,7 +439,7 @@ GameMessageDisposition LookAtTranslator::translateGameMessage(const GameMessage
{
// TheSuperHackers @bugfix Mauller 07/06/2025 The camera scrolling is now decoupled from the render update.
- const Real fpsRatio = (Real)BaseFps / TheGameEngine->getUpdateFps();
+ const Real fpsRatio = (Real)BaseFps / TheFramePacer->getUpdateFps();
switch (m_scrollType)
{
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp
index 710b49eeee..dbe9e4a5e8 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptActions.cpp
@@ -31,8 +31,8 @@
#include "Common/AudioAffect.h"
#include "Common/AudioHandleSpecialValues.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
-#include "Common/GameEngine.h"
#include "Common/MapObject.h" // For MAP_XY_FACTOR
#include "Common/PartitionSolver.h"
#include "Common/Player.h"
@@ -7129,11 +7129,11 @@ void ScriptActions::executeAction( ScriptAction *pAction )
case ScriptAction::SET_FPS_LIMIT:
if (!pAction->getParameter(0)->getInt())
{
- TheGameEngine->setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
+ TheFramePacer->setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
}
else
{
- TheGameEngine->setFramesPerSecondLimit(pAction->getParameter(0)->getInt());
+ TheFramePacer->setFramesPerSecondLimit(pAction->getParameter(0)->getInt());
}
// Setting the fps limit doesn't do much good if we don't use it. jba.
TheWritableGlobalData->m_useFpsLimit = true;
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp
index 364cadf7ec..ee5d5fd1f5 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp
@@ -31,7 +31,7 @@
#include "Common/DataChunk.h"
#include "Common/file.h"
#include "Common/FileSystem.h"
-#include "Common/GameEngine.h"
+#include "Common/FramePacer.h"
#include "Common/GameState.h"
#include "Common/LatchRestore.h"
#include "Common/MessageStream.h"
@@ -5265,8 +5265,8 @@ void ScriptEngine::init( void )
void ScriptEngine::reset( void )
{
// setting FPS limit in case a script had changed it
- if (TheGameEngine && TheGlobalData)
- TheGameEngine->setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
+ if (TheFramePacer && TheGlobalData)
+ TheFramePacer->setFramesPerSecondLimit(TheGlobalData->m_framesPerSecondLimit);
if (TheScriptActions) {
TheScriptActions->reset();
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
index d7f82871e4..6e9ec822f2 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
@@ -33,6 +33,7 @@
#include "Common/AudioHandleSpecialValues.h"
#include "Common/BuildAssistant.h"
#include "Common/CRCDebug.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
#include "Common/GameEngine.h"
#include "Common/GameLOD.h"
@@ -4271,8 +4272,8 @@ void GameLogic::setGamePausedInFrame( UnsignedInt frame, Bool disableLogicTimeSc
if (disableLogicTimeScale)
{
- m_logicTimeScaleEnabledMemory = TheGameEngine->isLogicTimeScaleEnabled();
- TheGameEngine->enableLogicTimeScale(FALSE);
+ m_logicTimeScaleEnabledMemory = TheFramePacer->isLogicTimeScaleEnabled();
+ TheFramePacer->enableLogicTimeScale(FALSE);
}
}
}
@@ -4311,7 +4312,7 @@ void GameLogic::pauseGameLogic(Bool paused)
if (!paused && m_logicTimeScaleEnabledMemory)
{
m_logicTimeScaleEnabledMemory = FALSE;
- TheGameEngine->enableLogicTimeScale(TRUE);
+ TheFramePacer->enableLogicTimeScale(TRUE);
}
}
diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
index 889b7a3ca5..b680f82c4f 100644
--- a/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
@@ -31,6 +31,7 @@
#include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
#include "Common/CRCDebug.h"
+#include "Common/FramePacer.h"
#include "Common/GameAudio.h"
#include "Common/GameEngine.h"
#include "Common/GlobalData.h"
@@ -436,7 +437,7 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )
if (maxFPS < 1 || maxFPS > 1000)
maxFPS = TheGlobalData->m_framesPerSecondLimit;
DEBUG_LOG(("Setting max FPS limit to %d FPS", maxFPS));
- TheGameEngine->setFramesPerSecondLimit(maxFPS);
+ TheFramePacer->setFramesPerSecondLimit(maxFPS);
TheWritableGlobalData->m_useFpsLimit = true;
}
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp
index b2949a68ca..352b9d97c7 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp
@@ -41,8 +41,8 @@ static void drawFramerateBar(void);
#include
// USER INCLUDES //////////////////////////////////////////////////////////////
+#include "Common/FramePacer.h"
#include "Common/ThingFactory.h"
-#include "Common/GameEngine.h"
#include "Common/GlobalData.h"
#include "Common/PerfTimer.h"
#include "Common/FileSystem.h"
@@ -1025,7 +1025,7 @@ void W3DDisplay::gatherDebugStats( void )
Int LOD = TheGlobalData->m_terrainLOD;
//unibuffer.format( L"FPS: %.2f, %.2fms mapLOD=%d [cumu FPS=%.2f] draws: %.2f sort: %.2f", fps, ms, LOD, cumuFPS, drawsPerFrame,sortPolysPerFrame);
if (TheGlobalData->m_useFpsLimit)
- unibuffer.format( L"%.2f/%d FPS, ", fps, TheGameEngine->getFramesPerSecondLimit());
+ unibuffer.format( L"%.2f/%d FPS, ", fps, TheFramePacer->getFramesPerSecondLimit());
else
unibuffer.format( L"%.2f FPS, ", fps);
@@ -1754,7 +1754,7 @@ void W3DDisplay::draw( void )
//
//PredictiveLODOptimizerClass::Optimize_LODs( 5000 );
- Bool freezeTime = TheGameEngine->isTimeFrozen() || TheGameEngine->isGameHalted();
+ Bool freezeTime = TheFramePacer->isTimeFrozen() || TheFramePacer->isGameHalted();
/// @todo: I'm assuming the first view is our main 3D view.
W3DView *primaryW3DView=(W3DView *)getFirstView();
@@ -1790,7 +1790,7 @@ void W3DDisplay::draw( void )
}
}
- WW3D::Update_Logic_Frame_Time(TheGameEngine->getLogicTimeStepMilliseconds());
+ WW3D::Update_Logic_Frame_Time(TheFramePacer->getLogicTimeStepMilliseconds());
// TheSuperHackers @info This binds the WW3D update to the logic update.
WW3D::Sync(TheGameLogic->hasUpdated());
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
index a8b8d3474e..3cf288e5da 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
@@ -38,7 +38,7 @@
// USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
#include "Common/BuildAssistant.h"
-#include "Common/GameEngine.h"
+#include "Common/FramePacer.h"
#include "Common/GlobalData.h"
#include "Common/Module.h"
#include "Common/RandomValue.h"
@@ -399,7 +399,7 @@ void W3DView::buildCameraTransform( Matrix3D *transform )
//WST 11/12/2002 New camera shaker system
// TheSuperHackers @tweak The camera shaker is now decoupled from the render update.
- CameraShakerSystem.Timestep(TheGameEngine->getLogicTimeStepMilliseconds());
+ CameraShakerSystem.Timestep(TheFramePacer->getLogicTimeStepMilliseconds());
CameraShakerSystem.Update_Camera_Shaker(sourcePos, &m_shakerAngles);
transform->Rotate_X(m_shakerAngles.X);
transform->Rotate_Y(m_shakerAngles.Y);
@@ -1046,7 +1046,7 @@ Bool W3DView::updateCameraMovements()
m_previousLookAtPosition = *getPosition();
// TheSuperHackers @tweak The scripted camera movement is now decoupled from the render update.
// The scripted camera will still move when the time is frozen, but not when the game is halted.
- moveAlongWaypointPath(TheGameEngine->getLogicTimeStepMilliseconds(GameEngine::IgnoreFrozenTime));
+ moveAlongWaypointPath(TheFramePacer->getLogicTimeStepMilliseconds(FramePacer::IgnoreFrozenTime));
didUpdate = true;
}
if (m_doingScriptedCameraLock)
@@ -1355,7 +1355,7 @@ void W3DView::update(void)
// if scrolling, only adjust if we're too close or too far
if (m_scrollAmount.length() < m_scrollAmountCutoff || (m_currentHeightAboveGround < m_minHeightAboveGround) || (TheGlobalData->m_enforceMaxCameraHeight && m_currentHeightAboveGround > m_maxHeightAboveGround))
{
- const Real fpsRatio = (Real)BaseFps / TheGameEngine->getUpdateFps();
+ const Real fpsRatio = (Real)BaseFps / TheFramePacer->getUpdateFps();
const Real zoomAdj = (desiredZoom - m_zoom) * TheGlobalData->m_cameraAdjustSpeed * fpsRatio;
if (fabs(zoomAdj) >= 0.0001f) // only do positive
{
@@ -1367,7 +1367,7 @@ void W3DView::update(void)
else if (!didScriptedMovement)
{
// we're not scrolling; settle toward desired height above ground
- const Real fpsRatio = (Real)BaseFps / TheGameEngine->getUpdateFps();
+ const Real fpsRatio = (Real)BaseFps / TheFramePacer->getUpdateFps();
const Real zoomAdj = (m_zoom - desiredZoom) * TheGlobalData->m_cameraAdjustSpeed * fpsRatio;
if (fabs(zoomAdj) >= 0.0001f)
{
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp
index e9599a7a25..9ef4271dcc 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp
@@ -49,7 +49,7 @@
#include "mesh.h"
#include "matinfo.h"
-#include "Common/GameEngine.h"
+#include "Common/FramePacer.h"
#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/PerfTimer.h"
@@ -1210,7 +1210,7 @@ void WaterRenderObjClass::enableWaterGrid(Bool state)
void WaterRenderObjClass::update( void )
{
// TheSuperHackers @tweak The water movement time step is now decoupled from the render update.
- const Real timeScale = TheGameEngine->getActualLogicTimeScaleOverFpsRatio();
+ const Real timeScale = TheFramePacer->getActualLogicTimeScaleOverFpsRatio();
{
constexpr const Real MagicOffset = 0.0125f * 33 / 5000; ///< the work of top Munkees; do not question it
diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWaterTracks.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWaterTracks.cpp
index 8169dbb7f7..98c2556fbf 100644
--- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWaterTracks.cpp
+++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWaterTracks.cpp
@@ -50,7 +50,7 @@
#include "GameClient/InGameUI.h"
#include "GameClient/Water.h"
#include "GameLogic/TerrainLogic.h"
-#include "Common/GameEngine.h"
+#include "Common/FramePacer.h"
#include "Common/GlobalData.h"
#include "Common/UnicodeString.h"
#include "Common/file.h"
@@ -296,7 +296,7 @@ Int WaterTracksObj::update(Int msElapsed)
Int WaterTracksObj::render(DX8VertexBufferClass *vertexBuffer, Int batchStart)
{
// TheSuperHackers @tweak The wave movement time step is now decoupled from the render update.
- m_elapsedMs += TheGameEngine->getLogicTimeStepMilliseconds();
+ m_elapsedMs += TheFramePacer->getLogicTimeStepMilliseconds();
VertexFormatXYZDUV1 *vb;
Vector2 waveTailOrigin,waveFrontOrigin;
From 00b8d399f30e0087f6ef8219bbda4ef71447ab0e Mon Sep 17 00:00:00 2001
From: xezon <4720891+xezon@users.noreply.github.com>
Date: Tue, 14 Oct 2025 18:33:48 +0200
Subject: [PATCH 2/2] bugfix(fps): Fix crashes and ww3d updates in tools
(#1688)
---
Core/GameEngine/Include/Common/FramePacer.h | 17 +++--
.../Include/Common/FrameRateLimit.h | 4 +-
Core/GameEngine/Source/Common/FramePacer.cpp | 65 +++++++++++++------
.../Source/Common/FrameRateLimit.cpp | 14 ++--
.../Libraries/Source/WWVegas/WWLib/WWCommon.h | 3 +-
Core/Tools/W3DView/GraphicView.cpp | 16 +++--
.../GameEngine/Source/Common/GameMain.cpp | 1 +
.../Libraries/Source/WWVegas/WW3D2/ww3d.h | 1 +
.../Code/Tools/GUIEdit/Source/EditWindow.cpp | 10 ++-
.../Code/Tools/GUIEdit/Source/WinMain.cpp | 9 ++-
.../Tools/WorldBuilder/src/WorldBuilder.cpp | 6 ++
.../Code/Tools/WorldBuilder/src/wbview3d.cpp | 10 ++-
.../GameEngine/Source/Common/GameMain.cpp | 1 +
.../Libraries/Source/WWVegas/WW3D2/ww3d.h | 1 +
.../Code/Tools/GUIEdit/Source/EditWindow.cpp | 10 ++-
.../Code/Tools/GUIEdit/Source/WinMain.cpp | 9 ++-
.../Tools/WorldBuilder/src/WorldBuilder.cpp | 6 ++
.../Code/Tools/WorldBuilder/src/wbview3d.cpp | 10 ++-
18 files changed, 136 insertions(+), 57 deletions(-)
diff --git a/Core/GameEngine/Include/Common/FramePacer.h b/Core/GameEngine/Include/Common/FramePacer.h
index 95c7652736..66955c4585 100644
--- a/Core/GameEngine/Include/Common/FramePacer.h
+++ b/Core/GameEngine/Include/Common/FramePacer.h
@@ -20,6 +20,9 @@
#include "Common/FrameRateLimit.h"
+// TheSuperHackers @todo Use unsigned integers for fps values
+// TheSuperHackers @todo Consolidate the GlobalData::m_useFpsLimit and FramePacer::m_enableFpsLimit
+// TheSuperHackers @todo Implement new fast forward in here
class FramePacer
{
public:
@@ -36,8 +39,13 @@ class FramePacer
void update(); ///< Signal that the app/render update is done and wait for the fps limit if applicable.
- void setFramesPerSecondLimit( Int fps ); ///< Set the max update fps.
- Int getFramesPerSecondLimit() const; ///< Get the max update fps.
+ void setFramesPerSecondLimit( Int fps ); ///< Set the update fps limit.
+ Int getFramesPerSecondLimit() const; ///< Get the update fps limit.
+ void enableFramesPerSecondLimit( Bool enable ); ///< Enable or disable the update fps limit.
+ Bool isFramesPerSecondLimitEnabled() const; ///< Returns whether the fps limit is enabled here.
+ Bool isActualFramesPerSecondLimitEnabled() const; ///< Returns whether the fps limit is actually enabled when considering all game settings and setups.
+ Int getActualFramesPerSecondLimit() const; // Get the actual update fps limit.
+
Real getUpdateTime() const; ///< Get the last update delta time in seconds.
Real getUpdateFps() const; ///< Get the last update fps.
@@ -47,8 +55,8 @@ class FramePacer
Bool isGameHalted() const;
void setLogicTimeScaleFps( Int fps ); ///< Set the logic time scale fps and therefore scale the simulation time. Is capped by the max render fps and does not apply to network matches.
- Int getLogicTimeScaleFps() const; ///< Get the raw logic time scale fps value.
- void enableLogicTimeScale( Bool enable ); ///< Enable the logic time scale setup. If disabled, the simulation time scale is bound to the render frame time or network update time.
+ Int getLogicTimeScaleFps() const; ///< Get the raw logic time scale fps value.
+ void enableLogicTimeScale( Bool enable ); ///< Enable or disable the logic time scale setup. If disabled, the simulation time scale is bound to the render frame time or network update time.
Bool isLogicTimeScaleEnabled() const; ///< Check whether the logic time scale setup is enabled.
Int getActualLogicTimeScaleFps(LogicTimeQueryFlags flags = 0) const; ///< Get the real logic time scale fps, depending on the max render fps, network state and enabled state.
Real getActualLogicTimeScaleRatio(LogicTimeQueryFlags flags = 0) const; ///< Get the real logic time scale ratio, depending on the max render fps, network state and enabled state.
@@ -65,6 +73,7 @@ class FramePacer
Real m_updateTime; ///< Last update delta time in seconds
+ Bool m_enableFpsLimit;
Bool m_enableLogicTimeScale;
Bool m_isTimeFrozen;
Bool m_isGameHalted;
diff --git a/Core/GameEngine/Include/Common/FrameRateLimit.h b/Core/GameEngine/Include/Common/FrameRateLimit.h
index 3e3c80f0ea..5bb2b5fd3a 100644
--- a/Core/GameEngine/Include/Common/FrameRateLimit.h
+++ b/Core/GameEngine/Include/Common/FrameRateLimit.h
@@ -29,8 +29,8 @@ class FrameRateLimit
Real wait(UnsignedInt maxFps);
private:
- LARGE_INTEGER m_freq;
- LARGE_INTEGER m_start;
+ Int64 m_freq;
+ Int64 m_start;
};
diff --git a/Core/GameEngine/Source/Common/FramePacer.cpp b/Core/GameEngine/Source/Common/FramePacer.cpp
index 8bbc9e914f..0f23389900 100644
--- a/Core/GameEngine/Source/Common/FramePacer.cpp
+++ b/Core/GameEngine/Source/Common/FramePacer.cpp
@@ -37,7 +37,8 @@ FramePacer::FramePacer()
m_maxFPS = BaseFps;
m_logicTimeScaleFPS = LOGICFRAMES_PER_SECOND;
- m_updateTime = 1.0f / BaseFps; // initialized to something to avoid division by zero on first use
+ m_updateTime = 1.0f / (Real)BaseFps; // initialized to something to avoid division by zero on first use
+ m_enableFpsLimit = FALSE;
m_enableLogicTimeScale = FALSE;
m_isTimeFrozen = FALSE;
m_isGameHalted = FALSE;
@@ -51,25 +52,9 @@ FramePacer::~FramePacer()
void FramePacer::update()
{
- Bool allowFpsLimit = TheTacticalView->getTimeMultiplier()<=1 && !TheScriptEngine->isTimeFast();
-
- // I'm disabling this in debug because many people need alt-tab capability. If you happen to be
- // doing performance tuning, please just change this on your local system. -MDC
-#if defined(RTS_DEBUG)
- if (allowFpsLimit)
- ::Sleep(1); // give everyone else a tiny time slice.
-#endif
-
-#if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
- allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode);
-#else //always allow this cheat key if we're in a replay game.
- allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode && TheGameLogic->isInReplayGame());
-#endif
-
// TheSuperHackers @bugfix xezon 05/08/2025 Re-implements the frame rate limiter
// with higher resolution counters to cap the frame rate more accurately to the desired limit.
- allowFpsLimit &= TheGlobalData->m_useFpsLimit;
- const UnsignedInt maxFps = allowFpsLimit ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue;
+ const UnsignedInt maxFps = getActualFramesPerSecondLimit();// allowFpsLimit ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue;
m_updateTime = m_frameRateLimit.wait(maxFps);
}
@@ -84,6 +69,45 @@ Int FramePacer::getFramesPerSecondLimit() const
return m_maxFPS;
}
+void FramePacer::enableFramesPerSecondLimit( Bool enable )
+{
+ m_enableFpsLimit = enable;
+}
+
+Bool FramePacer::isFramesPerSecondLimitEnabled() const
+{
+ return m_enableFpsLimit;
+}
+
+Bool FramePacer::isActualFramesPerSecondLimitEnabled() const
+{
+ Bool allowFpsLimit = true;
+
+ if (TheTacticalView != NULL)
+ {
+ allowFpsLimit &= TheTacticalView->getTimeMultiplier()<=1 && !TheScriptEngine->isTimeFast();
+ }
+
+ if (TheGameLogic != NULL)
+ {
+#if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
+ allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode);
+#else //always allow this cheat key if we're in a replay game.
+ allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode && TheGameLogic->isInReplayGame());
+#endif
+ }
+
+ allowFpsLimit &= TheGlobalData->m_useFpsLimit;
+ allowFpsLimit &= isFramesPerSecondLimitEnabled();
+
+ return allowFpsLimit;
+}
+
+Int FramePacer::getActualFramesPerSecondLimit() const
+{
+ return isActualFramesPerSecondLimitEnabled() ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue;
+}
+
Real FramePacer::getUpdateTime() const
{
return m_updateTime;
@@ -153,10 +177,11 @@ Int FramePacer::getActualLogicTimeScaleFps(LogicTimeQueryFlags flags) const
if (isLogicTimeScaleEnabled())
{
- return min(getLogicTimeScaleFps(), getFramesPerSecondLimit());
+ return getLogicTimeScaleFps();
}
- return getFramesPerSecondLimit();
+ // Returns uncapped value to align with the render update as per the original game behavior.
+ return RenderFpsPreset::UncappedFpsValue;
}
Real FramePacer::getActualLogicTimeScaleRatio(LogicTimeQueryFlags flags) const
diff --git a/Core/GameEngine/Source/Common/FrameRateLimit.cpp b/Core/GameEngine/Source/Common/FrameRateLimit.cpp
index 5144f05403..b8c7f1ff7a 100644
--- a/Core/GameEngine/Source/Common/FrameRateLimit.cpp
+++ b/Core/GameEngine/Source/Common/FrameRateLimit.cpp
@@ -22,15 +22,19 @@
FrameRateLimit::FrameRateLimit()
{
- QueryPerformanceFrequency(&m_freq);
- QueryPerformanceCounter(&m_start);
+ LARGE_INTEGER freq;
+ LARGE_INTEGER start;
+ QueryPerformanceFrequency(&freq);
+ QueryPerformanceCounter(&start);
+ m_freq = freq.QuadPart;
+ m_start = start.QuadPart;
}
Real FrameRateLimit::wait(UnsignedInt maxFps)
{
LARGE_INTEGER tick;
QueryPerformanceCounter(&tick);
- double elapsedSeconds = static_cast(tick.QuadPart - m_start.QuadPart) / m_freq.QuadPart;
+ double elapsedSeconds = static_cast(tick.QuadPart - m_start) / m_freq;
const double targetSeconds = 1.0 / maxFps;
const double sleepSeconds = targetSeconds - elapsedSeconds - 0.002; // leave ~2ms for spin wait
@@ -45,11 +49,11 @@ Real FrameRateLimit::wait(UnsignedInt maxFps)
do
{
QueryPerformanceCounter(&tick);
- elapsedSeconds = static_cast(tick.QuadPart - m_start.QuadPart) / m_freq.QuadPart;
+ elapsedSeconds = static_cast(tick.QuadPart - m_start) / m_freq;
}
while (elapsedSeconds < targetSeconds);
- m_start = tick;
+ m_start = tick.QuadPart;
return (Real)elapsedSeconds;
}
diff --git a/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h b/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h
index 77c231a6d5..1f59b88449 100644
--- a/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h
+++ b/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h
@@ -31,7 +31,8 @@ enum
{
// TheSuperHackers @info The original WWSync was 33 ms, ~30 fps, integer.
// Changing this will require tweaking all Drawable code that concerns the ww3d time step, including locomotion physics.
- WWSyncPerSecond = 30
+ WWSyncPerSecond = 30,
+ WWSyncMilliseconds = 1000 / WWSyncPerSecond,
};
#if defined(_MSC_VER) && _MSC_VER < 1300
diff --git a/Core/Tools/W3DView/GraphicView.cpp b/Core/Tools/W3DView/GraphicView.cpp
index 185c4465c6..bb31526a1d 100644
--- a/Core/Tools/W3DView/GraphicView.cpp
+++ b/Core/Tools/W3DView/GraphicView.cpp
@@ -436,7 +436,8 @@ CGraphicView::RepaintView
// Simple check to avoid re-entrance
//
static bool _already_painting = false;
- if (_already_painting) return;
+ if (_already_painting)
+ return;
_already_painting = true;
//
@@ -452,10 +453,15 @@ CGraphicView::RepaintView
m_dwLastFrameUpdate = cur_ticks;
// Update the W3D frame times according to our elapsed tick count
- if (ticks_to_use == 0) {
- WW3D::Sync (WW3D::Get_Sync_Time() + (ticks_elapsed * m_animationSpeed));
- } else {
- WW3D::Sync (WW3D::Get_Sync_Time() + ticks_to_use);
+ if (ticks_to_use == 0)
+ {
+ WW3D::Update_Logic_Frame_Time(ticks_elapsed * m_animationSpeed);
+ WW3D::Sync(WW3D::Get_Fractional_Sync_Milliseconds() >= WWSyncMilliseconds);
+ }
+ else
+ {
+ WW3D::Update_Logic_Frame_Time(ticks_to_use);
+ WW3D::Sync(true);
}
// Do we need to update the current animation?
diff --git a/Generals/Code/GameEngine/Source/Common/GameMain.cpp b/Generals/Code/GameEngine/Source/Common/GameMain.cpp
index 6255d6384c..870829597b 100644
--- a/Generals/Code/GameEngine/Source/Common/GameMain.cpp
+++ b/Generals/Code/GameEngine/Source/Common/GameMain.cpp
@@ -41,6 +41,7 @@ Int GameMain()
int exitcode = 0;
// initialize the game engine using factory function
TheFramePacer = new FramePacer();
+ TheFramePacer->enableFramesPerSecondLimit(TRUE);
TheGameEngine = CreateGameEngine();
TheGameEngine->init();
diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/ww3d.h b/Generals/Code/Libraries/Source/WWVegas/WW3D2/ww3d.h
index 4cb8c5bf9b..47069d614c 100644
--- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/ww3d.h
+++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/ww3d.h
@@ -173,6 +173,7 @@ class WW3D
static void Sync(bool step);
static unsigned int Get_Sync_Time(void) { return SyncTime; }
static unsigned int Get_Sync_Frame_Time(void) { return SyncTime - PreviousSyncTime; }
+ static unsigned int Get_Fractional_Sync_Milliseconds() { return FractionalSyncMs; }
static float Get_Logic_Frame_Time_Milliseconds() { return LogicFrameTimeMs; }
static float Get_Logic_Frame_Time_Seconds() { return LogicFrameTimeMs * 0.001f; }
static unsigned int Get_Frame_Count(void) { return FrameCount; }
diff --git a/Generals/Code/Tools/GUIEdit/Source/EditWindow.cpp b/Generals/Code/Tools/GUIEdit/Source/EditWindow.cpp
index 3b8c95eb68..08eec4d1a3 100644
--- a/Generals/Code/Tools/GUIEdit/Source/EditWindow.cpp
+++ b/Generals/Code/Tools/GUIEdit/Source/EditWindow.cpp
@@ -49,6 +49,7 @@
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "Common/Debug.h"
+#include "Common/FramePacer.h"
#include "GameClient/Display.h"
#include "GameClient/GameWindowManager.h"
#include "W3DDevice/GameClient/W3DFileSystem.h"
@@ -1456,13 +1457,9 @@ void EditWindow::drawGrid( void )
//=============================================================================
void EditWindow::draw( void )
{
- static UnsignedInt syncTime = 0;
-
// allow W3D to update its internals
- WW3D::Sync( syncTime );
-
- // for now, use constant time steps to avoid animations running independent of framerate
- syncTime += 50;
+ WW3D::Update_Logic_Frame_Time(TheFramePacer->getLogicTimeStepMilliseconds());
+ WW3D::Sync(WW3D::Get_Fractional_Sync_Milliseconds() >= WWSyncMilliseconds);
// start render block
WW3D::Begin_Render( true, true, Vector3( m_backgroundColor.red,
@@ -1479,6 +1476,7 @@ void EditWindow::draw( void )
// render is all done!
WW3D::End_Render();
+ TheFramePacer->update();
}
// EditWindow::setSize ========================================================
diff --git a/Generals/Code/Tools/GUIEdit/Source/WinMain.cpp b/Generals/Code/Tools/GUIEdit/Source/WinMain.cpp
index 1d692600e3..9cfb49d204 100644
--- a/Generals/Code/Tools/GUIEdit/Source/WinMain.cpp
+++ b/Generals/Code/Tools/GUIEdit/Source/WinMain.cpp
@@ -50,8 +50,8 @@
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "Common/Debug.h"
+#include "Common/FramePacer.h"
#include "Common/GameMemory.h"
-#include "Common/GameEngine.h"
#include "GameClient/GameWindowManager.h"
#include "Win32Device/GameClient/Win32Mouse.h"
#include "resource.h"
@@ -224,6 +224,8 @@ Int APIENTRY WinMain(HINSTANCE hInstance,
return FALSE;
TheEditor->init();
+ TheFramePacer = new FramePacer();
+
//
// see if we have any messages to process, a NULL window handle tells the
// OS to look at the main window associated with the calling thread, us!
@@ -262,7 +264,7 @@ Int APIENTRY WinMain(HINSTANCE hInstance,
else
{
- // udpate our universe
+ // update our universe
TheEditor->update();
Sleep(1);
@@ -271,6 +273,9 @@ Int APIENTRY WinMain(HINSTANCE hInstance,
}
// shutdown GUIEdit data
+ delete TheFramePacer;
+ TheFramePacer = NULL;
+
delete TheEditor;
TheEditor = NULL;
diff --git a/Generals/Code/Tools/WorldBuilder/src/WorldBuilder.cpp b/Generals/Code/Tools/WorldBuilder/src/WorldBuilder.cpp
index bf4afd9aa6..9d440098c8 100644
--- a/Generals/Code/Tools/WorldBuilder/src/WorldBuilder.cpp
+++ b/Generals/Code/Tools/WorldBuilder/src/WorldBuilder.cpp
@@ -33,6 +33,7 @@
//#include
#include "W3DDevice/GameClient/W3DFileSystem.h"
+#include "Common/FramePacer.h"
#include "Common/GlobalData.h"
#include "WHeightMapEdit.h"
//#include "Common/GameFileSystem.h"
@@ -338,6 +339,8 @@ BOOL CWorldBuilderApp::InitInstance()
initSubsystem(TheWritableGlobalData, new GlobalData(), "Data\\INI\\Default\\GameData", "Data\\INI\\GameData");
+ TheFramePacer = new FramePacer();
+
#if defined(RTS_DEBUG)
ini.loadFileDirectory( AsciiString( "Data\\INI\\GameDataDebug" ), INI_LOAD_MULTIFILE, NULL );
#endif
@@ -634,6 +637,9 @@ int CWorldBuilderApp::ExitInstance()
WorldHeightMapEdit::shutdown();
+ delete TheFramePacer;
+ TheFramePacer = NULL;
+
delete TheFileSystem;
TheFileSystem = NULL;
TextureLoadTaskClass::shutdown();
diff --git a/Generals/Code/Tools/WorldBuilder/src/wbview3d.cpp b/Generals/Code/Tools/WorldBuilder/src/wbview3d.cpp
index 2b07796448..1bb3ce444e 100644
--- a/Generals/Code/Tools/WorldBuilder/src/wbview3d.cpp
+++ b/Generals/Code/Tools/WorldBuilder/src/wbview3d.cpp
@@ -31,7 +31,6 @@
#include "W3DDevice/GameClient/Module/W3DModelDraw.h"
#include "agg_def.h"
#include "part_ldr.h"
-#include "rendobj.h"
#include "hanim.h"
#include "dx8wrapper.h"
#include "dx8indexbuffer.h"
@@ -78,6 +77,7 @@
#include "WorldBuilder.h"
#include "wbview3d.h"
#include "Common/Debug.h"
+#include "Common/FramePacer.h"
#include "Common/ThingFactory.h"
#include "GameClient/Water.h"
#include "Common/WellKnownKeys.h"
@@ -1981,12 +1981,18 @@ void WbView3d::redraw(void)
m_showPolygonTriggers || PolygonTool::isActive());
}
- WW3D::Sync( GetTickCount() );
+ WW3D::Update_Logic_Frame_Time(TheFramePacer->getLogicTimeStepMilliseconds());
+ WW3D::Sync(WW3D::Get_Fractional_Sync_Milliseconds() >= WWSyncMilliseconds);
+
m_buildRedMultiplier += (GetTickCount()-m_time)/500.0f;
if (m_buildRedMultiplier>4.0f || m_buildRedMultiplier<0) {
m_buildRedMultiplier = 0;
}
+
render();
+
+ TheFramePacer->update();
+
m_time = ::GetTickCount();
}
diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp
index 76cfecef5d..7c72fc4e1e 100644
--- a/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp
+++ b/GeneralsMD/Code/GameEngine/Source/Common/GameMain.cpp
@@ -41,6 +41,7 @@ Int GameMain()
int exitcode = 0;
// initialize the game engine using factory function
TheFramePacer = new FramePacer();
+ TheFramePacer->enableFramesPerSecondLimit(TRUE);
TheGameEngine = CreateGameEngine();
TheGameEngine->init();
diff --git a/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/ww3d.h b/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/ww3d.h
index 0057dbf989..5e8c5a9fc2 100644
--- a/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/ww3d.h
+++ b/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/ww3d.h
@@ -173,6 +173,7 @@ class WW3D
static void Sync(bool step);
static unsigned int Get_Sync_Time(void) { return SyncTime; }
static unsigned int Get_Sync_Frame_Time(void) { return SyncTime - PreviousSyncTime; }
+ static unsigned int Get_Fractional_Sync_Milliseconds() { return FractionalSyncMs; }
static float Get_Logic_Frame_Time_Milliseconds() { return LogicFrameTimeMs; }
static float Get_Logic_Frame_Time_Seconds() { return LogicFrameTimeMs * 0.001f; }
static unsigned int Get_Frame_Count(void) { return FrameCount; }
diff --git a/GeneralsMD/Code/Tools/GUIEdit/Source/EditWindow.cpp b/GeneralsMD/Code/Tools/GUIEdit/Source/EditWindow.cpp
index b53ed8a602..9c577027cc 100644
--- a/GeneralsMD/Code/Tools/GUIEdit/Source/EditWindow.cpp
+++ b/GeneralsMD/Code/Tools/GUIEdit/Source/EditWindow.cpp
@@ -49,6 +49,7 @@
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "Common/Debug.h"
+#include "Common/FramePacer.h"
#include "GameClient/Display.h"
#include "GameClient/GameWindowManager.h"
#include "W3DDevice/GameClient/W3DFileSystem.h"
@@ -1470,13 +1471,9 @@ void EditWindow::drawGrid( void )
//=============================================================================
void EditWindow::draw( void )
{
- static UnsignedInt syncTime = 0;
-
// allow W3D to update its internals
- WW3D::Sync( syncTime );
-
- // for now, use constant time steps to avoid animations running independent of framerate
- syncTime += 50;
+ WW3D::Update_Logic_Frame_Time(TheFramePacer->getLogicTimeStepMilliseconds());
+ WW3D::Sync(WW3D::Get_Fractional_Sync_Milliseconds() >= WWSyncMilliseconds);
// start render block
WW3D::Begin_Render( true, true, Vector3( m_backgroundColor.red,
@@ -1493,6 +1490,7 @@ void EditWindow::draw( void )
// render is all done!
WW3D::End_Render();
+ TheFramePacer->update();
}
// EditWindow::setSize ========================================================
diff --git a/GeneralsMD/Code/Tools/GUIEdit/Source/WinMain.cpp b/GeneralsMD/Code/Tools/GUIEdit/Source/WinMain.cpp
index 4a1a529a61..b6b4375057 100644
--- a/GeneralsMD/Code/Tools/GUIEdit/Source/WinMain.cpp
+++ b/GeneralsMD/Code/Tools/GUIEdit/Source/WinMain.cpp
@@ -50,8 +50,8 @@
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "Common/Debug.h"
+#include "Common/FramePacer.h"
#include "Common/GameMemory.h"
-#include "Common/GameEngine.h"
#include "GameClient/GameWindowManager.h"
#include "Win32Device/GameClient/Win32Mouse.h"
#include "resource.h"
@@ -224,6 +224,8 @@ Int APIENTRY WinMain(HINSTANCE hInstance,
return FALSE;
TheEditor->init();
+ TheFramePacer = new FramePacer();
+
//
// see if we have any messages to process, a NULL window handle tells the
// OS to look at the main window associated with the calling thread, us!
@@ -262,7 +264,7 @@ Int APIENTRY WinMain(HINSTANCE hInstance,
else
{
- // udpate our universe
+ // update our universe
TheEditor->update();
Sleep(1);
@@ -271,6 +273,9 @@ Int APIENTRY WinMain(HINSTANCE hInstance,
}
// shutdown GUIEdit data
+ delete TheFramePacer;
+ TheFramePacer = NULL;
+
delete TheEditor;
TheEditor = NULL;
diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilder.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilder.cpp
index 2225c6f9a2..dead6bdcc3 100644
--- a/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilder.cpp
+++ b/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilder.cpp
@@ -33,6 +33,7 @@
//#include
#include "W3DDevice/GameClient/W3DFileSystem.h"
+#include "Common/FramePacer.h"
#include "Common/GlobalData.h"
#include "WHeightMapEdit.h"
//#include "Common/GameFileSystem.h"
@@ -350,6 +351,8 @@ BOOL CWorldBuilderApp::InitInstance()
initSubsystem(TheWritableGlobalData, new GlobalData(), "Data\\INI\\Default\\GameData", "Data\\INI\\GameData");
+ TheFramePacer = new FramePacer();
+
#if defined(RTS_DEBUG)
ini.loadFileDirectory( AsciiString( "Data\\INI\\GameDataDebug" ), INI_LOAD_MULTIFILE, NULL );
#endif
@@ -654,6 +657,9 @@ int CWorldBuilderApp::ExitInstance()
WorldHeightMapEdit::shutdown();
+ delete TheFramePacer;
+ TheFramePacer = NULL;
+
delete TheFileSystem;
TheFileSystem = NULL;
diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/wbview3d.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/wbview3d.cpp
index df6af034c5..751e1d830d 100644
--- a/GeneralsMD/Code/Tools/WorldBuilder/src/wbview3d.cpp
+++ b/GeneralsMD/Code/Tools/WorldBuilder/src/wbview3d.cpp
@@ -31,7 +31,6 @@
#include "W3DDevice/GameClient/Module/W3DModelDraw.h"
#include "agg_def.h"
#include "part_ldr.h"
-#include "rendobj.h"
#include "hanim.h"
#include "dx8wrapper.h"
#include "dx8indexbuffer.h"
@@ -78,6 +77,7 @@
#include "WorldBuilder.h"
#include "wbview3d.h"
#include "Common/Debug.h"
+#include "Common/FramePacer.h"
#include "Common/ThingFactory.h"
#include "GameClient/Water.h"
#include "Common/WellKnownKeys.h"
@@ -2058,12 +2058,18 @@ void WbView3d::redraw(void)
m_showBoundingBoxes, m_showSightRanges, m_showWeaponRanges, m_showSoundCircles, m_highlightTestArt, m_showLetterbox);
}
- WW3D::Sync( GetTickCount() );
+ WW3D::Update_Logic_Frame_Time(TheFramePacer->getLogicTimeStepMilliseconds());
+ WW3D::Sync(WW3D::Get_Fractional_Sync_Milliseconds() >= WWSyncMilliseconds);
+
m_buildRedMultiplier += (GetTickCount()-m_time)/500.0f;
if (m_buildRedMultiplier>4.0f || m_buildRedMultiplier<0) {
m_buildRedMultiplier = 0;
}
+
render();
+
+ TheFramePacer->update();
+
m_time = ::GetTickCount();
}