From 646a5692108ec8c84e21bc0a303a7c99b37692ac Mon Sep 17 00:00:00 2001 From: ns6089 <61738816+ns6089@users.noreply.github.com> Date: Sat, 2 Sep 2023 15:12:02 +0300 Subject: [PATCH] nvenc: add option to disable realtime hags --- src/config.cpp | 2 + src/config.h | 1 + src/platform/windows/display.h | 37 +++++++++++++ src/platform/windows/display_base.cpp | 67 ++++++++++++++++++++---- src_assets/common/assets/web/config.html | 14 +++++ 5 files changed, 111 insertions(+), 10 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 8302ab67..7c738679 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -326,6 +326,7 @@ namespace config { }, // software {}, // nv + true, // nv_realtime_hags {}, // nv_legacy { @@ -924,6 +925,7 @@ namespace config { int_between_f(vars, "nvenc_preset", video.nv.quality_preset, { 1, 7 }); 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); #ifndef __APPLE__ video.nv_legacy.preset = video.nv.quality_preset + 11; diff --git a/src/config.h b/src/config.h index 8a6c1a2b..14d11359 100644 --- a/src/config.h +++ b/src/config.h @@ -29,6 +29,7 @@ namespace config { } sw; nvenc::nvenc_config nv; + bool nv_realtime_hags; struct { int preset; diff --git a/src/platform/windows/display.h b/src/platform/windows/display.h index 04975e93..cd210812 100644 --- a/src/platform/windows/display.h +++ b/src/platform/windows/display.h @@ -157,7 +157,44 @@ namespace platf::dxgi { D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME } D3DKMT_SCHEDULINGPRIORITYCLASS; + typedef UINT D3DKMT_HANDLE; + + typedef struct _D3DKMT_OPENADAPTERFROMLUID { + LUID AdapterLuid; + D3DKMT_HANDLE hAdapter; + } D3DKMT_OPENADAPTERFROMLUID; + + typedef struct _D3DKMT_WDDM_2_7_CAPS { + union { + struct + { + UINT HwSchSupported : 1; + UINT HwSchEnabled : 1; + UINT HwSchEnabledByDefault : 1; + UINT IndependentVidPnVSyncControl : 1; + UINT Reserved : 28; + }; + UINT Value; + }; + } D3DKMT_WDDM_2_7_CAPS; + + typedef struct _D3DKMT_QUERYADAPTERINFO { + D3DKMT_HANDLE hAdapter; + UINT Type; + VOID *pPrivateDriverData; + UINT PrivateDriverDataSize; + } D3DKMT_QUERYADAPTERINFO; + + const UINT KMTQAITYPE_WDDM_2_7_CAPS = 70; + + typedef struct _D3DKMT_CLOSEADAPTER { + D3DKMT_HANDLE hAdapter; + } D3DKMT_CLOSEADAPTER; + typedef NTSTATUS(WINAPI *PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS); + typedef NTSTATUS(WINAPI *PD3DKMTOpenAdapterFromLuid)(D3DKMT_OPENADAPTERFROMLUID *); + typedef NTSTATUS(WINAPI *PD3DKMTQueryAdapterInfo)(D3DKMT_QUERYADAPTERINFO *); + typedef NTSTATUS(WINAPI *PD3DKMTCloseAdapter)(D3DKMT_CLOSEADAPTER *); virtual bool is_hdr() override; diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index 67a08e57..30f85224 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -564,17 +564,64 @@ namespace platf::dxgi { HMODULE gdi32 = GetModuleHandleA("GDI32"); if (gdi32) { - PD3DKMTSetProcessSchedulingPriorityClass fn = - (PD3DKMTSetProcessSchedulingPriorityClass) GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass"); - if (fn) { - // Set scheduling priority to "high" for NVIDIA, or to "realtime" for other gpu vendors - // As of 2023.07, NVIDIA driver has unfixed bug(s) where "realtime" can cause unrecoverable encoding freeze or outright driver crash - // This issue happens more frequently with HAGS, in DX12 games or when VRAM is filled close to max capacity - // Track OBS to see if they find better workaround or NVIDIA fixes it on their end, they seem to be in communication - status = fn(GetCurrentProcess(), adapter_desc.VendorId == 0x10DE ? D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH : D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME); - if (FAILED(status)) { - BOOST_LOG(warning) << "Failed to set realtime GPU priority. Please run application as administrator for optimal performance."; + auto check_hags = [&](const LUID &adapter) -> bool { + auto d3dkmt_open_adapter = (PD3DKMTOpenAdapterFromLuid) GetProcAddress(gdi32, "D3DKMTOpenAdapterFromLuid"); + auto d3dkmt_query_adapter_info = (PD3DKMTQueryAdapterInfo) GetProcAddress(gdi32, "D3DKMTQueryAdapterInfo"); + auto d3dkmt_close_adapter = (PD3DKMTCloseAdapter) GetProcAddress(gdi32, "D3DKMTCloseAdapter"); + if (!d3dkmt_open_adapter || !d3dkmt_query_adapter_info || !d3dkmt_close_adapter) { + BOOST_LOG(error) << "Couldn't load d3dkmt functions from gdi32.dll to determine GPU HAGS status"; + return false; } + + D3DKMT_OPENADAPTERFROMLUID d3dkmt_adapter = { adapter }; + if (FAILED(d3dkmt_open_adapter(&d3dkmt_adapter))) { + BOOST_LOG(error) << "D3DKMTOpenAdapterFromLuid() failed while trying to determine GPU HAGS status"; + return false; + } + + bool result; + + D3DKMT_WDDM_2_7_CAPS d3dkmt_adapter_caps = {}; + D3DKMT_QUERYADAPTERINFO d3dkmt_adapter_info = {}; + d3dkmt_adapter_info.hAdapter = d3dkmt_adapter.hAdapter; + d3dkmt_adapter_info.Type = KMTQAITYPE_WDDM_2_7_CAPS; + d3dkmt_adapter_info.pPrivateDriverData = &d3dkmt_adapter_caps; + d3dkmt_adapter_info.PrivateDriverDataSize = sizeof(d3dkmt_adapter_caps); + + if (SUCCEEDED(d3dkmt_query_adapter_info(&d3dkmt_adapter_info))) { + result = d3dkmt_adapter_caps.HwSchEnabled; + } + else { + BOOST_LOG(warning) << "D3DKMTQueryAdapterInfo() failed while trying to determine GPU HAGS status"; + result = false; + } + + D3DKMT_CLOSEADAPTER d3dkmt_close_adapter_wrap = { d3dkmt_adapter.hAdapter }; + if (FAILED(d3dkmt_close_adapter(&d3dkmt_close_adapter_wrap))) { + BOOST_LOG(error) << "D3DKMTCloseAdapter() failed while trying to determine GPU HAGS status"; + } + + return result; + }; + + auto d3dkmt_set_process_priority = (PD3DKMTSetProcessSchedulingPriorityClass) GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass"); + if (d3dkmt_set_process_priority) { + auto priority = D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME; + bool hags_enabled = check_hags(adapter_desc.AdapterLuid); + if (adapter_desc.VendorId == 0x10DE) { + // As of 2023.07, NVIDIA driver has unfixed bug(s) where "realtime" can cause unrecoverable encoding freeze or outright driver crash + // This issue happens more frequently with HAGS, in DX12 games or when VRAM is filled close to max capacity + // Track OBS to see if they find better workaround or NVIDIA fixes it on their end, they seem to be in communication + if (hags_enabled && !config::video.nv_realtime_hags) priority = D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH; + } + BOOST_LOG(info) << "Active GPU has HAGS " << (hags_enabled ? "enabled" : "disabled"); + BOOST_LOG(info) << "Using " << (priority == D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH ? "high" : "realtime") << " GPU priority"; + if (FAILED(d3dkmt_set_process_priority(GetCurrentProcess(), priority))) { + BOOST_LOG(warning) << "Failed to adjust GPU priority. Please run application as administrator for optimal performance."; + } + } + else { + BOOST_LOG(error) << "Couldn't load D3DKMTSetProcessSchedulingPriorityClass function from gdi32.dll to adjust GPU priority"; } } diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 0ebc2e2e..e263ddcf 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -912,6 +912,19 @@