Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions dllmain/DisplayTweaks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "Patches.h"
#include "Game.h"
#include "Settings.h"
#include <timeapi.h>

int g_UserRefreshRate;

Expand Down Expand Up @@ -76,8 +77,112 @@ BOOL __stdcall MoveWindow_Hook(HWND hWnd, int X, int Y, int nWidth, int nHeight,
return MoveWindow(hWnd, windowX, windowY, nWidth, nHeight, bRepaint);
}

double FramelimiterFrequency = 0;
double FramelimiterPrevTicks = 0;
void Framelimiter_Hook(uint8_t isAliveEvt_result)
{
// Change thread to core 0 before running QueryPerformance* funcs, game does this, so guess we should too
HANDLE curThread = GetCurrentThread();
DWORD_PTR prevAffinityMask = SetThreadAffinityMask(curThread, 0);

static bool framelimiterInited = false;
if (!framelimiterInited)
{
LARGE_INTEGER result;
QueryPerformanceFrequency(&result);
FramelimiterFrequency = (double)result.QuadPart / 1000.0;

QueryPerformanceCounter(&result);
FramelimiterPrevTicks = (double)result.QuadPart / FramelimiterFrequency;

typedef MMRESULT(__stdcall* timeBeginPeriod_Fn) (UINT Period);
timeBeginPeriod_Fn timeBeginPeriod_actual = (timeBeginPeriod_Fn)GetProcAddress(LoadLibraryA("winmm.dll"), "timeBeginPeriod");
timeBeginPeriod_actual(1);

framelimiterInited = true;
}

int gameFramerate = GameVariableFrameRate();

// The games IsAliveEvt check seems to (indirectly) result in framelimiter loop limiting to 60FPS
// maybe a remnant of some time when more framerate options were available?
if (isAliveEvt_result && gameFramerate != 30)
gameFramerate = 60;

double TargetFrametime = 1000.0 / (double)gameFramerate;

double timeElapsed = 0;
double timeCurrent = 0;
do
{
// Framelimiter based on FPS_ACCURATE code from
// https://github.com/ThirteenAG/d3d9-wrapper/blob/c1480b0c1b40e0ba7b55b8660bd67f911a967421/source/dllmain.cpp#L46

LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
timeCurrent = (double)counter.QuadPart / FramelimiterFrequency;
timeElapsed = timeCurrent - FramelimiterPrevTicks;

if (TargetFrametime <= timeElapsed || pConfig->bDisableFramelimiting)
break;
else if (TargetFrametime - timeElapsed > 2.0) // > 2ms
Sleep(1); // Sleep for ~1ms
else
Sleep(0); // yield thread's time-slice (does not actually sleep)
}
while (TargetFrametime > timeElapsed);

FramelimiterPrevTicks = timeCurrent;

// Not really sure what the second part of IsAliveEvt check is doing
// Seems to skip setting timeElapsed to the fixed FramelimiterTargetFrametime at least
// Guess that means the true timeElapsed gets passed to the game? (after being limited to 60 like above)
if (isAliveEvt_result && gameFramerate != 30)
{
if (timeElapsed > 33.333333333333333)
timeElapsed = 33.333333333333333;
}
else
{
// TODO: using the actual time elapsed since last frame instead of FramelimiterTargetFrametime would solve slowdown issues
// (similar to https://github.com/nipkownix/re4_tweaks/pull/25)
// but it's not known how well the game works with values that aren't 0.5 (60fps) or 1 (30fps)
// so for now we'll just work pretty much the same as the game itself, unless UseDynamicFrametime is set
if (!pConfig->bUseDynamicFrametime)
timeElapsed = TargetFrametime;
}

GlobalPtr()->deltaTime_70 = float((timeElapsed / 1000) * 30.0);

SetThreadAffinityMask(curThread, prevAffinityMask);
}

void Init_DisplayTweaks()
{
// Implements new reduced-CPU-usage limiter
if (pConfig->bReplaceFramelimiter)
{
// nop beginning of framelimiter code (sets up thread affinity to core 0)
auto pattern = hook::pattern("A3 ? ? ? ? 6A 00 FF 15 ? ? ? ? 50 FF");
uint8_t* framelimiterStart = pattern.count(1).get(0).get<uint8_t>(5);
pattern = hook::pattern("E8 ? ? ? ? 85 C0 75 ? D9 EE EB ?");
uint8_t* framelimiterEnd = pattern.count(1).get(0).get<uint8_t>(0);
Nop(framelimiterStart, framelimiterEnd - framelimiterStart); // 6549C3 to 6549D9 (1.1.0)

pattern = hook::pattern("B9 ? ? ? ? E8 ? ? ? ? 84 C0 74 ? E8 ? ? ? ? 83 F8 1E");
framelimiterStart = pattern.count(1).get(0).get<uint8_t>(0xA); // 654A1E
pattern = hook::pattern("8D 85 ? ? ? ? 50 FF D3 DF AD ? ? ? ? 8D 8D");
framelimiterEnd = pattern.count(1).get(0).get<uint8_t>(0); // 654B0A

Nop(framelimiterStart, framelimiterEnd - framelimiterStart);

Patch(framelimiterStart, uint8_t(0x50)); // push eax (pass return value of EventMgr::IsAliveEvt)
InjectHook(framelimiterStart + 1, Framelimiter_Hook, PATCH_CALL);
Patch(framelimiterStart + 1 + 5, { 0x83, 0xc4, 0x04 }); // add esp, 4 (needed to allow WinMain to exit properly)

spd::log()->info("Framelimiter replaced");
}

// Fix broken effects
Init_FilterXXFixes();

Expand Down
10 changes: 10 additions & 0 deletions dllmain/Game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ bool GameVersionIsDebug()
return gameIsDebugBuild;
}

uint32_t* ptrGameVariableFrameRate;
int GameVariableFrameRate()
{
return *(int32_t*)(ptrGameVariableFrameRate);
}

uint32_t* ptrLastUsedDevice = nullptr;
InputDevices LastUsedDevice()
{
Expand Down Expand Up @@ -166,6 +172,10 @@ bool Init_Game()
con.AddConcatLog("Game version = ", GameVersion().data());
#endif

// Pointer to users variableframerate setting value
pattern = hook::pattern("89 0D ? ? ? ? 0F 95 ? 88 15 ? ? ? ? D9 1D ? ? ? ? A3 ? ? ? ? DB 46 ? D9 1D ? ? ? ? 8B 4E ? 89 0D ? ? ? ? 8B 4D ? 5E");
ptrGameVariableFrameRate = *pattern.count(1).get(0).get<uint32_t*>(2);

// LastUsedDevice pointer
pattern = hook::pattern("A1 ? ? ? ? 85 C0 74 ? 83 F8 ? 74 ? 81 F9");
ptrLastUsedDevice = *pattern.count(1).get(0).get<uint32_t*>(1);
Expand Down
1 change: 1 addition & 0 deletions dllmain/Game.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extern SND_CTRL* Snd_ctrl_work;

std::string GameVersion();
bool GameVersionIsDebug();
int GameVariableFrameRate();
InputDevices LastUsedDevice();
bool isController();
bool isKeyboardMouse();
Expand Down
14 changes: 11 additions & 3 deletions dllmain/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,15 @@ void Config::ReadSettings(std::string_view ini_path)
else
pConfig->bEnableFOV = false;

pConfig->bDisableVsync = iniReader.ReadBoolean("DISPLAY", "DisableVsync", pConfig->bDisableVsync);

pConfig->bUltraWideAspectSupport = iniReader.ReadBoolean("DISPLAY", "UltraWideAspectSupport", pConfig->bUltraWideAspectSupport);
pConfig->bSideAlignHUD = iniReader.ReadBoolean("DISPLAY", "SideAlignHUD", pConfig->bSideAlignHUD);
pConfig->bStretchFullscreenImages = iniReader.ReadBoolean("DISPLAY", "StretchFullscreenImages", pConfig->bStretchFullscreenImages);
pConfig->bStretchVideos = iniReader.ReadBoolean("DISPLAY", "StretchVideos", pConfig->bStretchVideos);
pConfig->bRemove16by10BlackBars = iniReader.ReadBoolean("DISPLAY", "Remove16by10BlackBars", pConfig->bRemove16by10BlackBars);

pConfig->bDisableVsync = iniReader.ReadBoolean("DISPLAY", "DisableVsync", pConfig->bDisableVsync);
pConfig->bReplaceFramelimiter = iniReader.ReadBoolean("DISPLAY", "ReplaceFramelimiter", pConfig->bReplaceFramelimiter);
pConfig->bFixDPIScale = iniReader.ReadBoolean("DISPLAY", "FixDPIScale", pConfig->bFixDPIScale);
pConfig->bFixDisplayMode = iniReader.ReadBoolean("DISPLAY", "FixDisplayMode", pConfig->bFixDisplayMode);
pConfig->iCustomRefreshRate = iniReader.ReadInteger("DISPLAY", "CustomRefreshRate", pConfig->iCustomRefreshRate);
Expand Down Expand Up @@ -360,6 +362,8 @@ void Config::ReadSettings(std::string_view ini_path)
// DEBUG
pConfig->bVerboseLog = iniReader.ReadBoolean("DEBUG", "VerboseLog", pConfig->bVerboseLog);
pConfig->bNeverHideCursor = iniReader.ReadBoolean("DEBUG", "NeverHideCursor", pConfig->bNeverHideCursor);
pConfig->bUseDynamicFrametime = iniReader.ReadBoolean("DEBUG", "UseDynamicFrametime", pConfig->bUseDynamicFrametime);
pConfig->bDisableFramelimiting = iniReader.ReadBoolean("DEBUG", "DisableFramelimiting", pConfig->bDisableFramelimiting);
}

std::mutex settingsThreadRunningMutex;
Expand Down Expand Up @@ -406,14 +410,15 @@ DWORD WINAPI WriteSettingsThread(LPVOID lpParameter)

// DISPLAY
iniReader.WriteFloat("DISPLAY", "FOVAdditional", pConfig->fFOVAdditional);
iniReader.WriteBoolean("DISPLAY", "DisableVsync", pConfig->bDisableVsync);

iniReader.WriteBoolean("DISPLAY", "UltraWideAspectSupport", pConfig->bUltraWideAspectSupport);
iniReader.WriteBoolean("DISPLAY", "SideAlignHUD", pConfig->bSideAlignHUD);
iniReader.WriteBoolean("DISPLAY", "StretchFullscreenImages", pConfig->bStretchFullscreenImages);
iniReader.WriteBoolean("DISPLAY", "StretchVideos", pConfig->bStretchVideos);
iniReader.WriteBoolean("DISPLAY", "Remove16by10BlackBars", pConfig->bRemove16by10BlackBars);

iniReader.WriteBoolean("DISPLAY", "DisableVsync", pConfig->bDisableVsync);
iniReader.WriteBoolean("DISPLAY", "ReplaceFramelimiter", pConfig->bReplaceFramelimiter);
iniReader.WriteBoolean("DISPLAY", "FixDPIScale", pConfig->bFixDPIScale);
iniReader.WriteBoolean("DISPLAY", "FixDisplayMode", pConfig->bFixDisplayMode);
iniReader.WriteInteger("DISPLAY", "CustomRefreshRate", pConfig->iCustomRefreshRate);
Expand Down Expand Up @@ -550,12 +555,13 @@ void Config::LogSettings()
// DISPLAY
spd::log()->info("+ DISPLAY------------------------+-----------------+");
spd::log()->info("| {:<30} | {:>15} |", "FOVAdditional", pConfig->fFOVAdditional);
spd::log()->info("| {:<30} | {:>15} |", "DisableVsync", pConfig->bDisableVsync ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "UltraWideAspectSupport", pConfig->bUltraWideAspectSupport ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "SideAlignHUD", pConfig->bSideAlignHUD ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "StretchFullscreenImages", pConfig->bStretchFullscreenImages ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "StretchVideos", pConfig->bStretchVideos ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "Remove16by10BlackBars", pConfig->bRemove16by10BlackBars ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "DisableVsync", pConfig->bDisableVsync ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "ReplaceFramelimiter", pConfig->bReplaceFramelimiter ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "FixDPIScale", pConfig->bFixDPIScale ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "FixDisplayMode", pConfig->bFixDisplayMode ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "CustomRefreshRate", pConfig->iCustomRefreshRate);
Expand Down Expand Up @@ -698,5 +704,7 @@ void Config::LogSettings()
spd::log()->info("+ DEBUG--------------------------+-----------------+");
spd::log()->info("| {:<30} | {:>15} |", "VerboseLog", pConfig->bVerboseLog ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "NeverHideCursor", pConfig->bNeverHideCursor ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "UseDynamicFrametime", pConfig->bUseDynamicFrametime ? "true" : "false");
spd::log()->info("| {:<30} | {:>15} |", "DisableFramelimiting", pConfig->bDisableFramelimiting ? "true" : "false");
spd::log()->info("+--------------------------------+-----------------+");
}
5 changes: 4 additions & 1 deletion dllmain/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ class Config
// DISPLAY
float fFOVAdditional = 0.0f;
bool bEnableFOV = false;
bool bDisableVsync = false;
bool bUltraWideAspectSupport = true;
bool bSideAlignHUD = true;
bool bStretchFullscreenImages = false;
bool bStretchVideos = false;
bool bRemove16by10BlackBars = true;
bool bDisableVsync = false;
bool bReplaceFramelimiter = true;
bool bFixDPIScale = true;
bool bFixDisplayMode = true;
int iCustomRefreshRate = -1;
Expand Down Expand Up @@ -131,6 +132,8 @@ class Config
// DEBUG
bool bVerboseLog = false;
bool bNeverHideCursor = false;
bool bUseDynamicFrametime = false;
bool bDisableFramelimiting = false;

bool HasUnsavedChanges = false;

Expand Down
23 changes: 20 additions & 3 deletions dllmain/cfgMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,22 @@ void cfgMenuRender()
pConfig->fFOVAdditional = 0.0f;
}

// DisableVsync
{
ImGui_ColumnSwitch();

if (ImGui::Checkbox("DisableVsync", &pConfig->bDisableVsync))
{
pConfig->HasUnsavedChanges = true;
NeedsToRestart = true;
}

ImGui_ItemSeparator();

ImGui::Dummy(ImVec2(10, 10));
ImGui::TextWrapped("Force V-Sync to be disabled. For some reason the vanilla game doesn't provide a functional way to do this.");
}

// Aspect ratio tweaks
{
ImGui_ColumnSwitch();
Expand Down Expand Up @@ -463,11 +479,11 @@ void cfgMenuRender()
ImGui::TextWrapped("(Change the resolution for this setting to take effect)");
}

// DisableVsync
// ReplaceFramelimiter
{
ImGui_ColumnSwitch();

if (ImGui::Checkbox("DisableVsync", &pConfig->bDisableVsync))
if (ImGui::Checkbox("ReplaceFramelimiter", &pConfig->bReplaceFramelimiter))
{
pConfig->HasUnsavedChanges = true;
NeedsToRestart = true;
Expand All @@ -476,7 +492,8 @@ void cfgMenuRender()
ImGui_ItemSeparator();

ImGui::Dummy(ImVec2(10, 10));
ImGui::TextWrapped("Force V-Sync to be disabled. For some reason the vanilla game doesn't provide a functional way to do this.");
ImGui::TextWrapped("Replaces the games 60/30FPS framelimiter with our own version, which reduces CPU usage quite a lot.");
ImGui::TextWrapped("(experimental, not known if the new framelimiter performs the same as the old one yet)");
}

// FixDPIScale
Expand Down
22 changes: 18 additions & 4 deletions settings/settings_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const char* defaultSettings = R""""(
; Additional FOV value. 20 seems good for most cases.
FOVAdditional = 0.0

; Force V-Sync to be disabled. For some reason the vanilla game doesn't provide a functional way to do this.
DisableVsync = false

; Fixes the incorrect aspect ratio when playing in ultrawide resolutions,
; preventing the image from being cut off and the HUD appearing off-screen.
UltraWideAspectSupport = true
Expand All @@ -21,10 +24,11 @@ StretchVideos = false

; Removes top and bottom black bars that are present when playing in 16:10.
; Will crop a few pixels from each side of the screen.
Remove16by10BlackBars = false
Remove16by10BlackBars = true

; Force V-Sync to be disabled. For some reason the vanilla game doesn't provide a functional way to do this.
DisableVsync = false
; Replaces the games 60/30FPS framelimiter with our own version, which reduces CPU usage quite a lot.
; (experimental, not known if the new framelimiter performs the same as the old one yet)
ReplaceFramelimiter = true

; Forces game to run at normal 100% DPI scaling, fixes resolution issues for players that have above 100% DPI scaling set.
FixDPIScale = true
Expand Down Expand Up @@ -346,4 +350,14 @@ DisableMenuTip = false
; Logs extra information.
VerboseLog = false
NeverHideCursor = false
)"""";

; Passes the actual elapsed frametime to the game instead of a fixed 30/60FPS frametime.
; Should help reduce slowdown in-game when FPS fails to reach the games framerate setting.
; (experimental, certain things may act strange when using non-fixed frametime, especially audio)
UseDynamicFrametime = false

; Disables any kind of framelimiting.
; Useful for comparing "true" FPS/frametime when making performance-related changes.
; (requires ReplaceFramelimiter = true, recommend DisableVsync too)
DisableFramelimiting = false
)"""";