Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose more nvenc and nvprefs options (rebased) #2051

Merged
merged 6 commits into from
Jan 21, 2024
Merged
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
116 changes: 116 additions & 0 deletions docs/source/about/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,59 @@ keybindings

nvenc_twopass = quarter_res

`nvenc_spatial_aq <https://localhost:47990/config/#nvenc_spatial_aq>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

**Description**
Assign higher QP values to flat regions of the video.
Recommended to enable when streaming at lower bitrates.

.. Note:: This option only applies when using NVENC `encoder`_.

**Choices**

.. table::
:widths: auto

========== ===========
Value Description
========== ===========
disabled Don't enable Spatial AQ (faster)
enabled Enable Spatial AQ (slower)
========== ===========

**Default**
``disabled``

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

nvenc_spatial_aq = disabled

`nvenc_vbv_increase <https://localhost:47990/config/#nvenc_vbv_increase>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

**Description**
Single-frame VBV/HRD percentage increase.
By default sunshine uses single-frame VBV/HRD, which means any encoded video frame size is not expected to exceed requested bitrate divided by requested frame rate.
Relaxing this restriction can be beneficial and act as low-latency variable bitrate, but may also lead to packet loss if the network doesn't have buffer headroom to handle bitrate spikes.
Maximum accepted value is 400, which corresponds to 5x increased encoded video frame upper size limit.

.. Note:: This option only applies when using NVENC `encoder`_.

.. Warning:: Can lead to network packet loss.

**Default**
``0``

**Range**
``0-400``

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

nvenc_vbv_increase = 0

`nvenc_realtime_hags <https://localhost:47990/config/#nvenc_realtime_hags>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -1214,6 +1267,69 @@ keybindings

nvenc_realtime_hags = enabled

`nvenc_latency_over_power <https://localhost:47990/config/#nvenc_latency_over_power>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

**Description**
Adaptive P-State algorithm which NVIDIA drivers employ doesn't work well with low latency streaming, so sunshine requests high power mode explicitly.

.. Note:: This option only applies when using NVENC `encoder`_.

.. Warning:: Disabling it is not recommended since this can lead to significantly increased encoding latency.

.. Caution:: Applies to Windows only.

**Choices**

.. table::
:widths: auto

========== ===========
Value Description
========== ===========
disabled Sunshine doesn't change GPU power preferences (not recommended)
enabled Sunshine requests high power mode explicitly
========== ===========

**Default**
``enabled``

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

nvenc_latency_over_power = enabled

`nvenc_opengl_vulkan_on_dxgi <https://localhost:47990/config/#nvenc_opengl_vulkan_on_dxgi>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

**Description**
Sunshine can't capture fullscreen OpenGL and Vulkan programs at full frame rate unless they present on top of DXGI.
This is system-wide setting that is reverted on sunshine program exit.

.. Note:: This option only applies when using NVENC `encoder`_.

.. Caution:: Applies to Windows only.

**Choices**

.. table::
:widths: auto

========== ===========
Value Description
========== ===========
disabled Sunshine leaves global Vulkan/OpenGL present method unchanged
enabled Sunshine changes global Vulkan/OpenGL present method to "Prefer layered on DXGI Swapchain"
========== ===========

**Default**
``enabled``

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

nvenc_opengl_vulkan_on_dxgi = enabled

`nvenc_h264_cavlc <https://localhost:47990/config/#nvenc_h264_cavlc>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
8 changes: 8 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,8 @@ namespace config {

{}, // nv
true, // nv_realtime_hags
true, // nv_opengl_vulkan_on_dxgi
true, // nv_sunshine_high_power_mode
{}, // nv_legacy

{
Expand Down Expand Up @@ -937,16 +939,22 @@ namespace config {
string_f(vars, "sw_tune", video.sw.sw_tune);

int_between_f(vars, "nvenc_preset", video.nv.quality_preset, { 1, 7 });
int_between_f(vars, "nvenc_vbv_increase", video.nv.vbv_percentage_increase, { 0, 400 });
bool_f(vars, "nvenc_spatial_aq", video.nv.adaptive_quantization);
generic_f(vars, "nvenc_twopass", video.nv.two_pass, nv::twopass_from_view);
bool_f(vars, "nvenc_h264_cavlc", video.nv.h264_cavlc);
bool_f(vars, "nvenc_realtime_hags", video.nv_realtime_hags);
bool_f(vars, "nvenc_opengl_vulkan_on_dxgi", video.nv_opengl_vulkan_on_dxgi);
bool_f(vars, "nvenc_latency_over_power", video.nv_sunshine_high_power_mode);

#ifndef __APPLE__
video.nv_legacy.preset = video.nv.quality_preset + 11;
video.nv_legacy.multipass = video.nv.two_pass == nvenc::nvenc_two_pass::quarter_resolution ? NV_ENC_TWO_PASS_QUARTER_RESOLUTION :
video.nv.two_pass == nvenc::nvenc_two_pass::full_resolution ? NV_ENC_TWO_PASS_FULL_RESOLUTION :
NV_ENC_MULTI_PASS_DISABLED;
video.nv_legacy.h264_coder = video.nv.h264_cavlc ? NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC : NV_ENC_H264_ENTROPY_CODING_MODE_CABAC;
video.nv_legacy.aq = video.nv.adaptive_quantization;
video.nv_legacy.vbv_percentage_increase = video.nv.vbv_percentage_increase;
#endif

int_f(vars, "qsv_preset", video.qsv.qsv_preset, qsv::preset_from_view);
Expand Down
4 changes: 4 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ namespace config {

nvenc::nvenc_config nv;
bool nv_realtime_hags;
bool nv_opengl_vulkan_on_dxgi;
bool nv_sunshine_high_power_mode;

struct {
int preset;
int multipass;
int h264_coder;
int aq;
int vbv_percentage_increase;
} nv_legacy;

struct {
Expand Down
6 changes: 5 additions & 1 deletion src/nvenc/nvenc_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ namespace nvenc {

if (get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) {
enc_config.rcParams.vbvBufferSize = client_config.bitrate * 1000 / client_config.framerate;
if (config.vbv_percentage_increase > 0) {
enc_config.rcParams.vbvBufferSize += enc_config.rcParams.vbvBufferSize * config.vbv_percentage_increase / 100;
}
}

auto set_h264_hevc_common_format_config = [&](auto &format_config) {
Expand Down Expand Up @@ -369,9 +372,10 @@ namespace nvenc {
if (init_params.enableEncodeAsync) extra += " async";
if (buffer_is_10bit()) extra += " 10-bit";
if (enc_config.rcParams.multiPass != NV_ENC_MULTI_PASS_DISABLED) extra += " two-pass";
if (config.vbv_percentage_increase > 0 && get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) extra += " vbv+" + std::to_string(config.vbv_percentage_increase);
if (encoder_params.rfi) extra += " rfi";
if (init_params.enableWeightedPrediction) extra += " weighted-prediction";
if (enc_config.rcParams.enableAQ) extra += " adaptive-quantization";
if (enc_config.rcParams.enableAQ) extra += " spatial-aq";
if (enc_config.rcParams.enableMinQP) extra += " qpmin=" + std::to_string(enc_config.rcParams.minQP.qpInterP);
if (config.insert_filler_data) extra += " filler-data";
BOOST_LOG(info) << "NvEnc: created encoder " << quality_preset_string_from_guid(init_params.presetGUID) << extra;
Expand Down
3 changes: 3 additions & 0 deletions src/nvenc/nvenc_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ namespace nvenc {
// Use optional preliminary pass for better motion vectors, bitrate distribution and stricter VBV(HRD), uses CUDA cores
nvenc_two_pass two_pass = nvenc_two_pass::quarter_resolution;

// Percentage increase of VBV/HRD from the default single frame, allows low-latency variable bitrate
int vbv_percentage_increase = 0;

// Improves fades compression, uses CUDA cores
bool weighted_prediction = false;

Expand Down
28 changes: 25 additions & 3 deletions src/platform/windows/nvprefs/driver_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ namespace nvprefs {
undo_data.reset();
NvAPI_Status status;

if (!get_nvprefs_options().opengl_vulkan_on_dxgi) {
// User requested to leave OpenGL/Vulkan DXGI swapchain setting alone
return true;
}

NvDRSProfileHandle profile_handle = 0;
status = NvAPI_DRS_GetBaseProfile(session_handle, &profile_handle);
if (status != NVAPI_OK) {
Expand Down Expand Up @@ -260,9 +265,26 @@ namespace nvprefs {
setting.version = NVDRS_SETTING_VER1;
status = NvAPI_DRS_GetSetting(session_handle, profile_handle, PREFERRED_PSTATE_ID, &setting);

if (status != NVAPI_OK ||
setting.settingLocation != NVDRS_CURRENT_PROFILE_LOCATION ||
setting.u32CurrentValue != PREFERRED_PSTATE_PREFER_MAX) {
if (!get_nvprefs_options().sunshine_high_power_mode) {
if (status == NVAPI_OK &&
setting.settingLocation == NVDRS_CURRENT_PROFILE_LOCATION) {
// User requested to not use high power mode for sunshine.exe,
// remove the setting from application profile if it's been set previously

status = NvAPI_DRS_DeleteProfileSetting(session_handle, profile_handle, PREFERRED_PSTATE_ID);
if (status != NVAPI_OK && status != NVAPI_SETTING_NOT_FOUND) {
nvapi_error_message(status);
error_message("NvAPI_DRS_DeleteProfileSetting() PREFERRED_PSTATE failed");
return false;
}
modified = true;

info_message(std::wstring(L"Removed PREFERRED_PSTATE for ") + sunshine_application_path);
}
}
else if (status != NVAPI_OK ||
setting.settingLocation != NVDRS_CURRENT_PROFILE_LOCATION ||
setting.u32CurrentValue != PREFERRED_PSTATE_PREFER_MAX) {
// Set power setting if needed
setting = {};
setting.version = NVDRS_SETTING_VER1;
Expand Down
11 changes: 11 additions & 0 deletions src/platform/windows/nvprefs/nvprefs_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#include "nvprefs_common.h"
#include "src/main.h" // sunshine boost::log severity levels

// read user override preferences from global sunshine config
#include "src/config.h"

namespace nvprefs {

void
Expand All @@ -24,4 +27,12 @@ namespace nvprefs {
BOOST_LOG(error) << "nvprefs: " << message;
}

nvprefs_options
get_nvprefs_options() {
nvprefs_options options;
options.opengl_vulkan_on_dxgi = config::video.nv_opengl_vulkan_on_dxgi;
options.sunshine_high_power_mode = config::video.nv_sunshine_high_power_mode;
return options;
}

} // namespace nvprefs
8 changes: 8 additions & 0 deletions src/platform/windows/nvprefs/nvprefs_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,12 @@ namespace nvprefs {
void
error_message(const std::string &message);

struct nvprefs_options {
bool opengl_vulkan_on_dxgi = true;
bool sunshine_high_power_mode = true;
};

nvprefs_options
get_nvprefs_options();

} // namespace nvprefs
6 changes: 3 additions & 3 deletions src/system_tray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ namespace system_tray {
tray.icon = TRAY_ICON_PLAYING;
tray.notification_title = "Stream Started";
char msg[256];
sprintf(msg, "Streaming started for %s", app_name.c_str());
snprintf(msg, std::size(msg), "Streaming started for %s", app_name.c_str());
tray.notification_text = msg;
tray.tooltip = msg;
tray.notification_icon = TRAY_ICON_PLAYING;
Expand All @@ -311,7 +311,7 @@ namespace system_tray {
tray.icon = TRAY_ICON_PAUSING;
tray_update(&tray);
char msg[256];
sprintf(msg, "Streaming paused for %s", app_name.c_str());
snprintf(msg, std::size(msg), "Streaming paused for %s", app_name.c_str());
tray.icon = TRAY_ICON_PAUSING;
tray.notification_title = "Stream Paused";
tray.notification_text = msg;
Expand All @@ -333,7 +333,7 @@ namespace system_tray {
tray.icon = TRAY_ICON;
tray_update(&tray);
char msg[256];
sprintf(msg, "Application %s successfully stopped", app_name.c_str());
snprintf(msg, std::size(msg), "Application %s successfully stopped", app_name.c_str());
tray.icon = TRAY_ICON;
tray.notification_icon = TRAY_ICON;
tray.notification_title = "Application Stopped";
Expand Down
9 changes: 9 additions & 0 deletions src/video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,7 @@ namespace video {
{ "tune"s, NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY },
{ "rc"s, NV_ENC_PARAMS_RC_CBR },
{ "multipass"s, &config::video.nv_legacy.multipass },
{ "aq"s, &config::video.nv_legacy.aq },
},
// SDR-specific options
{},
Expand All @@ -658,6 +659,7 @@ namespace video {
{ "tune"s, NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY },
{ "rc"s, NV_ENC_PARAMS_RC_CBR },
{ "multipass"s, &config::video.nv_legacy.multipass },
{ "aq"s, &config::video.nv_legacy.aq },
},
// SDR-specific options
{
Expand All @@ -681,6 +683,7 @@ namespace video {
{ "rc"s, NV_ENC_PARAMS_RC_CBR },
{ "coder"s, &config::video.nv_legacy.h264_coder },
{ "multipass"s, &config::video.nv_legacy.multipass },
{ "aq"s, &config::video.nv_legacy.aq },
},
// SDR-specific options
{
Expand Down Expand Up @@ -1698,6 +1701,12 @@ namespace video {
}
else {
ctx->rc_buffer_size = bitrate / config.framerate;

#ifndef __APPLE__
if (encoder.name == "nvenc" && config::video.nv_legacy.vbv_percentage_increase > 0) {
ctx->rc_buffer_size += ctx->rc_buffer_size * config::video.nv_legacy.vbv_percentage_increase / 100;
}
#endif
}
}
}
Expand Down
Loading