Added basic Nvidia NVML library support and power usage sensor (#60)
* Added basic Nvidia NVML library support and power usage sensor * Added basic Nvidia NVML library support and power usage sensor * Added Initialised check * Made NvmlDevice internal * Code formatting * Move NvmlDevice and NvmlReturn to separate files * Move code from NvidiaNVMLGPU class to NvidiaGPU class * Replace call to SetDllDirectory with LoadLibrary/FreeLibrary and code formatting changes * Made native methods private * Fixed p/invoke definitions, removed unnecessary try/catch blocks. Code style cleanup
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace OpenHardwareMonitor.Hardware.Nvidia {
|
||||
internal class NVML {
|
||||
private const string LinuxDllName = "nvidia-ml";
|
||||
|
||||
private delegate NvmlReturn WindowsNvmlDelegate();
|
||||
private WindowsNvmlDelegate WindowsNvmlInit;
|
||||
private WindowsNvmlDelegate WindowsNvmlShutdown;
|
||||
|
||||
private delegate NvmlReturn WindowsNvmlGetHandleDelegate(int index, out NvmlDevice device);
|
||||
private WindowsNvmlGetHandleDelegate WindowsNvmlDeviceGetHandleByIndex;
|
||||
|
||||
private delegate NvmlReturn WindowsNvmlGetPowerUsageDelegate(NvmlDevice device, out int power);
|
||||
private WindowsNvmlGetPowerUsageDelegate WindowsNvmlDeviceGetPowerUsage;
|
||||
|
||||
private readonly IntPtr windowsDll;
|
||||
|
||||
internal bool Initialised { get; }
|
||||
|
||||
internal NVML() {
|
||||
if (Software.OperatingSystem.IsLinux) {
|
||||
try {
|
||||
Initialised = (LinuxNvmlInit() == NvmlReturn.Success);
|
||||
}
|
||||
catch (DllNotFoundException) {
|
||||
return;
|
||||
}
|
||||
catch (EntryPointNotFoundException) {
|
||||
try {
|
||||
Initialised = (LinuxNvmlInitLegacy() == NvmlReturn.Success);
|
||||
}
|
||||
catch (EntryPointNotFoundException) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IsNvmlCompatibleWindowsVersion()) {
|
||||
var programFilesDirectory = Environment.ExpandEnvironmentVariables("%ProgramW6432%");
|
||||
var dllPath = Path.Combine(programFilesDirectory, @"NVIDIA Corporation\NVSMI\nvml.dll");
|
||||
|
||||
windowsDll = LoadLibrary(dllPath);
|
||||
|
||||
if (windowsDll == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
Initialised = InitialiseDelegates() && (WindowsNvmlInit() == NvmlReturn.Success);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNvmlCompatibleWindowsVersion()
|
||||
{
|
||||
return Software.OperatingSystem.Is64Bit &&
|
||||
((Environment.OSVersion.Version.Major > 6) || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 1));
|
||||
}
|
||||
|
||||
private bool InitialiseDelegates()
|
||||
{
|
||||
var nvmlInit = GetProcAddress(windowsDll, "nvmlInit_v2");
|
||||
if (nvmlInit != IntPtr.Zero)
|
||||
WindowsNvmlInit = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlInit, typeof(WindowsNvmlDelegate));
|
||||
else {
|
||||
nvmlInit = GetProcAddress(windowsDll, "nvmlInit");
|
||||
if (nvmlInit != IntPtr.Zero)
|
||||
WindowsNvmlInit = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlInit, typeof(WindowsNvmlDelegate));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
var nvmlShutdown = GetProcAddress(windowsDll, "nvmlShutdown");
|
||||
if (nvmlShutdown != IntPtr.Zero)
|
||||
WindowsNvmlShutdown = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlShutdown, typeof(WindowsNvmlDelegate));
|
||||
else
|
||||
return false;
|
||||
|
||||
var nvmlGetHandle = GetProcAddress(windowsDll, "nvmlDeviceGetHandleByIndex_v2");
|
||||
if (nvmlGetHandle != IntPtr.Zero)
|
||||
WindowsNvmlDeviceGetHandleByIndex = (WindowsNvmlGetHandleDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandle, typeof(WindowsNvmlGetHandleDelegate));
|
||||
else {
|
||||
nvmlGetHandle = GetProcAddress(windowsDll, "nvmlDeviceGetHandleByIndex");
|
||||
if (nvmlGetHandle != IntPtr.Zero)
|
||||
WindowsNvmlDeviceGetHandleByIndex = (WindowsNvmlGetHandleDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandle, typeof(WindowsNvmlGetHandleDelegate));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
var nvmlGetPowerUsage = GetProcAddress(windowsDll, "nvmlDeviceGetPowerUsage");
|
||||
if (nvmlGetPowerUsage != IntPtr.Zero)
|
||||
WindowsNvmlDeviceGetPowerUsage = (WindowsNvmlGetPowerUsageDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetPowerUsage, typeof(WindowsNvmlGetPowerUsageDelegate));
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void Close() {
|
||||
if (Initialised) {
|
||||
if (Software.OperatingSystem.IsLinux)
|
||||
LinuxNvmlShutdown();
|
||||
else if (windowsDll != IntPtr.Zero) {
|
||||
WindowsNvmlShutdown();
|
||||
FreeLibrary(windowsDll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal NvmlDevice? NvmlDeviceGetHandleByIndex(int index) {
|
||||
if (Initialised) {
|
||||
NvmlDevice nvmlDevice;
|
||||
if (Software.OperatingSystem.IsLinux) {
|
||||
try {
|
||||
if (LinuxNvmlDeviceGetHandleByIndex(index, out nvmlDevice) == NvmlReturn.Success)
|
||||
return nvmlDevice;
|
||||
}
|
||||
catch (EntryPointNotFoundException) {
|
||||
if (LinuxNvmlDeviceGetHandleByIndexLegacy(index, out nvmlDevice) == NvmlReturn.Success)
|
||||
return nvmlDevice;
|
||||
}
|
||||
}
|
||||
else if (WindowsNvmlDeviceGetHandleByIndex(index, out nvmlDevice) == NvmlReturn.Success)
|
||||
return nvmlDevice;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal int? NvmlDeviceGetPowerUsage(NvmlDevice nvmlDevice) {
|
||||
if (Initialised) {
|
||||
int powerUsage;
|
||||
if (Software.OperatingSystem.IsLinux) {
|
||||
if (LinuxNvmlDeviceGetPowerUsage(nvmlDevice, out powerUsage) == NvmlReturn.Success)
|
||||
return powerUsage;
|
||||
}
|
||||
else if (WindowsNvmlDeviceGetPowerUsage(nvmlDevice, out powerUsage) == NvmlReturn.Success)
|
||||
return powerUsage;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[DllImport("kernel32")]
|
||||
private static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string dllPath);
|
||||
|
||||
[DllImport("kernel32", ExactSpelling = true)]
|
||||
private static extern IntPtr GetProcAddress(IntPtr module, string methodName);
|
||||
|
||||
[DllImport("kernel32")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool FreeLibrary(IntPtr module);
|
||||
|
||||
[DllImport(LinuxDllName, EntryPoint = "nvmlInit_v2", ExactSpelling = true)]
|
||||
private static extern NvmlReturn LinuxNvmlInit();
|
||||
|
||||
[DllImport(LinuxDllName, EntryPoint = "nvmlInit", ExactSpelling = true)]
|
||||
private static extern NvmlReturn LinuxNvmlInitLegacy();
|
||||
|
||||
[DllImport(LinuxDllName, EntryPoint = "nvmlShutdown", ExactSpelling = true)]
|
||||
private static extern NvmlReturn LinuxNvmlShutdown();
|
||||
|
||||
[DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByIndex_v2", ExactSpelling = true)]
|
||||
private static extern NvmlReturn LinuxNvmlDeviceGetHandleByIndex(int index, out NvmlDevice device);
|
||||
|
||||
[DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByIndex", ExactSpelling = true)]
|
||||
private static extern NvmlReturn LinuxNvmlDeviceGetHandleByIndexLegacy(int index, out NvmlDevice device);
|
||||
|
||||
[DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetPowerUsage", ExactSpelling = true)]
|
||||
private static extern NvmlReturn LinuxNvmlDeviceGetPowerUsage(NvmlDevice device, out int power);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
|
||||
private readonly int adapterIndex;
|
||||
private readonly NvPhysicalGpuHandle handle;
|
||||
private readonly NvDisplayHandle? displayHandle;
|
||||
private readonly NVML nvml;
|
||||
private readonly NvmlDevice? nvmlDevice;
|
||||
|
||||
private readonly Sensor[] temperatures;
|
||||
private readonly Sensor fan;
|
||||
@@ -29,15 +31,17 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
|
||||
private readonly Sensor memoryUsed;
|
||||
private readonly Sensor memoryFree;
|
||||
private readonly Sensor memoryAvail;
|
||||
private readonly Sensor powerUsage;
|
||||
private readonly Control fanControl;
|
||||
|
||||
public NvidiaGPU(int adapterIndex, NvPhysicalGpuHandle handle,
|
||||
NvDisplayHandle? displayHandle, ISettings settings)
|
||||
NvDisplayHandle? displayHandle, ISettings settings, NVML nvml)
|
||||
: base(GetName(handle), new Identifier("nvidiagpu",
|
||||
adapterIndex.ToString(CultureInfo.InvariantCulture)), settings) {
|
||||
this.adapterIndex = adapterIndex;
|
||||
this.handle = handle;
|
||||
this.displayHandle = displayHandle;
|
||||
this.nvml = nvml;
|
||||
|
||||
NvGPUThermalSettings thermalSettings = GetThermalSettings();
|
||||
temperatures = new Sensor[thermalSettings.Count];
|
||||
@@ -93,6 +97,15 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
|
||||
ControlModeChanged(fanControl);
|
||||
control.Control = fanControl;
|
||||
}
|
||||
|
||||
if (nvml.Initialised)
|
||||
{
|
||||
nvmlDevice = nvml.NvmlDeviceGetHandleByIndex(adapterIndex);
|
||||
if (nvmlDevice.HasValue) {
|
||||
powerUsage = new Sensor("GPU Package", 0, SensorType.Power, this, settings);
|
||||
}
|
||||
}
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
@@ -219,6 +232,14 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
|
||||
ActivateSensor(memoryFree);
|
||||
ActivateSensor(memoryLoad);
|
||||
}
|
||||
|
||||
if (nvml.Initialised && nvmlDevice.HasValue) {
|
||||
var result = nvml.NvmlDeviceGetPowerUsage(nvmlDevice.Value);
|
||||
if (result.HasValue) {
|
||||
powerUsage.Value = (float)result.Value / 1000;
|
||||
ActivateSensor(powerUsage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetReport() {
|
||||
|
||||
@@ -15,9 +15,9 @@ using System.Text;
|
||||
namespace OpenHardwareMonitor.Hardware.Nvidia {
|
||||
|
||||
internal class NvidiaGroup : IGroup {
|
||||
|
||||
private readonly List<Hardware> hardware = new List<Hardware>();
|
||||
private readonly StringBuilder report = new StringBuilder();
|
||||
private readonly NVML nvml = new NVML();
|
||||
|
||||
public NvidiaGroup(ISettings settings) {
|
||||
if (!NVAPI.IsAvailable)
|
||||
@@ -82,7 +82,7 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
|
||||
for (int i = 0; i < count; i++) {
|
||||
NvDisplayHandle displayHandle;
|
||||
displayHandles.TryGetValue(handles[i], out displayHandle);
|
||||
hardware.Add(new NvidiaGPU(i, handles[i], displayHandle, settings));
|
||||
hardware.Add(new NvidiaGPU(i, handles[i], displayHandle, settings, nvml));
|
||||
}
|
||||
|
||||
report.AppendLine();
|
||||
@@ -96,7 +96,9 @@ namespace OpenHardwareMonitor.Hardware.Nvidia {
|
||||
|
||||
public void Close() {
|
||||
foreach (Hardware gpu in hardware)
|
||||
gpu.Close();
|
||||
gpu.Close();
|
||||
|
||||
nvml.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace OpenHardwareMonitor.Hardware.Nvidia {
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct NvmlDevice {
|
||||
public IntPtr Handle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
namespace OpenHardwareMonitor.Hardware.Nvidia {
|
||||
internal enum NvmlReturn {
|
||||
/// <summary>
|
||||
/// The operation was successful
|
||||
/// </summary>
|
||||
Success = 0,
|
||||
/// <summary>
|
||||
/// NVML was not first initialized with nvmlInit()
|
||||
/// </summary>
|
||||
Uninitialized = 1,
|
||||
/// <summary>
|
||||
/// A supplied argument is invalid
|
||||
/// </summary>
|
||||
InvalidArgument = 2,
|
||||
/// <summary>
|
||||
/// The requested operation is not available on target device
|
||||
/// </summary>
|
||||
NotSupported = 3,
|
||||
/// <summary>
|
||||
/// The current user does not have permission for operation
|
||||
/// </summary>
|
||||
NoPermission = 4,
|
||||
/// <summary>
|
||||
/// A query to find an object was unsuccessful
|
||||
/// </summary>
|
||||
NotFound = 6,
|
||||
/// <summary>
|
||||
/// An input argument is not large enough
|
||||
/// </summary>
|
||||
InsufficientSize = 7,
|
||||
/// <summary>
|
||||
/// A device's external power cables are not properly attached
|
||||
/// </summary>
|
||||
InsufficientPower = 8,
|
||||
/// <summary>
|
||||
/// NVIDIA driver is not loaded
|
||||
/// </summary>
|
||||
DriverNotLoaded = 9,
|
||||
/// <summary>
|
||||
/// User provided timeout passed
|
||||
/// </summary>
|
||||
TimeOut = 10,
|
||||
/// <summary>
|
||||
/// NVIDIA Kernel detected an interrupt issue with a GPU
|
||||
/// </summary>
|
||||
IRQIssue = 11,
|
||||
/// <summary>
|
||||
/// NVML Shared Library couldn't be found or loaded
|
||||
/// </summary>
|
||||
LibraryNotFound = 12,
|
||||
/// <summary>
|
||||
/// Local version of NVML doesn't implement this function
|
||||
/// </summary>
|
||||
FunctionNotFound = 13,
|
||||
/// <summary>
|
||||
/// infoROM is corrupted
|
||||
/// </summary>
|
||||
CorruptedInfoROM = 14,
|
||||
/// <summary>
|
||||
/// The GPU has fallen off the bus or has otherwise become inaccessible
|
||||
/// </summary>
|
||||
GPUIsLost = 15,
|
||||
/// <summary>
|
||||
/// The GPU requires a reset before it can be used again
|
||||
/// </summary>
|
||||
ResetRequired = 16,
|
||||
/// <summary>
|
||||
/// The GPU control device has been blocked by the operating system/cgroups
|
||||
/// </summary>
|
||||
OperatingSystem = 17,
|
||||
/// <summary>
|
||||
/// RM detects a driver/library version mismatch
|
||||
/// </summary>
|
||||
LibRMVersionMismatch = 18,
|
||||
/// <summary>
|
||||
/// An operation cannot be performed because the GPU is currently in use
|
||||
/// </summary>
|
||||
InUse = 19,
|
||||
/// <summary>
|
||||
/// An internal driver error occurred
|
||||
/// </summary>
|
||||
Unknown = 999
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,10 @@
|
||||
<Compile Include="Hardware\Mainboard\GigabyteTAMG.cs" />
|
||||
<Compile Include="Hardware\Mainboard\Identification.cs" />
|
||||
<Compile Include="Hardware\Nic\Nic.cs" />
|
||||
<Compile Include="Hardware\Nic\NicGroup.cs" />
|
||||
<Compile Include="Hardware\Nic\NicGroup.cs" />
|
||||
<Compile Include="Hardware\Nvidia\NVML.cs" />
|
||||
<Compile Include="Hardware\Nvidia\NvmlDevice.cs" />
|
||||
<Compile Include="Hardware\Nvidia\NvmlReturn.cs" />
|
||||
<Compile Include="Hardware\Opcode.cs" />
|
||||
<Compile Include="Hardware\RAM\GenericRAM.cs" />
|
||||
<Compile Include="Hardware\RAM\RAMGroup.cs" />
|
||||
|
||||
Reference in New Issue
Block a user