moel@1: /* moel@1: moel@344: This Source Code Form is subject to the terms of the Mozilla Public moel@344: License, v. 2.0. If a copy of the MPL was not distributed with this moel@344: file, You can obtain one at http://mozilla.org/MPL/2.0/. moel@344: moel@344: Copyright (C) 2009-2012 Michael Möller moel@344: Copyright (C) 2011 Christian Vallières moel@1: moel@1: */ moel@1: moel@1: using System; moel@166: using System.Globalization; moel@140: using System.Text; moel@1: moel@1: namespace OpenHardwareMonitor.Hardware.Nvidia { moel@195: internal class NvidiaGPU : Hardware { moel@1: moel@195: private readonly int adapterIndex; moel@195: private readonly NvPhysicalGpuHandle handle; moel@195: private readonly NvDisplayHandle? displayHandle; moel@1: moel@195: private readonly Sensor[] temperatures; moel@195: private readonly Sensor fan; moel@195: private readonly Sensor[] clocks; moel@195: private readonly Sensor[] loads; moel@195: private readonly Sensor control; moel@195: private readonly Sensor memoryLoad; moel@309: private readonly Control fanControl; moel@38: moel@309: private bool restoreDefaultFanSpeedRequired; moel@309: private NvLevel initialFanSpeedValue; moel@309: moel@309: public NvidiaGPU(int adapterIndex, NvPhysicalGpuHandle handle, moel@309: NvDisplayHandle? displayHandle, ISettings settings) moel@309: : base(GetName(handle), new Identifier("nvidiagpu", moel@309: adapterIndex.ToString(CultureInfo.InvariantCulture)), settings) { moel@56: this.adapterIndex = adapterIndex; moel@56: this.handle = handle; moel@140: this.displayHandle = displayHandle; moel@1: moel@165: NvGPUThermalSettings thermalSettings = GetThermalSettings(); moel@165: temperatures = new Sensor[thermalSettings.Count]; moel@56: for (int i = 0; i < temperatures.Length; i++) { moel@165: NvSensor sensor = thermalSettings.Sensor[i]; moel@56: string name; moel@56: switch (sensor.Target) { moel@56: case NvThermalTarget.BOARD: name = "GPU Board"; break; moel@56: case NvThermalTarget.GPU: name = "GPU Core"; break; moel@56: case NvThermalTarget.MEMORY: name = "GPU Memory"; break; moel@56: case NvThermalTarget.POWER_SUPPLY: name = "GPU Power Supply"; break; moel@56: case NvThermalTarget.UNKNOWN: name = "GPU Unknown"; break; moel@56: default: name = "GPU"; break; moel@38: } moel@165: temperatures[i] = new Sensor(name, i, SensorType.Temperature, this, moel@165: new ParameterDescription[0], settings); moel@56: ActivateSensor(temperatures[i]); moel@56: } moel@1: moel@56: int value; moel@56: if (NVAPI.NvAPI_GPU_GetTachReading != null && moel@56: NVAPI.NvAPI_GPU_GetTachReading(handle, out value) == NvStatus.OK) { moel@56: if (value > 0) { moel@165: fan = new Sensor("GPU", 0, SensorType.Fan, this, settings); moel@56: ActivateSensor(fan); moel@1: } moel@1: } moel@140: moel@140: clocks = new Sensor[3]; moel@165: clocks[0] = new Sensor("GPU Core", 0, SensorType.Clock, this, settings); moel@165: clocks[1] = new Sensor("GPU Memory", 1, SensorType.Clock, this, settings); moel@165: clocks[2] = new Sensor("GPU Shader", 2, SensorType.Clock, this, settings); moel@140: for (int i = 0; i < clocks.Length; i++) moel@140: ActivateSensor(clocks[i]); moel@140: moel@140: loads = new Sensor[3]; moel@165: loads[0] = new Sensor("GPU Core", 0, SensorType.Load, this, settings); moel@165: loads[1] = new Sensor("GPU Memory Controller", 1, SensorType.Load, this, settings); moel@165: loads[2] = new Sensor("GPU Video Engine", 2, SensorType.Load, this, settings); moel@165: memoryLoad = new Sensor("GPU Memory", 3, SensorType.Load, this, settings); moel@140: moel@165: control = new Sensor("GPU Fan", 0, SensorType.Control, this, settings); moel@309: moel@309: NvGPUCoolerSettings coolerSettings = GetCoolerSettings(); moel@309: if (coolerSettings.Count > 0) { moel@309: fanControl = new Control(control, settings, moel@309: coolerSettings.Cooler[0].DefaultMin, moel@309: coolerSettings.Cooler[0].DefaultMax); moel@309: fanControl.ControlModeChanged += ControlModeChanged; moel@309: fanControl.SoftwareControlValueChanged += SoftwareControlValueChanged; moel@309: ControlModeChanged(fanControl); moel@309: control.Control = fanControl; moel@309: } moel@309: Update(); moel@1: } moel@1: moel@275: private static string GetName(NvPhysicalGpuHandle handle) { moel@275: string gpuName; moel@275: if (NVAPI.NvAPI_GPU_GetFullName(handle, out gpuName) == NvStatus.OK) { moel@275: return "NVIDIA " + gpuName.Trim(); moel@275: } else { moel@275: return "NVIDIA"; moel@166: } moel@1: } moel@1: moel@165: public override HardwareType HardwareType { moel@176: get { return HardwareType.GpuNvidia; } moel@1: } moel@1: moel@1: private NvGPUThermalSettings GetThermalSettings() { moel@1: NvGPUThermalSettings settings = new NvGPUThermalSettings(); moel@1: settings.Version = NVAPI.GPU_THERMAL_SETTINGS_VER; moel@1: settings.Count = NVAPI.MAX_THERMAL_SENSORS_PER_GPU; moel@1: settings.Sensor = new NvSensor[NVAPI.MAX_THERMAL_SENSORS_PER_GPU]; moel@309: if (!(NVAPI.NvAPI_GPU_GetThermalSettings != null && moel@140: NVAPI.NvAPI_GPU_GetThermalSettings(handle, (int)NvThermalTarget.ALL, moel@309: ref settings) == NvStatus.OK)) moel@309: { moel@309: settings.Count = 0; moel@309: } moel@309: return settings; moel@309: } moel@309: moel@309: private NvGPUCoolerSettings GetCoolerSettings() { moel@309: NvGPUCoolerSettings settings = new NvGPUCoolerSettings(); moel@309: settings.Version = NVAPI.GPU_COOLER_SETTINGS_VER; moel@309: settings.Cooler = new NvCooler[NVAPI.MAX_COOLER_PER_GPU]; moel@309: if (!(NVAPI.NvAPI_GPU_GetCoolerSettings != null && moel@309: NVAPI.NvAPI_GPU_GetCoolerSettings(handle, 0, moel@309: ref settings) == NvStatus.OK)) moel@309: { moel@309: settings.Count = 0; moel@38: } moel@309: return settings; moel@1: } moel@1: moel@140: private uint[] GetClocks() { moel@166: NvClocks allClocks = new NvClocks(); moel@166: allClocks.Version = NVAPI.GPU_CLOCKS_VER; moel@166: allClocks.Clock = new uint[NVAPI.MAX_CLOCKS_PER_GPU]; moel@140: if (NVAPI.NvAPI_GPU_GetAllClocks != null && moel@166: NVAPI.NvAPI_GPU_GetAllClocks(handle, ref allClocks) == NvStatus.OK) { moel@166: return allClocks.Clock; moel@140: } moel@140: return null; moel@140: } moel@140: moel@110: public override void Update() { moel@1: NvGPUThermalSettings settings = GetThermalSettings(); moel@309: foreach (Sensor sensor in temperatures) moel@38: sensor.Value = settings.Sensor[sensor.Index].CurrentTemp; moel@38: moel@38: if (fan != null) { moel@38: int value = 0; moel@38: NVAPI.NvAPI_GPU_GetTachReading(handle, out value); moel@38: fan.Value = value; moel@38: } moel@140: moel@140: uint[] values = GetClocks(); moel@140: if (values != null) { moel@140: clocks[0].Value = 0.001f * values[0]; moel@140: clocks[1].Value = 0.001f * values[8]; moel@140: clocks[2].Value = 0.001f * values[14]; moel@140: if (values[30] != 0) { moel@140: clocks[0].Value = 0.0005f * values[30]; moel@140: clocks[2].Value = 0.001f * values[30]; moel@140: } moel@140: } moel@140: moel@140: NvPStates states = new NvPStates(); moel@140: states.Version = NVAPI.GPU_PSTATES_VER; moel@140: states.PStates = new NvPState[NVAPI.MAX_PSTATES_PER_GPU]; moel@309: if (NVAPI.NvAPI_GPU_GetPStates != null && moel@140: NVAPI.NvAPI_GPU_GetPStates(handle, ref states) == NvStatus.OK) { moel@140: for (int i = 0; i < 3; i++) moel@140: if (states.PStates[i].Present) { moel@140: loads[i].Value = states.PStates[i].Percentage; moel@140: ActivateSensor(loads[i]); moel@140: } moel@140: } else { moel@140: NvUsages usages = new NvUsages(); moel@140: usages.Version = NVAPI.GPU_USAGES_VER; moel@140: usages.Usage = new uint[NVAPI.MAX_USAGES_PER_GPU]; moel@140: if (NVAPI.NvAPI_GPU_GetUsages != null && moel@140: NVAPI.NvAPI_GPU_GetUsages(handle, ref usages) == NvStatus.OK) { moel@140: loads[0].Value = usages.Usage[2]; moel@140: loads[1].Value = usages.Usage[6]; moel@140: loads[2].Value = usages.Usage[10]; moel@140: for (int i = 0; i < 3; i++) moel@140: ActivateSensor(loads[i]); moel@140: } moel@140: } moel@140: moel@309: moel@309: NvGPUCoolerSettings coolerSettings = GetCoolerSettings(); moel@309: if (coolerSettings.Count > 0) { moel@140: control.Value = coolerSettings.Cooler[0].CurrentLevel; moel@140: ActivateSensor(control); moel@140: } moel@140: moel@140: NvMemoryInfo memoryInfo = new NvMemoryInfo(); moel@140: memoryInfo.Version = NVAPI.GPU_MEMORY_INFO_VER; moel@140: memoryInfo.Values = new uint[NVAPI.MAX_MEMORY_VALUES_PER_GPU]; moel@140: if (NVAPI.NvAPI_GPU_GetMemoryInfo != null && displayHandle.HasValue && moel@309: NVAPI.NvAPI_GPU_GetMemoryInfo(displayHandle.Value, ref memoryInfo) == moel@309: NvStatus.OK) { moel@140: uint totalMemory = memoryInfo.Values[0]; moel@140: uint freeMemory = memoryInfo.Values[4]; moel@140: float usedMemory = Math.Max(totalMemory - freeMemory, 0); moel@140: memoryLoad.Value = 100f * usedMemory / totalMemory; moel@140: ActivateSensor(memoryLoad); moel@140: } moel@140: } moel@140: moel@140: public override string GetReport() { moel@140: StringBuilder r = new StringBuilder(); moel@140: moel@140: r.AppendLine("Nvidia GPU"); moel@140: r.AppendLine(); moel@140: moel@140: r.AppendFormat("Name: {0}{1}", name, Environment.NewLine); moel@140: r.AppendFormat("Index: {0}{1}", adapterIndex, Environment.NewLine); moel@309: moel@309: if (displayHandle.HasValue && NVAPI.NvAPI_GetDisplayDriverVersion != null) { moel@140: NvDisplayDriverVersion driverVersion = new NvDisplayDriverVersion(); moel@140: driverVersion.Version = NVAPI.DISPLAY_DRIVER_VERSION_VER; moel@140: if (NVAPI.NvAPI_GetDisplayDriverVersion(displayHandle.Value, moel@140: ref driverVersion) == NvStatus.OK) { moel@140: r.Append("Driver Version: "); moel@140: r.Append(driverVersion.DriverVersion / 100); moel@140: r.Append("."); moel@309: r.Append((driverVersion.DriverVersion % 100).ToString("00", moel@167: CultureInfo.InvariantCulture)); moel@140: r.AppendLine(); moel@140: r.Append("Driver Branch: "); moel@140: r.AppendLine(driverVersion.BuildBranch); moel@140: } moel@140: } moel@140: r.AppendLine(); moel@140: moel@334: if (NVAPI.NvAPI_GPU_GetPCIIdentifiers != null) { moel@334: uint deviceId, subSystemId, revisionId, extDeviceId; moel@334: moel@334: NvStatus status = NVAPI.NvAPI_GPU_GetPCIIdentifiers(handle, moel@334: out deviceId, out subSystemId, out revisionId, out extDeviceId); moel@334: moel@334: if (status == NvStatus.OK) { moel@334: r.Append("DeviceID: 0x"); moel@334: r.AppendLine(deviceId.ToString("X", CultureInfo.InvariantCulture)); moel@334: r.Append("SubSystemID: 0x"); moel@334: r.AppendLine(subSystemId.ToString("X", CultureInfo.InvariantCulture)); moel@334: r.Append("RevisionID: 0x"); moel@334: r.AppendLine(revisionId.ToString("X", CultureInfo.InvariantCulture)); moel@334: r.Append("ExtDeviceID: 0x"); moel@334: r.AppendLine(extDeviceId.ToString("X", CultureInfo.InvariantCulture)); moel@334: r.AppendLine(); moel@334: } moel@334: } moel@334: moel@140: if (NVAPI.NvAPI_GPU_GetThermalSettings != null) { moel@140: NvGPUThermalSettings settings = new NvGPUThermalSettings(); moel@140: settings.Version = NVAPI.GPU_THERMAL_SETTINGS_VER; moel@140: settings.Count = NVAPI.MAX_THERMAL_SENSORS_PER_GPU; moel@140: settings.Sensor = new NvSensor[NVAPI.MAX_THERMAL_SENSORS_PER_GPU]; moel@140: moel@140: NvStatus status = NVAPI.NvAPI_GPU_GetThermalSettings(handle, moel@140: (int)NvThermalTarget.ALL, ref settings); moel@140: moel@140: r.AppendLine("Thermal Settings"); moel@140: r.AppendLine(); moel@140: if (status == NvStatus.OK) { moel@140: for (int i = 0; i < settings.Count; i++) { moel@140: r.AppendFormat(" Sensor[{0}].Controller: {1}{2}", i, moel@140: settings.Sensor[i].Controller, Environment.NewLine); moel@140: r.AppendFormat(" Sensor[{0}].DefaultMinTemp: {1}{2}", i, moel@140: settings.Sensor[i].DefaultMinTemp, Environment.NewLine); moel@140: r.AppendFormat(" Sensor[{0}].DefaultMaxTemp: {1}{2}", i, moel@140: settings.Sensor[i].DefaultMaxTemp, Environment.NewLine); moel@140: r.AppendFormat(" Sensor[{0}].CurrentTemp: {1}{2}", i, moel@140: settings.Sensor[i].CurrentTemp, Environment.NewLine); moel@140: r.AppendFormat(" Sensor[{0}].Target: {1}{2}", i, moel@140: settings.Sensor[i].Target, Environment.NewLine); moel@140: } moel@140: } else { moel@140: r.Append(" Status: "); moel@140: r.AppendLine(status.ToString()); moel@140: } moel@140: r.AppendLine(); moel@309: } moel@140: moel@140: if (NVAPI.NvAPI_GPU_GetAllClocks != null) { moel@166: NvClocks allClocks = new NvClocks(); moel@166: allClocks.Version = NVAPI.GPU_CLOCKS_VER; moel@166: allClocks.Clock = new uint[NVAPI.MAX_CLOCKS_PER_GPU]; moel@166: NvStatus status = NVAPI.NvAPI_GPU_GetAllClocks(handle, ref allClocks); moel@140: moel@140: r.AppendLine("Clocks"); moel@140: r.AppendLine(); moel@140: if (status == NvStatus.OK) { moel@166: for (int i = 0; i < allClocks.Clock.Length; i++) moel@166: if (allClocks.Clock[i] > 0) { moel@166: r.AppendFormat(" Clock[{0}]: {1}{2}", i, allClocks.Clock[i], moel@140: Environment.NewLine); moel@140: } moel@140: } else { moel@140: r.Append(" Status: "); moel@140: r.AppendLine(status.ToString()); moel@140: } moel@140: r.AppendLine(); moel@309: } moel@309: moel@140: if (NVAPI.NvAPI_GPU_GetTachReading != null) { moel@309: int tachValue; moel@140: NvStatus status = NVAPI.NvAPI_GPU_GetTachReading(handle, out tachValue); moel@140: moel@140: r.AppendLine("Tachometer"); moel@140: r.AppendLine(); moel@140: if (status == NvStatus.OK) { moel@140: r.AppendFormat(" Value: {0}{1}", tachValue, Environment.NewLine); moel@140: } else { moel@140: r.Append(" Status: "); moel@140: r.AppendLine(status.ToString()); moel@140: } moel@140: r.AppendLine(); moel@140: } moel@140: moel@140: if (NVAPI.NvAPI_GPU_GetPStates != null) { moel@140: NvPStates states = new NvPStates(); moel@140: states.Version = NVAPI.GPU_PSTATES_VER; moel@140: states.PStates = new NvPState[NVAPI.MAX_PSTATES_PER_GPU]; moel@140: NvStatus status = NVAPI.NvAPI_GPU_GetPStates(handle, ref states); moel@140: moel@140: r.AppendLine("P-States"); moel@140: r.AppendLine(); moel@140: if (status == NvStatus.OK) { moel@140: for (int i = 0; i < states.PStates.Length; i++) moel@140: if (states.PStates[i].Present) moel@140: r.AppendFormat(" Percentage[{0}]: {1}{2}", i, moel@140: states.PStates[i].Percentage, Environment.NewLine); moel@140: } else { moel@140: r.Append(" Status: "); moel@140: r.AppendLine(status.ToString()); moel@140: } moel@140: r.AppendLine(); moel@140: } moel@140: moel@140: if (NVAPI.NvAPI_GPU_GetUsages != null) { moel@140: NvUsages usages = new NvUsages(); moel@140: usages.Version = NVAPI.GPU_USAGES_VER; moel@140: usages.Usage = new uint[NVAPI.MAX_USAGES_PER_GPU]; moel@140: NvStatus status = NVAPI.NvAPI_GPU_GetUsages(handle, ref usages); moel@309: moel@140: r.AppendLine("Usages"); moel@140: r.AppendLine(); moel@140: if (status == NvStatus.OK) { moel@140: for (int i = 0; i < usages.Usage.Length; i++) moel@140: if (usages.Usage[i] > 0) moel@140: r.AppendFormat(" Usage[{0}]: {1}{2}", i, moel@140: usages.Usage[i], Environment.NewLine); moel@140: } else { moel@140: r.Append(" Status: "); moel@140: r.AppendLine(status.ToString()); moel@140: } moel@140: r.AppendLine(); moel@140: } moel@140: moel@140: if (NVAPI.NvAPI_GPU_GetCoolerSettings != null) { moel@140: NvGPUCoolerSettings settings = new NvGPUCoolerSettings(); moel@140: settings.Version = NVAPI.GPU_COOLER_SETTINGS_VER; moel@140: settings.Cooler = new NvCooler[NVAPI.MAX_COOLER_PER_GPU]; moel@140: NvStatus status = moel@140: NVAPI.NvAPI_GPU_GetCoolerSettings(handle, 0, ref settings); moel@140: moel@140: r.AppendLine("Cooler Settings"); moel@140: r.AppendLine(); moel@140: if (status == NvStatus.OK) { moel@140: for (int i = 0; i < settings.Count; i++) { moel@140: r.AppendFormat(" Cooler[{0}].Type: {1}{2}", i, moel@140: settings.Cooler[i].Type, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].Controller: {1}{2}", i, moel@140: settings.Cooler[i].Controller, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].DefaultMin: {1}{2}", i, moel@140: settings.Cooler[i].DefaultMin, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].DefaultMax: {1}{2}", i, moel@140: settings.Cooler[i].DefaultMax, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].CurrentMin: {1}{2}", i, moel@140: settings.Cooler[i].CurrentMin, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].CurrentMax: {1}{2}", i, moel@140: settings.Cooler[i].CurrentMax, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].CurrentLevel: {1}{2}", i, moel@140: settings.Cooler[i].CurrentLevel, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].DefaultPolicy: {1}{2}", i, moel@140: settings.Cooler[i].DefaultPolicy, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].CurrentPolicy: {1}{2}", i, moel@140: settings.Cooler[i].CurrentPolicy, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].Target: {1}{2}", i, moel@140: settings.Cooler[i].Target, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].ControlType: {1}{2}", i, moel@140: settings.Cooler[i].ControlType, Environment.NewLine); moel@140: r.AppendFormat(" Cooler[{0}].Active: {1}{2}", i, moel@140: settings.Cooler[i].Active, Environment.NewLine); moel@140: } moel@140: } else { moel@140: r.Append(" Status: "); moel@140: r.AppendLine(status.ToString()); moel@140: } moel@140: r.AppendLine(); moel@140: } moel@140: moel@140: if (NVAPI.NvAPI_GPU_GetMemoryInfo != null && displayHandle.HasValue) { moel@140: NvMemoryInfo memoryInfo = new NvMemoryInfo(); moel@140: memoryInfo.Version = NVAPI.GPU_MEMORY_INFO_VER; moel@140: memoryInfo.Values = new uint[NVAPI.MAX_MEMORY_VALUES_PER_GPU]; moel@309: NvStatus status = NVAPI.NvAPI_GPU_GetMemoryInfo(displayHandle.Value, moel@140: ref memoryInfo); moel@140: moel@140: r.AppendLine("Memory Info"); moel@140: r.AppendLine(); moel@140: if (status == NvStatus.OK) { moel@140: for (int i = 0; i < memoryInfo.Values.Length; i++) moel@140: r.AppendFormat(" Value[{0}]: {1}{2}", i, moel@140: memoryInfo.Values[i], Environment.NewLine); moel@140: } else { moel@140: r.Append(" Status: "); moel@140: r.AppendLine(status.ToString()); moel@140: } moel@140: r.AppendLine(); moel@140: } moel@140: moel@140: return r.ToString(); moel@1: } moel@309: moel@309: private void SoftwareControlValueChanged(IControl control) { moel@309: SaveDefaultFanSpeed(); moel@309: NvGPUCoolerLevels coolerLevels = new NvGPUCoolerLevels(); moel@309: coolerLevels.Version = NVAPI.GPU_COOLER_LEVELS_VER; moel@309: coolerLevels.Levels = new NvLevel[NVAPI.MAX_COOLER_PER_GPU]; moel@309: coolerLevels.Levels[0].Level = (int)control.SoftwareValue; moel@309: coolerLevels.Levels[0].Policy = 1; moel@309: NVAPI.NvAPI_GPU_SetCoolerLevels(handle, 0, ref coolerLevels); moel@309: } moel@309: moel@309: private void SaveDefaultFanSpeed() { moel@309: if (!restoreDefaultFanSpeedRequired) { moel@309: NvGPUCoolerSettings coolerSettings = GetCoolerSettings(); moel@309: if (coolerSettings.Count > 0) { moel@309: restoreDefaultFanSpeedRequired = true; moel@309: initialFanSpeedValue.Level = coolerSettings.Cooler[0].CurrentLevel; moel@309: initialFanSpeedValue.Policy = coolerSettings.Cooler[0].CurrentPolicy; moel@309: } moel@309: } moel@309: } moel@309: moel@309: private void ControlModeChanged(IControl control) { moel@309: if (control.ControlMode == ControlMode.Default) { moel@309: RestoreDefaultFanSpeed(); moel@309: } else { moel@309: SoftwareControlValueChanged(control); moel@309: } moel@309: } moel@309: moel@309: private void RestoreDefaultFanSpeed() { moel@309: if (restoreDefaultFanSpeedRequired) { moel@309: NvGPUCoolerLevels coolerLevels = new NvGPUCoolerLevels(); moel@309: coolerLevels.Version = NVAPI.GPU_COOLER_LEVELS_VER; moel@309: coolerLevels.Levels = new NvLevel[NVAPI.MAX_COOLER_PER_GPU]; moel@309: coolerLevels.Levels[0] = initialFanSpeedValue; moel@309: NVAPI.NvAPI_GPU_SetCoolerLevels(handle, 0, ref coolerLevels); moel@309: restoreDefaultFanSpeedRequired = false; moel@309: } moel@309: } moel@1: } moel@1: }