Hardware/Nvidia/NvidiaGPU.cs
author moel.mich
Sun, 15 Jan 2012 22:06:59 +0000
changeset 335 9549c46bebb1
parent 309 65a1ae21325d
child 344 3145aadca3d2
permissions -rw-r--r--
Fixed Issue 313.
     1 /*
     2   
     3   Version: MPL 1.1/GPL 2.0/LGPL 2.1
     4 
     5   The contents of this file are subject to the Mozilla Public License Version
     6   1.1 (the "License"); you may not use this file except in compliance with
     7   the License. You may obtain a copy of the License at
     8  
     9   http://www.mozilla.org/MPL/
    10 
    11   Software distributed under the License is distributed on an "AS IS" basis,
    12   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    13   for the specific language governing rights and limitations under the License.
    14 
    15   The Original Code is the Open Hardware Monitor code.
    16 
    17   The Initial Developer of the Original Code is 
    18   Michael Möller <m.moeller@gmx.ch>.
    19   Portions created by the Initial Developer are Copyright (C) 2009-2012
    20   the Initial Developer. All Rights Reserved.
    21 
    22   Contributor(s):
    23   Christian Vallières
    24 
    25   Alternatively, the contents of this file may be used under the terms of
    26   either the GNU General Public License Version 2 or later (the "GPL"), or
    27   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    28   in which case the provisions of the GPL or the LGPL are applicable instead
    29   of those above. If you wish to allow use of your version of this file only
    30   under the terms of either the GPL or the LGPL, and not to allow others to
    31   use your version of this file under the terms of the MPL, indicate your
    32   decision by deleting the provisions above and replace them with the notice
    33   and other provisions required by the GPL or the LGPL. If you do not delete
    34   the provisions above, a recipient may use your version of this file under
    35   the terms of any one of the MPL, the GPL or the LGPL.
    36  
    37 */
    38 
    39 using System;
    40 using System.Globalization;
    41 using System.Text;
    42 
    43 namespace OpenHardwareMonitor.Hardware.Nvidia {
    44   internal class NvidiaGPU : Hardware {
    45 
    46     private readonly int adapterIndex;
    47     private readonly NvPhysicalGpuHandle handle;
    48     private readonly NvDisplayHandle? displayHandle;
    49 
    50     private readonly Sensor[] temperatures;
    51     private readonly Sensor fan;
    52     private readonly Sensor[] clocks;
    53     private readonly Sensor[] loads;
    54     private readonly Sensor control;
    55     private readonly Sensor memoryLoad;
    56     private readonly Control fanControl;
    57 
    58     private bool restoreDefaultFanSpeedRequired;
    59     private NvLevel initialFanSpeedValue;
    60 
    61     public NvidiaGPU(int adapterIndex, NvPhysicalGpuHandle handle,
    62       NvDisplayHandle? displayHandle, ISettings settings)
    63       : base(GetName(handle), new Identifier("nvidiagpu",
    64           adapterIndex.ToString(CultureInfo.InvariantCulture)), settings) {
    65       this.adapterIndex = adapterIndex;
    66       this.handle = handle;
    67       this.displayHandle = displayHandle;
    68 
    69       NvGPUThermalSettings thermalSettings = GetThermalSettings();
    70       temperatures = new Sensor[thermalSettings.Count];
    71       for (int i = 0; i < temperatures.Length; i++) {
    72         NvSensor sensor = thermalSettings.Sensor[i];
    73         string name;
    74         switch (sensor.Target) {
    75           case NvThermalTarget.BOARD: name = "GPU Board"; break;
    76           case NvThermalTarget.GPU: name = "GPU Core"; break;
    77           case NvThermalTarget.MEMORY: name = "GPU Memory"; break;
    78           case NvThermalTarget.POWER_SUPPLY: name = "GPU Power Supply"; break;
    79           case NvThermalTarget.UNKNOWN: name = "GPU Unknown"; break;
    80           default: name = "GPU"; break;
    81         }
    82         temperatures[i] = new Sensor(name, i, SensorType.Temperature, this,
    83           new ParameterDescription[0], settings);
    84         ActivateSensor(temperatures[i]);
    85       }
    86 
    87       int value;
    88       if (NVAPI.NvAPI_GPU_GetTachReading != null &&
    89         NVAPI.NvAPI_GPU_GetTachReading(handle, out value) == NvStatus.OK) {
    90         if (value > 0) {
    91           fan = new Sensor("GPU", 0, SensorType.Fan, this, settings);
    92           ActivateSensor(fan);
    93         }
    94       }
    95 
    96       clocks = new Sensor[3];
    97       clocks[0] = new Sensor("GPU Core", 0, SensorType.Clock, this, settings);
    98       clocks[1] = new Sensor("GPU Memory", 1, SensorType.Clock, this, settings);
    99       clocks[2] = new Sensor("GPU Shader", 2, SensorType.Clock, this, settings);
   100       for (int i = 0; i < clocks.Length; i++)
   101         ActivateSensor(clocks[i]);
   102 
   103       loads = new Sensor[3];
   104       loads[0] = new Sensor("GPU Core", 0, SensorType.Load, this, settings);
   105       loads[1] = new Sensor("GPU Memory Controller", 1, SensorType.Load, this, settings);
   106       loads[2] = new Sensor("GPU Video Engine", 2, SensorType.Load, this, settings);
   107       memoryLoad = new Sensor("GPU Memory", 3, SensorType.Load, this, settings);
   108 
   109       control = new Sensor("GPU Fan", 0, SensorType.Control, this, settings);
   110 
   111       NvGPUCoolerSettings coolerSettings = GetCoolerSettings();
   112       if (coolerSettings.Count > 0) {
   113         fanControl = new Control(control, settings,
   114           coolerSettings.Cooler[0].DefaultMin, 
   115           coolerSettings.Cooler[0].DefaultMax);
   116         fanControl.ControlModeChanged += ControlModeChanged;
   117         fanControl.SoftwareControlValueChanged += SoftwareControlValueChanged;
   118         ControlModeChanged(fanControl);
   119         control.Control = fanControl;
   120       }
   121       Update();
   122     }
   123 
   124     private static string GetName(NvPhysicalGpuHandle handle) {
   125       string gpuName;
   126       if (NVAPI.NvAPI_GPU_GetFullName(handle, out gpuName) == NvStatus.OK) {
   127         return "NVIDIA " + gpuName.Trim();
   128       } else {
   129         return "NVIDIA";
   130       }
   131     }
   132 
   133     public override HardwareType HardwareType {
   134       get { return HardwareType.GpuNvidia; }
   135     }
   136 
   137     private NvGPUThermalSettings GetThermalSettings() {
   138       NvGPUThermalSettings settings = new NvGPUThermalSettings();
   139       settings.Version = NVAPI.GPU_THERMAL_SETTINGS_VER;
   140       settings.Count = NVAPI.MAX_THERMAL_SENSORS_PER_GPU;
   141       settings.Sensor = new NvSensor[NVAPI.MAX_THERMAL_SENSORS_PER_GPU];
   142       if (!(NVAPI.NvAPI_GPU_GetThermalSettings != null &&
   143         NVAPI.NvAPI_GPU_GetThermalSettings(handle, (int)NvThermalTarget.ALL,
   144           ref settings) == NvStatus.OK)) 
   145       {
   146         settings.Count = 0;
   147       }       
   148       return settings;    
   149     }
   150 
   151     private NvGPUCoolerSettings GetCoolerSettings() {
   152       NvGPUCoolerSettings settings = new NvGPUCoolerSettings();
   153       settings.Version = NVAPI.GPU_COOLER_SETTINGS_VER;
   154       settings.Cooler = new NvCooler[NVAPI.MAX_COOLER_PER_GPU];
   155       if (!(NVAPI.NvAPI_GPU_GetCoolerSettings != null &&
   156         NVAPI.NvAPI_GPU_GetCoolerSettings(handle, 0, 
   157           ref settings) == NvStatus.OK)) 
   158       {
   159         settings.Count = 0;
   160       }
   161       return settings;  
   162     }
   163 
   164     private uint[] GetClocks() {
   165       NvClocks allClocks = new NvClocks();
   166       allClocks.Version = NVAPI.GPU_CLOCKS_VER;
   167       allClocks.Clock = new uint[NVAPI.MAX_CLOCKS_PER_GPU];
   168       if (NVAPI.NvAPI_GPU_GetAllClocks != null &&
   169         NVAPI.NvAPI_GPU_GetAllClocks(handle, ref allClocks) == NvStatus.OK) {
   170         return allClocks.Clock;
   171       }
   172       return null;
   173     }
   174 
   175     public override void Update() {
   176       NvGPUThermalSettings settings = GetThermalSettings();
   177       foreach (Sensor sensor in temperatures)
   178         sensor.Value = settings.Sensor[sensor.Index].CurrentTemp;
   179 
   180       if (fan != null) {
   181         int value = 0;
   182         NVAPI.NvAPI_GPU_GetTachReading(handle, out value);
   183         fan.Value = value;
   184       }
   185 
   186       uint[] values = GetClocks();
   187       if (values != null) {
   188         clocks[0].Value = 0.001f * values[0];
   189         clocks[1].Value = 0.001f * values[8];
   190         clocks[2].Value = 0.001f * values[14];
   191         if (values[30] != 0) {
   192           clocks[0].Value = 0.0005f * values[30];
   193           clocks[2].Value = 0.001f * values[30];
   194         }
   195       }
   196 
   197       NvPStates states = new NvPStates();
   198       states.Version = NVAPI.GPU_PSTATES_VER;
   199       states.PStates = new NvPState[NVAPI.MAX_PSTATES_PER_GPU];
   200       if (NVAPI.NvAPI_GPU_GetPStates != null &&
   201         NVAPI.NvAPI_GPU_GetPStates(handle, ref states) == NvStatus.OK) {
   202         for (int i = 0; i < 3; i++)
   203           if (states.PStates[i].Present) {
   204             loads[i].Value = states.PStates[i].Percentage;
   205             ActivateSensor(loads[i]);
   206           }
   207       } else {
   208         NvUsages usages = new NvUsages();
   209         usages.Version = NVAPI.GPU_USAGES_VER;
   210         usages.Usage = new uint[NVAPI.MAX_USAGES_PER_GPU];
   211         if (NVAPI.NvAPI_GPU_GetUsages != null &&
   212           NVAPI.NvAPI_GPU_GetUsages(handle, ref usages) == NvStatus.OK) {
   213           loads[0].Value = usages.Usage[2];
   214           loads[1].Value = usages.Usage[6];
   215           loads[2].Value = usages.Usage[10];
   216           for (int i = 0; i < 3; i++)
   217             ActivateSensor(loads[i]);
   218         }
   219       }
   220 
   221 
   222       NvGPUCoolerSettings coolerSettings = GetCoolerSettings();
   223       if (coolerSettings.Count > 0) {
   224         control.Value = coolerSettings.Cooler[0].CurrentLevel;
   225         ActivateSensor(control);
   226       }
   227 
   228       NvMemoryInfo memoryInfo = new NvMemoryInfo();
   229       memoryInfo.Version = NVAPI.GPU_MEMORY_INFO_VER;
   230       memoryInfo.Values = new uint[NVAPI.MAX_MEMORY_VALUES_PER_GPU];
   231       if (NVAPI.NvAPI_GPU_GetMemoryInfo != null && displayHandle.HasValue &&
   232         NVAPI.NvAPI_GPU_GetMemoryInfo(displayHandle.Value, ref memoryInfo) ==
   233         NvStatus.OK) {
   234         uint totalMemory = memoryInfo.Values[0];
   235         uint freeMemory = memoryInfo.Values[4];
   236         float usedMemory = Math.Max(totalMemory - freeMemory, 0);
   237         memoryLoad.Value = 100f * usedMemory / totalMemory;
   238         ActivateSensor(memoryLoad);
   239       }
   240     }
   241 
   242     public override string GetReport() {
   243       StringBuilder r = new StringBuilder();
   244 
   245       r.AppendLine("Nvidia GPU");
   246       r.AppendLine();
   247 
   248       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
   249       r.AppendFormat("Index: {0}{1}", adapterIndex, Environment.NewLine);
   250 
   251       if (displayHandle.HasValue && NVAPI.NvAPI_GetDisplayDriverVersion != null) {
   252         NvDisplayDriverVersion driverVersion = new NvDisplayDriverVersion();
   253         driverVersion.Version = NVAPI.DISPLAY_DRIVER_VERSION_VER;
   254         if (NVAPI.NvAPI_GetDisplayDriverVersion(displayHandle.Value,
   255           ref driverVersion) == NvStatus.OK) {
   256           r.Append("Driver Version: ");
   257           r.Append(driverVersion.DriverVersion / 100);
   258           r.Append(".");
   259           r.Append((driverVersion.DriverVersion % 100).ToString("00",
   260             CultureInfo.InvariantCulture));
   261           r.AppendLine();
   262           r.Append("Driver Branch: ");
   263           r.AppendLine(driverVersion.BuildBranch);
   264         }
   265       }
   266       r.AppendLine();
   267 
   268       if (NVAPI.NvAPI_GPU_GetPCIIdentifiers != null) {
   269         uint deviceId, subSystemId, revisionId, extDeviceId;
   270 
   271         NvStatus status = NVAPI.NvAPI_GPU_GetPCIIdentifiers(handle, 
   272           out deviceId, out subSystemId, out revisionId, out extDeviceId);
   273 
   274         if (status == NvStatus.OK) {
   275           r.Append("DeviceID: 0x");
   276           r.AppendLine(deviceId.ToString("X", CultureInfo.InvariantCulture));
   277           r.Append("SubSystemID: 0x");
   278           r.AppendLine(subSystemId.ToString("X", CultureInfo.InvariantCulture));
   279           r.Append("RevisionID: 0x");
   280           r.AppendLine(revisionId.ToString("X", CultureInfo.InvariantCulture));
   281           r.Append("ExtDeviceID: 0x");
   282           r.AppendLine(extDeviceId.ToString("X", CultureInfo.InvariantCulture));
   283           r.AppendLine();
   284         }
   285       }
   286 
   287       if (NVAPI.NvAPI_GPU_GetThermalSettings != null) {
   288         NvGPUThermalSettings settings = new NvGPUThermalSettings();
   289         settings.Version = NVAPI.GPU_THERMAL_SETTINGS_VER;
   290         settings.Count = NVAPI.MAX_THERMAL_SENSORS_PER_GPU;
   291         settings.Sensor = new NvSensor[NVAPI.MAX_THERMAL_SENSORS_PER_GPU];
   292 
   293         NvStatus status = NVAPI.NvAPI_GPU_GetThermalSettings(handle,
   294           (int)NvThermalTarget.ALL, ref settings);
   295 
   296         r.AppendLine("Thermal Settings");
   297         r.AppendLine();
   298         if (status == NvStatus.OK) {
   299           for (int i = 0; i < settings.Count; i++) {
   300             r.AppendFormat(" Sensor[{0}].Controller: {1}{2}", i,
   301               settings.Sensor[i].Controller, Environment.NewLine);
   302             r.AppendFormat(" Sensor[{0}].DefaultMinTemp: {1}{2}", i,
   303               settings.Sensor[i].DefaultMinTemp, Environment.NewLine);
   304             r.AppendFormat(" Sensor[{0}].DefaultMaxTemp: {1}{2}", i,
   305               settings.Sensor[i].DefaultMaxTemp, Environment.NewLine);
   306             r.AppendFormat(" Sensor[{0}].CurrentTemp: {1}{2}", i,
   307               settings.Sensor[i].CurrentTemp, Environment.NewLine);
   308             r.AppendFormat(" Sensor[{0}].Target: {1}{2}", i,
   309               settings.Sensor[i].Target, Environment.NewLine);
   310           }
   311         } else {
   312           r.Append(" Status: ");
   313           r.AppendLine(status.ToString());
   314         }
   315         r.AppendLine();
   316       }
   317 
   318       if (NVAPI.NvAPI_GPU_GetAllClocks != null) {
   319         NvClocks allClocks = new NvClocks();
   320         allClocks.Version = NVAPI.GPU_CLOCKS_VER;
   321         allClocks.Clock = new uint[NVAPI.MAX_CLOCKS_PER_GPU];
   322         NvStatus status = NVAPI.NvAPI_GPU_GetAllClocks(handle, ref allClocks);
   323 
   324         r.AppendLine("Clocks");
   325         r.AppendLine();
   326         if (status == NvStatus.OK) {
   327           for (int i = 0; i < allClocks.Clock.Length; i++)
   328             if (allClocks.Clock[i] > 0) {
   329               r.AppendFormat(" Clock[{0}]: {1}{2}", i, allClocks.Clock[i],
   330                 Environment.NewLine);
   331             }
   332         } else {
   333           r.Append(" Status: ");
   334           r.AppendLine(status.ToString());
   335         }
   336         r.AppendLine();
   337       }
   338 
   339       if (NVAPI.NvAPI_GPU_GetTachReading != null) {
   340         int tachValue;
   341         NvStatus status = NVAPI.NvAPI_GPU_GetTachReading(handle, out tachValue);
   342 
   343         r.AppendLine("Tachometer");
   344         r.AppendLine();
   345         if (status == NvStatus.OK) {
   346           r.AppendFormat(" Value: {0}{1}", tachValue, Environment.NewLine);
   347         } else {
   348           r.Append(" Status: ");
   349           r.AppendLine(status.ToString());
   350         }
   351         r.AppendLine();
   352       }
   353 
   354       if (NVAPI.NvAPI_GPU_GetPStates != null) {
   355         NvPStates states = new NvPStates();
   356         states.Version = NVAPI.GPU_PSTATES_VER;
   357         states.PStates = new NvPState[NVAPI.MAX_PSTATES_PER_GPU];
   358         NvStatus status = NVAPI.NvAPI_GPU_GetPStates(handle, ref states);
   359 
   360         r.AppendLine("P-States");
   361         r.AppendLine();
   362         if (status == NvStatus.OK) {
   363           for (int i = 0; i < states.PStates.Length; i++)
   364             if (states.PStates[i].Present)
   365               r.AppendFormat(" Percentage[{0}]: {1}{2}", i,
   366                 states.PStates[i].Percentage, Environment.NewLine);
   367         } else {
   368           r.Append(" Status: ");
   369           r.AppendLine(status.ToString());
   370         }
   371         r.AppendLine();
   372       }
   373 
   374       if (NVAPI.NvAPI_GPU_GetUsages != null) {
   375         NvUsages usages = new NvUsages();
   376         usages.Version = NVAPI.GPU_USAGES_VER;
   377         usages.Usage = new uint[NVAPI.MAX_USAGES_PER_GPU];
   378         NvStatus status = NVAPI.NvAPI_GPU_GetUsages(handle, ref usages);
   379 
   380         r.AppendLine("Usages");
   381         r.AppendLine();
   382         if (status == NvStatus.OK) {
   383           for (int i = 0; i < usages.Usage.Length; i++)
   384             if (usages.Usage[i] > 0)
   385               r.AppendFormat(" Usage[{0}]: {1}{2}", i,
   386                 usages.Usage[i], Environment.NewLine);
   387         } else {
   388           r.Append(" Status: ");
   389           r.AppendLine(status.ToString());
   390         }
   391         r.AppendLine();
   392       }
   393 
   394       if (NVAPI.NvAPI_GPU_GetCoolerSettings != null) {
   395         NvGPUCoolerSettings settings = new NvGPUCoolerSettings();
   396         settings.Version = NVAPI.GPU_COOLER_SETTINGS_VER;
   397         settings.Cooler = new NvCooler[NVAPI.MAX_COOLER_PER_GPU];
   398         NvStatus status =
   399           NVAPI.NvAPI_GPU_GetCoolerSettings(handle, 0, ref settings);
   400 
   401         r.AppendLine("Cooler Settings");
   402         r.AppendLine();
   403         if (status == NvStatus.OK) {
   404           for (int i = 0; i < settings.Count; i++) {
   405             r.AppendFormat(" Cooler[{0}].Type: {1}{2}", i,
   406               settings.Cooler[i].Type, Environment.NewLine);
   407             r.AppendFormat(" Cooler[{0}].Controller: {1}{2}", i,
   408               settings.Cooler[i].Controller, Environment.NewLine);
   409             r.AppendFormat(" Cooler[{0}].DefaultMin: {1}{2}", i,
   410               settings.Cooler[i].DefaultMin, Environment.NewLine);
   411             r.AppendFormat(" Cooler[{0}].DefaultMax: {1}{2}", i,
   412               settings.Cooler[i].DefaultMax, Environment.NewLine);
   413             r.AppendFormat(" Cooler[{0}].CurrentMin: {1}{2}", i,
   414               settings.Cooler[i].CurrentMin, Environment.NewLine);
   415             r.AppendFormat(" Cooler[{0}].CurrentMax: {1}{2}", i,
   416               settings.Cooler[i].CurrentMax, Environment.NewLine);
   417             r.AppendFormat(" Cooler[{0}].CurrentLevel: {1}{2}", i,
   418               settings.Cooler[i].CurrentLevel, Environment.NewLine);
   419             r.AppendFormat(" Cooler[{0}].DefaultPolicy: {1}{2}", i,
   420               settings.Cooler[i].DefaultPolicy, Environment.NewLine);
   421             r.AppendFormat(" Cooler[{0}].CurrentPolicy: {1}{2}", i,
   422               settings.Cooler[i].CurrentPolicy, Environment.NewLine);
   423             r.AppendFormat(" Cooler[{0}].Target: {1}{2}", i,
   424               settings.Cooler[i].Target, Environment.NewLine);
   425             r.AppendFormat(" Cooler[{0}].ControlType: {1}{2}", i,
   426               settings.Cooler[i].ControlType, Environment.NewLine);
   427             r.AppendFormat(" Cooler[{0}].Active: {1}{2}", i,
   428               settings.Cooler[i].Active, Environment.NewLine);
   429           }
   430         } else {
   431           r.Append(" Status: ");
   432           r.AppendLine(status.ToString());
   433         }
   434         r.AppendLine();
   435       }
   436 
   437       if (NVAPI.NvAPI_GPU_GetMemoryInfo != null && displayHandle.HasValue) {
   438         NvMemoryInfo memoryInfo = new NvMemoryInfo();
   439         memoryInfo.Version = NVAPI.GPU_MEMORY_INFO_VER;
   440         memoryInfo.Values = new uint[NVAPI.MAX_MEMORY_VALUES_PER_GPU];
   441         NvStatus status = NVAPI.NvAPI_GPU_GetMemoryInfo(displayHandle.Value,
   442           ref memoryInfo);
   443 
   444         r.AppendLine("Memory Info");
   445         r.AppendLine();
   446         if (status == NvStatus.OK) {
   447           for (int i = 0; i < memoryInfo.Values.Length; i++)
   448             r.AppendFormat(" Value[{0}]: {1}{2}", i,
   449                 memoryInfo.Values[i], Environment.NewLine);
   450         } else {
   451           r.Append(" Status: ");
   452           r.AppendLine(status.ToString());
   453         }
   454         r.AppendLine();
   455       }
   456 
   457       return r.ToString();
   458     }
   459 
   460     private void SoftwareControlValueChanged(IControl control) {
   461       SaveDefaultFanSpeed();
   462       NvGPUCoolerLevels coolerLevels = new NvGPUCoolerLevels();
   463       coolerLevels.Version = NVAPI.GPU_COOLER_LEVELS_VER;
   464       coolerLevels.Levels = new NvLevel[NVAPI.MAX_COOLER_PER_GPU];
   465       coolerLevels.Levels[0].Level = (int)control.SoftwareValue;
   466       coolerLevels.Levels[0].Policy = 1;
   467       NVAPI.NvAPI_GPU_SetCoolerLevels(handle, 0, ref coolerLevels);
   468     }
   469 
   470     private void SaveDefaultFanSpeed() {
   471       if (!restoreDefaultFanSpeedRequired) {
   472         NvGPUCoolerSettings coolerSettings = GetCoolerSettings();
   473         if (coolerSettings.Count > 0) {
   474           restoreDefaultFanSpeedRequired = true;
   475           initialFanSpeedValue.Level = coolerSettings.Cooler[0].CurrentLevel;
   476           initialFanSpeedValue.Policy = coolerSettings.Cooler[0].CurrentPolicy;
   477         }
   478       }
   479     }
   480 
   481     private void ControlModeChanged(IControl control) {
   482       if (control.ControlMode == ControlMode.Default) {
   483         RestoreDefaultFanSpeed();
   484       } else {
   485         SoftwareControlValueChanged(control);
   486       }
   487     }
   488 
   489     private void RestoreDefaultFanSpeed() {
   490       if (restoreDefaultFanSpeedRequired) {
   491         NvGPUCoolerLevels coolerLevels = new NvGPUCoolerLevels();
   492         coolerLevels.Version = NVAPI.GPU_COOLER_LEVELS_VER;
   493         coolerLevels.Levels = new NvLevel[NVAPI.MAX_COOLER_PER_GPU];
   494         coolerLevels.Levels[0] = initialFanSpeedValue;
   495         NVAPI.NvAPI_GPU_SetCoolerLevels(handle, 0, ref coolerLevels);
   496         restoreDefaultFanSpeedRequired = false;
   497       }
   498     }
   499   }
   500 }