Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,9 @@ namespace config {
{}, // encoder
{}, // adapter_name
{}, // output_name
true // dwmflush
true, // dwmflush
false, // unpaced
false, // serial
};

audio_t audio {};
Expand Down Expand Up @@ -957,6 +959,8 @@ namespace config {
string_f(vars, "adapter_name", video.adapter_name);
string_f(vars, "output_name", video.output_name);
bool_f(vars, "dwmflush", video.dwmflush);
bool_f(vars, "unpaced", video.unpaced);
bool_f(vars, "serial", video.serial);
Comment thread
ReenigneArcher marked this conversation as resolved.

path_f(vars, "pkey", nvhttp.pkey);
path_f(vars, "cert", nvhttp.cert);
Expand Down
2 changes: 2 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ namespace config {
std::string adapter_name;
std::string output_name;
bool dwmflush;
bool unpaced;
bool serial;
};

struct audio_t {
Expand Down
2 changes: 2 additions & 0 deletions src/platform/windows/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ namespace platf::dxgi {
return (capture_format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4;
}

bool mouse_pointer_visible = false;
Comment thread
ReenigneArcher marked this conversation as resolved.

const char *
dxgi_format_to_string(DXGI_FORMAT format);
const char *
Expand Down
57 changes: 40 additions & 17 deletions src/platform/windows/display_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace platf::dxgi {
return capture_status;
}

if (use_dwmflush) {
if (!config::video.unpaced && use_dwmflush) {
DwmFlush();
}

Expand Down Expand Up @@ -110,6 +110,13 @@ namespace platf::dxgi {
CloseHandle(timer);
});

if (config::video.unpaced) {
BOOST_LOG(info) << "Using experimental unpaced path";
}
if (config::video.serial) {
BOOST_LOG(info) << "Using experimental serial path";
}

while (true) {
// 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
Expand All @@ -118,25 +125,36 @@ namespace platf::dxgi {
return platf::capture_e::reinit;
}

// If the wait time is between 1 us and 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<std::chrono::microseconds>(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);
next_frame += delay;
}
else {
// If the wait time is negative (meaning the frame is past due) or the
// computed wait time is beyond a second (meaning possible clock issues),
// just capture the frame now and resynchronize the frame interval with
// the current time.
next_frame = std::chrono::steady_clock::now() + delay;
if (!config::video.unpaced) {
// If the wait time is between 1 us and 1 second, wait the specified time
Comment thread
ReenigneArcher marked this conversation as resolved.
Outdated
// and offset the next frame time from the exact current frame time target.
auto wait_time_us = std::chrono::duration_cast<std::chrono::microseconds>(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);
next_frame += delay;
}
else {
// If the wait time is negative (meaning the frame is past due) or the
// computed wait time is beyond a second (meaning possible clock issues),
// just capture the frame now and resynchronize the frame interval with
// the current time.
next_frame = std::chrono::steady_clock::now() + delay;
}
}

std::shared_ptr<img_t> img_out;
auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor);

std::chrono::milliseconds timeout = 1000ms;
if (config::video.serial) {
timeout = 100ms;
if (config::video.unpaced && mouse_pointer_visible) {
timeout = 0ms;
}
}

auto status = snapshot(pull_free_image_cb, img_out, timeout, *cursor);
switch (status) {
case platf::capture_e::reinit:
case platf::capture_e::error:
Expand All @@ -156,6 +174,11 @@ namespace platf::dxgi {
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
return status;
}

if (config::video.unpaced && mouse_pointer_visible) {
// Wait for vblank and flush Desktop Duplication API mouse pointer updates.
DwmFlush();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "use_dwmflush" has some logic behind it and using DwmFlush bypasses it completely.

@ns6089 ns6089 Apr 22, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and this was intentional.
DwmFlush() basically does WaitForVBlank() from direct3d and ensures duplication api has the most recent mouse pointer position (for some reason it normally gets delayed). Because this is unpaced path that disables sleeps, we need to limit update rate and call AcquireNextFrame() at even intervals for smooth mouse movement, WaitForVBlank() (or in this case DwmFlush()) accomplishes both.

@psyke83 psyke83 Apr 23, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WaitForVBlank() and DwmFlush() don't behave similarly in terms of resolving capture issues related to the mouse pointer.

DwmFlush() was explicitly added to alleviate this issue: loki-47-6F-64/sunshine#227, but using it causes problems if the host monitor refresh rate is lower than the client requested framerate. See the description here: 2d969c2

Bypassing the host refresh rate detection before using DwmFlush() will cause this issue to reoccur on those configurations. I have not checked this PR, but I would recommend investigating ways to smoothen frametime consistency without involving DwmFlush() at all. IMO, it should be removed entirely, as the original mouse-induced stuttering bug can be resolved by invoking ReleaseFrame() immediately after acquiring the frame and copying the buffer (see PR on original repo for reference: loki-47-6F-64/sunshine#286)

@ns6089 ns6089 Apr 23, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will see if calling ReleaseFrame() before sleeping with WaitForVBlank() eliminates mouse delay in the next AcquireNextFrame(). If it does, we can safely move from DwmFlush() to WaitForVBlank() in this particular path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard to test this objectively, but looking at mouse trails over recursive moonlight window, this seems to work. Thanks!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

return capture_e::ok;
Expand Down
Loading