nvenc: add option to disable realtime hags

This commit is contained in:
ns6089 2023-09-02 15:12:02 +03:00 committed by Cameron Gutman
parent cadb45ec3d
commit 646a569210
5 changed files with 111 additions and 10 deletions

View File

@ -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;

View File

@ -29,6 +29,7 @@ namespace config {
} sw;
nvenc::nvenc_config nv;
bool nv_realtime_hags;
struct {
int preset;

View File

@ -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;

View File

@ -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";
}
}

View File

@ -912,6 +912,19 @@
<div id="panelsStayOpen-collapseOne" class="accordion-collapse collapse show"
aria-labelledby="panelsStayOpen-headingOne">
<div class="accordion-body">
<div class="mb-3" v-if="platform === 'windows'">
<label for="nvenc_realtime_hags" class="form-label">Use realtime priority in hardware accelerated gpu scheduling</label>
<select id="nvenc_realtime_hags" class="form-select" v-model="config.nvenc_realtime_hags">
<option value="disabled">Disabled</option>
<option value="enabled">Enabled (default)</option>
</select>
<div class="form-text">Currently NVIDIA drivers may freeze in encoder when
<a href="https://devblogs.microsoft.com/directx/hardware-accelerated-gpu-scheduling/">HAGS</a>
is enabled, realtime priority is used and VRAM utilization is close to maximum.<br>
Disabling this option lowers the priority to high, sidestepping the freeze at the cost of reduced capture
performance when the GPU is heavily loaded.
</div>
</div>
<div>
<label for="nvenc_h264_cavlc" class="form-label">Prefer CAVLC over CABAC in H.264</label>
<select id="nvenc_h264_cavlc" class="form-select" v-model="config.nvenc_h264_cavlc">
@ -1082,6 +1095,7 @@
"mouse": "enabled",
"nvenc_preset": "1",
"nvenc_twopass": "quarter_res",
"nvenc_realtime_hags": "enabled",
"nvenc_h264_cavlc": "disabled",
"origin_pin_allowed": "pc",
"origin_web_ui_allowed": "lan",