[vulkan] Improve frame pacing (#3535)
This improves frame pacing control, fixes accumulated drift, and adds the ability to change it while the game is running. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3535 Co-authored-by: MaranBr <maranbr@outlook.com> Co-committed-by: MaranBr <maranbr@outlook.com>
This commit is contained in:
parent
75ebfaa090
commit
8e373eb714
|
|
@ -534,8 +534,8 @@
|
||||||
<item>@string/frame_pacing_mode_target_Auto</item>
|
<item>@string/frame_pacing_mode_target_Auto</item>
|
||||||
<item>@string/frame_pacing_mode_target_30</item>
|
<item>@string/frame_pacing_mode_target_30</item>
|
||||||
<item>@string/frame_pacing_mode_target_60</item>
|
<item>@string/frame_pacing_mode_target_60</item>
|
||||||
|
<item>@string/frame_pacing_mode_target_90</item>
|
||||||
<item>@string/frame_pacing_mode_target_120</item>
|
<item>@string/frame_pacing_mode_target_120</item>
|
||||||
<item>@string/frame_pacing_mode_target_240</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
<integer-array name="framePacingModeValues">
|
<integer-array name="framePacingModeValues">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
|
|
|
||||||
|
|
@ -1038,8 +1038,8 @@
|
||||||
<string name="frame_pacing_mode_target_Auto">Auto</string>
|
<string name="frame_pacing_mode_target_Auto">Auto</string>
|
||||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||||
|
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||||
<string name="frame_pacing_mode_target_240">240 FPS</string>
|
|
||||||
|
|
||||||
<!-- ASTC Decoding Method Choices -->
|
<!-- ASTC Decoding Method Choices -->
|
||||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||||
|
|
|
||||||
|
|
@ -462,9 +462,12 @@ struct Values {
|
||||||
SwitchableSetting<FramePacingMode, true> frame_pacing_mode{linkage,
|
SwitchableSetting<FramePacingMode, true> frame_pacing_mode{linkage,
|
||||||
FramePacingMode::Target_Auto,
|
FramePacingMode::Target_Auto,
|
||||||
FramePacingMode::Target_Auto,
|
FramePacingMode::Target_Auto,
|
||||||
FramePacingMode::Target_240,
|
FramePacingMode::Target_120,
|
||||||
"frame_pacing_mode",
|
"frame_pacing_mode",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced,
|
||||||
|
Specialization::Default,
|
||||||
|
true,
|
||||||
|
true};
|
||||||
|
|
||||||
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
|
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
|
||||||
AstcRecompression::Uncompressed,
|
AstcRecompression::Uncompressed,
|
||||||
|
|
|
||||||
|
|
@ -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(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None);
|
||||||
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
||||||
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
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(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
|
||||||
ENUM(VramUsageMode, Conservative, Aggressive);
|
ENUM(VramUsageMode, Conservative, Aggressive);
|
||||||
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
||||||
|
|
|
||||||
|
|
@ -517,8 +517,8 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent)
|
||||||
PAIR(FramePacingMode, Target_Auto, tr("Auto")),
|
PAIR(FramePacingMode, Target_Auto, tr("Auto")),
|
||||||
PAIR(FramePacingMode, Target_30, tr("30 FPS")),
|
PAIR(FramePacingMode, Target_30, tr("30 FPS")),
|
||||||
PAIR(FramePacingMode, Target_60, tr("60 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_120, tr("120 FPS")),
|
||||||
PAIR(FramePacingMode, Target_240, tr("240 FPS")),
|
|
||||||
}});
|
}});
|
||||||
translations->insert({Settings::EnumMetadata<Settings::VramUsageMode>::Index(),
|
translations->insert({Settings::EnumMetadata<Settings::VramUsageMode>::Index(),
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -347,7 +347,7 @@ void Scheduler::EndRenderPass()
|
||||||
Record([num_images = num_renderpass_images,
|
Record([num_images = num_renderpass_images,
|
||||||
images = renderpass_images,
|
images = renderpass_images,
|
||||||
ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) {
|
ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) {
|
||||||
std::vector<VkImageMemoryBarrier> barriers(num_images);
|
std::array<VkImageMemoryBarrier, 9> barriers;
|
||||||
VkPipelineStageFlags src_stages = 0;
|
VkPipelineStageFlags src_stages = 0;
|
||||||
for (size_t i = 0; i < num_images; ++i) {
|
for (size_t i = 0; i < num_images; ++i) {
|
||||||
const VkImageSubresourceRange& range = ranges[i];
|
const VkImageSubresourceRange& range = ranges[i];
|
||||||
|
|
|
||||||
|
|
@ -115,28 +115,27 @@ public:
|
||||||
/// Waits for the given GPU tick, optionally pacing frames.
|
/// Waits for the given GPU tick, optionally pacing frames.
|
||||||
void Wait(u64 tick, double target_fps = 0.0) {
|
void Wait(u64 tick, double target_fps = 0.0) {
|
||||||
if (Settings::values.use_speed_limit.GetValue() && target_fps > 0.0) {
|
if (Settings::values.use_speed_limit.GetValue() && target_fps > 0.0) {
|
||||||
auto frame_duration = std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::duration<double>(1.0 / target_fps));
|
const auto now = std::chrono::steady_clock::now();
|
||||||
auto now = std::chrono::steady_clock::now();
|
if (start_time == std::chrono::steady_clock::time_point{} || current_target_fps != target_fps) {
|
||||||
if (now < next_frame_time) {
|
start_time = now;
|
||||||
std::this_thread::sleep_until(next_frame_time);
|
frame_counter = 0;
|
||||||
next_frame_time += frame_duration;
|
current_target_fps = target_fps;
|
||||||
|
}
|
||||||
|
frame_counter++;
|
||||||
|
std::chrono::duration<double> 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 {
|
} else {
|
||||||
next_frame_time = now + frame_duration;
|
start_time = now;
|
||||||
|
frame_counter = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tick > master_semaphore->CurrentTick() && !chunk->Empty()) {
|
if (tick > 0) {
|
||||||
Flush();
|
if (tick >= master_semaphore->CurrentTick()) {
|
||||||
}
|
Flush();
|
||||||
master_semaphore->Wait(tick);
|
}
|
||||||
}
|
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::steady_clock::duration>(std::chrono::duration<double>(1.0 / target_fps));
|
|
||||||
next_frame_time = std::chrono::steady_clock::now() + frame_duration;
|
|
||||||
} else {
|
|
||||||
next_frame_time = std::chrono::steady_clock::time_point{};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,7 +281,9 @@ private:
|
||||||
std::condition_variable_any event_cv;
|
std::condition_variable_any event_cv;
|
||||||
std::jthread worker_thread;
|
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
|
} // namespace Vulkan
|
||||||
|
|
|
||||||
|
|
@ -146,25 +146,6 @@ void Swapchain::Create(
|
||||||
{
|
{
|
||||||
is_outdated = false;
|
is_outdated = false;
|
||||||
is_suboptimal = 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_;
|
width = width_;
|
||||||
height = height_;
|
height = height_;
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
|
|
@ -213,24 +194,22 @@ bool Swapchain::AcquireNextImage() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource_ticks[image_index] != 0 && !scheduler.IsFree(resource_ticks[image_index])) {
|
switch (Settings::values.frame_pacing_mode.GetValue()) {
|
||||||
switch (Settings::values.frame_pacing_mode.GetValue()) {
|
case Settings::FramePacingMode::Target_Auto:
|
||||||
case Settings::FramePacingMode::Target_Auto:
|
scheduler.Wait(resource_ticks[image_index]);
|
||||||
scheduler.Wait(resource_ticks[image_index]);
|
break;
|
||||||
break;
|
case Settings::FramePacingMode::Target_30:
|
||||||
case Settings::FramePacingMode::Target_30:
|
scheduler.Wait(resource_ticks[image_index], 30.0);
|
||||||
scheduler.Wait(resource_ticks[image_index], 30.0);
|
break;
|
||||||
break;
|
case Settings::FramePacingMode::Target_60:
|
||||||
case Settings::FramePacingMode::Target_60:
|
scheduler.Wait(resource_ticks[image_index], 60.0);
|
||||||
scheduler.Wait(resource_ticks[image_index], 60.0);
|
break;
|
||||||
break;
|
case Settings::FramePacingMode::Target_90:
|
||||||
case Settings::FramePacingMode::Target_120:
|
scheduler.Wait(resource_ticks[image_index], 90.0);
|
||||||
scheduler.Wait(resource_ticks[image_index], 120.0);
|
break;
|
||||||
break;
|
case Settings::FramePacingMode::Target_120:
|
||||||
case Settings::FramePacingMode::Target_240:
|
scheduler.Wait(resource_ticks[image_index], 120.0);
|
||||||
scheduler.Wait(resource_ticks[image_index], 240.0);
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource_ticks[image_index] = scheduler.CurrentTick();
|
resource_ticks[image_index] = scheduler.CurrentTick();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue