diff --git a/src/common/settings.h b/src/common/settings.h index 5dc2f30cf8..669d38b17c 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -437,7 +437,7 @@ struct Values { SwitchableSetting frame_pacing_mode{linkage, FramePacingMode::Target_Auto, FramePacingMode::Target_Auto, - FramePacingMode::Target_120, + FramePacingMode::Target_240, "frame_pacing_mode", Category::RendererAdvanced}; diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index def99db21b..7e3bef9bea 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); +ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_120, Target_240); 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 b5ad941cc5..40fd42720f 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -505,6 +505,7 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(FramePacingMode, Target_30, tr("30 FPS")), PAIR(FramePacingMode, Target_60, tr("60 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 aafcfdf65b..2a69d6d244 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::array barriers; + std::vector barriers(num_images); 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 094607775c..2c7100c727 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -111,37 +111,34 @@ public: return master_semaphore->IsFree(tick); } - /// Waits for the given tick to trigger on the GPU. - void Wait(u64 tick) { - if (tick >= master_semaphore->CurrentTick()) { - // Make sure we are not waiting for the current tick without signalling + /// 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; + } else { + next_frame_time = now + frame_duration; + } + } + if (tick > master_semaphore->CurrentTick() && !chunk->Empty()) { Flush(); } master_semaphore->Wait(tick); } - /// Waits until the next game frame based on the current game FPS. - void WaitFPS(u64 tick, double target_fps) { - if (master_semaphore->CurrentTick() >= tick) { - return; - } - const auto frame_duration = std::chrono::duration_cast(std::chrono::duration(1.0 / target_fps)); - const auto now = std::chrono::steady_clock::now(); - if (next_frame_time == std::chrono::steady_clock::time_point{}) { - next_frame_time = now; - } - next_frame_time += frame_duration; - if (next_frame_time > now) { - std::this_thread::sleep_until(next_frame_time); + /// Resets the frame pacing state by updating the next frame time to agora + 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 = now; + next_frame_time = std::chrono::steady_clock::time_point{}; } } - void ResetFramePacing() { - next_frame_time = std::chrono::steady_clock::now(); - } - /// Returns the master timeline semaphore. [[nodiscard]] MasterSemaphore& GetMasterSemaphore() const noexcept { return *master_semaphore; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 3f56a950a3..7e7e67639d 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -147,7 +147,23 @@ void Swapchain::Create( is_outdated = false; is_suboptimal = false; - scheduler.ResetFramePacing(); + 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_; @@ -197,21 +213,22 @@ bool Swapchain::AcquireNextImage() { break; } - if (!Settings::values.use_speed_limit.GetValue()) { - scheduler.Wait(resource_ticks[image_index]); - } else { + 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.WaitFPS(resource_ticks[image_index], 30.0); + scheduler.Wait(resource_ticks[image_index], 30.0); break; case Settings::FramePacingMode::Target_60: - scheduler.WaitFPS(resource_ticks[image_index], 60.0); + scheduler.Wait(resource_ticks[image_index], 60.0); break; case Settings::FramePacingMode::Target_120: - scheduler.WaitFPS(resource_ticks[image_index], 120.0); + scheduler.Wait(resource_ticks[image_index], 120.0); + break; + case Settings::FramePacingMode::Target_240: + scheduler.Wait(resource_ticks[image_index], 240.0); break; } }