diff --git a/Core/Config.cpp b/Core/Config.cpp index 8ff95fcde5eb..22b88a760484 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -621,6 +621,7 @@ static ConfigSetting graphicsSettings[] = { ReportedConfigSetting("AndroidHwScale", &g_Config.iAndroidHwScale, &DefaultAndroidHwScale), ReportedConfigSetting("HighQualityDepth", &g_Config.bHighQualityDepth, true, true, true), ReportedConfigSetting("FrameSkip", &g_Config.iFrameSkip, 0, true, true), + ReportedConfigSetting("FrameSkipType", &g_Config.iFrameSkipType, 0, true, true), ReportedConfigSetting("AutoFrameSkip", &g_Config.bAutoFrameSkip, false, true, true), ConfigSetting("FrameRate", &g_Config.iFpsLimit1, 0, true, true), ConfigSetting("FrameRate2", &g_Config.iFpsLimit2, -1, true, true), diff --git a/Core/Config.h b/Core/Config.h index 715800c79aa1..311516d7695d 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -143,6 +143,7 @@ struct Config { bool bSustainedPerformanceMode; // Android: Slows clocks down to avoid overheating/speed fluctuations. bool bVSync; int iFrameSkip; + int iFrameSkipType; bool bAutoFrameSkip; bool bFrameSkipUnthrottle; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 9d8bfe0990df..da3bf70f86f7 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -503,6 +503,17 @@ static void DoFrameDropLogging(float scaledTimestep) { } } +static int CalculateFrameSkip() { + // Used to calculate the FPS to skip if the user sets the type to percent of fps + int frameSkipNum; + if (g_Config.iFrameSkipType == 1) { + frameSkipNum = ceil( flips * (static_cast(g_Config.iFrameSkip) / 100.00) ); + } else { + frameSkipNum = g_Config.iFrameSkip; + } + return frameSkipNum; +} + // Let's collect all the throttling and frameskipping logic here. static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { PROFILE_THIS_SCOPE("timing"); @@ -553,15 +564,16 @@ static void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) { // Auto-frameskip automatically if speed limit is set differently than the default. bool useAutoFrameskip = g_Config.bAutoFrameSkip && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; bool forceFrameskip = (fpsLimiter == FPSLimit::CUSTOM1 && g_Config.iFpsLimit1 > 60) || (fpsLimiter == FPSLimit::CUSTOM2 && g_Config.iFpsLimit2 > 60); + int frameSkipNum = CalculateFrameSkip(); if (g_Config.bAutoFrameSkip || forceFrameskip) { // autoframeskip // Argh, we are falling behind! Let's skip a frame and see if we catch up. if (curFrameTime > nextFrameTime && doFrameSkip) { skipFrame = true; } - } else if (g_Config.iFrameSkip >= 1) { + } else if (frameSkipNum >= 1) { // fixed frameskip - if (numSkippedFrames >= g_Config.iFrameSkip) + if (numSkippedFrames >= frameSkipNum) skipFrame = false; else skipFrame = true; @@ -734,9 +746,10 @@ void __DisplayFlip(int cyclesLate) { DoFrameTiming(throttle, skipFrame, (float)numVBlanksSinceFlip * timePerVblank); int maxFrameskip = 8; + int frameSkipNum = CalculateFrameSkip(); if (throttle) { // 4 here means 1 drawn, 4 skipped - so 12 fps minimum. - maxFrameskip = g_Config.iFrameSkip; + maxFrameskip = frameSkipNum; } if (numSkippedFrames >= maxFrameskip || GPURecord::IsActivePending()) { skipFrame = false; diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 123f6605c8d7..f2d7723d79ec 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -511,6 +511,8 @@ void MainWindow::createMenus() ->addEventChecked(&g_Config.bVertexCache); videoMenu->add(new MenuAction(this, SLOT(frameskipAct()), QT_TR_NOOP("&Frameskip"))) ->addEventChecked(&g_Config.iFrameSkip); + videoMenu->add(new MenuAction(this, SLOT(frameskipTypeAct()), QT_TR_NOOP("&FrameSkipType"))) + ->addEventChecked(&g_Config.iFrameSkipType); optionsMenu->add(new MenuAction(this, SLOT(audioAct()), QT_TR_NOOP("&Audio"))) ->addEventChecked(&g_Config.bEnableSound); optionsMenu->addSeparator(); diff --git a/Qt/mainwindow.h b/Qt/mainwindow.h index 90ab051b9123..2745d731833e 100644 --- a/Qt/mainwindow.h +++ b/Qt/mainwindow.h @@ -114,6 +114,7 @@ private slots: void transformAct() { g_Config.bHardwareTransform = !g_Config.bHardwareTransform; } void vertexCacheAct() { g_Config.bVertexCache = !g_Config.bVertexCache; } void frameskipAct() { g_Config.iFrameSkip = !g_Config.iFrameSkip; } + void frameskipTypeAct() { g_Config.iFrameSkipType = !g_Config.iFrameSkipType; } // Sound void audioAct() { g_Config.bEnableSound = !g_Config.bEnableSound; } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 43bb8dd58a35..a1db27bd3b29 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -275,6 +275,8 @@ void GameSettingsScreen::CreateViews() { graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control"))); static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"}; graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkip, gr->T("Frame Skipping"), frameSkip, 0, ARRAY_SIZE(frameSkip), gr->GetName(), screenManager())); + static const char *frameSkipType[] = {"Number of Frames", "Percent of FPS"}; + graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkipType, gr->T("Frame Skipping Type"), frameSkipType, 0, ARRAY_SIZE(frameSkipType), gr->GetName(), screenManager())); frameSkipAuto_ = graphicsSettings->Add(new CheckBox(&g_Config.bAutoFrameSkip, gr->T("Auto FrameSkip"))); frameSkipAuto_->OnClick.Handle(this, &GameSettingsScreen::OnAutoFrameskip); graphicsSettings->Add(new CheckBox(&cap60FPS_, gr->T("Force max 60 FPS (helps GoW)"))); diff --git a/Windows/MainWindow.h b/Windows/MainWindow.h index bf879b1bc132..835d22cb2ecc 100644 --- a/Windows/MainWindow.h +++ b/Windows/MainWindow.h @@ -35,6 +35,9 @@ namespace MainWindow FRAMESKIP_8 = 8, FRAMESKIP_MAX = FRAMESKIP_8, + FRAMESKIPTYPE_0 = 0, + FRAMESKIPTYPE_1 = 1, + RESOLUTION_AUTO = 0, RESOLUTION_NATIVE = 1, RESOLUTION_2X = 2, diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 470599f71f04..ba25f60bed48 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -337,6 +337,7 @@ namespace MainWindow { TranslateSubMenu(menu, "Frame Skipping", MENU_OPTIONS, SUBMENU_FRAME_SKIPPING, L"\tF7"); TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_AUTO); TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIP_0); + TranslateMenuItem(menu, ID_OPTIONS_FRAMESKIPTYPE_0); // Skip frameskipping 1-8.. TranslateSubMenu(menu, "Texture Filtering", MENU_OPTIONS, SUBMENU_TEXTURE_FILTERING); TranslateMenuItem(menu, ID_OPTIONS_TEXTUREFILTERING_AUTO); @@ -542,6 +543,26 @@ namespace MainWindow { osm.Show(messageStream.str()); } + static void setFrameSkippingType(int fskipType = -1) { + if (fskipType >= 0 && fskipType <= 1) { + g_Config.iFrameSkipType = fskipType; + } else { + g_Config.iFrameSkipType = 0; + } + + I18NCategory *gr = GetI18NCategory("Graphics"); + + std::ostringstream messageStream; + messageStream << gr->T("Frame Skipping Type") << ":" << " "; + + if (g_Config.iFrameSkipType == 0) + messageStream << gr->T("Number of Frames"); + else + messageStream << gr->T("Percent of FPS"); + + osm.Show(messageStream.str()); + } + static void enableCheats(bool cheats) { g_Config.bEnableCheats = cheats; } @@ -825,8 +846,12 @@ namespace MainWindow { case ID_OPTIONS_FRAMESKIP_7: setFrameSkipping(FRAMESKIP_7); break; case ID_OPTIONS_FRAMESKIP_8: setFrameSkipping(FRAMESKIP_MAX); break; + case ID_OPTIONS_FRAMESKIPTYPE_0: setFrameSkippingType(FRAMESKIPTYPE_0); break; + case ID_OPTIONS_FRAMESKIPTYPE_1: setFrameSkippingType(FRAMESKIPTYPE_1); break; + case ID_OPTIONS_FRAMESKIPDUMMY: setFrameSkipping(); + setFrameSkippingType(); break; case ID_FILE_EXIT: @@ -1076,6 +1101,7 @@ namespace MainWindow { CHECKITEM(ID_OPTIONS_SHOWFPS, g_Config.iShowFPSCounter); CHECKITEM(ID_OPTIONS_FRAMESKIP_AUTO, g_Config.bAutoFrameSkip); CHECKITEM(ID_OPTIONS_FRAMESKIP, g_Config.iFrameSkip != FRAMESKIP_OFF); + CHECKITEM(ID_OPTIONS_FRAMESKIPTYPE, g_Config.iFrameSkipType); CHECKITEM(ID_OPTIONS_VSYNC, g_Config.bVSync); CHECKITEM(ID_OPTIONS_TOPMOST, g_Config.bTopMost); CHECKITEM(ID_OPTIONS_PAUSE_FOCUS, g_Config.bPauseOnLostFocus); @@ -1248,6 +1274,12 @@ namespace MainWindow { ID_OPTIONS_FRAMESKIP_7, ID_OPTIONS_FRAMESKIP_8, }; + + static const int frameskippingType[] = { + ID_OPTIONS_FRAMESKIPTYPE_0, + ID_OPTIONS_FRAMESKIPTYPE_1, + }; + if (g_Config.iFrameSkip < FRAMESKIP_OFF) g_Config.iFrameSkip = FRAMESKIP_OFF; @@ -1258,6 +1290,10 @@ namespace MainWindow { CheckMenuItem(menu, frameskipping[i], MF_BYCOMMAND | ((i == g_Config.iFrameSkip) ? MF_CHECKED : MF_UNCHECKED)); } + for (int i = 0; i < ARRAY_SIZE(frameskippingType); i++) { + CheckMenuItem(menu, frameskippingType[i], MF_BYCOMMAND | ((i == g_Config.iFrameSkipType) ? MF_CHECKED : MF_UNCHECKED)); + } + static const int savestateSlot[] = { ID_FILE_SAVESTATE_SLOT_1, ID_FILE_SAVESTATE_SLOT_2, diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 6fdcdaf9e999..64ebceda3f87 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -589,6 +589,11 @@ BEGIN MENUITEM "&7", ID_OPTIONS_FRAMESKIP_7 MENUITEM "&8", ID_OPTIONS_FRAMESKIP_8 END + POPUP "Frame Skipping Type" + BEGIN + MENUITEM "Skip Number of Frames", ID_OPTIONS_FRAMESKIPTYPE_0 + MENUITEM "Skip Percent of FPS", ID_OPTIONS_FRAMESKIPTYPE_1 + END POPUP "Texture Filtering" BEGIN MENUITEM "Auto", ID_OPTIONS_TEXTUREFILTERING_AUTO diff --git a/Windows/resource.h b/Windows/resource.h index 0c7873911301..2dddf88dd478 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -343,6 +343,8 @@ #define ID_EMULATION_PAUSE 40177 #define ID_HELP_DISCORD 40178 #define IDC_GEDBG_STEPCURVE 40179 +#define ID_OPTIONS_FRAMESKIPTYPE_0 40180 +#define ID_OPTIONS_FRAMESKIPTYPE_1 40181 // Dummy option to let the buffered rendering hotkey cycle through all the options. #define ID_OPTIONS_BUFFEREDRENDERINGDUMMY 40500 diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 3c656fe55b1d..22615e27f43c 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -170,6 +170,7 @@ static RetroOption ppsspp_rendering_mode("ppsspp_rendering_mode", "Renderin static RetroOption ppsspp_true_color("ppsspp_true_color", "True Color Depth", true); static RetroOption ppsspp_auto_frameskip("ppsspp_auto_frameskip", "Auto Frameskip", false); static RetroOption ppsspp_frameskip("ppsspp_frameskip", "Frameskip", 0, 10); +static RetroOption ppsspp_frameskiptype("ppsspp_frameskiptype", "Frameskip Type", 0, 10); static RetroOption ppsspp_force_max_fps("ppsspp_force_max_fps", "Force Max FPS", { { "disabled", 0 }, { "enabled", 60 } }); static RetroOption ppsspp_audio_latency("ppsspp_audio_latency", "Audio latency", { "low", "medium", "high" }); static RetroOption ppsspp_internal_resolution("ppsspp_internal_resolution", "Internal Resolution", 1, { "480x272", "960x544", "1440x816", "1920x1088", "2400x1360", "2880x1632", "3360x1904", "3840x2176", "4320x2448", "4800x2720" }); @@ -198,6 +199,7 @@ void retro_set_environment(retro_environment_t cb) { vars.push_back(ppsspp_true_color.GetOptions()); vars.push_back(ppsspp_auto_frameskip.GetOptions()); vars.push_back(ppsspp_frameskip.GetOptions()); + vars.push_back(ppsspp_frameskiptype.GetOptions()); vars.push_back(ppsspp_force_max_fps.GetOptions()); vars.push_back(ppsspp_audio_latency.GetOptions()); vars.push_back(ppsspp_internal_resolution.GetOptions()); @@ -268,6 +270,7 @@ static void check_variables(CoreParameter &coreParam) { ppsspp_vertex_cache.Update(&g_Config.bVertexCache); ppsspp_gpu_hardware_transform.Update(&g_Config.bHardwareTransform); ppsspp_frameskip.Update(&g_Config.iFrameSkip); + ppsspp_frameskiptype.Update(&g_Config.iFrameSkipType); ppsspp_audio_latency.Update(&g_Config.iAudioLatency); ppsspp_true_color.Update(&g_Config.bTrueColor); ppsspp_auto_frameskip.Update(&g_Config.bAutoFrameSkip);