diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index 6886dac172..8dfea40693 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp @@ -124,9 +124,18 @@ float EmuWindow_Android::GetFrameTimeVerifiedHint() const { return QuantizeFrameRateHint(verified_rate); } +float EmuWindow_Android::GetPresentedFrameMultiplier() { + if (!Settings::values.frame_gen.GetValue()) { + return 1.0f; + } + return static_cast(std::clamp(Settings::values.frame_gen_multiplier.GetValue(), 2, 4)); +} + float EmuWindow_Android::GetFrameRateHint() const { - const float observed_rate = std::clamp(m_smoothed_present_rate, 0.0f, 240.0f); - const float frame_time_verified_hint = GetFrameTimeVerifiedHint(); + const float presented_multiplier = GetPresentedFrameMultiplier(); + const float observed_rate = + std::clamp(m_smoothed_present_rate * presented_multiplier, 0.0f, 240.0f); + const float frame_time_verified_hint = GetFrameTimeVerifiedHint() * presented_multiplier; if (m_last_frame_rate_hint > 0.0f && observed_rate > 0.0f) { const float tolerance = std::max(m_last_frame_rate_hint * 0.12f, 4.0f); @@ -150,9 +159,9 @@ float EmuWindow_Android::GetFrameRateHint() const { return frame_time_verified_hint; } - constexpr float NominalFrameRate = 60.0f; + const float nominal_rate = 60.0f * presented_multiplier; if (!Settings::values.use_speed_limit.GetValue()) { - return NominalFrameRate; + return QuantizeFrameRateHint(nominal_rate); } const u16 speed_limit = Settings::SpeedLimit(); @@ -161,7 +170,7 @@ float EmuWindow_Android::GetFrameRateHint() const { } const float speed_limited_rate = - NominalFrameRate * (static_cast(std::min(speed_limit, 100)) / 100.0f); + nominal_rate * (static_cast(std::min(speed_limit, 100)) / 100.0f); return QuantizeFrameRateHint(speed_limited_rate); } diff --git a/src/android/app/src/main/jni/emu_window/emu_window.h b/src/android/app/src/main/jni/emu_window/emu_window.h index b73e8b9b4d..fca83b91fa 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.h +++ b/src/android/app/src/main/jni/emu_window/emu_window.h @@ -61,6 +61,7 @@ private: void UpdateObservedFrameRate(); [[nodiscard]] float GetFrameRateHint() const; [[nodiscard]] float GetFrameTimeVerifiedHint() const; + [[nodiscard]] static float GetPresentedFrameMultiplier(); [[nodiscard]] static float QuantizeFrameRateHint(float frame_rate); float m_window_width{}; diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 6648aaedd4..ac90874943 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -299,7 +299,7 @@ GPU driver manager Install alternative drivers for potentially better performance or accuracy Frame generation - Run the Lossless Scaling interpolation shaders on every frame. Incomplete: the interpolated frame is computed but not yet displayed, so this only costs performance for now. + Insert interpolated frames between rendered ones using Lossless Scaling. Forces FIFO presentation while enabled, since other modes discard the generated frames. Adds about one frame of latency. Frame multiplier How many frames to display for each rendered frame. Higher values cost proportionally more GPU time. 2x (one generated frame) diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index b810862f73..c2aa3fb996 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -146,9 +146,10 @@ public: frame_counter++; auto target_time = start_time + frame_interval * frame_counter; if (target_time >= now) { + constexpr auto spin_tail = std::chrono::milliseconds(1); auto sleep_time = target_time - now; - if (sleep_time > std::chrono::milliseconds(15)) { - std::this_thread::sleep_for(sleep_time - std::chrono::milliseconds(1)); + if (sleep_time > spin_tail * 2) { + std::this_thread::sleep_for(sleep_time - spin_tail); } while (std::chrono::steady_clock::now() < target_time) { std::this_thread::yield(); diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index fdbad16721..fea5398557 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -48,6 +48,9 @@ static VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox, Settings::VSyncMode setting = [has_imm, has_mailbox]() { // Choose Mailbox or Immediate if unlocked and those modes are supported const auto mode = Settings::values.vsync_mode.GetValue(); + if (Settings::values.frame_gen.GetValue()) { + return mode == Settings::VSyncMode::FifoRelaxed ? mode : Settings::VSyncMode::Fifo; + } if (Settings::values.use_speed_limit.GetValue() && Settings::values.current_speed_mode.GetValue() != Settings::SpeedMode::Turbo) { return mode;