Skip to content

[3.x] Disable NVIDIA's threaded OpenGL optimization on Windows #106551

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

Draft
wants to merge 1 commit into
base: 3.x
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions COPYRIGHT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,11 @@ Comment: NanoSVG
Copyright: 2013-2014, Mikko Mononen
License: Zlib

Files: ./thirdparty/nvapi/nvapi_minimal.h
Comment: Stripped down version of "nvapi.h" from the NVIDIA NVAPI SDK
Copyright: 2019-2022, NVIDIA Corporation
License: Expat

Files: ./thirdparty/opus/
Comment: Opus
Copyright: 2001-2011, Xiph.Org, Skype Limited, Octasic,
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,10 @@
<member name="rendering/limits/time/time_rollover_secs" type="float" setter="" getter="" default="3600">
Shaders have a time variable that constantly increases. At some point, it needs to be rolled back to zero to avoid precision errors on shader animations. This setting specifies when (in seconds).
</member>
<member name="rendering/misc/compatibility/nvidia_disable_threaded_optimization" type="bool" setter="" getter="" default="false">
If [code]true[/code], disables the threaded optimization feature from the NVIDIA drivers, which are known to cause stuttering in most OpenGL applications.
[b]Note:[/b] This setting only works on Windows, as threaded optimization is disabled by default on other platforms.
</member>
<member name="rendering/misc/lossless_compression/force_png" type="bool" setter="" getter="" default="false">
If [code]true[/code], the texture importer will import lossless textures using the PNG format. Otherwise, it will default to using WebP.
</member>
Expand Down
1 change: 1 addition & 0 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
rtm = GLOBAL_DEF("rendering/threads/thread_model", OS::RENDER_THREAD_SAFE);
}
GLOBAL_DEF("rendering/threads/thread_safe_bvh", false);
GLOBAL_DEF_RST("rendering/misc/compatibility/nvidia_disable_threaded_optimization", false);

if (rtm >= 0 && rtm < 3) {
#ifdef NO_THREADS
Expand Down
216 changes: 215 additions & 1 deletion platform/windows/os_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "core/io/marshalls.h"
#include "core/math/geometry.h"
#include "core/project_settings.h"
#include "core/version_generated.gen.h"
#include "drivers/gles2/rasterizer_gles2.h"
#include "drivers/gles3/rasterizer_gles3.h"
Expand All @@ -44,6 +45,7 @@
#include "servers/audio_server.h"
#include "servers/visual/visual_server_raster.h"
#include "servers/visual/visual_server_wrap_mt.h"
#include "thirdparty/nvapi/nvapi_minimal.h"
#include "windows_terminal_logger.h"

#include <avrt.h>
Expand All @@ -60,6 +62,218 @@
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}

const int OGL_THREAD_CONTROL_ID = 0x20C1221E;
const int OGL_THREAD_CONTROL_DISABLE = 0x00000002;
const int OGL_THREAD_CONTROL_ENABLE = 0x00000001;

typedef int(__cdecl *NvAPI_Initialize_t)();
typedef int(__cdecl *NvAPI_Unload_t)();
typedef int(__cdecl *NvAPI_GetErrorMessage_t)(unsigned int, NvAPI_ShortString);
typedef int(__cdecl *NvAPI_DRS_CreateSession_t)(NvDRSSessionHandle *);
typedef int(__cdecl *NvAPI_DRS_DestroySession_t)(NvDRSSessionHandle);
typedef int(__cdecl *NvAPI_DRS_LoadSettings_t)(NvDRSSessionHandle);
typedef int(__cdecl *NvAPI_DRS_CreateProfile_t)(NvDRSSessionHandle, NVDRS_PROFILE *, NvDRSProfileHandle *);
typedef int(__cdecl *NvAPI_DRS_CreateApplication_t)(NvDRSSessionHandle, NvDRSProfileHandle, NVDRS_APPLICATION *);
typedef int(__cdecl *NvAPI_DRS_SaveSettings_t)(NvDRSSessionHandle);
typedef int(__cdecl *NvAPI_DRS_SetSetting_t)(NvDRSSessionHandle, NvDRSProfileHandle, NVDRS_SETTING *);
typedef int(__cdecl *NvAPI_DRS_FindProfileByName_t)(NvDRSSessionHandle, NvAPI_UnicodeString, NvDRSProfileHandle *);
typedef int(__cdecl *NvAPI_DRS_GetApplicationInfo_t)(NvDRSSessionHandle, NvDRSProfileHandle, NvAPI_UnicodeString, NVDRS_APPLICATION *);
typedef int(__cdecl *NvAPI_DRS_DeleteProfile_t)(NvDRSSessionHandle, NvDRSProfileHandle);
NvAPI_GetErrorMessage_t NvAPI_GetErrorMessage__;

static bool nvapi_err_check(const char *msg, int status) {
if (status != 0) {
if (OS::get_singleton()->is_stdout_verbose()) {
NvAPI_ShortString err_desc = { 0 };
NvAPI_GetErrorMessage__(status, err_desc);
print_verbose(vformat("%s: %s(code %d)", msg, err_desc, status));
}
return false;
}
return true;
}

// On windows we have to customize the NVIDIA application profile:
// * disable threaded optimization when using NVIDIA cards to avoid stuttering, see
// https://stackoverflow.com/questions/36959508/nvidia-graphics-driver-causing-noticeable-frame-stuttering/37632948
// https://github.com/Ryujinx/Ryujinx/blob/master/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs
// * disable G-SYNC in windowed mode, as it results in unstable editor refresh rates
void GLManagerNative_Windows::_nvapi_setup_profile() {

Check failure on line 101 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=release_debug, tools=yes)

'GLManagerNative_Windows': is not a class or namespace name

Check failure on line 101 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=release, tools=no)

'GLManagerNative_Windows': is not a class or namespace name
HMODULE nvapi = nullptr;
#ifdef _WIN64
nvapi = LoadLibraryA("nvapi64.dll");
#else
nvapi = LoadLibraryA("nvapi.dll");
#endif

if (nvapi == nullptr) {
return;
}

void *(__cdecl * NvAPI_QueryInterface)(unsigned int interface_id) = nullptr;

NvAPI_QueryInterface = (void *(__cdecl *)(unsigned int))(void *)GetProcAddress(nvapi, "nvapi_QueryInterface");

if (NvAPI_QueryInterface == nullptr) {
print_verbose("Error getting NVAPI NvAPI_QueryInterface");
return;
}

// Setup NVAPI function pointers
NvAPI_Initialize_t NvAPI_Initialize = (NvAPI_Initialize_t)NvAPI_QueryInterface(0x0150E828);
NvAPI_GetErrorMessage__ = (NvAPI_GetErrorMessage_t)NvAPI_QueryInterface(0x6C2D048C);
NvAPI_DRS_CreateSession_t NvAPI_DRS_CreateSession = (NvAPI_DRS_CreateSession_t)NvAPI_QueryInterface(0x0694D52E);
NvAPI_DRS_DestroySession_t NvAPI_DRS_DestroySession = (NvAPI_DRS_DestroySession_t)NvAPI_QueryInterface(0xDAD9CFF8);
NvAPI_Unload_t NvAPI_Unload = (NvAPI_Unload_t)NvAPI_QueryInterface(0xD22BDD7E);
NvAPI_DRS_LoadSettings_t NvAPI_DRS_LoadSettings = (NvAPI_DRS_LoadSettings_t)NvAPI_QueryInterface(0x375DBD6B);
NvAPI_DRS_CreateProfile_t NvAPI_DRS_CreateProfile = (NvAPI_DRS_CreateProfile_t)NvAPI_QueryInterface(0xCC176068);
NvAPI_DRS_CreateApplication_t NvAPI_DRS_CreateApplication = (NvAPI_DRS_CreateApplication_t)NvAPI_QueryInterface(0x4347A9DE);
NvAPI_DRS_SaveSettings_t NvAPI_DRS_SaveSettings = (NvAPI_DRS_SaveSettings_t)NvAPI_QueryInterface(0xFCBC7E14);
NvAPI_DRS_SetSetting_t NvAPI_DRS_SetSetting = (NvAPI_DRS_SetSetting_t)NvAPI_QueryInterface(0x577DD202);
NvAPI_DRS_FindProfileByName_t NvAPI_DRS_FindProfileByName = (NvAPI_DRS_FindProfileByName_t)NvAPI_QueryInterface(0x7E4A9A0B);
NvAPI_DRS_GetApplicationInfo_t NvAPI_DRS_GetApplicationInfo = (NvAPI_DRS_GetApplicationInfo_t)NvAPI_QueryInterface(0xED1F8C69);
NvAPI_DRS_DeleteProfile_t NvAPI_DRS_DeleteProfile = (NvAPI_DRS_DeleteProfile_t)NvAPI_QueryInterface(0x17093206);

if (!nvapi_err_check("NVAPI: Init failed", NvAPI_Initialize())) {
return;
}

print_verbose("NVAPI: Init OK!");

NvDRSSessionHandle session_handle;

if (NvAPI_DRS_CreateSession == nullptr) {
return;
}

if (!nvapi_err_check("NVAPI: Error creating DRS session", NvAPI_DRS_CreateSession(&session_handle))) {
NvAPI_Unload();
return;
}

if (!nvapi_err_check("NVAPI: Error loading DRS settings", NvAPI_DRS_LoadSettings(session_handle))) {
NvAPI_DRS_DestroySession(session_handle);
NvAPI_Unload();
return;
}

String app_executable_name = OS::get_singleton()->get_executable_path().get_file();
String app_profile_name = GLOBAL_GET("application/config/name");
// We need a name anyways, so let's use the engine name if an application name is not available
// (this is used mostly by the Project Manager)
if (app_profile_name.is_empty()) {

Check failure on line 164 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=release_debug, tools=yes)

'is_empty': is not a member of 'String'

Check failure on line 164 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=release, tools=no)

'is_empty': is not a member of 'String'
app_profile_name = GODOT_VERSION_NAME;

Check failure on line 165 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=release_debug, tools=yes)

'GODOT_VERSION_NAME': undeclared identifier

Check failure on line 165 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=release, tools=no)

'GODOT_VERSION_NAME': undeclared identifier
}
String old_profile_name = app_profile_name + " Nvidia Profile";
Char16String app_profile_name_u16 = app_profile_name.utf16();

Check failure on line 168 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=release_debug, tools=yes)

'Char16String': undeclared identifier

Check failure on line 168 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=release_debug, tools=yes)

syntax error: missing ';' before identifier 'app_profile_name_u16'

Check failure on line 168 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=release_debug, tools=yes)

'app_profile_name_u16': undeclared identifier

Check failure on line 168 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=release_debug, tools=yes)

'utf16': is not a member of 'String'

Check failure on line 168 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=release, tools=no)

'Char16String': undeclared identifier

Check failure on line 168 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=release, tools=no)

syntax error: missing ';' before identifier 'app_profile_name_u16'

Check failure on line 168 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=release, tools=no)

'app_profile_name_u16': undeclared identifier

Check failure on line 168 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=release, tools=no)

'utf16': is not a member of 'String'
Char16String old_profile_name_u16 = old_profile_name.utf16();

Check failure on line 169 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=release_debug, tools=yes)

'Char16String': undeclared identifier

Check failure on line 169 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=release_debug, tools=yes)

syntax error: missing ';' before identifier 'old_profile_name_u16'

Check failure on line 169 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=release_debug, tools=yes)

'old_profile_name_u16': undeclared identifier

Check failure on line 169 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=release, tools=no)

'Char16String': undeclared identifier

Check failure on line 169 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=release, tools=no)

syntax error: missing ';' before identifier 'old_profile_name_u16'

Check failure on line 169 in platform/windows/os_windows.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=release, tools=no)

'old_profile_name_u16': undeclared identifier
Char16String app_executable_name_u16 = app_executable_name.utf16();

// A previous error in app creation logic could result in invalid profiles,
// clean these if they exist before proceeding.
NvDRSProfileHandle old_profile_handle;

int old_status = NvAPI_DRS_FindProfileByName(session_handle, (NvU16 *)(old_profile_name_u16.ptrw()), &old_profile_handle);

if (old_status == 0) {
print_verbose("NVAPI: Deleting old profile...");

if (!nvapi_err_check("NVAPI: Error deleting old profile", NvAPI_DRS_DeleteProfile(session_handle, old_profile_handle))) {
NvAPI_DRS_DestroySession(session_handle);
NvAPI_Unload();
return;
}

if (!nvapi_err_check("NVAPI: Error deleting old profile", NvAPI_DRS_SaveSettings(session_handle))) {
NvAPI_DRS_DestroySession(session_handle);
NvAPI_Unload();
return;
}
}

NvDRSProfileHandle profile_handle = nullptr;

int profile_status = NvAPI_DRS_FindProfileByName(session_handle, (NvU16 *)(app_profile_name_u16.ptrw()), &profile_handle);

if (profile_status != 0) {
print_verbose("NVAPI: Profile not found, creating...");

NVDRS_PROFILE profile_info;
profile_info.version = NVDRS_PROFILE_VER;
profile_info.isPredefined = 0;
memcpy(profile_info.profileName, app_profile_name_u16.get_data(), sizeof(char16_t) * app_profile_name_u16.size());

if (!nvapi_err_check("NVAPI: Error creating profile", NvAPI_DRS_CreateProfile(session_handle, &profile_info, &profile_handle))) {
NvAPI_DRS_DestroySession(session_handle);
NvAPI_Unload();
return;
}
}

NVDRS_APPLICATION_V4 app;
app.version = NVDRS_APPLICATION_VER_V4;

int app_status = NvAPI_DRS_GetApplicationInfo(session_handle, profile_handle, (NvU16 *)(app_executable_name_u16.ptrw()), &app);

if (app_status != 0) {
print_verbose("NVAPI: Application not found in profile, creating...");

app.isPredefined = 0;
memcpy(app.appName, app_executable_name_u16.get_data(), sizeof(char16_t) * app_executable_name_u16.size());
memcpy(app.launcher, L"", sizeof(wchar_t));
memcpy(app.fileInFolder, L"", sizeof(wchar_t));

if (!nvapi_err_check("NVAPI: Error creating application", NvAPI_DRS_CreateApplication(session_handle, profile_handle, &app))) {
NvAPI_DRS_DestroySession(session_handle);
NvAPI_Unload();
return;
}
}

NVDRS_SETTING ogl_thread_control_setting = {};
ogl_thread_control_setting.version = NVDRS_SETTING_VER;
ogl_thread_control_setting.settingId = OGL_THREAD_CONTROL_ID;
ogl_thread_control_setting.settingType = NVDRS_DWORD_TYPE;
int thread_control_val = OGL_THREAD_CONTROL_DISABLE;
if (!GLOBAL_GET("rendering/misc/compatibility/nvidia_disable_threaded_optimization")) {
thread_control_val = OGL_THREAD_CONTROL_ENABLE;
}
ogl_thread_control_setting.u32CurrentValue = thread_control_val;

if (!nvapi_err_check("NVAPI: Error calling NvAPI_DRS_SetSetting", NvAPI_DRS_SetSetting(session_handle, profile_handle, &ogl_thread_control_setting))) {
NvAPI_DRS_DestroySession(session_handle);
NvAPI_Unload();
return;
}

NVDRS_SETTING vrr_mode_setting = {};
vrr_mode_setting.version = NVDRS_SETTING_VER;
vrr_mode_setting.settingId = VRR_MODE_ID;
vrr_mode_setting.settingType = NVDRS_DWORD_TYPE;
vrr_mode_setting.u32CurrentValue = VRR_MODE_FULLSCREEN_ONLY;

if (!nvapi_err_check("NVAPI: Error calling NvAPI_DRS_SetSetting", NvAPI_DRS_SetSetting(session_handle, profile_handle, &vrr_mode_setting))) {
NvAPI_DRS_DestroySession(session_handle);
NvAPI_Unload();
return;
}

if (!nvapi_err_check("NVAPI: Error saving settings", NvAPI_DRS_SaveSettings(session_handle))) {
NvAPI_DRS_DestroySession(session_handle);
NvAPI_Unload();
return;
}

if (thread_control_val == OGL_THREAD_CONTROL_DISABLE) {
print_verbose("NVAPI: Disabled OpenGL threaded optimization successfully");
} else {
print_verbose("NVAPI: Enabled OpenGL threaded optimization successfully");
}
print_verbose("NVAPI: Disabled G-SYNC for windowed mode successfully");

NvAPI_DRS_DestroySession(session_handle);
}

// Workaround mingw-w64 < 4.0 bug
#ifndef WM_TOUCH
#define WM_TOUCH 576
Expand Down Expand Up @@ -1705,7 +1919,7 @@
}

update_real_mouse_position();

_nvapi_disable_threaded_optimization();
return OK;
}

Expand Down
1 change: 1 addition & 0 deletions platform/windows/os_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ class OS_Windows : public OS {
void _update_window_mouse_passthrough();

void _set_mouse_mode_impl(MouseMode p_mode);
void _nvapi_disable_threaded_optimization();

// functions used by main to initialize/deinitialize the OS
protected:
Expand Down
9 changes: 9 additions & 0 deletions thirdparty/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,15 @@ Files extracted from the upstream source:
library.


## nvapi

- Upstream: http://download.nvidia.com/XFree86/nvapi-open-source-sdk
- Version: R525
- License: MIT

- `nvapi_minimal.h` was created by using `nvapi.h` from upstream and removing unnecessary code.


## oidn

- Upstream: https://github.com/OpenImageDenoise/oidn
Expand Down
Loading