Skip to content

Commit

Permalink
Implement basic network flow control (LizardByte#2803)
Browse files Browse the repository at this point in the history
Co-authored-by: Cameron Gutman <[email protected]>
  • Loading branch information
2 people authored and KuleRucket committed Oct 9, 2024
1 parent ca1b990 commit b19d6ab
Show file tree
Hide file tree
Showing 9 changed files with 354 additions and 103 deletions.
1 change: 1 addition & 0 deletions cmake/compile_definitions/windows.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ list(PREPEND PLATFORM_LIBRARIES
libstdc++.a
libwinpthread.a
libssp.a
ntdll
ksuser
wsock32
ws2_32
Expand Down
28 changes: 28 additions & 0 deletions src/platform/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <mutex>
#include <string>

#include <boost/core/noncopyable.hpp>

#include "src/config.h"
#include "src/logging.h"
#include "src/stat_trackers.h"
Expand Down Expand Up @@ -799,4 +801,30 @@ namespace platf {
*/
std::vector<supported_gamepad_t> &
supported_gamepads(input_t *input);

struct high_precision_timer: private boost::noncopyable {
virtual ~high_precision_timer() = default;

/**
* @brief Sleep for the duration
* @param duration Sleep duration
*/
virtual void
sleep_for(const std::chrono::nanoseconds &duration) = 0;

/**
* @brief Check if platform-specific timer backend has been initialized successfully
* @return `true` on success, `false` on error
*/
virtual
operator bool() = 0;
};

/**
* @brief Create platform-specific timer capable of high-precision sleep
* @return A unique pointer to timer
*/
std::unique_ptr<high_precision_timer>
create_high_precision_timer();

} // namespace platf
17 changes: 17 additions & 0 deletions src/platform/linux/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -933,4 +933,21 @@ namespace platf {

return std::make_unique<deinit_t>();
}

class linux_high_precision_timer: public high_precision_timer {
public:
void
sleep_for(const std::chrono::nanoseconds &duration) override {
std::this_thread::sleep_for(duration);
}

operator bool() override {
return true;
}
};

std::unique_ptr<high_precision_timer>
create_high_precision_timer() {
return std::make_unique<linux_high_precision_timer>();
}
} // namespace platf
16 changes: 16 additions & 0 deletions src/platform/macos/misc.mm
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,22 @@
return std::make_unique<qos_t>(sockfd, reset_options);
}

class macos_high_precision_timer: public high_precision_timer {
public:
void
sleep_for(const std::chrono::nanoseconds &duration) override {
std::this_thread::sleep_for(duration);
}

operator bool() override {
return true;
}
};

std::unique_ptr<high_precision_timer>
create_high_precision_timer() {
return std::make_unique<macos_high_precision_timer>();
}
} // namespace platf

namespace dyn {
Expand Down
5 changes: 1 addition & 4 deletions src/platform/windows/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,6 @@ 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;

Expand All @@ -184,7 +181,7 @@ namespace platf::dxgi {
DXGI_FORMAT capture_format;
D3D_FEATURE_LEVEL feature_level;

util::safe_ptr_v2<std::remove_pointer_t<HANDLE>, BOOL, CloseHandle> timer;
std::unique_ptr<high_precision_timer> timer = create_high_precision_timer();

typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS {
D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE, ///< Idle priority class
Expand Down
35 changes: 4 additions & 31 deletions src/platform/windows/display_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,27 +182,6 @@ 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 adjust_client_frame_rate = [&]() -> DXGI_RATIONAL {
Expand Down Expand Up @@ -268,7 +247,7 @@ namespace platf::dxgi {
status = capture_e::timeout;
}
else {
high_precision_sleep(sleep_period);
timer->sleep_for(sleep_period);
std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - sleep_target;
log_sleep_overshoot(overshoot_ns);

Expand Down Expand Up @@ -799,15 +778,9 @@ namespace platf::dxgi {
<< "Max Full Luminance : "sv << desc1.MaxFullFrameLuminance << " nits"sv;
}

// 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;
}
if (!timer || !*timer) {
BOOST_LOG(error) << "Uninitialized high precision timer";
return -1;
}

return 0;
Expand Down
106 changes: 102 additions & 4 deletions src/platform/windows/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,38 @@
#define WLAN_API_MAKE_VERSION(_major, _minor) (((DWORD) (_minor)) << 16 | (_major))
#endif

#include <winternl.h>
extern "C" {
NTSTATUS NTAPI
NtSetTimerResolution(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
}

namespace {

std::atomic<bool> used_nt_set_timer_resolution = false;

bool
nt_set_timer_resolution_max() {
ULONG minimum, maximum, current;
if (!NT_SUCCESS(NtQueryTimerResolution(&minimum, &maximum, &current)) ||
!NT_SUCCESS(NtSetTimerResolution(maximum, TRUE, &current))) {
return false;
}
return true;
}

bool
nt_set_timer_resolution_min() {
ULONG minimum, maximum, current;
if (!NT_SUCCESS(NtQueryTimerResolution(&minimum, &maximum, &current)) ||
!NT_SUCCESS(NtSetTimerResolution(minimum, TRUE, &current))) {
return false;
}
return true;
}

} // namespace

namespace bp = boost::process;

using namespace std::literals;
Expand Down Expand Up @@ -1115,8 +1147,15 @@ namespace platf {
// Enable MMCSS scheduling for DWM
DwmEnableMMCSS(true);

// Reduce timer period to 1ms
timeBeginPeriod(1);
// Reduce timer period to 0.5ms
if (nt_set_timer_resolution_max()) {
used_nt_set_timer_resolution = true;
}
else {
BOOST_LOG(error) << "NtSetTimerResolution() failed, falling back to timeBeginPeriod()";
timeBeginPeriod(1);
used_nt_set_timer_resolution = false;
}

// Promote ourselves to high priority class
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
Expand Down Expand Up @@ -1199,8 +1238,16 @@ namespace platf {
// Demote ourselves back to normal priority class
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);

// End our 1ms timer request
timeEndPeriod(1);
// End our 0.5ms timer request
if (used_nt_set_timer_resolution) {
used_nt_set_timer_resolution = false;
if (!nt_set_timer_resolution_min()) {
BOOST_LOG(error) << "nt_set_timer_resolution_min() failed even though nt_set_timer_resolution_max() succeeded";
}
}
else {
timeEndPeriod(1);
}

// Disable MMCSS scheduling for DWM
DwmEnableMMCSS(false);
Expand Down Expand Up @@ -1756,4 +1803,55 @@ namespace platf {

return output;
}

class win32_high_precision_timer: public high_precision_timer {
public:
win32_high_precision_timer() {
// Use CREATE_WAITABLE_TIMER_HIGH_RESOLUTION if supported (Windows 10 1809+)
timer = CreateWaitableTimerEx(nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
if (!timer) {
timer = CreateWaitableTimerEx(nullptr, nullptr, 0, TIMER_ALL_ACCESS);
if (!timer) {
BOOST_LOG(error) << "Unable to create high_precision_timer, CreateWaitableTimerEx() failed: " << GetLastError();
}
}
}

~win32_high_precision_timer() {
if (timer) CloseHandle(timer);
}

void
sleep_for(const std::chrono::nanoseconds &duration) override {
if (!timer) {
BOOST_LOG(error) << "Attempting high_precision_timer::sleep_for() with uninitialized timer";
return;
}
if (duration < 0s) {
BOOST_LOG(error) << "Attempting high_precision_timer::sleep_for() with negative duration";
return;
}
if (duration > 5s) {
BOOST_LOG(error) << "Attempting high_precision_timer::sleep_for() with unexpectedly large duration (>5s)";
return;
}

LARGE_INTEGER due_time;
due_time.QuadPart = duration.count() / -100;
SetWaitableTimer(timer, &due_time, 0, nullptr, nullptr, false);
WaitForSingleObject(timer, INFINITE);
}

operator bool() override {
return timer != NULL;
}

private:
HANDLE timer = NULL;
};

std::unique_ptr<high_precision_timer>
create_high_precision_timer() {
return std::make_unique<win32_high_precision_timer>();
}
} // namespace platf
Loading

0 comments on commit b19d6ab

Please sign in to comment.