From fcd4c07bd0e049696413d1157d2a308455fc1722 Mon Sep 17 00:00:00 2001 From: Gilles Schintgen Date: Sat, 13 Apr 2024 01:36:56 +0200 Subject: [PATCH] Improve frametiming for linux capture (#2333) --- src/platform/common.h | 20 ++++++++++++++++++ src/platform/linux/cuda.cpp | 15 +++++++++----- src/platform/linux/kmsgrab.cpp | 30 ++++++++++++++++++--------- src/platform/linux/wlgrab.cpp | 28 ++++++++++++++++++------- src/platform/linux/x11grab.cpp | 30 ++++++++++++++++++--------- src/platform/windows/display_base.cpp | 14 +++---------- 6 files changed, 93 insertions(+), 44 deletions(-) diff --git a/src/platform/common.h b/src/platform/common.h index 7a4102ad..007f7ece 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -10,7 +10,9 @@ #include #include +#include "src/config.h" #include "src/logging.h" +#include "src/stat_trackers.h" #include "src/thread_safe.h" #include "src/utility.h" #include "src/video_colorspace.h" @@ -19,6 +21,8 @@ extern "C" { #include } +using namespace std::literals; + struct sockaddr; struct AVFrame; struct AVBufferRef; @@ -499,6 +503,22 @@ namespace platf { int env_width, env_height; int width, height; + + protected: + // collect capture timing data (at loglevel debug) + stat_trackers::min_max_avg_tracker sleep_overshoot_tracker; + void + log_sleep_overshoot(std::chrono::nanoseconds overshoot_ns) { + if (config::sunshine.min_log_level <= 1) { + // Print sleep overshoot stats to debug log every 20 seconds + auto print_info = [&](double min_overshoot, double max_overshoot, double avg_overshoot) { + auto f = stat_trackers::one_digit_after_decimal(); + BOOST_LOG(debug) << "Sleep overshoot (min/max/avg): " << f % min_overshoot << "ms/" << f % max_overshoot << "ms/" << f % avg_overshoot << "ms"; + }; + // std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - next_frame; + sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() / 1000000., print_info, 20s); + } + } }; class mic_t { diff --git a/src/platform/linux/cuda.cpp b/src/platform/linux/cuda.cpp index 33c93924..ee535d21 100644 --- a/src/platform/linux/cuda.cpp +++ b/src/platform/linux/cuda.cpp @@ -800,16 +800,21 @@ namespace cuda { handle.reset(); }); + sleep_overshoot_tracker.reset(); + while (true) { auto now = std::chrono::steady_clock::now(); if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); + std::this_thread::sleep_for(next_frame - now); } - while (next_frame > now) { - std::this_thread::sleep_for(1ns); - now = std::chrono::steady_clock::now(); + now = std::chrono::steady_clock::now(); + std::chrono::nanoseconds overshoot_ns = now - next_frame; + log_sleep_overshoot(overshoot_ns); + + next_frame += delay; + if (next_frame < now) { // some major slowdown happened; we couldn't keep up + next_frame = now + delay; } - next_frame = now + delay; std::shared_ptr img_out; auto status = snapshot(pull_free_image_cb, img_out, 150ms, *cursor); diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index e553f0be..30a2470f 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -1193,17 +1193,22 @@ namespace platf { capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); + sleep_overshoot_tracker.reset(); + while (true) { auto now = std::chrono::steady_clock::now(); if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); + std::this_thread::sleep_for(next_frame - now); } - while (next_frame > now) { - std::this_thread::sleep_for(1ns); - now = std::chrono::steady_clock::now(); + now = std::chrono::steady_clock::now(); + std::chrono::nanoseconds overshoot_ns = now - next_frame; + log_sleep_overshoot(overshoot_ns); + + next_frame += delay; + if (next_frame < now) { // some major slowdown happened; we couldn't keep up + next_frame = now + delay; } - next_frame = now + delay; std::shared_ptr img_out; auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); @@ -1412,17 +1417,22 @@ namespace platf { capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) { auto next_frame = std::chrono::steady_clock::now(); + sleep_overshoot_tracker.reset(); + while (true) { auto now = std::chrono::steady_clock::now(); if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); + std::this_thread::sleep_for(next_frame - now); } - while (next_frame > now) { - std::this_thread::sleep_for(1ns); - now = std::chrono::steady_clock::now(); + now = std::chrono::steady_clock::now(); + std::chrono::nanoseconds overshoot_ns = now - next_frame; + log_sleep_overshoot(overshoot_ns); + + next_frame += delay; + if (next_frame < now) { // some major slowdown happened; we couldn't keep up + next_frame = now + delay; } - next_frame = now + delay; std::shared_ptr img_out; auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); diff --git a/src/platform/linux/wlgrab.cpp b/src/platform/linux/wlgrab.cpp index 2ea15359..a6ac4adb 100644 --- a/src/platform/linux/wlgrab.cpp +++ b/src/platform/linux/wlgrab.cpp @@ -129,16 +129,22 @@ namespace wl { capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); + sleep_overshoot_tracker.reset(); + while (true) { auto now = std::chrono::steady_clock::now(); if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); + std::this_thread::sleep_for(next_frame - now); } - while (next_frame > now) { - now = std::chrono::steady_clock::now(); + now = std::chrono::steady_clock::now(); + std::chrono::nanoseconds overshoot_ns = now - next_frame; + log_sleep_overshoot(overshoot_ns); + + next_frame += delay; + if (next_frame < now) { // some major slowdown happened; we couldn't keep up + next_frame = now + delay; } - next_frame = now + delay; std::shared_ptr img_out; auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); @@ -259,16 +265,22 @@ namespace wl { capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); + sleep_overshoot_tracker.reset(); + while (true) { auto now = std::chrono::steady_clock::now(); if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); + std::this_thread::sleep_for(next_frame - now); } - while (next_frame > now) { - now = std::chrono::steady_clock::now(); + now = std::chrono::steady_clock::now(); + std::chrono::nanoseconds overshoot_ns = now - next_frame; + log_sleep_overshoot(overshoot_ns); + + next_frame += delay; + if (next_frame < now) { // some major slowdown happened; we couldn't keep up + next_frame = now + delay; } - next_frame = now + delay; std::shared_ptr img_out; auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); diff --git a/src/platform/linux/x11grab.cpp b/src/platform/linux/x11grab.cpp index 0a639b9c..0d4c3d38 100644 --- a/src/platform/linux/x11grab.cpp +++ b/src/platform/linux/x11grab.cpp @@ -481,17 +481,22 @@ namespace platf { capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); + sleep_overshoot_tracker.reset(); + while (true) { auto now = std::chrono::steady_clock::now(); if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); + std::this_thread::sleep_for(next_frame - now); } - while (next_frame > now) { - std::this_thread::sleep_for(1ns); - now = std::chrono::steady_clock::now(); + now = std::chrono::steady_clock::now(); + std::chrono::nanoseconds overshoot_ns = now - next_frame; + log_sleep_overshoot(overshoot_ns); + + next_frame += delay; + if (next_frame < now) { // some major slowdown happened; we couldn't keep up + next_frame = now + delay; } - next_frame = now + delay; std::shared_ptr img_out; auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); @@ -622,17 +627,22 @@ namespace platf { capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); + sleep_overshoot_tracker.reset(); + while (true) { auto now = std::chrono::steady_clock::now(); if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); + std::this_thread::sleep_for(next_frame - now); } - while (next_frame > now) { - std::this_thread::sleep_for(1ns); - now = std::chrono::steady_clock::now(); + now = std::chrono::steady_clock::now(); + std::chrono::nanoseconds overshoot_ns = now - next_frame; + log_sleep_overshoot(overshoot_ns); + + next_frame += delay; + if (next_frame < now) { // some major slowdown happened; we couldn't keep up + next_frame = now + delay; } - next_frame = now + delay; std::shared_ptr img_out; auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index b258690e..227efe76 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -154,7 +154,7 @@ namespace platf::dxgi { SetThreadExecutionState(ES_CONTINUOUS); }); - stat_trackers::min_max_avg_tracker sleep_overshoot_tracker; + sleep_overshoot_tracker.reset(); while (true) { // This will return false if the HDR state changes or for any number of other @@ -184,16 +184,8 @@ namespace platf::dxgi { } else { high_precision_sleep(sleep_period); - - if (config::sunshine.min_log_level <= 1) { - // Print sleep overshoot stats to debug log every 20 seconds - auto print_info = [&](double min_overshoot, double max_overshoot, double avg_overshoot) { - auto f = stat_trackers::one_digit_after_decimal(); - BOOST_LOG(debug) << "Sleep overshoot (min/max/avg): " << f % min_overshoot << "ms/" << f % max_overshoot << "ms/" << f % avg_overshoot << "ms"; - }; - std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - sleep_target; - sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() / 1000000., print_info, 20s); - } + std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - sleep_target; + log_sleep_overshoot(overshoot_ns); status = snapshot(pull_free_image_cb, img_out, 0ms, *cursor);