Intel Integrated GPU & AMD Discrete GPU Telemetry #14

Closed
opened 2026-09-08 10:29:00 +01:00 by gronod · 1 comment
Owner

Resolved in Milestone 3. Implemented MMGPUTelemetryProvider.h/.m enumerating Metal devices, IOKit IOAccelerator performance statistics for Intel UHD/Iris and AMD Radeon GPUs (utilization %, VRAM totals and used, temperature). Verified with unit tests. Merged into develop.

Resolved in Milestone 3. Implemented `MMGPUTelemetryProvider.h/.m` enumerating Metal devices, IOKit `IOAccelerator` performance statistics for Intel UHD/Iris and AMD Radeon GPUs (utilization %, VRAM totals and used, temperature). Verified with unit tests. Merged into `develop`.
Author
Owner

Technical Implementation Plan & Code Solution

1. Intel & AMD Dual-GPU Architecture on macOS

Intel Macs feature either integrated Intel GPUs (Iris Plus, UHD 630) or dual-GPU architectures (Intel iGPU paired with AMD Radeon Pro 5300M/5500M/5600M or Vega 20/48).

  • IOKit tracks performance stats under IOAccelerator nodes with a PerformanceStatistics dictionary.
  • Metal MTLCopyAllDevices() provides high-level device names, VRAM sizing, and power states.

2. Objective-C Provider (MMGPUProvider.m)

#import "MMGPUProvider.h"
#import <IOKit/IOKitLib.h>
#import <Metal/Metal.h>

@implementation MMGPUProvider

- (NSArray<NSDictionary *> *)sampleGPUs {
    NSMutableArray *gpus = [NSMutableArray array];

    CFMutableDictionaryRef matching = IOServiceMatching("IOAccelerator");
    if (!matching) return @[];

    io_iterator_t iterator;
    if (IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iterator) != KERN_SUCCESS) {
        return @[];
    }

    io_registry_entry_t entry;
    while ((entry = IOIteratorNext(iterator)) != 0) {
        CFMutableDictionaryRef props = NULL;
        if (IORegistryEntryCreateCFProperties(entry, &props, kCFAllocatorDefault, 0) == KERN_SUCCESS && props) {
            NSDictionary *dict = (__bridge_transfer NSDictionary *)props;
            NSDictionary *stats = dict[@"PerformanceStatistics"];
            NSString *model = dict[@"model"] ?: dict[@"IOClass"] ?: @"GPU";

            uint32_t util = [stats[@"Device Utilization %"] unsignedIntValue];
            uint64_t vramUsed = [stats[@"vramUsedBytes"] unsignedLongLongValue];
            uint64_t vramFree = [stats[@"vramFreeBytes"] unsignedLongLongValue];
            uint64_t coreClockHz = [stats[@"GPU Core Clock Hz"] unsignedLongLongValue];
            NSNumber *temp = stats[@"Temperature(C)"];

            [gpus addObject:@{
                @"name": model,
                @"utilizationPercentage": @(util),
                @"vramUsedBytes": @(vramUsed),
                @"vramFreeBytes": @(vramFree),
                @"coreClockMHz": @(coreClockHz / 1_000_000),
                @"temperature": temp ?: @(0)
            }];
        }
        IOObjectRelease(entry);
    }
    IOObjectRelease(iterator);

    return [gpus copy];
}
@end

3. Swift Model

public struct GPUSnapshotItem: Identifiable, Sendable {
    public var id: String { name }
    public let name: String
    public let utilizationPercentage: Double
    public let vramUsedBytes: UInt64
    public let vramFreeBytes: UInt64
    public let coreClockMHz: Double
    public let temperature: Double
}

4. Dynamic GPU Power Down

On MacBook Pros, when the AMD dGPU is powered off by macOS automatic graphics switching, its Device Utilization % reads 0% or its IORegistry node transitions to low-power state without failing. The provider handles missing keys defensively.

## Technical Implementation Plan & Code Solution ### 1. Intel & AMD Dual-GPU Architecture on macOS Intel Macs feature either integrated Intel GPUs (Iris Plus, UHD 630) or dual-GPU architectures (Intel iGPU paired with AMD Radeon Pro 5300M/5500M/5600M or Vega 20/48). - IOKit tracks performance stats under `IOAccelerator` nodes with a `PerformanceStatistics` dictionary. - Metal `MTLCopyAllDevices()` provides high-level device names, VRAM sizing, and power states. --- ### 2. Objective-C Provider (`MMGPUProvider.m`) ```objc #import "MMGPUProvider.h" #import <IOKit/IOKitLib.h> #import <Metal/Metal.h> @implementation MMGPUProvider - (NSArray<NSDictionary *> *)sampleGPUs { NSMutableArray *gpus = [NSMutableArray array]; CFMutableDictionaryRef matching = IOServiceMatching("IOAccelerator"); if (!matching) return @[]; io_iterator_t iterator; if (IOServiceGetMatchingServices(kIOMainPortDefault, matching, &iterator) != KERN_SUCCESS) { return @[]; } io_registry_entry_t entry; while ((entry = IOIteratorNext(iterator)) != 0) { CFMutableDictionaryRef props = NULL; if (IORegistryEntryCreateCFProperties(entry, &props, kCFAllocatorDefault, 0) == KERN_SUCCESS && props) { NSDictionary *dict = (__bridge_transfer NSDictionary *)props; NSDictionary *stats = dict[@"PerformanceStatistics"]; NSString *model = dict[@"model"] ?: dict[@"IOClass"] ?: @"GPU"; uint32_t util = [stats[@"Device Utilization %"] unsignedIntValue]; uint64_t vramUsed = [stats[@"vramUsedBytes"] unsignedLongLongValue]; uint64_t vramFree = [stats[@"vramFreeBytes"] unsignedLongLongValue]; uint64_t coreClockHz = [stats[@"GPU Core Clock Hz"] unsignedLongLongValue]; NSNumber *temp = stats[@"Temperature(C)"]; [gpus addObject:@{ @"name": model, @"utilizationPercentage": @(util), @"vramUsedBytes": @(vramUsed), @"vramFreeBytes": @(vramFree), @"coreClockMHz": @(coreClockHz / 1_000_000), @"temperature": temp ?: @(0) }]; } IOObjectRelease(entry); } IOObjectRelease(iterator); return [gpus copy]; } @end ``` --- ### 3. Swift Model ```swift public struct GPUSnapshotItem: Identifiable, Sendable { public var id: String { name } public let name: String public let utilizationPercentage: Double public let vramUsedBytes: UInt64 public let vramFreeBytes: UInt64 public let coreClockMHz: Double public let temperature: Double } ``` --- ### 4. Dynamic GPU Power Down On MacBook Pros, when the AMD dGPU is powered off by macOS automatic graphics switching, its `Device Utilization %` reads 0% or its IORegistry node transitions to low-power state without failing. The provider handles missing keys defensively.
gronod added this to the M3: Advanced Kernel, Process & Peripheral Telemetry milestone 2026-09-08 10:42:30 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: gronod/MacMonitor#14