Created
July 27, 2025 16:15
-
-
Save parzivail/e97ef414661b1168359254a4fbf9ccbb to your computer and use it in GitHub Desktop.
Creates a profile in the NVIDIA Control Panel that disables Threaded Optimization
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Runtime.CompilerServices; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
namespace NvidiaInterop_cs; | |
internal static class NvidiaInterop | |
{ | |
[StructLayout(LayoutKind.Sequential, Pack = 4)] | |
private unsafe struct NvAPI_UnicodeString | |
{ | |
private fixed byte _data[4096]; | |
public NvAPI_UnicodeString(string text) | |
{ | |
Set(text); | |
} | |
public void Set(string text) | |
{ | |
text += '\0'; | |
fixed (char* textPtr = text) | |
fixed (byte* data = _data) | |
{ | |
Encoding.Unicode.GetBytes(textPtr, text.Length, data, 4096); | |
} | |
} | |
} | |
[Flags] | |
private enum NVDRS_APPLICATION_V4_FLAGS : uint | |
{ | |
Metro = 0b01, | |
CommandLine = 0b10 | |
} | |
[StructLayout(LayoutKind.Sequential, Pack = 4)] | |
private struct NVDRS_APPLICATION_V4 | |
{ | |
public uint Version; | |
public NvBool32 IsPredefined; | |
public NvAPI_UnicodeString AppName; | |
public NvAPI_UnicodeString UserFriendlyName; | |
public NvAPI_UnicodeString Launcher; | |
public NvAPI_UnicodeString FileInFolder; | |
public NVDRS_APPLICATION_V4_FLAGS Flags; | |
public NvAPI_UnicodeString CommandLine; | |
} | |
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |
private struct NVDRS_PROFILE | |
{ | |
public uint Version; | |
public NvAPI_UnicodeString ProfileName; | |
public uint GpuSupport; | |
public NvBool32 IsPredefined; | |
public uint NumOfApps; | |
public uint NumOfSettings; | |
} | |
private enum NVDRS_SETTING_TYPE : uint | |
{ | |
NVDRS_DWORD_TYPE = 0x00000000 | |
} | |
private enum NVDRS_SETTING_LOCATION : uint | |
{ | |
NvdrsCurrentProfileLocation = 0x00000000 | |
} | |
private enum NvBool32 : uint | |
{ | |
False = 0x00000000, | |
True = 0x00000001 | |
} | |
[StructLayout(LayoutKind.Explicit, Size = 0x3020)] | |
private struct NVDRS_SETTING | |
{ | |
[FieldOffset(0x0)] | |
public uint Version; | |
[FieldOffset(0x4)] | |
public NvAPI_UnicodeString SettingName; | |
[FieldOffset(0x1004)] | |
public NvAPIConstants SettingId; | |
[FieldOffset(0x1008)] | |
public NVDRS_SETTING_TYPE SettingType; | |
[FieldOffset(0x100C)] | |
public NVDRS_SETTING_LOCATION SettingLocation; | |
[FieldOffset(0x1010)] | |
public NvBool32 IsCurrentPredefined; | |
[FieldOffset(0x1014)] | |
public NvBool32 IsPredefinedValid; | |
[FieldOffset(0x1018)] | |
public uint PredefinedValue; | |
[FieldOffset(0x201C)] | |
public uint CurrentValue; | |
} | |
private enum NvAPI_Status : uint | |
{ | |
NVAPI_OK = 0x00000000 | |
} | |
private enum NvAPIConstants : uint | |
{ | |
SHIM_MCCOMPAT_ID = 0x10F9DC80, | |
SHIM_MCCOMPAT_ENABLE = 0x00000001, | |
SHIM_RENDERING_MODE_ID = 0x10F9DC81, | |
SHIM_RENDERING_MODE_ENABLE = 0x00000001, | |
SHIM_RENDERING_OPTIONS_ID = 0x10F9DC84, | |
SHIM_RENDERING_OPTIONS_DEFAULT_RENDERING_MODE = 0x00000000, | |
OGL_THREAD_CONTROL_ID = 0x20C1221E, | |
OGL_THREAD_CONTROL_DISABLE = 0x00000002 | |
} | |
private enum NvAPIMethods : uint | |
{ | |
NvAPI_Initialize_ID = 0x0150E828, | |
NvAPI_DRS_CreateSession_ID = 0x0694D52E, | |
NvAPI_DRS_LoadSettings_ID = 0x375DBD6B, | |
NvAPI_DRS_FindProfileByName_ID = 0x7E4A9A0B, | |
NvAPI_DRS_CreateProfile_ID = 0x0CC176068, | |
NvAPI_DRS_CreateApplication_ID = 0x4347A9DE, | |
NvAPI_DRS_SetSetting_ID = 0x577DD202, | |
NvAPI_DRS_SaveSettings_ID = 0xFCBC7E14, | |
NvAPI_DRS_DestroySession_ID = 0x0DAD9CFF8 | |
} | |
private const string LibraryPath = "nvapi64.dll"; | |
[DllImport(LibraryPath)] | |
private static extern IntPtr nvapi_QueryInterface(NvAPIMethods id); | |
private static unsafe delegate*<NvAPI_Status> NvAPI_Initialize; | |
private static unsafe delegate*<out IntPtr, NvAPI_Status> NvAPI_DRS_CreateSession; | |
private static unsafe delegate*<IntPtr, NvAPI_Status> NvAPI_DRS_LoadSettings; | |
private static unsafe delegate*<IntPtr, NvAPI_UnicodeString, out IntPtr, NvAPI_Status> NvAPI_DRS_FindProfileByName; | |
private static unsafe delegate*<IntPtr, ref NVDRS_PROFILE, ref IntPtr, NvAPI_Status> NvAPI_DRS_CreateProfile; | |
private static unsafe delegate*<IntPtr, IntPtr, ref NVDRS_APPLICATION_V4, NvAPI_Status> NvAPI_DRS_CreateApplication; | |
private static unsafe delegate*<IntPtr, IntPtr, ref NVDRS_SETTING, NvAPI_Status> NvAPI_DRS_SetSetting; | |
private static unsafe delegate*<IntPtr, NvAPI_Status> NvAPI_DRS_SaveSettings; | |
private static unsafe delegate*<IntPtr, NvAPI_Status> NvAPI_DRS_DestroySession; | |
private static void AssertSuccess(string method, NvAPI_Status status) | |
{ | |
if (status == NvAPI_Status.NVAPI_OK) | |
return; | |
throw new Exception($"NvAPI failure: {method} returned {status}"); | |
} | |
private static uint MAKE_NVAPI_VERSION<T>(uint version) where T : unmanaged | |
{ | |
return (uint)Unsafe.SizeOf<T>() | (version << 16); | |
} | |
public static unsafe bool Load(string profileName, string executableName, string friendlyName) | |
{ | |
// A C# port of: https://stackoverflow.com/questions/36959508/nvidia-graphics-driver-causing-noticeable-frame-stuttering | |
// Attempt to locate the API. If we can't find it, we probably don't have an NVIDIA GPU. | |
if (!TryLoadApi()) | |
return false; | |
// Attempt to initialize the API. If we can't, we have the drivers for an NVIDIA GPU, but no hardware. | |
NvAPI_Initialize = (delegate*<NvAPI_Status>)GetMethodPointer(NvAPIMethods.NvAPI_Initialize_ID); | |
if (NvAPI_Initialize() != NvAPI_Status.NVAPI_OK) | |
return false; | |
// After we initialize the API, all other calls become assertions for NvAPI_Status.NVAPI_OK | |
// since the only call we will let fail is the initialization. | |
// If the initialization went well, continue on to find the rest of the handles | |
NvAPI_DRS_CreateSession = (delegate*<out IntPtr, NvAPI_Status>)GetMethodPointer(NvAPIMethods.NvAPI_DRS_CreateSession_ID); | |
NvAPI_DRS_LoadSettings = (delegate*<IntPtr, NvAPI_Status>)GetMethodPointer(NvAPIMethods.NvAPI_DRS_LoadSettings_ID); | |
NvAPI_DRS_FindProfileByName = (delegate*<IntPtr, NvAPI_UnicodeString, out IntPtr, NvAPI_Status>)GetMethodPointer(NvAPIMethods.NvAPI_DRS_FindProfileByName_ID); | |
NvAPI_DRS_CreateProfile = (delegate*<IntPtr, ref NVDRS_PROFILE, ref IntPtr, NvAPI_Status>)GetMethodPointer(NvAPIMethods.NvAPI_DRS_CreateProfile_ID); | |
NvAPI_DRS_CreateApplication = (delegate*<IntPtr, IntPtr, ref NVDRS_APPLICATION_V4, NvAPI_Status>)GetMethodPointer(NvAPIMethods.NvAPI_DRS_CreateApplication_ID); | |
NvAPI_DRS_SetSetting = (delegate*<IntPtr, IntPtr, ref NVDRS_SETTING, NvAPI_Status>)GetMethodPointer(NvAPIMethods.NvAPI_DRS_SetSetting_ID); | |
NvAPI_DRS_SaveSettings = (delegate*<IntPtr, NvAPI_Status>)GetMethodPointer(NvAPIMethods.NvAPI_DRS_SaveSettings_ID); | |
NvAPI_DRS_DestroySession = (delegate*<IntPtr, NvAPI_Status>)GetMethodPointer(NvAPIMethods.NvAPI_DRS_DestroySession_ID); | |
// Get the session settings | |
AssertSuccess(nameof(NvAPI_DRS_CreateSession), NvAPI_DRS_CreateSession(out var session)); | |
AssertSuccess(nameof(NvAPI_DRS_LoadSettings), NvAPI_DRS_LoadSettings(session)); | |
// If the requested profile doesn't exist, create one | |
if (NvAPI_DRS_FindProfileByName(session, new NvAPI_UnicodeString(profileName), out var profile) != 0) | |
{ | |
// Create a profile. This can be found in the NVIDIA Control Panel under the given name. | |
var profileValue = new NVDRS_PROFILE | |
{ | |
Version = MAKE_NVAPI_VERSION<NVDRS_PROFILE>(1), | |
IsPredefined = NvBool32.False, | |
GpuSupport = uint.MaxValue, | |
ProfileName = new NvAPI_UnicodeString(profileName) | |
}; | |
AssertSuccess(nameof(NvAPI_DRS_CreateProfile), NvAPI_DRS_CreateProfile(session, ref profileValue, ref profile)); | |
// Create an application and assign it to that profile. The NVIDIA Control Panel will show | |
// this line item as: | |
// Profile Name (Friendly Executable Name) | |
// and it will only apply to the executable with the specified name. | |
var application = new NVDRS_APPLICATION_V4 | |
{ | |
Version = MAKE_NVAPI_VERSION<NVDRS_APPLICATION_V4>(4), | |
IsPredefined = NvBool32.False, | |
AppName = new NvAPI_UnicodeString(executableName), | |
UserFriendlyName = new NvAPI_UnicodeString(friendlyName), | |
Flags = NVDRS_APPLICATION_V4_FLAGS.Metro | NVDRS_APPLICATION_V4_FLAGS.CommandLine | |
}; | |
AssertSuccess(nameof(NvAPI_DRS_CreateApplication), NvAPI_DRS_CreateApplication(session, profile, ref application)); | |
} | |
// Apply our profile settings | |
// Disable Threaded Optimization | |
UpdateProfileSetting( | |
session, | |
profile, | |
NvAPIConstants.OGL_THREAD_CONTROL_ID, | |
NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE, | |
NvAPIConstants.OGL_THREAD_CONTROL_DISABLE | |
); | |
// Force the application to use the dedicated GPU, otherwise the previous settings do not apply. | |
UpdateProfileSetting( | |
session, | |
profile, | |
NvAPIConstants.SHIM_MCCOMPAT_ID, | |
NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE, | |
NvAPIConstants.SHIM_MCCOMPAT_ENABLE | |
); | |
UpdateProfileSetting( | |
session, | |
profile, | |
NvAPIConstants.SHIM_RENDERING_MODE_ID, | |
NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE, | |
NvAPIConstants.SHIM_RENDERING_MODE_ENABLE | |
); | |
UpdateProfileSetting( | |
session, | |
profile, | |
NvAPIConstants.SHIM_RENDERING_OPTIONS_ID, | |
NVDRS_SETTING_TYPE.NVDRS_DWORD_TYPE, | |
NvAPIConstants.SHIM_RENDERING_OPTIONS_DEFAULT_RENDERING_MODE | |
); | |
AssertSuccess(nameof(NvAPI_DRS_SaveSettings), NvAPI_DRS_SaveSettings(session)); | |
NvAPI_DRS_DestroySession(session); | |
return true; | |
} | |
private static unsafe void UpdateProfileSetting(IntPtr handle, IntPtr profileHandle, NvAPIConstants settingId, NVDRS_SETTING_TYPE settingType, NvAPIConstants targetValue) | |
{ | |
var setting = new NVDRS_SETTING | |
{ | |
Version = MAKE_NVAPI_VERSION<NVDRS_SETTING>(1), | |
SettingId = settingId, | |
SettingType = settingType, | |
SettingLocation = NVDRS_SETTING_LOCATION.NvdrsCurrentProfileLocation, | |
IsCurrentPredefined = NvBool32.False, | |
IsPredefinedValid = NvBool32.False, | |
CurrentValue = (uint)targetValue, | |
PredefinedValue = (uint)targetValue | |
}; | |
AssertSuccess(nameof(NvAPI_DRS_SetSetting), NvAPI_DRS_SetSetting(handle, profileHandle, ref setting)); | |
} | |
private static IntPtr GetMethodPointer(NvAPIMethods method) | |
{ | |
var ptr = nvapi_QueryInterface(method); | |
if (ptr == IntPtr.Zero) | |
throw new InvalidOperationException($"Failed to locate NvAPI method handle: {method}"); | |
return ptr; | |
} | |
/// <summary> | |
/// Determine if the NVIDIA API is available. If it is, we probably | |
/// have a dedicated GPU at our disposal. | |
/// </summary> | |
public static bool TryLoadApi() | |
{ | |
return NativeLibrary.TryLoad(LibraryPath, out _); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment