Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
35 changes: 35 additions & 0 deletions docs/source/about/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,41 @@ dwmflush

dwmflush = enabled

unpaced
^^^^^^^^

**Description**
Don't try to match client frame rate and stream every frame the host produces as soon as possible.
Can significantly improve frame time stability.

.. Caution:: Applies to Windows only. Experimental option.

**Default**
``disabled``

**Example**
.. code-block:: text

unpaced = disabled

serial
^^^^^^^^

**Description**
Don't use parallel devices for capture and encode.
Improves frame time stability and lowers overall streaming performance hit.
Lowers max theoretical throughput, use only if your gpu hardware encoder has enough headroom (most do).

.. Caution:: Applies to Windows only. Experimental option.

**Default**
``disabled``

**Example**
.. code-block:: text

serial = disabled

Audio
-----

Expand Down
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);

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;

const char *
dxgi_format_to_string(DXGI_FORMAT format);
const char *
Expand Down
79 changes: 55 additions & 24 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 @@ -70,19 +70,20 @@ namespace platf::dxgi {
}

auto status = dup->ReleaseFrame();
has_frame = false;
switch (status) {
case S_OK:
has_frame = false;
return capture_e::ok;
case DXGI_ERROR_WAIT_TIMEOUT:
return capture_e::timeout;
case WAIT_ABANDONED:

case DXGI_ERROR_INVALID_CALL:
BOOST_LOG(warning) << "Duplication frame already released";
return capture_e::ok;

case DXGI_ERROR_ACCESS_LOST:
case DXGI_ERROR_ACCESS_DENIED:
has_frame = false;
return capture_e::reinit;

default:
BOOST_LOG(error) << "Couldn't release frame [0x"sv << util::hex(status).to_string_view();
BOOST_LOG(error) << "Error while releasing duplication frame [0x"sv << util::hex(status).to_string_view();
return capture_e::error;
}
}
Expand Down Expand Up @@ -110,6 +111,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 +126,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 microsecond 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;
}
}

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 +175,18 @@ namespace platf::dxgi {
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
return status;
}

if (config::video.unpaced && mouse_pointer_visible) {
// Release desktop duplication frame so mouse pointer updates won't be delayed.
auto status = dup.release_frame();
if (status != platf::capture_e::ok) {
return status;
}
// Limit and pace mouse pointer updates.
if (output) {
output->WaitForVBlank();
}
}
}

return capture_e::ok;
Expand Down
Loading