aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs
blob: ad2236310701704480a1addeb11ff23b3b27356a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
using Ryujinx.Common.GraphicsDriver.NVAPI;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Ryujinx.Common.GraphicsDriver
{
    static class NVThreadedOptimization
    {
        private const string ProfileName = "Ryujinx Nvidia Profile";

        private const uint NvAPI_Initialize_ID = 0x0150E828;
        private const uint NvAPI_DRS_CreateSession_ID = 0x0694D52E;
        private const uint NvAPI_DRS_LoadSettings_ID = 0x375DBD6B;
        private const uint NvAPI_DRS_FindProfileByName_ID = 0x7E4A9A0B;
        private const uint NvAPI_DRS_CreateProfile_ID = 0x0CC176068;
        private const uint NvAPI_DRS_CreateApplication_ID = 0x4347A9DE;
        private const uint NvAPI_DRS_SetSetting_ID = 0x577DD202;
        private const uint NvAPI_DRS_SaveSettings_ID = 0xFCBC7E14;
        private const uint NvAPI_DRS_DestroySession_ID = 0x0DAD9CFF8;

        [DllImport("nvapi64")]
        private static extern IntPtr nvapi_QueryInterface(uint id);

        private delegate int NvAPI_InitializeDelegate();
        private static NvAPI_InitializeDelegate NvAPI_Initialize;

        private delegate int NvAPI_DRS_CreateSessionDelegate(out IntPtr handle);
        private static NvAPI_DRS_CreateSessionDelegate NvAPI_DRS_CreateSession;

        private delegate int NvAPI_DRS_LoadSettingsDelegate(IntPtr handle);
        private static NvAPI_DRS_LoadSettingsDelegate NvAPI_DRS_LoadSettings;

        private delegate int NvAPI_DRS_FindProfileByNameDelegate(IntPtr handle, NvapiUnicodeString profileName, out IntPtr profileHandle);
        private static NvAPI_DRS_FindProfileByNameDelegate NvAPI_DRS_FindProfileByName;

        private delegate int NvAPI_DRS_CreateProfileDelegate(IntPtr handle, ref NvdrsProfile profileInfo, out IntPtr profileHandle);
        private static NvAPI_DRS_CreateProfileDelegate NvAPI_DRS_CreateProfile;

        private delegate int NvAPI_DRS_CreateApplicationDelegate(IntPtr handle, IntPtr profileHandle, ref NvdrsApplicationV4 app);
        private static NvAPI_DRS_CreateApplicationDelegate NvAPI_DRS_CreateApplication;

        private delegate int NvAPI_DRS_SetSettingDelegate(IntPtr handle, IntPtr profileHandle, ref NvdrsSetting setting);
        private static NvAPI_DRS_SetSettingDelegate NvAPI_DRS_SetSetting;

        private delegate int NvAPI_DRS_SaveSettingsDelegate(IntPtr handle);
        private static NvAPI_DRS_SaveSettingsDelegate NvAPI_DRS_SaveSettings;

        private delegate int NvAPI_DRS_DestroySessionDelegate(IntPtr handle);
        private static NvAPI_DRS_DestroySessionDelegate NvAPI_DRS_DestroySession;

        private static bool _initialized;

        private static void Check(int status)
        {
            if (status != 0)
            {
                throw new Exception($"NVAPI Error: {status}");
            }
        }

        private static void Initialize()
        {
            if (!_initialized)
            {
                NvAPI_Initialize = NvAPI_Delegate<NvAPI_InitializeDelegate>(NvAPI_Initialize_ID);

                Check(NvAPI_Initialize());

                NvAPI_DRS_CreateSession = NvAPI_Delegate<NvAPI_DRS_CreateSessionDelegate>(NvAPI_DRS_CreateSession_ID);
                NvAPI_DRS_LoadSettings = NvAPI_Delegate<NvAPI_DRS_LoadSettingsDelegate>(NvAPI_DRS_LoadSettings_ID);
                NvAPI_DRS_FindProfileByName = NvAPI_Delegate<NvAPI_DRS_FindProfileByNameDelegate>(NvAPI_DRS_FindProfileByName_ID);
                NvAPI_DRS_CreateProfile = NvAPI_Delegate<NvAPI_DRS_CreateProfileDelegate>(NvAPI_DRS_CreateProfile_ID);
                NvAPI_DRS_CreateApplication = NvAPI_Delegate<NvAPI_DRS_CreateApplicationDelegate>(NvAPI_DRS_CreateApplication_ID);
                NvAPI_DRS_SetSetting = NvAPI_Delegate<NvAPI_DRS_SetSettingDelegate>(NvAPI_DRS_SetSetting_ID);
                NvAPI_DRS_SaveSettings = NvAPI_Delegate<NvAPI_DRS_SaveSettingsDelegate>(NvAPI_DRS_SaveSettings_ID);
                NvAPI_DRS_DestroySession = NvAPI_Delegate<NvAPI_DRS_DestroySessionDelegate>(NvAPI_DRS_DestroySession_ID);

                _initialized = true;
            }
        }

        private static uint MakeVersion<T>(uint version) where T : unmanaged
        {
            return (uint)Unsafe.SizeOf<T>() | version << 16;
        }

        public static void SetThreadedOptimization(bool enabled)
        {
            Initialize();

            uint targetValue = (uint)(enabled ? Nvapi.OglThreadControlEnable : Nvapi.OglThreadControlDisable);

            Check(NvAPI_Initialize());

            Check(NvAPI_DRS_CreateSession(out IntPtr handle));

            Check(NvAPI_DRS_LoadSettings(handle));

            IntPtr profileHandle;

            // Check if the profile already exists.

            int status = NvAPI_DRS_FindProfileByName(handle, new NvapiUnicodeString(ProfileName), out profileHandle);

            if (status != 0)
            {
                NvdrsProfile profile = new NvdrsProfile { 
                    Version = MakeVersion<NvdrsProfile>(1), 
                    IsPredefined = 0, 
                    GpuSupport = uint.MaxValue 
                };
                profile.ProfileName.Set(ProfileName);
                Check(NvAPI_DRS_CreateProfile(handle, ref profile, out profileHandle));

                NvdrsApplicationV4 application = new NvdrsApplicationV4
                {
                    Version = MakeVersion<NvdrsApplicationV4>(4),
                    IsPredefined = 0,
                    Flags = 3 // IsMetro, IsCommandLine
                };
                application.AppName.Set("Ryujinx.exe");
                application.UserFriendlyName.Set("Ryujinx");
                application.Launcher.Set("");
                application.FileInFolder.Set("");

                Check(NvAPI_DRS_CreateApplication(handle, profileHandle, ref application));
            }

            NvdrsSetting setting = new NvdrsSetting
            {
                Version = MakeVersion<NvdrsSetting>(1),
                SettingId = Nvapi.OglThreadControlId,
                SettingType = NvdrsSettingType.NvdrsDwordType,
                SettingLocation = NvdrsSettingLocation.NvdrsCurrentProfileLocation,
                IsCurrentPredefined = 0,
                IsPredefinedValid = 0,
                CurrentValue = targetValue,
                PredefinedValue = targetValue
            };

            Check(NvAPI_DRS_SetSetting(handle, profileHandle, ref setting));

            Check(NvAPI_DRS_SaveSettings(handle));

            NvAPI_DRS_DestroySession(handle);
        }

        private static T NvAPI_Delegate<T>(uint id) where T : class
        {
            IntPtr ptr = nvapi_QueryInterface(id);

            if (ptr != IntPtr.Zero)
            {
                return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T;
            }
            else
            {
                return null;
            }
        }
    }
}