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 02950484ac..e8ccf8ad0a 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 @@ -41,6 +41,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting { FSR_SHARPENING_SLIDER("fsr_sharpening_slider"), 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 13dd32fcdd..e324ecf049 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 @@ -234,15 +234,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 f4b6f1b4ed..46091fb62d 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 @@ -449,6 +449,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 41c042bcf1..74a70afa99 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.