Add VRAM memory and bandwidth sensors for Intel discrete GPUs (#2067)
- Add GPU Memory Total, Used, Free sensors (SmallData type, in MB) - Add GPU Memory load sensor (percentage) - Add GPU Memory Read/Write bandwidth sensors (Throughput type, in B/s) - Use D3DDisplayDevice API to query VRAM usage via Windows kernel - Match Intel discrete GPUs using PCI vendor/device IDs - Include fallback detection for Intel GPUs if initial matching fails - Bandwidth sensors read from Intel GCL telemetry with unit conversion
This commit is contained in:
@@ -38,9 +38,20 @@ internal sealed class IntelDiscreteGpu : GenericGpu
|
||||
private readonly Sensor _voltageCore;
|
||||
private readonly Sensor _voltageMemory;
|
||||
|
||||
// Memory sensors
|
||||
private readonly Sensor _memoryFree;
|
||||
private readonly Sensor _memoryTotal;
|
||||
private readonly Sensor _memoryUsed;
|
||||
private readonly Sensor _memoryLoad;
|
||||
|
||||
// Bandwidth sensors
|
||||
private readonly Sensor _memoryBandwidthRead;
|
||||
private readonly Sensor _memoryBandwidthWrite;
|
||||
|
||||
// Timestamps
|
||||
private double _currentTimestamp = double.NaN;
|
||||
private string _deviceId;
|
||||
private string _d3dDeviceId;
|
||||
|
||||
// Intel GCL properties and data
|
||||
private readonly IntelGcl.ctl_device_adapter_handle_t _handle;
|
||||
@@ -54,6 +65,8 @@ internal sealed class IntelDiscreteGpu : GenericGpu
|
||||
private double _lastRenderComputeActivityCounter = double.NaN;
|
||||
private double _lastTimestamp = double.NaN;
|
||||
private double _lastTotalCardEnergyReading = double.NaN;
|
||||
private double _lastVramReadBandwidthCounter = double.NaN;
|
||||
private double _lastVramWriteBandwidthCounter = double.NaN;
|
||||
private IntelGcl.ctl_device_adapter_properties_t _properties;
|
||||
|
||||
// Telemetry data
|
||||
@@ -69,6 +82,9 @@ internal sealed class IntelDiscreteGpu : GenericGpu
|
||||
if (!InitializeDevice())
|
||||
return;
|
||||
|
||||
// Try to get D3D device identifier for memory monitoring
|
||||
_d3dDeviceId = GetD3DDeviceId();
|
||||
|
||||
// Initialize temperature sensors
|
||||
_temperatureGpuCore = new Sensor("GPU Core", 0, SensorType.Temperature, this, settings);
|
||||
_temperatureMemory = new Sensor("GPU Memory", 1, SensorType.Temperature, this, settings);
|
||||
@@ -90,6 +106,16 @@ internal sealed class IntelDiscreteGpu : GenericGpu
|
||||
_loadRenderCompute = new Sensor("GPU Render/Compute", 1, SensorType.Load, this, settings);
|
||||
_loadMedia = new Sensor("GPU Media", 2, SensorType.Load, this, settings);
|
||||
|
||||
// Initialize memory sensors
|
||||
_memoryFree = new Sensor("GPU Memory Free", 0, SensorType.SmallData, this, settings);
|
||||
_memoryUsed = new Sensor("GPU Memory Used", 1, SensorType.SmallData, this, settings);
|
||||
_memoryTotal = new Sensor("GPU Memory Total", 2, SensorType.SmallData, this, settings);
|
||||
_memoryLoad = new Sensor("GPU Memory", 3, SensorType.Load, this, settings);
|
||||
|
||||
// Initialize bandwidth sensors
|
||||
_memoryBandwidthRead = new Sensor("GPU Memory Read", 0, SensorType.Throughput, this, settings);
|
||||
_memoryBandwidthWrite = new Sensor("GPU Memory Write", 1, SensorType.Throughput, this, settings);
|
||||
|
||||
// Initialize fan sensors based on available fans
|
||||
int fanCount = (int)GetFanCount();
|
||||
_fans = new Sensor[fanCount];
|
||||
@@ -164,6 +190,34 @@ internal sealed class IntelDiscreteGpu : GenericGpu
|
||||
return false;
|
||||
}
|
||||
|
||||
private string GetD3DDeviceId()
|
||||
{
|
||||
// Try to find matching D3D device using PCI device ID
|
||||
string[] deviceIdentifiers = D3DDisplayDevice.GetDeviceIdentifiers();
|
||||
if (deviceIdentifiers == null || deviceIdentifiers.Length == 0)
|
||||
return null;
|
||||
|
||||
// Intel vendor ID is 0x8086
|
||||
string vendorPattern = $"VEN_{_properties.pci_vendor_id:X}";
|
||||
string devicePattern = $"DEV_{_properties.pci_device_id:X}";
|
||||
|
||||
foreach (string deviceIdentifier in deviceIdentifiers)
|
||||
{
|
||||
// Check if this device matches Intel vendor and device IDs
|
||||
if (deviceIdentifier.IndexOf(vendorPattern, StringComparison.OrdinalIgnoreCase) != -1 &&
|
||||
deviceIdentifier.IndexOf(devicePattern, StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
// Verify it's a valid D3D device by trying to get device info
|
||||
if (D3DDisplayDevice.GetDeviceInfoByIdentifier(deviceIdentifier, out D3DDisplayDevice.D3DDeviceInfo deviceInfo))
|
||||
{
|
||||
return deviceIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (!IsValid)
|
||||
@@ -196,6 +250,19 @@ internal sealed class IntelDiscreteGpu : GenericGpu
|
||||
UpdateUtilizationFromActivityCounter(_telemetry.renderComputeActivityCounter, ref _lastRenderComputeActivityCounter, _loadRenderCompute);
|
||||
UpdateUtilizationFromActivityCounter(_telemetry.mediaActivityCounter, ref _lastMediaActivityCounter, _loadMedia);
|
||||
|
||||
// Try to get D3D device ID if we haven't found it yet
|
||||
if (string.IsNullOrEmpty(_d3dDeviceId))
|
||||
{
|
||||
_d3dDeviceId = GetD3DDeviceId();
|
||||
}
|
||||
|
||||
// Update VRAM memory sensors (using D3D API)
|
||||
UpdateMemorySensors();
|
||||
|
||||
// Update VRAM bandwidth sensors
|
||||
UpdateBandwidthFromCounter(_telemetry.vramReadBandwidth, ref _lastVramReadBandwidthCounter, _memoryBandwidthRead);
|
||||
UpdateBandwidthFromCounter(_telemetry.vramWriteBandwidth, ref _lastVramWriteBandwidthCounter, _memoryBandwidthWrite);
|
||||
|
||||
// Update fan sensors
|
||||
UpdateFanSpeeds(_fans);
|
||||
}
|
||||
@@ -425,4 +492,97 @@ internal sealed class IntelDiscreteGpu : GenericGpu
|
||||
|
||||
lastActivityReading = currentActivity;
|
||||
}
|
||||
|
||||
private void UpdateMemorySensors()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_d3dDeviceId))
|
||||
{
|
||||
// Fallback: Try to find any Intel discrete GPU
|
||||
string[] deviceIdentifiers = D3DDisplayDevice.GetDeviceIdentifiers();
|
||||
if (deviceIdentifiers != null)
|
||||
{
|
||||
foreach (string deviceId in deviceIdentifiers)
|
||||
{
|
||||
if (deviceId.IndexOf("VEN_8086", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
if (D3DDisplayDevice.GetDeviceInfoByIdentifier(deviceId, out D3DDisplayDevice.D3DDeviceInfo testInfo))
|
||||
{
|
||||
if (testInfo.GpuDedicatedLimit > 0 && !testInfo.Integrated)
|
||||
{
|
||||
_d3dDeviceId = deviceId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_d3dDeviceId))
|
||||
return;
|
||||
}
|
||||
|
||||
if (D3DDisplayDevice.GetDeviceInfoByIdentifier(_d3dDeviceId, out D3DDisplayDevice.D3DDeviceInfo deviceInfo))
|
||||
{
|
||||
// Get dedicated video memory (VRAM) usage
|
||||
ulong totalBytes = deviceInfo.GpuDedicatedLimit;
|
||||
ulong usedBytes = deviceInfo.GpuDedicatedUsed;
|
||||
ulong freeBytes = totalBytes > usedBytes ? totalBytes - usedBytes : 0;
|
||||
|
||||
if (totalBytes > 0)
|
||||
{
|
||||
// Convert bytes to MB for display
|
||||
_memoryTotal.Value = totalBytes / (1024.0f * 1024.0f);
|
||||
ActivateSensor(_memoryTotal);
|
||||
|
||||
_memoryUsed.Value = usedBytes / (1024.0f * 1024.0f);
|
||||
ActivateSensor(_memoryUsed);
|
||||
|
||||
_memoryFree.Value = freeBytes / (1024.0f * 1024.0f);
|
||||
ActivateSensor(_memoryFree);
|
||||
|
||||
// Calculate load percentage
|
||||
_memoryLoad.Value = (float)((double)usedBytes / totalBytes * 100.0);
|
||||
ActivateSensor(_memoryLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBandwidthFromCounter(IntelGcl.ctl_oc_telemetry_item_t bandwidthItem, ref double lastBandwidthReading, Sensor bandwidthSensor)
|
||||
{
|
||||
if (!IsValid || bandwidthSensor == null)
|
||||
return;
|
||||
|
||||
// If the telemetry item directly provides bandwidth value (not a counter)
|
||||
if (bandwidthItem.bSupported)
|
||||
{
|
||||
double bandwidthValue = GetTelemetryValue(bandwidthItem);
|
||||
|
||||
if (!double.IsNaN(bandwidthValue) && bandwidthValue >= 0)
|
||||
{
|
||||
// Bandwidth is typically in GB/s or MB/s, convert to B/s for Throughput sensor
|
||||
// Check the units to determine if conversion is needed
|
||||
if (bandwidthItem.units == IntelGcl.ctl_units_t.CTL_UNITS_BANDWIDTH_MBPS)
|
||||
{
|
||||
// Convert MB/s to B/s (multiply by 1024*1024)
|
||||
bandwidthValue = bandwidthValue * 1024.0 * 1024.0;
|
||||
}
|
||||
else if (bandwidthItem.units == IntelGcl.ctl_units_t.CTL_UNITS_MEM_SPEED_GBPS)
|
||||
{
|
||||
// Convert GB/s to B/s (multiply by 1024*1024*1024)
|
||||
bandwidthValue = bandwidthValue * 1024.0 * 1024.0 * 1024.0;
|
||||
}
|
||||
|
||||
bandwidthSensor.Value = (float)bandwidthValue;
|
||||
ActivateSensor(bandwidthSensor);
|
||||
}
|
||||
else
|
||||
{
|
||||
bandwidthSensor.Value = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bandwidthSensor.Value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user