Skip to content

Commit c12bab6

Browse files
committed
bugfix(fps): Fix crashes and ww3d updates in tools (#1688)
1 parent 6620cc3 commit c12bab6

File tree

18 files changed

+136
-57
lines changed

18 files changed

+136
-57
lines changed

Core/GameEngine/Include/Common/FramePacer.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
#include "Common/FrameRateLimit.h"
2121

2222

23+
// TheSuperHackers @todo Use unsigned integers for fps values
24+
// TheSuperHackers @todo Consolidate the GlobalData::m_useFpsLimit and FramePacer::m_enableFpsLimit
25+
// TheSuperHackers @todo Implement new fast forward in here
2326
class FramePacer
2427
{
2528
public:
@@ -36,8 +39,13 @@ class FramePacer
3639

3740
void update(); ///< Signal that the app/render update is done and wait for the fps limit if applicable.
3841

39-
void setFramesPerSecondLimit( Int fps ); ///< Set the max update fps.
40-
Int getFramesPerSecondLimit() const; ///< Get the max update fps.
42+
void setFramesPerSecondLimit( Int fps ); ///< Set the update fps limit.
43+
Int getFramesPerSecondLimit() const; ///< Get the update fps limit.
44+
void enableFramesPerSecondLimit( Bool enable ); ///< Enable or disable the update fps limit.
45+
Bool isFramesPerSecondLimitEnabled() const; ///< Returns whether the fps limit is enabled here.
46+
Bool isActualFramesPerSecondLimitEnabled() const; ///< Returns whether the fps limit is actually enabled when considering all game settings and setups.
47+
Int getActualFramesPerSecondLimit() const; // Get the actual update fps limit.
48+
4149
Real getUpdateTime() const; ///< Get the last update delta time in seconds.
4250
Real getUpdateFps() const; ///< Get the last update fps.
4351

@@ -47,8 +55,8 @@ class FramePacer
4755
Bool isGameHalted() const;
4856

4957
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.
50-
Int getLogicTimeScaleFps() const; ///< Get the raw logic time scale fps value.
51-
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.
58+
Int getLogicTimeScaleFps() const; ///< Get the raw logic time scale fps value.
59+
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.
5260
Bool isLogicTimeScaleEnabled() const; ///< Check whether the logic time scale setup is enabled.
5361
Int getActualLogicTimeScaleFps(LogicTimeQueryFlags flags = 0) const; ///< Get the real logic time scale fps, depending on the max render fps, network state and enabled state.
5462
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
6573

6674
Real m_updateTime; ///< Last update delta time in seconds
6775

76+
Bool m_enableFpsLimit;
6877
Bool m_enableLogicTimeScale;
6978
Bool m_isTimeFrozen;
7079
Bool m_isGameHalted;

Core/GameEngine/Include/Common/FrameRateLimit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ class FrameRateLimit
2929
Real wait(UnsignedInt maxFps);
3030

3131
private:
32-
LARGE_INTEGER m_freq;
33-
LARGE_INTEGER m_start;
32+
Int64 m_freq;
33+
Int64 m_start;
3434
};
3535

3636

Core/GameEngine/Source/Common/FramePacer.cpp

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ FramePacer::FramePacer()
3737

3838
m_maxFPS = BaseFps;
3939
m_logicTimeScaleFPS = LOGICFRAMES_PER_SECOND;
40-
m_updateTime = 1.0f / BaseFps; // initialized to something to avoid division by zero on first use
40+
m_updateTime = 1.0f / (Real)BaseFps; // initialized to something to avoid division by zero on first use
41+
m_enableFpsLimit = FALSE;
4142
m_enableLogicTimeScale = FALSE;
4243
m_isTimeFrozen = FALSE;
4344
m_isGameHalted = FALSE;
@@ -51,25 +52,9 @@ FramePacer::~FramePacer()
5152

5253
void FramePacer::update()
5354
{
54-
Bool allowFpsLimit = TheTacticalView->getTimeMultiplier()<=1 && !TheScriptEngine->isTimeFast();
55-
56-
// I'm disabling this in debug because many people need alt-tab capability. If you happen to be
57-
// doing performance tuning, please just change this on your local system. -MDC
58-
#if defined(RTS_DEBUG)
59-
if (allowFpsLimit)
60-
::Sleep(1); // give everyone else a tiny time slice.
61-
#endif
62-
63-
#if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
64-
allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode);
65-
#else //always allow this cheat key if we're in a replay game.
66-
allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode && TheGameLogic->isInReplayGame());
67-
#endif
68-
6955
// TheSuperHackers @bugfix xezon 05/08/2025 Re-implements the frame rate limiter
7056
// with higher resolution counters to cap the frame rate more accurately to the desired limit.
71-
allowFpsLimit &= TheGlobalData->m_useFpsLimit;
72-
const UnsignedInt maxFps = allowFpsLimit ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue;
57+
const UnsignedInt maxFps = getActualFramesPerSecondLimit();// allowFpsLimit ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue;
7358
m_updateTime = m_frameRateLimit.wait(maxFps);
7459
}
7560

@@ -84,6 +69,45 @@ Int FramePacer::getFramesPerSecondLimit() const
8469
return m_maxFPS;
8570
}
8671

72+
void FramePacer::enableFramesPerSecondLimit( Bool enable )
73+
{
74+
m_enableFpsLimit = enable;
75+
}
76+
77+
Bool FramePacer::isFramesPerSecondLimitEnabled() const
78+
{
79+
return m_enableFpsLimit;
80+
}
81+
82+
Bool FramePacer::isActualFramesPerSecondLimitEnabled() const
83+
{
84+
Bool allowFpsLimit = true;
85+
86+
if (TheTacticalView != NULL)
87+
{
88+
allowFpsLimit &= TheTacticalView->getTimeMultiplier()<=1 && !TheScriptEngine->isTimeFast();
89+
}
90+
91+
if (TheGameLogic != NULL)
92+
{
93+
#if defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
94+
allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode);
95+
#else //always allow this cheat key if we're in a replay game.
96+
allowFpsLimit &= !(!TheGameLogic->isGamePaused() && TheGlobalData->m_TiVOFastMode && TheGameLogic->isInReplayGame());
97+
#endif
98+
}
99+
100+
allowFpsLimit &= TheGlobalData->m_useFpsLimit;
101+
allowFpsLimit &= isFramesPerSecondLimitEnabled();
102+
103+
return allowFpsLimit;
104+
}
105+
106+
Int FramePacer::getActualFramesPerSecondLimit() const
107+
{
108+
return isActualFramesPerSecondLimitEnabled() ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue;
109+
}
110+
87111
Real FramePacer::getUpdateTime() const
88112
{
89113
return m_updateTime;
@@ -153,10 +177,11 @@ Int FramePacer::getActualLogicTimeScaleFps(LogicTimeQueryFlags flags) const
153177

154178
if (isLogicTimeScaleEnabled())
155179
{
156-
return min(getLogicTimeScaleFps(), getFramesPerSecondLimit());
180+
return getLogicTimeScaleFps();
157181
}
158182

159-
return getFramesPerSecondLimit();
183+
// Returns uncapped value to align with the render update as per the original game behavior.
184+
return RenderFpsPreset::UncappedFpsValue;
160185
}
161186

162187
Real FramePacer::getActualLogicTimeScaleRatio(LogicTimeQueryFlags flags) const

Core/GameEngine/Source/Common/FrameRateLimit.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,19 @@
2222

2323
FrameRateLimit::FrameRateLimit()
2424
{
25-
QueryPerformanceFrequency(&m_freq);
26-
QueryPerformanceCounter(&m_start);
25+
LARGE_INTEGER freq;
26+
LARGE_INTEGER start;
27+
QueryPerformanceFrequency(&freq);
28+
QueryPerformanceCounter(&start);
29+
m_freq = freq.QuadPart;
30+
m_start = start.QuadPart;
2731
}
2832

2933
Real FrameRateLimit::wait(UnsignedInt maxFps)
3034
{
3135
LARGE_INTEGER tick;
3236
QueryPerformanceCounter(&tick);
33-
double elapsedSeconds = static_cast<double>(tick.QuadPart - m_start.QuadPart) / m_freq.QuadPart;
37+
double elapsedSeconds = static_cast<double>(tick.QuadPart - m_start) / m_freq;
3438
const double targetSeconds = 1.0 / maxFps;
3539
const double sleepSeconds = targetSeconds - elapsedSeconds - 0.002; // leave ~2ms for spin wait
3640

@@ -45,11 +49,11 @@ Real FrameRateLimit::wait(UnsignedInt maxFps)
4549
do
4650
{
4751
QueryPerformanceCounter(&tick);
48-
elapsedSeconds = static_cast<double>(tick.QuadPart - m_start.QuadPart) / m_freq.QuadPart;
52+
elapsedSeconds = static_cast<double>(tick.QuadPart - m_start) / m_freq;
4953
}
5054
while (elapsedSeconds < targetSeconds);
5155

52-
m_start = tick;
56+
m_start = tick.QuadPart;
5357
return (Real)elapsedSeconds;
5458
}
5559

Core/Libraries/Source/WWVegas/WWLib/WWCommon.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ enum
3131
{
3232
// TheSuperHackers @info The original WWSync was 33 ms, ~30 fps, integer.
3333
// Changing this will require tweaking all Drawable code that concerns the ww3d time step, including locomotion physics.
34-
WWSyncPerSecond = 30
34+
WWSyncPerSecond = 30,
35+
WWSyncMilliseconds = 1000 / WWSyncPerSecond,
3536
};
3637

3738
#if defined(_MSC_VER) && _MSC_VER < 1300

Core/Tools/W3DView/GraphicView.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,8 @@ CGraphicView::RepaintView
436436
// Simple check to avoid re-entrance
437437
//
438438
static bool _already_painting = false;
439-
if (_already_painting) return;
439+
if (_already_painting)
440+
return;
440441
_already_painting = true;
441442

442443
//
@@ -452,10 +453,15 @@ CGraphicView::RepaintView
452453
m_dwLastFrameUpdate = cur_ticks;
453454

454455
// Update the W3D frame times according to our elapsed tick count
455-
if (ticks_to_use == 0) {
456-
WW3D::Sync (WW3D::Get_Sync_Time() + (ticks_elapsed * m_animationSpeed));
457-
} else {
458-
WW3D::Sync (WW3D::Get_Sync_Time() + ticks_to_use);
456+
if (ticks_to_use == 0)
457+
{
458+
WW3D::Update_Logic_Frame_Time(ticks_elapsed * m_animationSpeed);
459+
WW3D::Sync(WW3D::Get_Fractional_Sync_Milliseconds() >= WWSyncMilliseconds);
460+
}
461+
else
462+
{
463+
WW3D::Update_Logic_Frame_Time(ticks_to_use);
464+
WW3D::Sync(true);
459465
}
460466

461467
// Do we need to update the current animation?

Generals/Code/GameEngine/Source/Common/GameMain.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Int GameMain()
4141
int exitcode = 0;
4242
// initialize the game engine using factory function
4343
TheFramePacer = new FramePacer();
44+
TheFramePacer->enableFramesPerSecondLimit(TRUE);
4445
TheGameEngine = CreateGameEngine();
4546
TheGameEngine->init();
4647

Generals/Code/Libraries/Source/WWVegas/WW3D2/ww3d.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ class WW3D
173173
static void Sync(bool step);
174174
static unsigned int Get_Sync_Time(void) { return SyncTime; }
175175
static unsigned int Get_Sync_Frame_Time(void) { return SyncTime - PreviousSyncTime; }
176+
static unsigned int Get_Fractional_Sync_Milliseconds() { return FractionalSyncMs; }
176177
static float Get_Logic_Frame_Time_Milliseconds() { return LogicFrameTimeMs; }
177178
static float Get_Logic_Frame_Time_Seconds() { return LogicFrameTimeMs * 0.001f; }
178179
static unsigned int Get_Frame_Count(void) { return FrameCount; }

Generals/Code/Tools/GUIEdit/Source/EditWindow.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050
// USER INCLUDES //////////////////////////////////////////////////////////////
5151
#include "Common/Debug.h"
52+
#include "Common/FramePacer.h"
5253
#include "GameClient/Display.h"
5354
#include "GameClient/GameWindowManager.h"
5455
#include "W3DDevice/GameClient/W3DFileSystem.h"
@@ -1456,13 +1457,9 @@ void EditWindow::drawGrid( void )
14561457
//=============================================================================
14571458
void EditWindow::draw( void )
14581459
{
1459-
static UnsignedInt syncTime = 0;
1460-
14611460
// allow W3D to update its internals
1462-
WW3D::Sync( syncTime );
1463-
1464-
// for now, use constant time steps to avoid animations running independent of framerate
1465-
syncTime += 50;
1461+
WW3D::Update_Logic_Frame_Time(TheFramePacer->getLogicTimeStepMilliseconds());
1462+
WW3D::Sync(WW3D::Get_Fractional_Sync_Milliseconds() >= WWSyncMilliseconds);
14661463

14671464
// start render block
14681465
WW3D::Begin_Render( true, true, Vector3( m_backgroundColor.red,
@@ -1479,6 +1476,7 @@ void EditWindow::draw( void )
14791476
// render is all done!
14801477
WW3D::End_Render();
14811478

1479+
TheFramePacer->update();
14821480
}
14831481

14841482
// EditWindow::setSize ========================================================

Generals/Code/Tools/GUIEdit/Source/WinMain.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@
5050

5151
// USER INCLUDES //////////////////////////////////////////////////////////////
5252
#include "Common/Debug.h"
53+
#include "Common/FramePacer.h"
5354
#include "Common/GameMemory.h"
54-
#include "Common/GameEngine.h"
5555
#include "GameClient/GameWindowManager.h"
5656
#include "Win32Device/GameClient/Win32Mouse.h"
5757
#include "resource.h"
@@ -224,6 +224,8 @@ Int APIENTRY WinMain(HINSTANCE hInstance,
224224
return FALSE;
225225
TheEditor->init();
226226

227+
TheFramePacer = new FramePacer();
228+
227229
//
228230
// see if we have any messages to process, a NULL window handle tells the
229231
// OS to look at the main window associated with the calling thread, us!
@@ -262,7 +264,7 @@ Int APIENTRY WinMain(HINSTANCE hInstance,
262264
else
263265
{
264266

265-
// udpate our universe
267+
// update our universe
266268
TheEditor->update();
267269
Sleep(1);
268270

@@ -271,6 +273,9 @@ Int APIENTRY WinMain(HINSTANCE hInstance,
271273
}
272274

273275
// shutdown GUIEdit data
276+
delete TheFramePacer;
277+
TheFramePacer = NULL;
278+
274279
delete TheEditor;
275280
TheEditor = NULL;
276281

0 commit comments

Comments
 (0)