diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index e85bc3592a..b0487302b3 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -534,8 +534,8 @@ @string/frame_pacing_mode_target_Auto @string/frame_pacing_mode_target_30 @string/frame_pacing_mode_target_60 + @string/frame_pacing_mode_target_90 @string/frame_pacing_mode_target_120 - @string/frame_pacing_mode_target_240 0 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index b703575cc5..b553402628 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1038,8 +1038,8 @@ Auto 30 FPS 60 FPS + 90 FPS 120 FPS - 240 FPS CPU diff --git a/src/common/settings.h b/src/common/settings.h index a7cf800142..a03c6e1a2c 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -462,9 +462,12 @@ struct Values { SwitchableSetting frame_pacing_mode{linkage, FramePacingMode::Target_Auto, FramePacingMode::Target_Auto, - FramePacingMode::Target_240, + FramePacingMode::Target_120, "frame_pacing_mode", - Category::RendererAdvanced}; + Category::RendererAdvanced, + Specialization::Default, + true, + true}; SwitchableSetting astc_recompression{linkage, AstcRecompression::Uncompressed, diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 62bf17c51a..d4935d9b6d 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -129,7 +129,7 @@ ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None); ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous); ENUM(AstcRecompression, Uncompressed, Bc1, Bc3); -ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_120, Target_240); +ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120); ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed); ENUM(VramUsageMode, Conservative, Aggressive); ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV); diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index 7d53eb609e..f4355197b0 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -517,8 +517,8 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(FramePacingMode, Target_Auto, tr("Auto")), PAIR(FramePacingMode, Target_30, tr("30 FPS")), PAIR(FramePacingMode, Target_60, tr("60 FPS")), + PAIR(FramePacingMode, Target_90, tr("90 FPS")), PAIR(FramePacingMode, Target_120, tr("120 FPS")), - PAIR(FramePacingMode, Target_240, tr("240 FPS")), }}); translations->insert({Settings::EnumMetadata::Index(), { diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 2a69d6d244..aafcfdf65b 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -347,7 +347,7 @@ void Scheduler::EndRenderPass() Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) { - std::vector barriers(num_images); + std::array barriers; VkPipelineStageFlags src_stages = 0; for (size_t i = 0; i < num_images; ++i) { const VkImageSubresourceRange& range = ranges[i]; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index fb0ac9b008..2913211480 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -115,28 +115,27 @@ public: /// Waits for the given GPU tick, optionally pacing frames. void Wait(u64 tick, double target_fps = 0.0) { if (Settings::values.use_speed_limit.GetValue() && target_fps > 0.0) { - auto frame_duration = std::chrono::duration_cast(std::chrono::duration(1.0 / target_fps)); - auto now = std::chrono::steady_clock::now(); - if (now < next_frame_time) { - std::this_thread::sleep_until(next_frame_time); - next_frame_time += frame_duration; + const auto now = std::chrono::steady_clock::now(); + if (start_time == std::chrono::steady_clock::time_point{} || current_target_fps != target_fps) { + start_time = now; + frame_counter = 0; + current_target_fps = target_fps; + } + frame_counter++; + std::chrono::duration frame_interval(1.0 / current_target_fps); + auto target_time = start_time + frame_interval * frame_counter; + if (target_time > now) { + std::this_thread::sleep_until(target_time); } else { - next_frame_time = now + frame_duration; + start_time = now; + frame_counter = 0; } } - if (tick > master_semaphore->CurrentTick() && !chunk->Empty()) { - Flush(); - } - master_semaphore->Wait(tick); - } - - /// Resets the frame pacing state by setting the next frame time. - void ResetFramePacing(double target_fps = 0.0) { - if (target_fps > 0.0) { - auto frame_duration = std::chrono::duration_cast(std::chrono::duration(1.0 / target_fps)); - next_frame_time = std::chrono::steady_clock::now() + frame_duration; - } else { - next_frame_time = std::chrono::steady_clock::time_point{}; + if (tick > 0) { + if (tick >= master_semaphore->CurrentTick()) { + Flush(); + } + master_semaphore->Wait(tick); } } @@ -282,7 +281,9 @@ private: std::condition_variable_any event_cv; std::jthread worker_thread; - std::chrono::steady_clock::time_point next_frame_time{}; + std::chrono::steady_clock::time_point start_time{}; + u64 frame_counter{}; + double current_target_fps{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 7e7e67639d..89aa6c4628 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -146,25 +146,6 @@ void Swapchain::Create( { is_outdated = false; is_suboptimal = false; - - switch (Settings::values.frame_pacing_mode.GetValue()) { - case Settings::FramePacingMode::Target_Auto: - scheduler.ResetFramePacing(); - break; - case Settings::FramePacingMode::Target_30: - scheduler.ResetFramePacing(30.0); - break; - case Settings::FramePacingMode::Target_60: - scheduler.ResetFramePacing(60.0); - break; - case Settings::FramePacingMode::Target_120: - scheduler.ResetFramePacing(120.0); - break; - case Settings::FramePacingMode::Target_240: - scheduler.ResetFramePacing(240.0); - break; - } - width = width_; height = height_; #ifdef ANDROID @@ -213,24 +194,22 @@ bool Swapchain::AcquireNextImage() { break; } - if (resource_ticks[image_index] != 0 && !scheduler.IsFree(resource_ticks[image_index])) { - switch (Settings::values.frame_pacing_mode.GetValue()) { - case Settings::FramePacingMode::Target_Auto: - scheduler.Wait(resource_ticks[image_index]); - break; - case Settings::FramePacingMode::Target_30: - scheduler.Wait(resource_ticks[image_index], 30.0); - break; - case Settings::FramePacingMode::Target_60: - scheduler.Wait(resource_ticks[image_index], 60.0); - break; - case Settings::FramePacingMode::Target_120: - scheduler.Wait(resource_ticks[image_index], 120.0); - break; - case Settings::FramePacingMode::Target_240: - scheduler.Wait(resource_ticks[image_index], 240.0); - break; - } + switch (Settings::values.frame_pacing_mode.GetValue()) { + case Settings::FramePacingMode::Target_Auto: + scheduler.Wait(resource_ticks[image_index]); + break; + case Settings::FramePacingMode::Target_30: + scheduler.Wait(resource_ticks[image_index], 30.0); + break; + case Settings::FramePacingMode::Target_60: + scheduler.Wait(resource_ticks[image_index], 60.0); + break; + case Settings::FramePacingMode::Target_90: + scheduler.Wait(resource_ticks[image_index], 90.0); + break; + case Settings::FramePacingMode::Target_120: + scheduler.Wait(resource_ticks[image_index], 120.0); + break; } resource_ticks[image_index] = scheduler.CurrentTick();