Rework capture sleeps for better frame stability
This commit is contained in:
@@ -17,6 +17,7 @@ typedef long NTSTATUS;
|
|||||||
#include "src/config.h"
|
#include "src/config.h"
|
||||||
#include "src/main.h"
|
#include "src/main.h"
|
||||||
#include "src/platform/common.h"
|
#include "src/platform/common.h"
|
||||||
|
#include "src/stat_trackers.h"
|
||||||
#include "src/video.h"
|
#include "src/video.h"
|
||||||
|
|
||||||
namespace platf {
|
namespace platf {
|
||||||
@@ -119,8 +120,8 @@ namespace platf::dxgi {
|
|||||||
|
|
||||||
capture_e
|
capture_e
|
||||||
display_base_t::capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) {
|
display_base_t::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();
|
|
||||||
const auto client_frame_interval = std::chrono::nanoseconds { 1s } / client_frame_rate;
|
const auto client_frame_interval = std::chrono::nanoseconds { 1s } / client_frame_rate;
|
||||||
|
std::optional<std::chrono::steady_clock::time_point> next_frame_time;
|
||||||
|
|
||||||
// Keep the display awake during capture. If the display goes to sleep during
|
// Keep the display awake during capture. If the display goes to sleep during
|
||||||
// capture, best case is that capture stops until it powers back on. However,
|
// capture, best case is that capture stops until it powers back on. However,
|
||||||
@@ -131,6 +132,8 @@ namespace platf::dxgi {
|
|||||||
SetThreadExecutionState(ES_CONTINUOUS);
|
SetThreadExecutionState(ES_CONTINUOUS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
stat_trackers::min_max_avg_tracker<double> sleep_overshoot_tracker;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// This will return false if the HDR state changes or for any number of other
|
// This will return false if the HDR state changes or for any number of other
|
||||||
// display or GPU changes. We should reinit to examine the updated state of
|
// display or GPU changes. We should reinit to examine the updated state of
|
||||||
@@ -139,23 +142,58 @@ namespace platf::dxgi {
|
|||||||
return platf::capture_e::reinit;
|
return platf::capture_e::reinit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the wait time is positive and below 1 second, wait the specified time
|
platf::capture_e status = capture_e::ok;
|
||||||
// and offset the next frame time from the exact current frame time target.
|
std::shared_ptr<img_t> img_out;
|
||||||
auto wait_time = next_frame - std::chrono::steady_clock::now();
|
|
||||||
if (wait_time > 0s && wait_time < 1s) {
|
// Try to continue frame pacing group, snapshot() is called with zero timeout after waiting for client frame interval
|
||||||
high_precision_sleep(wait_time);
|
if (next_frame_time) {
|
||||||
next_frame += client_frame_interval;
|
const auto sleep_period = *next_frame_time - std::chrono::steady_clock::now();
|
||||||
}
|
|
||||||
else {
|
if (sleep_period <= 0ns) {
|
||||||
// If the wait time is negative (meaning the frame is past due) or the
|
// We missed next frame time, invalidating current frame pacing group
|
||||||
// computed wait time is beyond a second (meaning possible clock issues),
|
next_frame_time = std::nullopt;
|
||||||
// just capture the frame now and resynchronize the frame interval with
|
status = capture_e::timeout;
|
||||||
// the current time.
|
}
|
||||||
next_frame = std::chrono::steady_clock::now() + client_frame_interval;
|
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() - *next_frame_time;
|
||||||
|
sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() / 1000000., print_info, 20s);
|
||||||
|
}
|
||||||
|
|
||||||
|
status = snapshot(pull_free_image_cb, img_out, 0ms, *cursor);
|
||||||
|
|
||||||
|
if (status == capture_e::ok && img_out) {
|
||||||
|
*next_frame_time += client_frame_interval;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
next_frame_time = std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start new frame pacing group if necessary, snapshot() is called with non-zero timeout
|
||||||
|
if (status == capture_e::timeout || (status == capture_e::ok && !next_frame_time)) {
|
||||||
|
status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
|
||||||
|
|
||||||
|
if (status == capture_e::ok && img_out) {
|
||||||
|
next_frame_time = img_out->frame_timestamp;
|
||||||
|
|
||||||
|
if (!next_frame_time) {
|
||||||
|
BOOST_LOG(warning) << "snapshot() provided image without timestamp";
|
||||||
|
next_frame_time = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
*next_frame_time += client_frame_interval;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<img_t> img_out;
|
|
||||||
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case platf::capture_e::reinit:
|
case platf::capture_e::reinit:
|
||||||
case platf::capture_e::error:
|
case platf::capture_e::error:
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ namespace stat_trackers {
|
|||||||
struct {
|
struct {
|
||||||
std::chrono::steady_clock::steady_clock::time_point last_callback_time = std::chrono::steady_clock::now();
|
std::chrono::steady_clock::steady_clock::time_point last_callback_time = std::chrono::steady_clock::now();
|
||||||
T stat_min = std::numeric_limits<T>::max();
|
T stat_min = std::numeric_limits<T>::max();
|
||||||
T stat_max = 0;
|
T stat_max = std::numeric_limits<T>::min();
|
||||||
double stat_total = 0;
|
double stat_total = 0;
|
||||||
uint32_t calls = 0;
|
uint32_t calls = 0;
|
||||||
} data;
|
} data;
|
||||||
|
|||||||
Reference in New Issue
Block a user