Added support for the NVML (NVIDIA Management Library). Added support for power and PCIE throughput sensors on NVIDIA GPUs.

This commit is contained in:
Michael Möller 2020-02-20 22:07:03 +01:00
parent 0a8923a998
commit 8e9bea8b6c
10 changed files with 366 additions and 40 deletions

View File

@ -8,11 +8,9 @@
*/
using OpenHardwareMonitor.Hardware;
using System;
using System.Drawing;
using System.Collections.Generic;
using OpenHardwareMonitor.Hardware;
using OpenHardwareMonitor.Utilities;
namespace OpenHardwareMonitor.GUI {
public class SensorNode : Node {
@ -20,18 +18,26 @@ namespace OpenHardwareMonitor.GUI {
private ISensor sensor;
private PersistentSettings settings;
private UnitManager unitManager;
private string format;
private string fixedFormat;
private bool plot = false;
private Color? penColor = null;
public string ValueToString(float? value) {
if (value.HasValue) {
if (sensor.SensorType == SensorType.Temperature &&
unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) {
return string.Format("{0:F1} °F", value * 1.8 + 32);
} else {
return string.Format(format, value);
}
switch (sensor.SensorType) {
case SensorType.Temperature:
if (unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit)
return string.Format("{0:F1} °F", value * 1.8 + 32);
else
return string.Format("{0:F1} °C", value);
case SensorType.Throughput:
if (value < 1)
return string.Format("{0:F1} KB/s", value * 0x400);
else
return string.Format("{0:F1} MB/s", value);
default:
return string.Format(fixedFormat, value);
}
} else
return "-";
}
@ -42,18 +48,18 @@ namespace OpenHardwareMonitor.GUI {
this.settings = settings;
this.unitManager = unitManager;
switch (sensor.SensorType) {
case SensorType.Voltage: format = "{0:F3} V"; break;
case SensorType.Clock: format = "{0:F1} MHz"; break;
case SensorType.Load: format = "{0:F1} %"; break;
case SensorType.Temperature: format = "{0:F1} °C"; break;
case SensorType.Fan: format = "{0:F0} RPM"; break;
case SensorType.Flow: format = "{0:F0} L/h"; break;
case SensorType.Control: format = "{0:F1} %"; break;
case SensorType.Level: format = "{0:F1} %"; break;
case SensorType.Power: format = "{0:F1} W"; break;
case SensorType.Data: format = "{0:F1} GB"; break;
case SensorType.SmallData: format = "{0:F1} MB"; break;
case SensorType.Factor: format = "{0:F3}"; break;
case SensorType.Voltage: fixedFormat = "{0:F3} V"; break;
case SensorType.Clock: fixedFormat = "{0:F1} MHz"; break;
case SensorType.Load: fixedFormat = "{0:F1} %"; break;
case SensorType.Fan: fixedFormat = "{0:F0} RPM"; break;
case SensorType.Flow: fixedFormat = "{0:F0} L/h"; break;
case SensorType.Control: fixedFormat = "{0:F1} %"; break;
case SensorType.Level: fixedFormat = "{0:F1} %"; break;
case SensorType.Power: fixedFormat = "{0:F1} W"; break;
case SensorType.Data: fixedFormat = "{0:F1} GB"; break;
case SensorType.SmallData: fixedFormat = "{0:F1} MB"; break;
case SensorType.Factor: fixedFormat = "{0:F3}"; break;
default: fixedFormat = ""; break;
}
bool hidden = settings.GetValue(new Identifier(sensor.Identifier,

View File

@ -69,6 +69,10 @@ namespace OpenHardwareMonitor.GUI {
this.Image = Utilities.EmbeddedResources.GetImage("factor.png");
this.Text = "Factors";
break;
case SensorType.Throughput:
this.Image = Utilities.EmbeddedResources.GetImage("throughput.png");
this.Text = "Throughput";
break;
}
NodeAdded += new NodeEventHandler(TypeNode_NodeAdded);

View File

@ -27,6 +27,7 @@ namespace OpenHardwareMonitor.Hardware {
Power, // W
Data, // GB = 2^30 Bytes
SmallData, // MB = 2^20 Bytes
Throughput, // MB/s = 2^20 Bytes/s
}
public struct SensorValue {

View File

@ -4,7 +4,7 @@
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
Copyright (C) 2009-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
Copyright (C) 2009-2020 Michael Möller <mmoeller@openhardwaremonitor.org>
Copyright (C) 2011 Christian Vallières
*/
@ -307,6 +307,10 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
NvPhysicalGpuHandle gpuHandle, out uint deviceId, out uint subSystemId,
out uint revisionId, out uint extDeviceId);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate NvStatus NvAPI_GPU_GetBusIdDelegate(
NvPhysicalGpuHandle gpuHandle, out uint busId);
private static readonly bool available;
private static readonly nvapi_QueryInterfaceDelegate nvapi_QueryInterface;
private static readonly NvAPI_InitializeDelegate NvAPI_Initialize;
@ -341,6 +345,8 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
NvAPI_GetDisplayDriverVersion;
public static readonly NvAPI_GPU_GetPCIIdentifiersDelegate
NvAPI_GPU_GetPCIIdentifiers;
public static readonly NvAPI_GPU_GetBusIdDelegate
NvAPI_GPU_GetBusId;
private NVAPI() { }
@ -416,6 +422,7 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
GetDelegate(0xF951A4D1, out NvAPI_GetDisplayDriverVersion);
GetDelegate(0x01053FA5, out _NvAPI_GetInterfaceVersionString);
GetDelegate(0x2DDFB66E, out NvAPI_GPU_GetPCIIdentifiers);
GetDelegate(0x1BE0B8E5, out NvAPI_GPU_GetBusId);
available = true;
}

243
Hardware/Nvidia/NVML.cs Normal file
View File

@ -0,0 +1,243 @@
/*
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
Copyright (C) 2020 Michael Möller <mmoeller@openhardwaremonitor.org>
*/
using System;
using System.Runtime.InteropServices;
namespace OpenHardwareMonitor.Hardware.Nvidia {
internal class NVML {
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate NvmlReturn nvmlInitDelegate();
private static readonly nvmlInitDelegate nvmlInit;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate NvmlReturn nvmlInit_v2Delegate();
private static readonly nvmlInit_v2Delegate nvmlInit_v2;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate NvmlReturn nvmlShutdownDelegate();
public static readonly nvmlShutdownDelegate NvmlShutdown;
public delegate NvmlReturn nvmlDeviceGetHandleByPciBusIdDelegate(
string pciBusId, out NvmlDevice device);
public static readonly nvmlDeviceGetHandleByPciBusIdDelegate
NvmlDeviceGetHandleByPciBusId;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate NvmlReturn nvmlDeviceGetPowerUsageDelegate(
NvmlDevice device, out int power);
public static readonly nvmlDeviceGetPowerUsageDelegate
NvmlDeviceGetPowerUsage;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate NvmlReturn nvmlDeviceGetPcieThroughputDelegate(
NvmlDevice devicee, NvmlPcieUtilCounter counter, out uint value);
public static readonly nvmlDeviceGetPcieThroughputDelegate
NvmlDeviceGetPcieThroughput;
public static NvmlReturn NvmlInit() {
try {
var result = nvmlInit_v2();
initialized = result == NvmlReturn.Success;
return result;
} catch {
try {
var result = nvmlInit();
initialized = result == NvmlReturn.Success;
return result;
} catch {
return NvmlReturn.ErrorLibraryNotFound;
}
}
}
private static string GetDllName() {
int p = (int)Environment.OSVersion.Platform;
if ((p == 4) || (p == 128))
return "libnvidia-ml.so";
else
return "nvml.dll";
}
private static T CreateDelegate<T>(string entryPoint) where T : Delegate {
var attribute = new DllImportAttribute(GetDllName()) {
CallingConvention = CallingConvention.Cdecl,
PreserveSig = true,
EntryPoint = entryPoint
};
PInvokeDelegateFactory.CreateDelegate(attribute, out T newDelegate);
return newDelegate;
}
static NVML() {
nvmlInit =
CreateDelegate<nvmlInitDelegate>(
"nvmlInit");
nvmlInit_v2 =
CreateDelegate<nvmlInit_v2Delegate>(
"nvmlInit_v2");
NvmlShutdown =
CreateDelegate<nvmlShutdownDelegate>(
"nvmlShutdown");
NvmlDeviceGetHandleByPciBusId =
CreateDelegate<nvmlDeviceGetHandleByPciBusIdDelegate>(
"nvmlDeviceGetHandleByPciBusId_v2");
NvmlDeviceGetPowerUsage =
CreateDelegate<nvmlDeviceGetPowerUsageDelegate>(
"nvmlDeviceGetPowerUsage");
NvmlDeviceGetPcieThroughput =
CreateDelegate<nvmlDeviceGetPcieThroughputDelegate>(
"nvmlDeviceGetPcieThroughput");
}
private static bool initialized;
public static bool IsInitialized {
get { return initialized; }
}
[StructLayout(LayoutKind.Sequential)]
internal struct NvmlDevice {
private readonly IntPtr ptr;
}
internal enum NvmlPcieUtilCounter {
TxBytes = 0,
RxBytes = 1
}
internal enum NvmlReturn {
/// <summary>
/// The operation was successful
/// </summary>
Success = 0,
/// <summary>
/// NVML was not first initialized with nvmlInit()
/// </summary>
ErrorUninitialized = 1,
/// <summary>
/// A supplied argument is invalid
/// </summary>
ErrorInvalidArgument = 2,
/// <summary>
/// The requested operation is not available on target device
/// </summary>
ErrorNotSupported = 3,
/// <summary>
/// The current user does not have permission for operation
/// </summary>
ErrorNoPermission = 4,
/// <summary>
/// Deprecated: Multiple initializations are now allowed through ref
/// counting
/// </summary>
ErrorAlreadyInitialized = 5,
/// <summary>
/// A query to find an object was unsuccessful
/// </summary>
ErrorNotFound = 6,
/// <summary>
/// An input argument is not large enough
/// </summary>
ErrorInsufficientSize = 7,
/// <summary>
/// A device's external power cables are not properly attached
/// </summary>
ErrorInsufficientPower = 8,
/// <summary>
/// NVIDIA driver is not loaded
/// </summary>
ErrorDriverNotLoaded = 9,
/// <summary>
/// User provided timeout passed
/// </summary>
ErrorTimeout = 10,
/// <summary>
/// NVIDIA Kernel detected an interrupt issue with a GPU
/// </summary>
ErrorIrqIssue = 11,
/// <summary>
/// NVML Shared Library couldn't be found or loaded
/// </summary>
ErrorLibraryNotFound = 12,
/// <summary>
/// Local version of NVML doesn't implement this function
/// </summary>
ErrorFunctionNotFound = 13,
/// <summary>
/// infoROM is corruptedinfoROM is corrupted
/// </summary>
ErrorCorruptedInforom = 14,
/// <summary>
/// The GPU has fallen off the bus or has otherwise become inaccessible
/// </summary>
ErrorGpuIsLost = 15,
/// <summary>
/// The GPU requires a reset before it can be used again
/// </summary>
ErrorResetRequired = 16,
/// <summary>
/// The GPU control device has been blocked by the operating
/// system/cgroups
/// </summary>
ErrorOperatingSystem = 17,
/// <summary>
/// RM detects a driver/library version mismatch
/// </summary>
ErrorLibRmVersionMismatch = 18,
/// <summary>
/// An operation cannot be performed because the GPU is currently in use
/// </summary>
ErrorInUse = 19,
/// <summary>
/// Insufficient memory
/// </summary>
ErrorMemory = 20,
/// <summary>
/// No data
/// </summary>
ErrorNoData = 21,
/// <summary>
/// The requested vgpu operation is not available on target device,
/// becasue ECC is enabled
/// </summary>
ErrorvGpuEccNotSupported = 22,
/// <summary>
/// An internal driver error occurred
/// </summary>
ErrorUnknown = 999
};
}
}

View File

@ -4,7 +4,7 @@
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
Copyright (C) 2009-2015 Michael Möller <mmoeller@openhardwaremonitor.org>
Copyright (C) 2009-2020 Michael Möller <mmoeller@openhardwaremonitor.org>
Copyright (C) 2011 Christian Vallières
*/
@ -19,6 +19,7 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
private readonly int adapterIndex;
private readonly NvPhysicalGpuHandle handle;
private readonly NvDisplayHandle? displayHandle;
private readonly NVML.NvmlDevice? device;
private readonly Sensor[] temperatures;
private readonly Sensor fan;
@ -29,12 +30,16 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
private readonly Sensor memoryUsed;
private readonly Sensor memoryFree;
private readonly Sensor memoryAvail;
private readonly Sensor power;
private readonly Sensor pcieThroughputRx;
private readonly Sensor pcieThroughputTx;
private readonly Control fanControl;
public NvidiaGPU(int adapterIndex, NvPhysicalGpuHandle handle,
NvDisplayHandle? displayHandle, ISettings settings)
: base(GetName(handle), new Identifier("nvidiagpu",
adapterIndex.ToString(CultureInfo.InvariantCulture)), settings) {
adapterIndex.ToString(CultureInfo.InvariantCulture)), settings)
{
this.adapterIndex = adapterIndex;
this.handle = handle;
this.displayHandle = displayHandle;
@ -57,13 +62,11 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
ActivateSensor(temperatures[i]);
}
int value;
if (NVAPI.NvAPI_GPU_GetTachReading != null &&
NVAPI.NvAPI_GPU_GetTachReading(handle, out value) == NvStatus.OK) {
if (value >= 0) {
fan = new Sensor("GPU", 0, SensorType.Fan, this, settings);
ActivateSensor(fan);
}
NVAPI.NvAPI_GPU_GetTachReading(handle, out _) == NvStatus.OK)
{
fan = new Sensor("GPU", 0, SensorType.Fan, this, settings);
ActivateSensor(fan);
}
clocks = new Sensor[3];
@ -93,6 +96,24 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
ControlModeChanged(fanControl);
control.Control = fanControl;
}
if (NVML.IsInitialized) {
if (NVAPI.NvAPI_GPU_GetBusId != null &&
NVAPI.NvAPI_GPU_GetBusId(handle, out uint busId) == NvStatus.OK) {
if (NVML.NvmlDeviceGetHandleByPciBusId(
"0000:" + busId.ToString("X2") + ":00.0", out var result)
== NVML.NvmlReturn.Success)
{
device = result;
power = new Sensor("GPU Power", 0, SensorType.Power, this, settings);
pcieThroughputRx = new Sensor("GPU PCIE Rx", 0,
SensorType.Throughput, this, settings);
pcieThroughputTx = new Sensor("GPU PCIE Tx", 1,
SensorType.Throughput, this, settings);
}
}
}
Update();
}
@ -152,10 +173,10 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
foreach (Sensor sensor in temperatures)
sensor.Value = settings.Sensor[sensor.Index].CurrentTemp;
if (fan != null) {
int value = 0;
NVAPI.NvAPI_GPU_GetTachReading(handle, out value);
fan.Value = value;
if (fan != null && NVAPI.NvAPI_GPU_GetTachReading(handle, out int fanValue)
== NvStatus.OK)
{
fan.Value = fanValue;
}
uint[] values = GetClocks();
@ -219,6 +240,34 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
ActivateSensor(memoryFree);
ActivateSensor(memoryLoad);
}
if (power != null) {
if (NVML.NvmlDeviceGetPowerUsage(device.Value, out int powerValue)
== NVML.NvmlReturn.Success)
{
power.Value = powerValue * 0.001f;
ActivateSensor(power);
}
}
if (pcieThroughputRx != null) {
if (NVML.NvmlDeviceGetPcieThroughput(device.Value,
NVML.NvmlPcieUtilCounter.RxBytes, out uint value)
== NVML.NvmlReturn.Success)
{
pcieThroughputRx.Value = value * (1.0f / 0x400);
ActivateSensor(pcieThroughputRx);
}
}
if (pcieThroughputTx != null) {
if (NVML.NvmlDeviceGetPcieThroughput(device.Value,
NVML.NvmlPcieUtilCounter.TxBytes, out uint value)
== NVML.NvmlReturn.Success) {
pcieThroughputTx.Value = value * (1.0f / 0x400);
ActivateSensor(pcieThroughputTx);
}
}
}
public override string GetReport() {

View File

@ -4,7 +4,7 @@
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
Copyright (C) 2009-2011 Michael Möller <mmoeller@openhardwaremonitor.org>
Copyright (C) 2009-2020 Michael Möller <mmoeller@openhardwaremonitor.org>
*/
@ -28,7 +28,7 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
string version;
if (NVAPI.NvAPI_GetInterfaceVersionString(out version) == NvStatus.OK) {
report.Append("Version: ");
report.Append(" Version: ");
report.AppendLine(version);
}
@ -36,18 +36,26 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
new NvPhysicalGpuHandle[NVAPI.MAX_PHYSICAL_GPUS];
int count;
if (NVAPI.NvAPI_EnumPhysicalGPUs == null) {
report.AppendLine("Error: NvAPI_EnumPhysicalGPUs not available");
report.AppendLine(" Error: NvAPI_EnumPhysicalGPUs not available");
report.AppendLine();
return;
} else {
NvStatus status = NVAPI.NvAPI_EnumPhysicalGPUs(handles, out count);
if (status != NvStatus.OK) {
report.AppendLine("Status: " + status);
report.AppendLine(" Status: " + status);
report.AppendLine();
return;
}
}
var result = NVML.NvmlInit();
report.AppendLine();
report.AppendLine("NVML");
report.AppendLine();
report.AppendLine(" Status: " + result);
report.AppendLine();
IDictionary<NvPhysicalGpuHandle, NvDisplayHandle> displayHandles =
new Dictionary<NvPhysicalGpuHandle, NvDisplayHandle>();
@ -100,7 +108,11 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
public void Close() {
foreach (Hardware gpu in hardware)
gpu.Close();
gpu.Close();
if (NVML.IsInitialized) {
NVML.NvmlShutdown();
}
}
}
}

View File

@ -295,6 +295,9 @@
<ItemGroup>
<EmbeddedResource Include="Resources\ram.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\throughput.png" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio AllowExistingFolder="true" />

View File

@ -94,6 +94,7 @@
<Compile Include="Hardware\LPC\NCT677X.cs" />
<Compile Include="Hardware\Mainboard\GigabyteTAMG.cs" />
<Compile Include="Hardware\Mainboard\Identification.cs" />
<Compile Include="Hardware\Nvidia\NVML.cs" />
<Compile Include="Hardware\Opcode.cs" />
<Compile Include="Hardware\OperatingSystem.cs" />
<Compile Include="Hardware\RAM\GenericRAM.cs" />

BIN
Resources/throughput.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B