diff --git a/docs/source/about/advanced_usage.rst b/docs/source/about/advanced_usage.rst index 08fb5834ace..975e88076c4 100644 --- a/docs/source/about/advanced_usage.rst +++ b/docs/source/about/advanced_usage.rst @@ -1182,6 +1182,59 @@ keybindings nvenc_twopass = quarter_res +`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 `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**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 `__ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1214,6 +1267,69 @@ keybindings nvenc_realtime_hags = enabled +`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 `__ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +**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 `__ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/config.cpp b/src/config.cpp index eb25f58a7da..7418fc0c112 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -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 { @@ -937,9 +939,13 @@ 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; @@ -947,6 +953,8 @@ namespace config { 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); diff --git a/src/config.h b/src/config.h index ba0ee8a37fd..e08a87f3c4d 100644 --- a/src/config.h +++ b/src/config.h @@ -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 { diff --git a/src/nvenc/nvenc_base.cpp b/src/nvenc/nvenc_base.cpp index f305f7682ad..205a163dd26 100644 --- a/src/nvenc/nvenc_base.cpp +++ b/src/nvenc/nvenc_base.cpp @@ -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) { @@ -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; diff --git a/src/nvenc/nvenc_config.h b/src/nvenc/nvenc_config.h index 632146b7db0..c4aae12a86e 100644 --- a/src/nvenc/nvenc_config.h +++ b/src/nvenc/nvenc_config.h @@ -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; diff --git a/src/platform/windows/nvprefs/driver_settings.cpp b/src/platform/windows/nvprefs/driver_settings.cpp index 3b66e7a5054..2fbda6dc698 100644 --- a/src/platform/windows/nvprefs/driver_settings.cpp +++ b/src/platform/windows/nvprefs/driver_settings.cpp @@ -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) { @@ -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; diff --git a/src/platform/windows/nvprefs/nvprefs_common.cpp b/src/platform/windows/nvprefs/nvprefs_common.cpp index cda867dfdbc..f6acb6c548a 100644 --- a/src/platform/windows/nvprefs/nvprefs_common.cpp +++ b/src/platform/windows/nvprefs/nvprefs_common.cpp @@ -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 @@ -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 diff --git a/src/platform/windows/nvprefs/nvprefs_common.h b/src/platform/windows/nvprefs/nvprefs_common.h index 5dc8ba4092d..2b286d9e8ad 100644 --- a/src/platform/windows/nvprefs/nvprefs_common.h +++ b/src/platform/windows/nvprefs/nvprefs_common.h @@ -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 diff --git a/src/system_tray.cpp b/src/system_tray.cpp index 95a492dc297..189e58d293d 100644 --- a/src/system_tray.cpp +++ b/src/system_tray.cpp @@ -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; @@ -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; @@ -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"; diff --git a/src/video.cpp b/src/video.cpp index 68402d29d81..206e7feeb62 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -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 {}, @@ -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 { @@ -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 { @@ -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 } } } diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 71e01ae2e0c..a6a26670cd7 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -889,6 +889,35 @@

+ +
+ + +
+ Assign higher QP values to flat regions of the video.
+ Recommended to enable when streaming at lower bitrates. +
+
+ + +
+ + +
+ 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. +
+
+
@@ -918,6 +947,36 @@

+ +
+ + +
+ Adaptive P-State algorithm which NVIDIA drivers employ doesn't work well with low latency streaming, so + sunshine requests high power mode explicitly.
+ Disabling it is not recommended since this can lead to + significantly increased encoding latency. +
+
+ + +
+ + +
+ 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. +
+
+
@@ -1220,7 +1279,11 @@

options: { "nvenc_preset": 1, "nvenc_twopass": "quarter_res", + "nvenc_spatial_aq": "disabled", + "nvenc_vbv_increase": 0, "nvenc_realtime_hags": "enabled", + "nvenc_latency_over_power": "enabled", + "nvenc_opengl_vulkan_on_dxgi": "enabled", "nvenc_h264_cavlc": "disabled", }, },