diff --git a/LibreHardwareMonitorLib/Hardware/Gpu/IntelDiscreteGpu.cs b/LibreHardwareMonitorLib/Hardware/Gpu/IntelDiscreteGpu.cs index a6184b6..cf52dca 100644 --- a/LibreHardwareMonitorLib/Hardware/Gpu/IntelDiscreteGpu.cs +++ b/LibreHardwareMonitorLib/Hardware/Gpu/IntelDiscreteGpu.cs @@ -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; + } + } }