Skip to content

Commit

Permalink
Add frame limiter
Browse files Browse the repository at this point in the history
  • Loading branch information
elishacloud committed Nov 30, 2024
1 parent 1e9dd06 commit f11eec3
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Common/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@
visit(fog_layer2_complexity, 0.055f) \
visit(fog_layer2_density_add, 100.0f) \
visit(fog_layer2_density_mult, 1.4f) \
visit(LimitPerFrameFPS, 0.0f) \
visit(water_spec_mult_apt_staircase, 0.035f) \
visit(water_spec_mult_strange_area, 0.017f) \
visit(water_spec_mult_labyrinth, 0.017f) \
Expand Down Expand Up @@ -254,6 +255,7 @@
visit(HookDirectSound) \
visit(HookWndProc) \
visit(LetterSpacing) \
visit(LimitPerFrameFPS) \
visit(LoadModulesFromMemory) \
visit(LockResolution) \
visit(NormalFontHeight) \
Expand Down
27 changes: 27 additions & 0 deletions Common/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <intrin.h>
#ifdef ISLAUNCHER
#undef NTDDI_VERSION
#define NTDDI_VERSION NTDDI_WINXPSP3
Expand Down Expand Up @@ -420,6 +421,32 @@ bool GetCoreCount(DWORD& pBits, DWORD& sBits)
return false;
}

void BusyWaitYield(DWORD RemainingMS)
{
static bool supports_pause = []() {
int cpu_info[4] = { 0 };
__cpuid(cpu_info, 1); // Query CPU features
return (cpu_info[3] & (1 << 26)) != 0; // Check for SSE2 support
}();

// If remaining time is very small (e.g., 1 ms or less), use busy-wait with no operations
if (RemainingMS < 3 && supports_pause)
{
// Use _mm_pause or __asm { nop } to prevent unnecessary CPU cycles
#ifdef YieldProcessor
YieldProcessor();
#else
_mm_pause();
#endif
}
else
{
// For larger remaining times, we can relax by yielding to the OS
// Sleep(0) yields without consuming CPU excessively
Sleep(0); // Let the OS schedule other tasks if there's significant time left
}
}

// Log process affinity
void LogAffinity()
{
Expand Down
1 change: 1 addition & 0 deletions Common/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ bool UpdateMemoryAddress(void *dataAddr, const void *dataBytes, size_t dataSize)
bool WriteCalltoMemory(BYTE *dataAddr, const void *JMPAddr, DWORD count = 5);
bool WriteJMPtoMemory(BYTE *dataAddr, const void *JMPAddr, DWORD count = 5);
DWORD ReplaceMemoryBytes(void *dataSrc, void *dataDest, size_t size, DWORD start, DWORD distance, DWORD count = 0);
void BusyWaitYield(DWORD RemainingMS);
void LogAffinity();
DWORD_PTR GetProcessMask();
void SetSingleCoreAffinity();
Expand Down
2 changes: 1 addition & 1 deletion Resources/BuildNo.rc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#define BUILD_NUMBER 2218
#define BUILD_NUMBER 2219
4 changes: 4 additions & 0 deletions Wrappers/d3d8/IDirect3D8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,10 @@ void SetScreenAndWindowSize()
{
Logging::Log() << __FUNCTION__ << " Setting display mode: " <<
(ScreenMode == WINDOWED ? "Windowed" : ScreenMode == WINDOWED_FULLSCREEN ? "Windowed Fullscreen" : "Exclusive Fullscreen");
if (ScreenMode != EXCLUSIVE_FULLSCREEN)
{
ShowCursor(FALSE);
}
}
LastScreenMode = ScreenMode;

Expand Down
63 changes: 63 additions & 0 deletions Wrappers/d3d8/IDirect3DDevice8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <chrono>
#include <deque>
#include <ctime>
#include <numeric>
#include "Common\Utils.h"
#include "stb_image.h"
#include "stb_image_dds.h"
Expand Down Expand Up @@ -1374,6 +1375,58 @@ bool m_IDirect3DDevice8::FixPauseMenuOnPresent()
return PauseMenuFlag;
}

void m_IDirect3DDevice8::LimitFrameRate()
{
// Count the number of frames
Counter.FrameCounter++;

// Get performance frequency if not already cached
static LARGE_INTEGER Frequency = {};
if (!Frequency.QuadPart)
{
QueryPerformanceFrequency(&Frequency);
}
static LONGLONG TicksPerMS = Frequency.QuadPart / 1000;

// Calculate the delay time in ticks
static long double PerFrameFPS = LimitPerFrameFPS ? LimitPerFrameFPS : (SetSixtyFPS ? 59.94 : 29.97);
static LONGLONG PreFrameTicks = static_cast<LONGLONG>(static_cast<long double>(Frequency.QuadPart) / PerFrameFPS);

// Get next tick time
LARGE_INTEGER ClickTime = {};
QueryPerformanceCounter(&ClickTime);
LONGLONG TargetEndTicks = Counter.LastPresentTime.QuadPart;
LONGLONG FramesSinceLastCall = ((ClickTime.QuadPart - Counter.LastPresentTime.QuadPart - 1) / PreFrameTicks) + 1;
if (Counter.LastPresentTime.QuadPart == 0 || FramesSinceLastCall > 2)
{
QueryPerformanceCounter(&Counter.LastPresentTime);
TargetEndTicks = Counter.LastPresentTime.QuadPart;
}
else
{
TargetEndTicks += FramesSinceLastCall * PreFrameTicks;
}

// Wait for time to expire
bool DoLoop;
do {
QueryPerformanceCounter(&ClickTime);
LONGLONG RemainingTicks = TargetEndTicks - ClickTime.QuadPart;

// Check if we still need to wait
DoLoop = RemainingTicks > 0;

if (DoLoop)
{
// Busy wait until we reach the target time
BusyWaitYield(static_cast<DWORD>(RemainingTicks / TicksPerMS));
}
} while (DoLoop);

// Update the last present time
Counter.LastPresentTime.QuadPart = TargetEndTicks;
}

HRESULT m_IDirect3DDevice8::Present(CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion)
{
Logging::LogDebug() << __FUNCTION__;
Expand Down Expand Up @@ -1438,6 +1491,11 @@ HRESULT m_IDirect3DDevice8::Present(CONST RECT* pSourceRect, CONST RECT* pDestRe
if (ClearScreen)
{
ClearScreen = false;
if (ScreenMode != EXCLUSIVE_FULLSCREEN)
{
LimitFrameRate();
}

HRESULT hr = ProxyInterface->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);

if (SUCCEEDED(hr))
Expand Down Expand Up @@ -1476,6 +1534,11 @@ HRESULT m_IDirect3DDevice8::Present(CONST RECT* pSourceRect, CONST RECT* pDestRe
// Present screen
if (!PauseMenuFlag)
{
if (ScreenMode != EXCLUSIVE_FULLSCREEN)
{
LimitFrameRate();
}

hr = ProxyInterface->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);

if (SUCCEEDED(hr))
Expand Down
7 changes: 7 additions & 0 deletions Wrappers/d3d8/IDirect3DDevice8.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ class m_IDirect3DDevice8 : public IDirect3DDevice8
UINT stream0Stride;
};

// Limit frame rate
struct {
DWORD FrameCounter = 0;
LARGE_INTEGER LastPresentTime = {};
} Counter;

// Helper functions
void EnableAntiAliasing();
void DisableAntiAliasing();
Expand All @@ -224,6 +230,7 @@ class m_IDirect3DDevice8 : public IDirect3DDevice8
void CaptureScreenShot();
HRESULT CreateDCSurface(EMUSURFACE& surface, LONG Width, LONG Height);
void ReleaseDCSurface(EMUSURFACE& surface);
void LimitFrameRate();

public:
m_IDirect3DDevice8(LPDIRECT3DDEVICE8 pDevice, m_IDirect3D8* pD3D) : ProxyInterface(pDevice), m_pD3D(pD3D)
Expand Down

0 comments on commit f11eec3

Please sign in to comment.