@@ -33,9 +33,20 @@ internal sealed class IntelDiscreteGpu : GenericGpu
3333 private readonly Sensor _voltageCore ;
3434 private readonly Sensor _voltageMemory ;
3535
36+ // Memory sensors
37+ private readonly Sensor _memoryFree ;
38+ private readonly Sensor _memoryTotal ;
39+ private readonly Sensor _memoryUsed ;
40+ private readonly Sensor _memoryLoad ;
41+
42+ // Bandwidth sensors
43+ private readonly Sensor _memoryBandwidthRead ;
44+ private readonly Sensor _memoryBandwidthWrite ;
45+
3646 // Timestamps
3747 private double _currentTimestamp = double . NaN ;
3848 private string _deviceId ;
49+ private string _d3dDeviceId ;
3950
4051 // Intel GCL properties and data
4152 private readonly IntelGcl . ctl_device_adapter_handle_t _handle ;
@@ -49,6 +60,8 @@ internal sealed class IntelDiscreteGpu : GenericGpu
4960 private double _lastRenderComputeActivityCounter = double . NaN ;
5061 private double _lastTimestamp = double . NaN ;
5162 private double _lastTotalCardEnergyReading = double . NaN ;
63+ private double _lastVramReadBandwidthCounter = double . NaN ;
64+ private double _lastVramWriteBandwidthCounter = double . NaN ;
5265 private IntelGcl . ctl_device_adapter_properties_t _properties ;
5366
5467 // Telemetry data
@@ -64,6 +77,9 @@ public IntelDiscreteGpu(IntelGcl.ctl_device_adapter_handle_t handle, ISettings s
6477 if ( ! InitializeDevice ( ) )
6578 return ;
6679
80+ // Try to get D3D device identifier for memory monitoring
81+ _d3dDeviceId = GetD3DDeviceId ( ) ;
82+
6783 // Initialize temperature sensors
6884 _temperatureGpuCore = new Sensor ( "GPU Core" , 0 , SensorType . Temperature , this , settings ) ;
6985 _temperatureMemory = new Sensor ( "GPU Memory" , 1 , SensorType . Temperature , this , settings ) ;
@@ -85,6 +101,16 @@ public IntelDiscreteGpu(IntelGcl.ctl_device_adapter_handle_t handle, ISettings s
85101 _loadRenderCompute = new Sensor ( "GPU Render/Compute" , 1 , SensorType . Load , this , settings ) ;
86102 _loadMedia = new Sensor ( "GPU Media" , 2 , SensorType . Load , this , settings ) ;
87103
104+ // Initialize memory sensors
105+ _memoryFree = new Sensor ( "GPU Memory Free" , 0 , SensorType . SmallData , this , settings ) ;
106+ _memoryUsed = new Sensor ( "GPU Memory Used" , 1 , SensorType . SmallData , this , settings ) ;
107+ _memoryTotal = new Sensor ( "GPU Memory Total" , 2 , SensorType . SmallData , this , settings ) ;
108+ _memoryLoad = new Sensor ( "GPU Memory" , 3 , SensorType . Load , this , settings ) ;
109+
110+ // Initialize bandwidth sensors
111+ _memoryBandwidthRead = new Sensor ( "GPU Memory Read" , 0 , SensorType . Throughput , this , settings ) ;
112+ _memoryBandwidthWrite = new Sensor ( "GPU Memory Write" , 1 , SensorType . Throughput , this , settings ) ;
113+
88114 // Initialize fan sensors based on available fans
89115 int fanCount = ( int ) GetFanCount ( ) ;
90116 _fans = new Sensor [ fanCount ] ;
@@ -159,6 +185,34 @@ private bool InitializeDevice()
159185 return false ;
160186 }
161187
188+ private string GetD3DDeviceId ( )
189+ {
190+ // Try to find matching D3D device using PCI device ID
191+ string [ ] deviceIdentifiers = D3DDisplayDevice . GetDeviceIdentifiers ( ) ;
192+ if ( deviceIdentifiers == null || deviceIdentifiers . Length == 0 )
193+ return null ;
194+
195+ // Intel vendor ID is 0x8086
196+ string vendorPattern = $ "VEN_{ _properties . pci_vendor_id : X} ";
197+ string devicePattern = $ "DEV_{ _properties . pci_device_id : X} ";
198+
199+ foreach ( string deviceIdentifier in deviceIdentifiers )
200+ {
201+ // Check if this device matches Intel vendor and device IDs
202+ if ( deviceIdentifier . IndexOf ( vendorPattern , StringComparison . OrdinalIgnoreCase ) != - 1 &&
203+ deviceIdentifier . IndexOf ( devicePattern , StringComparison . OrdinalIgnoreCase ) != - 1 )
204+ {
205+ // Verify it's a valid D3D device by trying to get device info
206+ if ( D3DDisplayDevice . GetDeviceInfoByIdentifier ( deviceIdentifier , out D3DDisplayDevice . D3DDeviceInfo deviceInfo ) )
207+ {
208+ return deviceIdentifier ;
209+ }
210+ }
211+ }
212+
213+ return null ;
214+ }
215+
162216 public override void Update ( )
163217 {
164218 if ( ! IsValid )
@@ -191,6 +245,19 @@ public override void Update()
191245 UpdateUtilizationFromActivityCounter ( _telemetry . renderComputeActivityCounter , ref _lastRenderComputeActivityCounter , _loadRenderCompute ) ;
192246 UpdateUtilizationFromActivityCounter ( _telemetry . mediaActivityCounter , ref _lastMediaActivityCounter , _loadMedia ) ;
193247
248+ // Try to get D3D device ID if we haven't found it yet
249+ if ( string . IsNullOrEmpty ( _d3dDeviceId ) )
250+ {
251+ _d3dDeviceId = GetD3DDeviceId ( ) ;
252+ }
253+
254+ // Update VRAM memory sensors (using D3D API)
255+ UpdateMemorySensors ( ) ;
256+
257+ // Update VRAM bandwidth sensors
258+ UpdateBandwidthFromCounter ( _telemetry . vramReadBandwidth , ref _lastVramReadBandwidthCounter , _memoryBandwidthRead ) ;
259+ UpdateBandwidthFromCounter ( _telemetry . vramWriteBandwidth , ref _lastVramWriteBandwidthCounter , _memoryBandwidthWrite ) ;
260+
194261 // Update fan sensors
195262 UpdateFanSpeeds ( _fans ) ;
196263 }
@@ -420,4 +487,97 @@ private void UpdateUtilizationFromActivityCounter(IntelGcl.ctl_oc_telemetry_item
420487
421488 lastActivityReading = currentActivity ;
422489 }
490+
491+ private void UpdateMemorySensors ( )
492+ {
493+ if ( string . IsNullOrEmpty ( _d3dDeviceId ) )
494+ {
495+ // Fallback: Try to find any Intel discrete GPU
496+ string [ ] deviceIdentifiers = D3DDisplayDevice . GetDeviceIdentifiers ( ) ;
497+ if ( deviceIdentifiers != null )
498+ {
499+ foreach ( string deviceId in deviceIdentifiers )
500+ {
501+ if ( deviceId . IndexOf ( "VEN_8086" , StringComparison . OrdinalIgnoreCase ) != - 1 )
502+ {
503+ if ( D3DDisplayDevice . GetDeviceInfoByIdentifier ( deviceId , out D3DDisplayDevice . D3DDeviceInfo testInfo ) )
504+ {
505+ if ( testInfo . GpuDedicatedLimit > 0 && ! testInfo . Integrated )
506+ {
507+ _d3dDeviceId = deviceId ;
508+ break ;
509+ }
510+ }
511+ }
512+ }
513+ }
514+
515+ if ( string . IsNullOrEmpty ( _d3dDeviceId ) )
516+ return ;
517+ }
518+
519+ if ( D3DDisplayDevice . GetDeviceInfoByIdentifier ( _d3dDeviceId , out D3DDisplayDevice . D3DDeviceInfo deviceInfo ) )
520+ {
521+ // Get dedicated video memory (VRAM) usage
522+ ulong totalBytes = deviceInfo . GpuDedicatedLimit ;
523+ ulong usedBytes = deviceInfo . GpuDedicatedUsed ;
524+ ulong freeBytes = totalBytes > usedBytes ? totalBytes - usedBytes : 0 ;
525+
526+ if ( totalBytes > 0 )
527+ {
528+ // Convert bytes to MB for display
529+ _memoryTotal . Value = totalBytes / ( 1024.0f * 1024.0f ) ;
530+ ActivateSensor ( _memoryTotal ) ;
531+
532+ _memoryUsed . Value = usedBytes / ( 1024.0f * 1024.0f ) ;
533+ ActivateSensor ( _memoryUsed ) ;
534+
535+ _memoryFree . Value = freeBytes / ( 1024.0f * 1024.0f ) ;
536+ ActivateSensor ( _memoryFree ) ;
537+
538+ // Calculate load percentage
539+ _memoryLoad . Value = ( float ) ( ( double ) usedBytes / totalBytes * 100.0 ) ;
540+ ActivateSensor ( _memoryLoad ) ;
541+ }
542+ }
543+ }
544+
545+ private void UpdateBandwidthFromCounter ( IntelGcl . ctl_oc_telemetry_item_t bandwidthItem , ref double lastBandwidthReading , Sensor bandwidthSensor )
546+ {
547+ if ( ! IsValid || bandwidthSensor == null )
548+ return ;
549+
550+ // If the telemetry item directly provides bandwidth value (not a counter)
551+ if ( bandwidthItem . bSupported )
552+ {
553+ double bandwidthValue = GetTelemetryValue ( bandwidthItem ) ;
554+
555+ if ( ! double . IsNaN ( bandwidthValue ) && bandwidthValue >= 0 )
556+ {
557+ // Bandwidth is typically in GB/s or MB/s, convert to B/s for Throughput sensor
558+ // Check the units to determine if conversion is needed
559+ if ( bandwidthItem . units == IntelGcl . ctl_units_t . CTL_UNITS_BANDWIDTH_MBPS )
560+ {
561+ // Convert MB/s to B/s (multiply by 1024*1024)
562+ bandwidthValue = bandwidthValue * 1024.0 * 1024.0 ;
563+ }
564+ else if ( bandwidthItem . units == IntelGcl . ctl_units_t . CTL_UNITS_MEM_SPEED_GBPS )
565+ {
566+ // Convert GB/s to B/s (multiply by 1024*1024*1024)
567+ bandwidthValue = bandwidthValue * 1024.0 * 1024.0 * 1024.0 ;
568+ }
569+
570+ bandwidthSensor . Value = ( float ) bandwidthValue ;
571+ ActivateSensor ( bandwidthSensor ) ;
572+ }
573+ else
574+ {
575+ bandwidthSensor . Value = null ;
576+ }
577+ }
578+ else
579+ {
580+ bandwidthSensor . Value = null ;
581+ }
582+ }
423583}
0 commit comments