From d81e2cecaabf184eaef7d4e93b71a244b0b7934a Mon Sep 17 00:00:00 2001 From: ns6089 <61738816+ns6089@users.noreply.github.com> Date: Fri, 12 May 2023 16:57:09 +0300 Subject: [PATCH] Add high_precision_sleep() method --- src/platform/windows/display.h | 5 +++ src/platform/windows/display_base.cpp | 57 +++++++++++++++++---------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/platform/windows/display.h b/src/platform/windows/display.h index b50acc77..238a87c2 100644 --- a/src/platform/windows/display.h +++ b/src/platform/windows/display.h @@ -126,6 +126,9 @@ namespace platf::dxgi { int init(const ::video::config_t &config, const std::string &display_name); + void + high_precision_sleep(std::chrono::nanoseconds duration); + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override; @@ -141,6 +144,8 @@ namespace platf::dxgi { DXGI_FORMAT capture_format; D3D_FEATURE_LEVEL feature_level; + util::safe_ptr_v2, BOOL, CloseHandle> timer; + typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS { D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE, D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL, diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index 2324a8c3..c016514a 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -95,25 +95,31 @@ namespace platf::dxgi { release_frame(); } + void + display_base_t::high_precision_sleep(std::chrono::nanoseconds duration) { + if (!timer) { + BOOST_LOG(error) << "Attempting high_precision_sleep() with uninitialized timer"; + return; + } + if (duration < 0s) { + BOOST_LOG(error) << "Attempting high_precision_sleep() with negative duration"; + return; + } + if (duration > 5s) { + BOOST_LOG(error) << "Attempting high_precision_sleep() with unexpectedly large duration (>5s)"; + return; + } + + LARGE_INTEGER due_time; + due_time.QuadPart = duration.count() / -100; + SetWaitableTimer(timer.get(), &due_time, 0, nullptr, nullptr, false); + WaitForSingleObject(timer.get(), INFINITE); + } + 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) { auto next_frame = std::chrono::steady_clock::now(); - // Use CREATE_WAITABLE_TIMER_HIGH_RESOLUTION if supported (Windows 10 1809+) - HANDLE timer = CreateWaitableTimerEx(nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); - if (!timer) { - timer = CreateWaitableTimerEx(nullptr, nullptr, 0, TIMER_ALL_ACCESS); - if (!timer) { - auto winerr = GetLastError(); - BOOST_LOG(error) << "Failed to create timer: "sv << winerr; - return capture_e::error; - } - } - - auto close_timer = util::fail_guard([timer]() { - CloseHandle(timer); - }); - // 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, // worst case it will trigger us to reinit DD, waking the display back up in @@ -131,13 +137,11 @@ namespace platf::dxgi { return platf::capture_e::reinit; } - // If the wait time is between 1 us and 1 second, wait the specified time + // If the wait time is positive and below 1 second, wait the specified time // and offset the next frame time from the exact current frame time target. - auto wait_time_us = std::chrono::duration_cast(next_frame - std::chrono::steady_clock::now()).count(); - if (wait_time_us > 0 && wait_time_us < 1000000) { - LARGE_INTEGER due_time { .QuadPart = -10LL * wait_time_us }; - SetWaitableTimer(timer, &due_time, 0, nullptr, nullptr, false); - WaitForSingleObject(timer, INFINITE); + auto wait_time = next_frame - std::chrono::steady_clock::now(); + if (wait_time > 0s && wait_time < 1s) { + high_precision_sleep(wait_time); next_frame += delay; } else { @@ -613,6 +617,17 @@ namespace platf::dxgi { // Capture format will be determined from the first call to AcquireNextFrame() capture_format = DXGI_FORMAT_UNKNOWN; + // Use CREATE_WAITABLE_TIMER_HIGH_RESOLUTION if supported (Windows 10 1809+) + timer.reset(CreateWaitableTimerEx(nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS)); + if (!timer) { + timer.reset(CreateWaitableTimerEx(nullptr, nullptr, 0, TIMER_ALL_ACCESS)); + if (!timer) { + auto winerr = GetLastError(); + BOOST_LOG(error) << "Failed to create timer: "sv << winerr; + return -1; + } + } + return 0; }