From 2c8a4ff1544b34a0051c3f0955bdea22f751b69b Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Tue, 3 Nov 2020 15:58:40 +1000 Subject: [PATCH] HostDisplay: Add an option to decouple display fps from emulator fps Makes Android so much faster... --- src/core/host_display.cpp | 20 +++++++++++++++++++ src/core/host_display.h | 6 ++++++ src/core/host_interface.cpp | 1 + src/core/settings.cpp | 2 ++ src/core/settings.h | 1 + src/duckstation-qt/advancedsettingswidget.cpp | 3 +++ src/frontend-common/common_host_interface.cpp | 8 +++++++- src/frontend-common/d3d11_host_display.cpp | 13 ++++++++++++ src/frontend-common/opengl_host_display.cpp | 13 ++++++++++++ src/frontend-common/vulkan_host_display.cpp | 13 ++++++++++++ 10 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp index 06db6768d..aa341a260 100644 --- a/src/core/host_display.cpp +++ b/src/core/host_display.cpp @@ -2,6 +2,7 @@ #include "common/file_system.h" #include "common/log.h" #include "common/string_util.h" +#include "common/timer.h" #include "stb_image.h" #include "stb_image_resize.h" #include "stb_image_write.h" @@ -14,6 +15,25 @@ HostDisplayTexture::~HostDisplayTexture() = default; HostDisplay::~HostDisplay() = default; +void HostDisplay::SetDisplayMaxFPS(float max_fps) +{ + m_display_frame_interval = (max_fps > 0.0f) ? (1.0f / max_fps) : 0.0f; +} + +bool HostDisplay::ShouldSkipDisplayingFrame() +{ + if (m_display_frame_interval == 0.0f) + return false; + + const u64 now = Common::Timer::GetValue(); + const double diff = Common::Timer::ConvertValueToSeconds(now - m_last_frame_displayed_time); + if (diff < m_display_frame_interval) + return true; + + m_last_frame_displayed_time = now; + return false; +} + void HostDisplay::SetSoftwareCursor(std::unique_ptr texture, float scale /*= 1.0f*/) { m_cursor_texture = std::move(texture); diff --git a/src/core/host_display.h b/src/core/host_display.h index f4cda7756..7241977b7 100644 --- a/src/core/host_display.h +++ b/src/core/host_display.h @@ -94,6 +94,9 @@ public: const s32 GetDisplayTopMargin() const { return m_display_top_margin; } const float GetDisplayAspectRatio() const { return m_display_aspect_ratio; } + void SetDisplayMaxFPS(float max_fps); + bool ShouldSkipDisplayingFrame(); + void ClearDisplayTexture() { m_display_texture_handle = nullptr; @@ -181,6 +184,8 @@ protected: WindowInfo m_window_info; + u64 m_last_frame_displayed_time = 0; + s32 m_mouse_position_x = 0; s32 m_mouse_position_y = 0; @@ -191,6 +196,7 @@ protected: s32 m_display_active_width = 0; s32 m_display_active_height = 0; float m_display_aspect_ratio = 1.0f; + float m_display_frame_interval = 0.0f; void* m_display_texture_handle = nullptr; s32 m_display_texture_width = 0; diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index 7331b7445..ed0bd5dd2 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -463,6 +463,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si) si.SetBoolValue("Display", "Fullscreen", false); si.SetBoolValue("Display", "VSync", true); si.SetStringValue("Display", "PostProcessChain", ""); + si.SetFloatValue("Display", "MaxFPS", 0.0f); si.SetBoolValue("CDROM", "ReadThread", true); si.SetBoolValue("CDROM", "RegionCheck", true); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 7e02f7366..e52583cae 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -177,6 +177,7 @@ void Settings::Load(SettingsInterface& si) display_show_resolution = si.GetBoolValue("Display", "ShowResolution", false); video_sync_enabled = si.GetBoolValue("Display", "VSync", true); display_post_process_chain = si.GetStringValue("Display", "PostProcessChain", ""); + display_max_fps = si.GetFloatValue("Display", "MaxFPS", 0.0f); cdrom_read_thread = si.GetBoolValue("CDROM", "ReadThread", true); cdrom_region_check = si.GetBoolValue("CDROM", "RegionCheck", true); @@ -304,6 +305,7 @@ void Settings::Save(SettingsInterface& si) const si.DeleteValue("Display", "PostProcessChain"); else si.SetStringValue("Display", "PostProcessChain", display_post_process_chain.c_str()); + si.SetFloatValue("Display", "MaxFPS", display_max_fps); si.SetBoolValue("CDROM", "ReadThread", cdrom_read_thread); si.SetBoolValue("CDROM", "RegionCheck", cdrom_region_check); diff --git a/src/core/settings.h b/src/core/settings.h index f5fec8764..b018320a5 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -123,6 +123,7 @@ struct Settings bool display_show_speed = false; bool display_show_resolution = false; bool video_sync_enabled = true; + float display_max_fps = 0.0f; bool cdrom_read_thread = true; bool cdrom_region_check = true; diff --git a/src/duckstation-qt/advancedsettingswidget.cpp b/src/duckstation-qt/advancedsettingswidget.cpp index b1eb72ff3..0b1865a7a 100644 --- a/src/duckstation-qt/advancedsettingswidget.cpp +++ b/src/duckstation-qt/advancedsettingswidget.cpp @@ -105,6 +105,8 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(QtHostInterface* host_interface, 1000, Settings::DEFAULT_GPU_MAX_RUN_AHEAD); addBooleanTweakOption(m_host_interface, m_ui.tweakOptionTable, tr("Use Debug Host GPU Device"), "GPU", "UseDebugDevice", false); + addIntRangeTweakOption(m_host_interface, m_ui.tweakOptionTable, tr("Display FPS Limit"), "Display", "MaxFPS", 0, 1000, + 0); } AdvancedSettingsWidget::~AdvancedSettingsWidget() = default; @@ -122,4 +124,5 @@ void AdvancedSettingsWidget::onResetToDefaultClicked() setIntRangeTweakOption(m_ui.tweakOptionTable, 8, static_cast(Settings::DEFAULT_GPU_FIFO_SIZE)); setIntRangeTweakOption(m_ui.tweakOptionTable, 9, static_cast(Settings::DEFAULT_GPU_MAX_RUN_AHEAD)); setBooleanTweakOption(m_ui.tweakOptionTable, 10, false); + setIntRangeTweakOption(m_ui.tweakOptionTable, 11, 0); } diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index 1a0c0e87c..91e15746c 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -605,8 +605,10 @@ void CommonHostInterface::UpdateSpeedLimiterState() !System::IsRunning() || (m_speed_limiter_enabled && g_settings.audio_sync_enabled && !is_non_standard_speed); const bool video_sync_enabled = !System::IsRunning() || (m_speed_limiter_enabled && g_settings.video_sync_enabled && !is_non_standard_speed); + const float max_display_fps = m_speed_limiter_enabled ? 0.0f : g_settings.display_max_fps; Log_InfoPrintf("Syncing to %s%s", audio_sync_enabled ? "audio" : "", (audio_sync_enabled && video_sync_enabled) ? " and video" : (video_sync_enabled ? "video" : "")); + Log_InfoPrintf("Max display fps: %f", max_display_fps); if (m_audio_stream) { @@ -616,7 +618,10 @@ void CommonHostInterface::UpdateSpeedLimiterState() } if (m_display) + { + m_display->SetDisplayMaxFPS(max_display_fps); m_display->SetVSync(video_sync_enabled); + } if (g_settings.increase_timer_resolution) SetTimerResolutionIncreased(m_speed_limiter_enabled); @@ -2077,7 +2082,8 @@ void CommonHostInterface::CheckForSettingsChanges(const Settings& old_settings) g_settings.audio_sync_enabled != old_settings.audio_sync_enabled || g_settings.speed_limiter_enabled != old_settings.speed_limiter_enabled || g_settings.increase_timer_resolution != old_settings.increase_timer_resolution || - g_settings.emulation_speed != old_settings.emulation_speed) + g_settings.emulation_speed != old_settings.emulation_speed || + g_settings.display_max_fps != old_settings.display_max_fps) { UpdateSpeedLimiterState(); } diff --git a/src/frontend-common/d3d11_host_display.cpp b/src/frontend-common/d3d11_host_display.cpp index ef15a9c16..23c2bc771 100644 --- a/src/frontend-common/d3d11_host_display.cpp +++ b/src/frontend-common/d3d11_host_display.cpp @@ -661,6 +661,19 @@ void D3D11HostDisplay::DestroyImGuiContext() bool D3D11HostDisplay::Render() { #ifndef LIBRETRO + if (ShouldSkipDisplayingFrame()) + { +#ifdef WITH_IMGUI + if (ImGui::GetCurrentContext()) + { + ImGui::Render(); + ImGui_ImplDX11_NewFrame(); + } +#endif + + return false; + } + static constexpr std::array clear_color = {}; m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color.data()); m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr); diff --git a/src/frontend-common/opengl_host_display.cpp b/src/frontend-common/opengl_host_display.cpp index 983f16025..b48d89bda 100644 --- a/src/frontend-common/opengl_host_display.cpp +++ b/src/frontend-common/opengl_host_display.cpp @@ -464,6 +464,19 @@ void OpenGLHostDisplay::DestroyResources() bool OpenGLHostDisplay::Render() { + if (ShouldSkipDisplayingFrame()) + { +#ifdef WITH_IMGUI + if (ImGui::GetCurrentContext()) + { + ImGui::Render(); + ImGui_ImplOpenGL3_NewFrame(); + } +#endif + + return false; + } + glDisable(GL_SCISSOR_TEST); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); diff --git a/src/frontend-common/vulkan_host_display.cpp b/src/frontend-common/vulkan_host_display.cpp index ba7484f3e..73d49b8ea 100644 --- a/src/frontend-common/vulkan_host_display.cpp +++ b/src/frontend-common/vulkan_host_display.cpp @@ -539,6 +539,19 @@ bool VulkanHostDisplay::CreateImGuiContext() bool VulkanHostDisplay::Render() { + if (ShouldSkipDisplayingFrame()) + { +#ifdef WITH_IMGUI + if (ImGui::GetCurrentContext()) + { + ImGui::Render(); + ImGui_ImplVulkan_NewFrame(); + } +#endif + + return false; + } + VkResult res = m_swap_chain->AcquireNextImage(); if (res != VK_SUCCESS) {