From 77da1a32e08102108c35f9b241f9432895d2c780 Mon Sep 17 00:00:00 2001
From: Gamer64 <76565986+Gamer64ytb@users.noreply.github.com>
Date: Thu, 14 Aug 2025 20:44:07 +0200
Subject: [PATCH 1/2] [GPU]: Implement a working Frame-Skip into Android
---
.../features/settings/model/BooleanSetting.kt | 1 -
.../features/settings/model/IntSetting.kt | 1 +
.../settings/model/view/SettingsItem.kt | 18 +++----
.../settings/ui/SettingsFragmentPresenter.kt | 1 +
.../app/src/main/res/values/arrays.xml | 12 +++++
src/common/settings.h | 2 +-
src/video_core/gpu.cpp | 54 +++++++++++++------
src/video_core/gpu.h | 3 ++
8 files changed, 66 insertions(+), 26 deletions(-)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 119517d99b..f6c222479c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -51,7 +51,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
ENABLE_RAII("enable_raii"),
FRAME_INTERPOLATION("frame_interpolation"),
-// FRAME_SKIPPING("frame_skipping"),
PERF_OVERLAY_BACKGROUND("perf_overlay_background"),
SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index d53aa46265..9cb1adb581 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -42,6 +42,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
RENDERER_SAMPLE_SHADING_FRACTION("sample_shading_fraction"),
FAST_CPU_TIME("fast_cpu_time"),
CPU_TICKS("cpu_ticks"),
+ FRAME_SKIPPING("frame_skipping"),
FAST_GPU_TIME("fast_gpu_time"),
BAT_TEMPERATURE_UNIT("bat_temperature_unit"),
CABINET_APPLET("cabinet_applet_mode"),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 73834c1184..c2fa66e6ce 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -242,15 +242,15 @@ abstract class SettingsItem(
descriptionId = R.string.frame_interpolation_description
)
)
-
-// put(
-// SwitchSetting(
-// BooleanSetting.FRAME_SKIPPING,
-// titleId = R.string.frame_skipping,
-// descriptionId = R.string.frame_skipping_description
-// )
-// )
-
+ put(
+ SingleChoiceSetting(
+ IntSetting.FRAME_SKIPPING,
+ titleId = R.string.frame_skipping,
+ descriptionId = R.string.frame_skipping_description,
+ choicesId = R.array.frameSkippingNames,
+ valuesId = R.array.frameSkippingValues
+ )
+ )
put(
SwitchSetting(
dockedModeSetting,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 28411309e6..ee8140d199 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -450,6 +450,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_EARLY_RELEASE_FENCES.key)
add(BooleanSetting.BUFFER_REORDER_DISABLE.key)
add(BooleanSetting.FRAME_INTERPOLATION.key)
+ add(IntSetting.FRAME_SKIPPING.key)
add(BooleanSetting.RENDERER_FAST_GPU.key)
add(IntSetting.FAST_GPU_TIME.key)
add(IntSetting.RENDERER_SHADER_BACKEND.key)
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 602003cf8a..a5ab9bc3d9 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -21,6 +21,18 @@
- 1
+
+ - @string/disabled
+ - 1
+ - 2
+
+
+
+ - 0
+ - 1
+ - 2
+
+
- @string/memory_4gb
- @string/memory_6gb
diff --git a/src/common/settings.h b/src/common/settings.h
index faf7210f5d..61f79163b9 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -325,9 +325,9 @@ struct Values {
#ifdef __ANDROID__
SwitchableSetting frame_interpolation{linkage, true, "frame_interpolation", Category::Renderer,
Specialization::RuntimeList};
+#endif
SwitchableSetting frame_skipping{linkage, false, "frame_skipping", Category::Renderer,
Specialization::RuntimeList};
-#endif
SwitchableSetting use_disk_shader_cache{linkage, true, "use_disk_shader_cache",
Category::Renderer};
SwitchableSetting optimize_spirv_output{linkage,
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index d97620ce91..327536661c 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -223,17 +223,23 @@ struct GPU::Impl {
system.GetPerfStats().EndGameFrame();
}
+ void ResetFrameCounter() {
+ frame_count.store(0, std::memory_order_relaxed);
+ }
+
/// Performs any additional setup necessary in order to begin GPU emulation.
/// This can be used to launch any necessary threads and register any necessary
/// core timing events.
void Start() {
Settings::UpdateGPUAccuracy();
+ ResetFrameCounter();
gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
}
void NotifyShutdown() {
std::unique_lock lk{sync_mutex};
shutting_down.store(true, std::memory_order::relaxed);
+ ResetFrameCounter();
sync_cv.notify_all();
}
@@ -291,10 +297,16 @@ struct GPU::Impl {
void RequestComposite(std::vector&& layers,
std::vector&& fences) {
+ // Increment frame counter
+ const u64 current_frame = frame_count.fetch_add(1, std::memory_order_relaxed) + 1;
+ const u32 frame_skip_value = Settings::values.frame_skipping.GetValue();
+ const bool skip_frame = frame_skip_value > 0 &&
+ (current_frame % (static_cast(frame_skip_value) + 1) != 0);
+
size_t num_fences{fences.size()};
size_t current_request_counter{};
{
- std::unique_lock lk(request_swap_mutex);
+ std::unique_lock lk{request_swap_mutex};
if (free_swap_counters.empty()) {
current_request_counter = request_swap_counters.size();
request_swap_counters.emplace_back(num_fences);
@@ -305,23 +317,33 @@ struct GPU::Impl {
}
}
const auto wait_fence =
- RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] {
+ RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences, skip_frame] {
auto& syncpoint_manager = host1x.GetSyncpointManager();
if (num_fences == 0) {
- renderer->Composite(layers);
- }
- const auto executer = [this, current_request_counter, layers_copy = layers]() {
- {
- std::unique_lock lk(request_swap_mutex);
- if (--request_swap_counters[current_request_counter] != 0) {
- return;
- }
- free_swap_counters.push_back(current_request_counter);
+ if (!skip_frame) {
+ renderer->Composite(layers);
+ } else {
+ system.GetPerfStats().EndGameFrame();
+ }
+ } else {
+ const auto executer = [this, current_request_counter, layers_copy = std::move(layers), skip_frame]() {
+ {
+ std::unique_lock lk{request_swap_mutex};
+ if (--request_swap_counters[current_request_counter] != 0) {
+ return;
+ }
+ free_swap_counters.push_back(current_request_counter);
+ }
+ // Handle frame skipping in executer
+ if (!skip_frame) {
+ renderer->Composite(layers_copy);
+ } else {
+ system.GetPerfStats().EndGameFrame();
+ }
+ };
+ for (size_t i = 0; i < num_fences; i++) {
+ syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
}
- renderer->Composite(layers_copy);
- };
- for (size_t i = 0; i < num_fences; i++) {
- syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer);
}
});
gpu_thread.TickGPU();
@@ -353,6 +375,8 @@ struct GPU::Impl {
/// When true, we are about to shut down emulation session, so terminate outstanding tasks
std::atomic_bool shutting_down{};
+ std::atomic frame_count{0};
+
std::array, Service::Nvidia::MaxSyncPoints> syncpoints{};
std::array, Service::Nvidia::MaxSyncPoints> syncpt_interrupts;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 538c4da85a..b8acd98a60 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -220,6 +220,9 @@ public:
std::vector GetAppletCaptureBuffer();
+ // Reset frame counter
+ void ResetFrameCounter();
+
/// Performs any additional setup necessary in order to begin GPU emulation.
/// This can be used to launch any necessary threads and register any necessary
/// core timing events.
--
2.39.5
From d342204e5d9ad15b45d68265872879a9c1fceefd Mon Sep 17 00:00:00 2001
From: crueter
Date: Thu, 14 Aug 2025 20:17:53 -0400
Subject: [PATCH 2/2] [frontend] fix frame skip setting prsentation
Signed-off-by: crueter
---
.../features/settings/model/BooleanSetting.kt | 1 +
.../features/settings/model/IntSetting.kt | 1 -
.../settings/model/view/SettingsItem.kt | 6 ++----
.../settings/ui/SettingsFragmentPresenter.kt | 2 +-
src/android/app/src/main/res/values/arrays.xml | 12 ------------
.../app/src/main/res/values/strings.xml | 4 ++--
src/common/settings.h | 3 +--
src/yuzu/configuration/shared_translation.cpp | 18 ++++++++++++------
8 files changed, 19 insertions(+), 28 deletions(-)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index f6c222479c..3af07c6c02 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -32,6 +32,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_DESCRIPTOR_INDEXING("descriptor_indexing"),
RENDERER_SAMPLE_SHADING("sample_shading"),
PICTURE_IN_PICTURE("picture_in_picture"),
+ FRAME_SKIPPING("frame_skipping"),
USE_CUSTOM_RTC("custom_rtc_enabled"),
BLACK_BACKGROUNDS("black_backgrounds"),
JOYSTICK_REL_CENTER("joystick_rel_center"),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index 9cb1adb581..d53aa46265 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -42,7 +42,6 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
RENDERER_SAMPLE_SHADING_FRACTION("sample_shading_fraction"),
FAST_CPU_TIME("fast_cpu_time"),
CPU_TICKS("cpu_ticks"),
- FRAME_SKIPPING("frame_skipping"),
FAST_GPU_TIME("fast_gpu_time"),
BAT_TEMPERATURE_UNIT("bat_temperature_unit"),
CABINET_APPLET("cabinet_applet_mode"),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index c2fa66e6ce..a940d2fca2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -243,12 +243,10 @@ abstract class SettingsItem(
)
)
put(
- SingleChoiceSetting(
- IntSetting.FRAME_SKIPPING,
+ SwitchSetting(
+ BooleanSetting.FRAME_SKIPPING,
titleId = R.string.frame_skipping,
descriptionId = R.string.frame_skipping_description,
- choicesId = R.array.frameSkippingNames,
- valuesId = R.array.frameSkippingValues
)
)
put(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index ee8140d199..a5daa9027f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -450,7 +450,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_EARLY_RELEASE_FENCES.key)
add(BooleanSetting.BUFFER_REORDER_DISABLE.key)
add(BooleanSetting.FRAME_INTERPOLATION.key)
- add(IntSetting.FRAME_SKIPPING.key)
+ add(BooleanSetting.FRAME_SKIPPING.key)
add(BooleanSetting.RENDERER_FAST_GPU.key)
add(IntSetting.FAST_GPU_TIME.key)
add(IntSetting.RENDERER_SHADER_BACKEND.key)
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index a5ab9bc3d9..602003cf8a 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -21,18 +21,6 @@
- 1
-
- - @string/disabled
- - 1
- - 2
-
-
-
- - 0
- - 1
- - 2
-
-
- @string/memory_4gb
- @string/memory_6gb
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index d649ba08ac..fa54f9cfa7 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -482,8 +482,8 @@
Display
Post-Processing
- WIP: Frameskip
- Toggle frame skipping to improve performance by reducing the number of rendered frames. This feature is still being worked on and will be enabled in future releases.
+ Frame Skipping
+ Toggle frame skipping to improve performance by reducing the number of rendered frames.
Accuracy level
Resolution (Handheld/Docked)
VSync mode
diff --git a/src/common/settings.h b/src/common/settings.h
index 61f79163b9..5a089507f6 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -326,8 +326,7 @@ struct Values {
SwitchableSetting frame_interpolation{linkage, true, "frame_interpolation", Category::Renderer,
Specialization::RuntimeList};
#endif
- SwitchableSetting frame_skipping{linkage, false, "frame_skipping", Category::Renderer,
- Specialization::RuntimeList};
+ SwitchableSetting frame_skipping{linkage, false, "frame_skipping", Category::Renderer};
SwitchableSetting use_disk_shader_cache{linkage, true, "use_disk_shader_cache",
Category::Renderer};
SwitchableSetting optimize_spirv_output{linkage,
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index 22f1921dbf..dbc6b35ddd 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -269,6 +269,18 @@ std::unique_ptr InitializeTranslations(QWidget* parent)
INSERT(Settings, bg_blue, QString(), QString());
// Renderer (Advanced Graphics)
+ INSERT(Settings,
+ enable_raii,
+ tr("RAII"),
+ tr("A method of automatic resource management in Vulkan "
+ "that ensures proper release of resources "
+ "when they are no longer needed, but may cause crashes in bundled games."));
+ INSERT(Settings,
+ frame_skipping,
+ tr("Frame Skipping"),
+ tr("Skips rendering every other frame while still presenting "
+ "the frame as rendered.\nThis cuts the effective FPS in half, but can make "
+ "games run stable at the game's target FPS on weaker hardware."));
INSERT(Settings,
async_presentation,
tr("Enable asynchronous presentation (Vulkan only)"),
@@ -335,12 +347,6 @@ std::unique_ptr InitializeTranslations(QWidget* parent)
tr("Improves rendering of transparency effects in specific games."));
// Renderer (Extensions)
- INSERT(Settings,
- enable_raii,
- tr("RAII"),
- tr("A method of automatic resource management in Vulkan "
- "that ensures proper release of resources "
- "when they are no longer needed, but may cause crashes in bundled games."));
INSERT(Settings,
dyna_state,
tr("Extended Dynamic State"),
--
2.39.5