From e6a89e1b2c0f32eee77219801d05e3e2dc4fdcb9 Mon Sep 17 00:00:00 2001 From: KojoZero Date: Wed, 11 Mar 2026 22:06:12 -0700 Subject: [PATCH 1/4] initial changes --- CMakeModules/GenerateSettingKeys.cmake | 1 + .../features/settings/SettingKeys.kt | 1 + src/android/app/src/main/jni/config.cpp | 1 + src/android/app/src/main/jni/default_ini.h | 4 + src/citra_qt/configuration/config.cpp | 2 + .../configuration/configure_graphics.cpp | 8 + .../configuration/configure_graphics.h | 1 + .../configuration/configure_graphics.ui | 166 ++++++++++-------- src/common/settings.cpp | 2 + src/common/settings.h | 1 + .../renderer_vulkan/renderer_vulkan.cpp | 1 - .../renderer_vulkan/renderer_vulkan.h | 2 +- 12 files changed, 110 insertions(+), 80 deletions(-) diff --git a/CMakeModules/GenerateSettingKeys.cmake b/CMakeModules/GenerateSettingKeys.cmake index 15a28a5de..8d9b3326b 100644 --- a/CMakeModules/GenerateSettingKeys.cmake +++ b/CMakeModules/GenerateSettingKeys.cmake @@ -40,6 +40,7 @@ foreach(KEY IN ITEMS "use_disk_shader_cache" "shaders_accurate_mul" "use_vsync" + "use_skip_duplicate_frames" "use_display_refresh_rate_detection" "use_shader_jit" "resolution_factor" diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt index 1d6e0dcee..b4acd2cbe 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/SettingKeys.kt @@ -38,6 +38,7 @@ object SettingKeys { external fun use_disk_shader_cache(): String external fun shaders_accurate_mul(): String external fun use_vsync(): String + external fun use_skip_duplicate_frames(): String external fun use_shader_jit(): String external fun resolution_factor(): String external fun frame_limit(): String diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index e0fa260c5..b88f0d29c 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -152,6 +152,7 @@ void Config::ReadValues() { ReadSetting("Renderer", Settings::values.resolution_factor); ReadSetting("Renderer", Settings::values.use_disk_shader_cache); ReadSetting("Renderer", Settings::values.use_vsync); + ReadSetting("Renderer", Settings::values.use_skip_duplicate_frames); ReadSetting("Renderer", Settings::values.texture_filter); ReadSetting("Renderer", Settings::values.texture_sampling); ReadSetting("Renderer", Settings::values.turbo_limit); diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h index 5e2eab1d1..677ca7ac9 100644 --- a/src/android/app/src/main/jni/default_ini.h +++ b/src/android/app/src/main/jni/default_ini.h @@ -127,6 +127,10 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"( # 0 (default): Off, 1: On )") DECLARE_KEY(use_vsync) BOOST_HANA_STRING(R"( +# Skips display of duplicated frames in 30 fps games +# 0 (default): Off, 1: On +)") DECLARE_KEY(use_skip_duplicate_frames) BOOST_HANA_STRING(R"( + # Reduce stuttering by storing and loading generated shaders to disk # 0: Off, 1 (default. On) )") DECLARE_KEY(use_disk_shader_cache) BOOST_HANA_STRING(R"( diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index fa6ff311d..9413b2b15 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -701,6 +701,7 @@ void QtConfig::ReadRendererValues() { ReadGlobalSetting(Settings::values.shaders_accurate_mul); ReadGlobalSetting(Settings::values.use_disk_shader_cache); ReadGlobalSetting(Settings::values.use_vsync); + ReadGlobalSetting(Settings::values.use_skip_duplicate_frames); ReadGlobalSetting(Settings::values.use_display_refresh_rate_detection); ReadGlobalSetting(Settings::values.resolution_factor); ReadGlobalSetting(Settings::values.use_integer_scaling); @@ -1242,6 +1243,7 @@ void QtConfig::SaveRendererValues() { WriteGlobalSetting(Settings::values.shaders_accurate_mul); WriteGlobalSetting(Settings::values.use_disk_shader_cache); WriteGlobalSetting(Settings::values.use_vsync); + WriteGlobalSetting(Settings::values.use_skip_duplicate_frames); WriteGlobalSetting(Settings::values.use_display_refresh_rate_detection); WriteGlobalSetting(Settings::values.resolution_factor); WriteGlobalSetting(Settings::values.use_integer_scaling); diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp index aac27ab16..bbf529757 100644 --- a/src/citra_qt/configuration/configure_graphics.cpp +++ b/src/citra_qt/configuration/configure_graphics.cpp @@ -7,6 +7,7 @@ #include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_graphics.h" #include "common/settings.h" +#include "configuration/ui_configure_graphics.h" #include "ui_configure_graphics.h" #ifdef ENABLE_VULKAN #include "video_core/renderer_vulkan/vk_instance.h" @@ -144,6 +145,7 @@ void ConfigureGraphics::SetConfiguration() { ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue()); ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); ui->toggle_vsync->setChecked(Settings::values.use_vsync.GetValue()); + ui->toggle_skip_duplicate_frames->setChecked(Settings::values.use_skip_duplicate_frames.GetValue()); ui->spirv_shader_gen->setChecked(Settings::values.spirv_shader_gen.GetValue()); ui->disable_spirv_optimizer->setChecked(Settings::values.disable_spirv_optimizer.GetValue()); ui->toggle_async_shaders->setChecked(Settings::values.async_shader_compilation.GetValue()); @@ -179,6 +181,8 @@ void ConfigureGraphics::ApplyConfiguration() { ui->toggle_disk_shader_cache, use_disk_shader_cache); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->toggle_vsync, use_vsync); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_skip_duplicate_frames, ui->toggle_skip_duplicate_frames, + use_skip_duplicate_frames); ConfigurationShared::ApplyPerGameSetting( &Settings::values.delay_game_render_thread_us, ui->delay_render_combo, [this](s32) { return ui->delay_render_slider->value(); }); @@ -204,6 +208,8 @@ void ConfigureGraphics::SetupPerGameUI() { Settings::values.use_disk_shader_cache.UsingGlobal()); ui->toggle_vsync->setEnabled(ui->toggle_vsync->isEnabled() && Settings::values.use_vsync.UsingGlobal()); + ui->toggle_skip_duplicate_frames->setEnabled(ui->toggle_skip_duplicate_frames->isEnabled() && + Settings::values.use_skip_duplicate_frames.UsingGlobal()); ui->toggle_async_shaders->setEnabled( Settings::values.async_shader_compilation.UsingGlobal()); ui->widget_texture_sampling->setEnabled(Settings::values.texture_sampling.UsingGlobal()); @@ -244,6 +250,8 @@ void ConfigureGraphics::SetupPerGameUI() { use_disk_shader_cache); ConfigurationShared::SetColoredTristate(ui->toggle_vsync, Settings::values.use_vsync, use_vsync); + ConfigurationShared::SetColoredTristate(ui->toggle_skip_duplicate_frames, Settings::values.use_skip_duplicate_frames, + use_skip_duplicate_frames); ConfigurationShared::SetColoredTristate(ui->toggle_async_shaders, Settings::values.async_shader_compilation, async_shader_compilation); diff --git a/src/citra_qt/configuration/configure_graphics.h b/src/citra_qt/configuration/configure_graphics.h index 61a462a67..8b387254a 100644 --- a/src/citra_qt/configuration/configure_graphics.h +++ b/src/citra_qt/configuration/configure_graphics.h @@ -39,6 +39,7 @@ private: ConfigurationShared::CheckState shaders_accurate_mul; ConfigurationShared::CheckState use_disk_shader_cache; ConfigurationShared::CheckState use_vsync; + ConfigurationShared::CheckState use_skip_duplicate_frames; ConfigurationShared::CheckState use_display_refresh_rate_detection; ConfigurationShared::CheckState async_shader_compilation; ConfigurationShared::CheckState async_presentation; diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index ff5ec613f..a73ec4468 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -7,7 +7,7 @@ 0 0 400 - 509 + 559 @@ -138,12 +138,12 @@ - - Disable GLSL -> SPIR-V optimizer - <html><head/><body><p>Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.</p></body></html> + + Disable GLSL -> SPIR-V optimizer + @@ -317,6 +317,16 @@ + + + + <html><head/><body><p>Skips presenting duplicated frames in 30fps games. This allows external frame generation tools to work correctly in 30fps games. This does not boost performance.</p></body></html> + + + Skip Duplicate Frame Display + + + @@ -328,81 +338,81 @@ - - - - 0 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + Use global - - 0 + + + + Use per-application - - 0 - - - 0 - - - - - - Use global - - - - - Use per-application - - - - - - - - Delay Application Render Thread - - - <html><head/><body><p>Delays the emulated application render thread the specified amount of milliseconds every time it submits render commands to the GPU.</p><p>Adjust this feature in the (very few) dynamic framerate applications to fix performance issues.</p></body></html> - - - - - - - 0 - - - 16000 - - - 100 - - - 250 - - - 0 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - + + + + + + + <html><head/><body><p>Delays the emulated application render thread the specified amount of milliseconds every time it submits render commands to the GPU.</p><p>Adjust this feature in the (very few) dynamic framerate applications to fix performance issues.</p></body></html> + + + Delay Application Render Thread + + + + + + + 0 + + + 16000 + + + 100 + + + 250 + + + 0 + + + Qt::Orientation::Horizontal + + + QSlider::TickPosition::TicksBelow + + + + + + + + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + @@ -410,7 +420,7 @@ - Qt::Vertical + Qt::Orientation::Vertical diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 2ac231f50..f0906711b 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -100,6 +100,7 @@ void LogSettings() { log_setting("Renderer_UseIntegerScaling", values.use_integer_scaling.GetValue()); log_setting("Renderer_FrameLimit", values.frame_limit.GetValue()); log_setting("Renderer_VSyncNew", values.use_vsync.GetValue()); + log_setting("Renderer_SkipDuplicateFrames", values.use_skip_duplicate_frames.GetValue()); log_setting("Renderer_PostProcessingShader", values.pp_shader_name.GetValue()); log_setting("Renderer_FilterMode", values.filter_mode.GetValue()); log_setting("Renderer_TextureFilter", GetTextureFilterName(values.texture_filter.GetValue())); @@ -209,6 +210,7 @@ void RestoreGlobalState(bool is_powered_on) { values.use_disk_shader_cache.SetGlobal(true); values.shaders_accurate_mul.SetGlobal(true); values.use_vsync.SetGlobal(true); + values.use_skip_duplicate_frames.SetGlobal(true); values.resolution_factor.SetGlobal(true); values.use_integer_scaling.SetGlobal(true); values.frame_limit.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index e03c46961..36c739e90 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -518,6 +518,7 @@ struct Values { SwitchableSetting async_presentation{true, Keys::async_presentation}; SwitchableSetting use_hw_shader{true, Keys::use_hw_shader}; SwitchableSetting use_disk_shader_cache{true, Keys::use_disk_shader_cache}; + SwitchableSetting use_skip_duplicate_frames{true, Keys::use_skip_duplicate_frames}; SwitchableSetting shaders_accurate_mul{true, Keys::shaders_accurate_mul}; #ifdef ANDROID // TODO: Fuck this -OS SwitchableSetting use_vsync{false, Keys::use_vsync}; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 0a25c2036..760e1eaa6 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -24,7 +24,6 @@ #include "video_core/host_shaders/vulkan_cursor_vert.h" #include - #if defined(__APPLE__) && !defined(HAVE_LIBRETRO) #include "common/apple_utils.h" #endif diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 14c9bd34f..c236adc7e 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -134,7 +134,7 @@ private: DescriptorUpdateQueue update_queue; RasterizerVulkan rasterizer; std::unique_ptr secondary_present_window_ptr; - + bool skip_frame_display; DescriptorHeap present_heap; vk::UniquePipelineLayout present_pipeline_layout; std::array present_pipelines; From 9b29295b07286d6dd3c365c5072cac417ecd1068 Mon Sep 17 00:00:00 2001 From: KojoZero Date: Thu, 12 Mar 2026 01:44:14 -0700 Subject: [PATCH 2/4] added basic skip duplicate frames implementation --- src/core/perf_stats.cpp | 3 ++ src/core/perf_stats.h | 2 ++ .../renderer_vulkan/renderer_vulkan.cpp | 35 +++++++++++-------- .../renderer_vulkan/renderer_vulkan.h | 1 - .../renderer_vulkan/vk_present_window.cpp | 33 +++++++++++++++++ .../renderer_vulkan/vk_present_window.h | 3 ++ 6 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 8d7724003..05449cd98 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -29,6 +29,8 @@ constexpr std::size_t IgnoreFrames = 5; namespace Core { +bool PerfStats::game_frames_updated = true; + PerfStats::PerfStats(u64 title_id) : title_id(title_id) {} PerfStats::~PerfStats() { @@ -109,6 +111,7 @@ void PerfStats::EndGameFrame() { std::scoped_lock lock{object_mutex}; game_frames += 1; + PerfStats::game_frames_updated = true; } double PerfStats::GetMeanFrametime() const { diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index de358de66..e335db7f3 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -120,6 +120,8 @@ public: artic_events.Set(event, set); } } + /// Boolean representing whether game_frames has been updated since last time it was presented + static bool game_frames_updated; private: mutable std::mutex object_mutex; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 760e1eaa6..a860039fd 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -236,23 +236,28 @@ void RendererVulkan::PrepareDraw(Frame* frame, const Layout::FramebufferLayout& void RendererVulkan::RenderToWindow(PresentWindow& window, const Layout::FramebufferLayout& layout, bool flipped) { - Frame* frame = window.GetRenderFrame(); + if (Core::PerfStats::game_frames_updated){ + Frame* frame = window.GetRenderFrame(); - if (layout.width != frame->width || layout.height != frame->height) { - window.WaitPresent(); - scheduler.Finish(); - window.RecreateFrame(frame, layout.width, layout.height); + if (layout.width != frame->width || layout.height != frame->height) { + window.WaitPresent(); + scheduler.Finish(); + window.RecreateFrame(frame, layout.width, layout.height); + } + + clear_color.float32[0] = Settings::values.bg_red.GetValue(); + clear_color.float32[1] = Settings::values.bg_green.GetValue(); + clear_color.float32[2] = Settings::values.bg_blue.GetValue(); + clear_color.float32[3] = 1.0f; + + DrawScreens(frame, layout, flipped); + scheduler.Flush(frame->render_ready); + window.Present(frame); + Core::PerfStats::game_frames_updated = false; + //LOG_INFO(Render_Vulkan, "Entered Present Thread"); + } else { + //LOG_INFO(Render_Vulkan, "Entered Skip Present Thread"); } - - clear_color.float32[0] = Settings::values.bg_red.GetValue(); - clear_color.float32[1] = Settings::values.bg_green.GetValue(); - clear_color.float32[2] = Settings::values.bg_blue.GetValue(); - clear_color.float32[3] = 1.0f; - - DrawScreens(frame, layout, flipped); - scheduler.Flush(frame->render_ready); - - window.Present(frame); } void RendererVulkan::LoadFBToScreenInfo(const Pica::FramebufferConfig& framebuffer, diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index c236adc7e..39f918051 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -134,7 +134,6 @@ private: DescriptorUpdateQueue update_queue; RasterizerVulkan rasterizer; std::unique_ptr secondary_present_window_ptr; - bool skip_frame_display; DescriptorHeap present_heap; vk::UniquePipelineLayout present_pipeline_layout; std::array present_pipelines; diff --git a/src/video_core/renderer_vulkan/vk_present_window.cpp b/src/video_core/renderer_vulkan/vk_present_window.cpp index 4d049d97d..2b7c765a3 100644 --- a/src/video_core/renderer_vulkan/vk_present_window.cpp +++ b/src/video_core/renderer_vulkan/vk_present_window.cpp @@ -290,6 +290,39 @@ void PresentWindow::Present(Frame* frame) { }); } +void PresentWindow::SkipPresent(Frame* frame) { + // Fix this later + if (!use_present_thread) { + scheduler.WaitWorker(); + CopyToSwapchain(frame); + free_queue.push(frame); + return; + } + + scheduler.Record([this, frame](vk::CommandBuffer) { + std::unique_lock lock{queue_mutex}; + //present_queue.push(frame); + frame_cv.notify_one(); + }); +} + +// Skip Present Bkup +// void PresentWindow::SkipPresent(Frame* frame) { +// if (!use_present_thread) { +// scheduler.WaitWorker(); +// CopyToSwapchain(frame); +// free_queue.push(frame); +// return; +// } + +// scheduler.Record([this, frame](vk::CommandBuffer) { +// std::unique_lock lock{queue_mutex}; +// present_queue.push(frame); +// frame_cv.notify_one(); +// }); +// } + + void PresentWindow::WaitPresent() { if (!use_present_thread) { return; diff --git a/src/video_core/renderer_vulkan/vk_present_window.h b/src/video_core/renderer_vulkan/vk_present_window.h index 012dbf81b..020e91a54 100644 --- a/src/video_core/renderer_vulkan/vk_present_window.h +++ b/src/video_core/renderer_vulkan/vk_present_window.h @@ -52,6 +52,9 @@ public: /// Queues the provided frame for presentation. void Present(Frame* frame); + /// Skip queuing the provided frame for presentation. + void SkipPresent(Frame* frame); + /// This is called to notify the rendering backend of a surface change void NotifySurfaceChanged(); From 8694533891ca3d1ca7c769f59c2e2c8af2ec5077 Mon Sep 17 00:00:00 2001 From: KojoZero Date: Thu, 12 Mar 2026 02:57:42 -0700 Subject: [PATCH 3/4] cleanup --- .../renderer_vulkan/renderer_vulkan.cpp | 3 -- .../renderer_vulkan/vk_present_window.cpp | 33 ------------------- .../renderer_vulkan/vk_present_window.h | 3 -- 3 files changed, 39 deletions(-) diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index a860039fd..7881502db 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -254,9 +254,6 @@ void RendererVulkan::RenderToWindow(PresentWindow& window, const Layout::Framebu scheduler.Flush(frame->render_ready); window.Present(frame); Core::PerfStats::game_frames_updated = false; - //LOG_INFO(Render_Vulkan, "Entered Present Thread"); - } else { - //LOG_INFO(Render_Vulkan, "Entered Skip Present Thread"); } } diff --git a/src/video_core/renderer_vulkan/vk_present_window.cpp b/src/video_core/renderer_vulkan/vk_present_window.cpp index 2b7c765a3..4d049d97d 100644 --- a/src/video_core/renderer_vulkan/vk_present_window.cpp +++ b/src/video_core/renderer_vulkan/vk_present_window.cpp @@ -290,39 +290,6 @@ void PresentWindow::Present(Frame* frame) { }); } -void PresentWindow::SkipPresent(Frame* frame) { - // Fix this later - if (!use_present_thread) { - scheduler.WaitWorker(); - CopyToSwapchain(frame); - free_queue.push(frame); - return; - } - - scheduler.Record([this, frame](vk::CommandBuffer) { - std::unique_lock lock{queue_mutex}; - //present_queue.push(frame); - frame_cv.notify_one(); - }); -} - -// Skip Present Bkup -// void PresentWindow::SkipPresent(Frame* frame) { -// if (!use_present_thread) { -// scheduler.WaitWorker(); -// CopyToSwapchain(frame); -// free_queue.push(frame); -// return; -// } - -// scheduler.Record([this, frame](vk::CommandBuffer) { -// std::unique_lock lock{queue_mutex}; -// present_queue.push(frame); -// frame_cv.notify_one(); -// }); -// } - - void PresentWindow::WaitPresent() { if (!use_present_thread) { return; diff --git a/src/video_core/renderer_vulkan/vk_present_window.h b/src/video_core/renderer_vulkan/vk_present_window.h index 020e91a54..012dbf81b 100644 --- a/src/video_core/renderer_vulkan/vk_present_window.h +++ b/src/video_core/renderer_vulkan/vk_present_window.h @@ -52,9 +52,6 @@ public: /// Queues the provided frame for presentation. void Present(Frame* frame); - /// Skip queuing the provided frame for presentation. - void SkipPresent(Frame* frame); - /// This is called to notify the rendering backend of a surface change void NotifySurfaceChanged(); From 367a38bdea0a1d3a91e33706f3de9f20b6b5b0dc Mon Sep 17 00:00:00 2001 From: KojoZero Date: Thu, 12 Mar 2026 04:42:14 -0700 Subject: [PATCH 4/4] add opengl implementation and link to ui --- src/citra_qt/configuration/configure_graphics.ui | 4 ++-- src/video_core/renderer_opengl/renderer_opengl.cpp | 4 ++++ src/video_core/renderer_vulkan/renderer_vulkan.cpp | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index a73ec4468..8a516e125 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -320,10 +320,10 @@ - <html><head/><body><p>Skips presenting duplicated frames in 30fps games. This allows external frame generation tools to work correctly in 30fps games. This does not boost performance.</p></body></html> + <html><head/><body><p>This detects and skips the presentation of frames that are not unique. It also allows external frame generation tools to work correctly with 30fps games. Works in OpenGL and Vulkan.</p></body></html> - Skip Duplicate Frame Display + Skip Presenting Duplicate Frames diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index c8cb6c000..7309d5bd5 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -196,6 +196,7 @@ void RendererOpenGL::PrepareRendertarget() { void RendererOpenGL::RenderToMailbox(const Layout::FramebufferLayout& layout, std::unique_ptr& mailbox, bool flipped) { + if ((Core::PerfStats::game_frames_updated && Settings::values.use_skip_duplicate_frames.GetValue()) || !Settings::values.use_skip_duplicate_frames.GetValue()){ Frontend::Frame* frame; { @@ -241,6 +242,9 @@ void RendererOpenGL::RenderToMailbox(const Layout::FramebufferLayout& layout, glFlush(); mailbox->ReleaseRenderFrame(frame); } + + Core::PerfStats::game_frames_updated = false; + } } /** diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 7881502db..58f2b20cb 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -236,7 +236,8 @@ void RendererVulkan::PrepareDraw(Frame* frame, const Layout::FramebufferLayout& void RendererVulkan::RenderToWindow(PresentWindow& window, const Layout::FramebufferLayout& layout, bool flipped) { - if (Core::PerfStats::game_frames_updated){ + + if ((Core::PerfStats::game_frames_updated && Settings::values.use_skip_duplicate_frames.GetValue()) || !Settings::values.use_skip_duplicate_frames.GetValue()){ Frame* frame = window.GetRenderFrame(); if (layout.width != frame->width || layout.height != frame->height) {