/ src / Ryujinx.Common / GraphicsDriver / NVThreadedOptimization.cs
NVThreadedOptimization.cs
  1  using Ryujinx.Common.GraphicsDriver.NVAPI;
  2  using System;
  3  using System.Runtime.CompilerServices;
  4  using System.Runtime.InteropServices;
  5  
  6  namespace Ryujinx.Common.GraphicsDriver
  7  {
  8      static partial class NVThreadedOptimization
  9      {
 10          private const string ProfileName = "Ryujinx Nvidia Profile";
 11  
 12          private const uint NvAPI_Initialize_ID = 0x0150E828;
 13          private const uint NvAPI_DRS_CreateSession_ID = 0x0694D52E;
 14          private const uint NvAPI_DRS_LoadSettings_ID = 0x375DBD6B;
 15          private const uint NvAPI_DRS_FindProfileByName_ID = 0x7E4A9A0B;
 16          private const uint NvAPI_DRS_CreateProfile_ID = 0x0CC176068;
 17          private const uint NvAPI_DRS_CreateApplication_ID = 0x4347A9DE;
 18          private const uint NvAPI_DRS_SetSetting_ID = 0x577DD202;
 19          private const uint NvAPI_DRS_SaveSettings_ID = 0xFCBC7E14;
 20          private const uint NvAPI_DRS_DestroySession_ID = 0x0DAD9CFF8;
 21  
 22          [LibraryImport("nvapi64")]
 23          private static partial IntPtr nvapi_QueryInterface(uint id);
 24  
 25          private delegate int NvAPI_InitializeDelegate();
 26          private static NvAPI_InitializeDelegate NvAPI_Initialize;
 27  
 28          private delegate int NvAPI_DRS_CreateSessionDelegate(out IntPtr handle);
 29          private static NvAPI_DRS_CreateSessionDelegate NvAPI_DRS_CreateSession;
 30  
 31          private delegate int NvAPI_DRS_LoadSettingsDelegate(IntPtr handle);
 32          private static NvAPI_DRS_LoadSettingsDelegate NvAPI_DRS_LoadSettings;
 33  
 34          private delegate int NvAPI_DRS_FindProfileByNameDelegate(IntPtr handle, NvapiUnicodeString profileName, out IntPtr profileHandle);
 35          private static NvAPI_DRS_FindProfileByNameDelegate NvAPI_DRS_FindProfileByName;
 36  
 37          private delegate int NvAPI_DRS_CreateProfileDelegate(IntPtr handle, ref NvdrsProfile profileInfo, out IntPtr profileHandle);
 38          private static NvAPI_DRS_CreateProfileDelegate NvAPI_DRS_CreateProfile;
 39  
 40          private delegate int NvAPI_DRS_CreateApplicationDelegate(IntPtr handle, IntPtr profileHandle, ref NvdrsApplicationV4 app);
 41          private static NvAPI_DRS_CreateApplicationDelegate NvAPI_DRS_CreateApplication;
 42  
 43          private delegate int NvAPI_DRS_SetSettingDelegate(IntPtr handle, IntPtr profileHandle, ref NvdrsSetting setting);
 44          private static NvAPI_DRS_SetSettingDelegate NvAPI_DRS_SetSetting;
 45  
 46          private delegate int NvAPI_DRS_SaveSettingsDelegate(IntPtr handle);
 47          private static NvAPI_DRS_SaveSettingsDelegate NvAPI_DRS_SaveSettings;
 48  
 49          private delegate int NvAPI_DRS_DestroySessionDelegate(IntPtr handle);
 50          private static NvAPI_DRS_DestroySessionDelegate NvAPI_DRS_DestroySession;
 51  
 52          private static bool _initialized;
 53  
 54          private static void Check(int status)
 55          {
 56              if (status != 0)
 57              {
 58                  throw new Exception($"NVAPI Error: {status}");
 59              }
 60          }
 61  
 62          private static void Initialize()
 63          {
 64              if (!_initialized)
 65              {
 66                  NvAPI_Initialize = NvAPI_Delegate<NvAPI_InitializeDelegate>(NvAPI_Initialize_ID);
 67  
 68                  Check(NvAPI_Initialize());
 69  
 70                  NvAPI_DRS_CreateSession = NvAPI_Delegate<NvAPI_DRS_CreateSessionDelegate>(NvAPI_DRS_CreateSession_ID);
 71                  NvAPI_DRS_LoadSettings = NvAPI_Delegate<NvAPI_DRS_LoadSettingsDelegate>(NvAPI_DRS_LoadSettings_ID);
 72                  NvAPI_DRS_FindProfileByName = NvAPI_Delegate<NvAPI_DRS_FindProfileByNameDelegate>(NvAPI_DRS_FindProfileByName_ID);
 73                  NvAPI_DRS_CreateProfile = NvAPI_Delegate<NvAPI_DRS_CreateProfileDelegate>(NvAPI_DRS_CreateProfile_ID);
 74                  NvAPI_DRS_CreateApplication = NvAPI_Delegate<NvAPI_DRS_CreateApplicationDelegate>(NvAPI_DRS_CreateApplication_ID);
 75                  NvAPI_DRS_SetSetting = NvAPI_Delegate<NvAPI_DRS_SetSettingDelegate>(NvAPI_DRS_SetSetting_ID);
 76                  NvAPI_DRS_SaveSettings = NvAPI_Delegate<NvAPI_DRS_SaveSettingsDelegate>(NvAPI_DRS_SaveSettings_ID);
 77                  NvAPI_DRS_DestroySession = NvAPI_Delegate<NvAPI_DRS_DestroySessionDelegate>(NvAPI_DRS_DestroySession_ID);
 78  
 79                  _initialized = true;
 80              }
 81          }
 82  
 83          private static uint MakeVersion<T>(uint version) where T : unmanaged
 84          {
 85              return (uint)Unsafe.SizeOf<T>() | version << 16;
 86          }
 87  
 88          public static void SetThreadedOptimization(bool enabled)
 89          {
 90              Initialize();
 91  
 92              uint targetValue = (uint)(enabled ? Nvapi.OglThreadControlEnable : Nvapi.OglThreadControlDisable);
 93  
 94              Check(NvAPI_Initialize());
 95  
 96              Check(NvAPI_DRS_CreateSession(out IntPtr handle));
 97  
 98              Check(NvAPI_DRS_LoadSettings(handle));
 99  
100              // Check if the profile already exists.
101  
102              int status = NvAPI_DRS_FindProfileByName(handle, new NvapiUnicodeString(ProfileName), out nint profileHandle);
103  
104              if (status != 0)
105              {
106                  NvdrsProfile profile = new()
107                  {
108                      Version = MakeVersion<NvdrsProfile>(1),
109                      IsPredefined = 0,
110                      GpuSupport = uint.MaxValue,
111                  };
112                  profile.ProfileName.Set(ProfileName);
113                  Check(NvAPI_DRS_CreateProfile(handle, ref profile, out profileHandle));
114  
115                  NvdrsApplicationV4 application = new()
116                  {
117                      Version = MakeVersion<NvdrsApplicationV4>(4),
118                      IsPredefined = 0,
119                      Flags = 3, // IsMetro, IsCommandLine
120                  };
121                  application.AppName.Set("Ryujinx.exe");
122                  application.UserFriendlyName.Set("Ryujinx");
123                  application.Launcher.Set("");
124                  application.FileInFolder.Set("");
125  
126                  Check(NvAPI_DRS_CreateApplication(handle, profileHandle, ref application));
127              }
128  
129              NvdrsSetting setting = new()
130              {
131                  Version = MakeVersion<NvdrsSetting>(1),
132                  SettingId = Nvapi.OglThreadControlId,
133                  SettingType = NvdrsSettingType.NvdrsDwordType,
134                  SettingLocation = NvdrsSettingLocation.NvdrsCurrentProfileLocation,
135                  IsCurrentPredefined = 0,
136                  IsPredefinedValid = 0,
137                  CurrentValue = targetValue,
138                  PredefinedValue = targetValue,
139              };
140  
141              Check(NvAPI_DRS_SetSetting(handle, profileHandle, ref setting));
142  
143              Check(NvAPI_DRS_SaveSettings(handle));
144  
145              NvAPI_DRS_DestroySession(handle);
146          }
147  
148          private static T NvAPI_Delegate<T>(uint id) where T : class
149          {
150              IntPtr ptr = nvapi_QueryInterface(id);
151  
152              if (ptr != IntPtr.Zero)
153              {
154                  return Marshal.GetDelegateForFunctionPointer<T>(ptr);
155              }
156  
157              return null;
158          }
159      }
160  }