From 6a845fd03bdfcf3435f7da840e6af1be6b74fee3 Mon Sep 17 00:00:00 2001 From: Zephyron Date: Wed, 16 Apr 2025 19:28:15 +1000 Subject: [PATCH] feat: add option to respect present interval 0 as unlocked FPS When enabled, this feature allows games using present interval 0 to run with truly unlocked FPS, matching actual hardware behavior more accurately. Previously, Citron would cap present interval 0 at 120FPS to conserve battery, but this prevented proper functionality of dynamic framerate mods like UltraCam by MaxLastBreath (https://www.nxoptimizer.com/). The setting is disabled by default to maintain the current behavior for most users. Signed-off-by: Zephyron --- src/common/settings.h | 2 ++ src/core/hle/service/nvnflinger/hardware_composer.cpp | 10 ++++++++++ src/yuzu/configuration/shared_translation.cpp | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/common/settings.h b/src/common/settings.h index d66f0279e9..ae90eda837 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -286,6 +286,8 @@ struct Values { Category::Renderer}; SwitchableSetting use_asynchronous_gpu_emulation{ linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; + SwitchableSetting respect_present_interval_zero{ + linkage, false, "respect_present_interval_zero", Category::Renderer}; SwitchableSetting accelerate_astc{linkage, #ifdef ANDROID AstcDecodeMode::Cpu, diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp index 2433f5017e..a3cdad71f8 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.cpp +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include @@ -6,6 +7,7 @@ #include "common/common_types.h" #include "common/microprofile.h" +#include "common/settings.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvnflinger/buffer_item.h" #include "core/hle/service/nvnflinger/buffer_item_consumer.h" @@ -19,6 +21,14 @@ namespace { s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) { if (swap_interval <= 0) { + // If swap_interval is 0 and setting enabled, respect it as unlocked FPS + if (swap_interval == 0 && Settings::values.respect_present_interval_zero.GetValue()) { + if (out_speed_scale) { + *out_speed_scale = 1.0f; + } + return 0; + } + // As an extension, treat nonpositive swap interval as speed multiplier. if (out_speed_scale) { *out_speed_scale = 2.f * static_cast(1 - swap_interval); diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index a4fcd818d2..cc7ffd9135 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "yuzu/configuration/shared_translation.h" @@ -155,6 +156,11 @@ std::unique_ptr InitializeTranslations(QWidget* parent) { INSERT( Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled.")); + INSERT( + Settings, respect_present_interval_zero, tr("Respect present interval 0 for unlocked FPS"), + tr("When enabled, present interval 0 will be used for games requesting unlocked FPS.\n" + "This matches console behavior more closely, but may cause higher battery usage and frame pacing issues.\n" + "When disabled (default), present interval 0 is capped at 120FPS to conserve battery.")); INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for " "decoding, or perform no decoding at all (black screen on videos).\n"