From 7af15c73bc8aa2f1e1089ced996ef83d3584c259 Mon Sep 17 00:00:00 2001 From: Nabile Rahmani Date: Sat, 13 Jul 2024 15:04:20 +0200 Subject: [PATCH 1/6] [Feature] Option to override the display refresh rate This adds a per-game graphics option, `DisplayRefreshRate`, to override the display refresh rate. It defaults to 60 Hz, and is located in Dev tools. Games using variable timesteps benefit from higher refresh rates. Closes #19319 --- Common/VR/PPSSPPVR.cpp | 2 +- Core/Config.cpp | 2 ++ Core/Config.h | 1 + Core/HLE/sceDisplay.cpp | 8 ++++++-- Core/HLE/sceDisplay.h | 2 -- Core/HW/Display.cpp | 2 +- UI/DebugOverlay.cpp | 2 +- UI/GameSettingsScreen.cpp | 3 +++ 8 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index 173243fda9..9741d197ac 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -684,7 +684,7 @@ bool StartVRRender() { vrCompat[VR_COMPAT_SKYPLANE] = PSP_CoreParameter().compat.vrCompat().Skyplane; // Set customizations - __DisplaySetFramerate(g_Config.bForce72Hz ? 72 : 60); + g_Config.iDisplayRefreshRate = g_Config.bForce72Hz ? 72 : 60; VR_SetConfigFloat(VR_CONFIG_CANVAS_DISTANCE, vrScene && (appMode == VR_GAME_MODE) ? g_Config.fCanvas3DDistance : g_Config.fCanvasDistance); VR_SetConfig(VR_CONFIG_PASSTHROUGH, g_Config.bPassthrough); return true; diff --git a/Core/Config.cpp b/Core/Config.cpp index fe82d69655..f7cee90453 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -693,6 +693,8 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("UberShaderVertex", &g_Config.bUberShaderVertex, true, CfgFlag::DEFAULT), ConfigSetting("UberShaderFragment", &g_Config.bUberShaderFragment, true, CfgFlag::DEFAULT), + + ConfigSetting("DisplayRefreshRate", &g_Config.iDisplayRefreshRate, 60, CfgFlag::PER_GAME), }; static const ConfigSetting soundSettings[] = { diff --git a/Core/Config.h b/Core/Config.h index fdacd88fc5..21da06a770 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -160,6 +160,7 @@ public: std::string sMicDevice; bool bCameraMirrorHorizontal; int iDisplayFramerateMode; // enum DisplayFramerateMode. Android-only. + int iDisplayRefreshRate; bool bSoftwareRendering; bool bSoftwareRenderingJit; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 55c3a62d1a..62f5390989 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -106,7 +106,7 @@ static int height; static bool wasPaused; static bool flippedThisFrame; -static int framerate = 60; +static int framerate = g_Config.iDisplayRefreshRate; // 1.001f to compensate for the classic 59.94 NTSC framerate that the PSP seems to have. static double timePerVblank = 1.001 / framerate; @@ -152,6 +152,7 @@ void __DisplayVblankBeginCallback(SceUID threadID, SceUID prevCallbackId); void __DisplayVblankEndCallback(SceUID threadID, SceUID prevCallbackId); void __DisplayFlip(int cyclesLate); +static void __DisplaySetFramerate(int value); static bool UseLagSync() { return g_Config.bForceLagSync && !g_Config.bAutoFrameSkip; @@ -562,6 +563,9 @@ static void NotifyUserIfSlow() { } void __DisplayFlip(int cyclesLate) { + if (g_Config.iDisplayRefreshRate != framerate) + __DisplaySetFramerate(g_Config.iDisplayRefreshRate); + flippedThisFrame = true; // We flip only if the framebuffer was dirty. This eliminates flicker when using // non-buffered rendering. The interaction with frame skipping seems to need @@ -1126,7 +1130,7 @@ void Register_sceDisplay_driver() { RegisterModule("sceDisplay_driver", ARRAY_SIZE(sceDisplay), sceDisplay); } -void __DisplaySetFramerate(int value) { +static void __DisplaySetFramerate(int value) { framerate = value; timePerVblank = 1.001 / (double)framerate; frameMs = 1001.0 / (double)framerate; diff --git a/Core/HLE/sceDisplay.h b/Core/HLE/sceDisplay.h index b68133e8a2..bb134af815 100644 --- a/Core/HLE/sceDisplay.h +++ b/Core/HLE/sceDisplay.h @@ -34,5 +34,3 @@ void __DisplaySetWasPaused(); void Register_sceDisplay_driver(); void __DisplayWaitForVblanks(const char* reason, int vblanks, bool callbacks = false); - -void __DisplaySetFramerate(int value); diff --git a/Core/HW/Display.cpp b/Core/HW/Display.cpp index 0759d8a9ee..146aaf8cf2 100644 --- a/Core/HW/Display.cpp +++ b/Core/HW/Display.cpp @@ -78,7 +78,7 @@ static void CalculateFPS() { actualFps = (float)(actualFlips - lastActualFlips); fps = frames / (now - lastFpsTime); - flips = (float)(60.0 * (double)(gpuStats.numFlips - lastNumFlips) / frames); + flips = (float)(g_Config.iDisplayRefreshRate * (double)(gpuStats.numFlips - lastNumFlips) / frames); lastFpsFrame = numVBlanks; lastNumFlips = gpuStats.numFlips; diff --git a/UI/DebugOverlay.cpp b/UI/DebugOverlay.cpp index 9e402144ac..fea1b03a04 100644 --- a/UI/DebugOverlay.cpp +++ b/UI/DebugOverlay.cpp @@ -437,7 +437,7 @@ void DrawFPS(UIContext *ctx, const Bounds &bounds) { char fpsbuf[256]{}; if (g_Config.iShowStatusFlags == ((int)ShowStatusFlags::FPS_COUNTER | (int)ShowStatusFlags::SPEED_COUNTER)) { - snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / (59.94f / 100.0f)); + snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / (g_Config.iDisplayRefreshRate / 100.0f)); } else { if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::FPS_COUNTER) { snprintf(fpsbuf, sizeof(fpsbuf), "FPS: %0.1f", actual_fps); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 4c1d301e61..8460d948c4 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1888,6 +1888,9 @@ void DeveloperToolsScreen::CreateViews() { ffMode->SetEnabledFunc([]() { return !g_Config.bVSync; }); ffMode->HideChoice(1); // not used + auto displayRefreshRate = list->Add(new PopupSliderChoice(&g_Config.iDisplayRefreshRate, 60, 1000, 60, dev->T("Display refresh rate"), 1, screenManager())); + displayRefreshRate->SetFormat(dev->T("%d Hz")); + Draw::DrawContext *draw = screenManager()->getDrawContext(); list->Add(new ItemHeader(dev->T("Ubershaders"))); From 1aec09a056b616034677f106e2450c8833a083b2 Mon Sep 17 00:00:00 2001 From: Nabile Rahmani Date: Sat, 13 Jul 2024 23:08:38 +0200 Subject: [PATCH 2/6] Initialise `iDisplayRefreshRate` to 60. Fixes tests as code would read an uninitialised value before the config's constructor gets to run. --- Core/Config.cpp | 2 +- Core/Config.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index f7cee90453..b2baef8c24 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -694,7 +694,7 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("UberShaderVertex", &g_Config.bUberShaderVertex, true, CfgFlag::DEFAULT), ConfigSetting("UberShaderFragment", &g_Config.bUberShaderFragment, true, CfgFlag::DEFAULT), - ConfigSetting("DisplayRefreshRate", &g_Config.iDisplayRefreshRate, 60, CfgFlag::PER_GAME), + ConfigSetting("DisplayRefreshRate", &g_Config.iDisplayRefreshRate, g_Config.iDisplayRefreshRate, CfgFlag::PER_GAME), }; static const ConfigSetting soundSettings[] = { diff --git a/Core/Config.h b/Core/Config.h index 21da06a770..846029f16f 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -160,7 +160,7 @@ public: std::string sMicDevice; bool bCameraMirrorHorizontal; int iDisplayFramerateMode; // enum DisplayFramerateMode. Android-only. - int iDisplayRefreshRate; + int iDisplayRefreshRate = 60; bool bSoftwareRendering; bool bSoftwareRenderingJit; From 273f4cce314ffc87501932e6d41dc6f896bfe514 Mon Sep 17 00:00:00 2001 From: Nabile Rahmani Date: Sun, 14 Jul 2024 04:47:26 +0200 Subject: [PATCH 3/6] Refresh rate: don't programmatically set configuration, detect VR instead --- Common/VR/PPSSPPVR.cpp | 1 - Core/HLE/sceDisplay.cpp | 13 ++++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index 9741d197ac..d592f4f70b 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -684,7 +684,6 @@ bool StartVRRender() { vrCompat[VR_COMPAT_SKYPLANE] = PSP_CoreParameter().compat.vrCompat().Skyplane; // Set customizations - g_Config.iDisplayRefreshRate = g_Config.bForce72Hz ? 72 : 60; VR_SetConfigFloat(VR_CONFIG_CANVAS_DISTANCE, vrScene && (appMode == VR_GAME_MODE) ? g_Config.fCanvas3DDistance : g_Config.fCanvasDistance); VR_SetConfig(VR_CONFIG_PASSTHROUGH, g_Config.bPassthrough); return true; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 62f5390989..5bcbcd4cf7 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -152,7 +152,7 @@ void __DisplayVblankBeginCallback(SceUID threadID, SceUID prevCallbackId); void __DisplayVblankEndCallback(SceUID threadID, SceUID prevCallbackId); void __DisplayFlip(int cyclesLate); -static void __DisplaySetFramerate(int value); +static void __DisplaySetFramerate(void); static bool UseLagSync() { return g_Config.bForceLagSync && !g_Config.bAutoFrameSkip; @@ -563,8 +563,7 @@ static void NotifyUserIfSlow() { } void __DisplayFlip(int cyclesLate) { - if (g_Config.iDisplayRefreshRate != framerate) - __DisplaySetFramerate(g_Config.iDisplayRefreshRate); + __DisplaySetFramerate(); flippedThisFrame = true; // We flip only if the framebuffer was dirty. This eliminates flicker when using @@ -1130,8 +1129,12 @@ void Register_sceDisplay_driver() { RegisterModule("sceDisplay_driver", ARRAY_SIZE(sceDisplay), sceDisplay); } -static void __DisplaySetFramerate(int value) { - framerate = value; +static void __DisplaySetFramerate(void) { + if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_VR) + framerate = g_Config.bForce72Hz ? 72 : 60; + else + framerate = g_Config.iDisplayRefreshRate; + timePerVblank = 1.001 / (double)framerate; frameMs = 1001.0 / (double)framerate; } From 480b94f4d2afe1df75c9566cb7628b343d8e929f Mon Sep 17 00:00:00 2001 From: Nabile Rahmani Date: Sun, 14 Jul 2024 05:10:28 +0200 Subject: [PATCH 4/6] Fix speed percentage calculation --- UI/DebugOverlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/DebugOverlay.cpp b/UI/DebugOverlay.cpp index fea1b03a04..b47f5fc8c7 100644 --- a/UI/DebugOverlay.cpp +++ b/UI/DebugOverlay.cpp @@ -437,7 +437,7 @@ void DrawFPS(UIContext *ctx, const Bounds &bounds) { char fpsbuf[256]{}; if (g_Config.iShowStatusFlags == ((int)ShowStatusFlags::FPS_COUNTER | (int)ShowStatusFlags::SPEED_COUNTER)) { - snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / (g_Config.iDisplayRefreshRate / 100.0f)); + snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / ((g_Config.iDisplayRefreshRate - 0.06f) / 100.0f)); } else { if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::FPS_COUNTER) { snprintf(fpsbuf, sizeof(fpsbuf), "FPS: %0.1f", actual_fps); From 9272f02ba2e04a9a24c9ff33b5401ed78a8d7b06 Mon Sep 17 00:00:00 2001 From: Nabile Rahmani Date: Sun, 14 Jul 2024 08:21:46 +0200 Subject: [PATCH 5/6] Initialise framerate-related variables in `__DisplayInit()` This takes into account the user configuration a frame in advance. --- Core/HLE/sceDisplay.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 5bcbcd4cf7..1e2c9c08ab 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -106,10 +106,10 @@ static int height; static bool wasPaused; static bool flippedThisFrame; -static int framerate = g_Config.iDisplayRefreshRate; +static int framerate; // 1.001f to compensate for the classic 59.94 NTSC framerate that the PSP seems to have. -static double timePerVblank = 1.001 / framerate; +static double timePerVblank; // Don't include this in the state, time increases regardless of state. static double curFrameTime; @@ -131,7 +131,7 @@ const double vblankMs = 0.7315; // These are guesses based on tests. const double vsyncStartMs = 0.5925; const double vsyncEndMs = 0.7265; -double frameMs = 1001.0 / (double)framerate; +double frameMs; enum { PSP_DISPLAY_SETBUF_IMMEDIATE = 0, @@ -172,6 +172,7 @@ static void ScheduleLagSync(int over = 0) { } void __DisplayInit() { + __DisplaySetFramerate(); DisplayHWInit(); hasSetMode = false; mode = 0; From 730613ce67120f8c1797515139a1d8340c159dc9 Mon Sep 17 00:00:00 2001 From: Nabile Rahmani Date: Sun, 14 Jul 2024 11:30:11 +0200 Subject: [PATCH 6/6] Scale speed percentage calculation to eliminate micro deviations --- UI/DebugOverlay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/DebugOverlay.cpp b/UI/DebugOverlay.cpp index b47f5fc8c7..f196f0f3b0 100644 --- a/UI/DebugOverlay.cpp +++ b/UI/DebugOverlay.cpp @@ -437,7 +437,7 @@ void DrawFPS(UIContext *ctx, const Bounds &bounds) { char fpsbuf[256]{}; if (g_Config.iShowStatusFlags == ((int)ShowStatusFlags::FPS_COUNTER | (int)ShowStatusFlags::SPEED_COUNTER)) { - snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / ((g_Config.iDisplayRefreshRate - 0.06f) / 100.0f)); + snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / ((g_Config.iDisplayRefreshRate / 60.0f * 59.94f) / 100.0f)); } else { if (g_Config.iShowStatusFlags & (int)ShowStatusFlags::FPS_COUNTER) { snprintf(fpsbuf, sizeof(fpsbuf), "FPS: %0.1f", actual_fps);