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;