Files
fastfetch/src/detection/gpu/gpu_apple.c
T

208 lines
8.4 KiB
C
Raw Normal View History

2022-09-05 17:42:42 +02:00
#include "gpu.h"
2026-01-06 09:48:38 +08:00
#include "common/apple/cf_helpers.h"
#include "common/apple/smc_temps.h"
2022-09-05 13:41:05 -07:00
#include <IOKit/graphics/IOGraphicsLib.h>
2022-09-05 17:42:42 +02:00
const char* ffGpuDetectMetal(FFlist* gpus);
2024-09-26 16:43:54 +08:00
const char* ffGpuDetectDriverVersion(FFlist* gpus);
2026-03-29 09:28:49 +08:00
static double detectGpuTemp(const FFstrbuf* gpuName) {
2023-08-07 10:15:08 +08:00
double result = 0;
2023-12-08 15:59:58 +08:00
const char* error = NULL;
2022-09-25 15:23:00 +08:00
2026-03-29 09:28:49 +08:00
if (ffStrbufStartsWithS(gpuName, "Apple M")) {
switch (strtol(gpuName->chars + strlen("Apple M"), NULL, 10)) {
case 0:
error = "Invalid Apple Silicon GPU";
break;
case 1:
error = ffDetectSmcTemps(FF_TEMP_GPU_M1X, &result);
break;
case 2:
error = ffDetectSmcTemps(FF_TEMP_GPU_M2X, &result);
break;
case 3:
error = ffDetectSmcTemps(FF_TEMP_GPU_M3X, &result);
break;
case 4:
error = ffDetectSmcTemps(FF_TEMP_GPU_M4X, &result);
break;
default:
error = "Unsupported Apple Silicon GPU";
break;
2023-12-08 15:59:58 +08:00
}
2026-03-29 09:28:49 +08:00
} else if (ffStrbufStartsWithS(gpuName, "Intel")) {
2023-12-27 08:46:49 +08:00
error = ffDetectSmcTemps(FF_TEMP_GPU_INTEL, &result);
2026-03-29 09:28:49 +08:00
} else if (ffStrbufStartsWithS(gpuName, "Radeon") || ffStrbufStartsWithS(gpuName, "AMD")) {
2023-12-27 08:46:49 +08:00
error = ffDetectSmcTemps(FF_TEMP_GPU_AMD, &result);
2026-03-29 09:28:49 +08:00
} else {
2023-12-27 08:46:49 +08:00
error = ffDetectSmcTemps(FF_TEMP_GPU_UNKNOWN, &result);
2026-03-29 09:28:49 +08:00
}
2022-09-25 15:23:00 +08:00
2026-03-29 09:28:49 +08:00
if (error) {
2022-09-25 15:23:00 +08:00
return FF_GPU_TEMP_UNSET;
2026-03-29 09:28:49 +08:00
}
2022-09-25 15:23:00 +08:00
return result;
}
#ifdef __aarch64__
#include <IOKit/IOKitLib.h>
2026-03-29 09:28:49 +08:00
static const char* detectFrequency(FFGPUResult* gpu) {
// https://github.com/giampaolo/psutil/pull/2222/files
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceNameMatching("pmgr"));
2026-03-29 09:28:49 +08:00
if (!entryDevice) {
return "IOServiceGetMatchingServices() failed";
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (!IOObjectConformsTo(entryDevice, "AppleARMIODevice")) {
return "\"pmgr\" should conform to \"AppleARMIODevice\"";
2026-03-29 09:28:49 +08:00
}
FF_CFTYPE_AUTO_RELEASE CFDataRef freqProperty = (CFDataRef) IORegistryEntryCreateCFProperty(entryDevice, CFSTR("voltage-states9-sram"), kCFAllocatorDefault, kNilOptions);
2026-03-29 09:28:49 +08:00
if (!freqProperty || CFGetTypeID(freqProperty) != CFDataGetTypeID()) {
return "\"voltage-states9-sram\" in \"pmgr\" is not found";
2026-03-29 09:28:49 +08:00
}
2025-11-17 08:59:39 +08:00
// voltage-states9-sram stores supported <frequency / voltage> pairs of gpu from the lowest to the highest
CFIndex propLength = CFDataGetLength(freqProperty);
if (propLength == 0 || propLength % (CFIndex) (sizeof(uint32_t) * 2) != 0) {
return "Invalid \"voltage-states9-sram\" length";
2026-03-29 09:28:49 +08:00
}
uint32_t* pStart = (uint32_t*) CFDataGetBytePtr(freqProperty);
uint32_t pMax = *pStart;
2026-03-29 09:28:49 +08:00
for (CFIndex i = 2; i < propLength / (CFIndex) sizeof(uint32_t) && pStart[i] > 0; i += 2 /* skip voltage */) {
pMax = pMax > pStart[i] ? pMax : pStart[i];
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (pMax > 0) {
// While this is not necessary for now (seems), we add this logic just in case. See cpu_apple.c
2026-03-29 09:28:49 +08:00
if (pMax > 100000000) { // Assume that pMax is in Hz
gpu->frequency = pMax / 1000 / 1000;
2026-03-29 09:28:49 +08:00
} else { // Assume that pMax is in kHz
gpu->frequency = pMax / 1000;
2026-03-29 09:28:49 +08:00
}
}
return NULL;
}
#endif
2026-03-29 09:28:49 +08:00
const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) {
FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = IO_OBJECT_NULL;
2024-08-01 08:54:12 +08:00
{
CFMutableDictionaryRef matches = IOServiceMatching(kIOAcceleratorClassName);
CFDictionaryAddValue(matches, CFSTR("IOMatchCategory"), CFSTR(kIOAcceleratorClassName));
2026-03-29 09:28:49 +08:00
if (IOServiceGetMatchingServices(MACH_PORT_NULL, matches, &iterator) != kIOReturnSuccess) {
2024-08-01 08:54:12 +08:00
return "IOServiceGetMatchingServices() failed";
2026-03-29 09:28:49 +08:00
}
2024-08-01 08:54:12 +08:00
}
2022-09-05 13:41:05 -07:00
io_registry_entry_t registryEntry;
2026-03-29 09:28:49 +08:00
while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) {
2022-09-06 02:28:32 -07:00
CFMutableDictionaryRef properties;
2026-03-29 09:28:49 +08:00
if (IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) {
2022-09-14 14:55:28 +02:00
IOObjectRelease(registryEntry);
2022-09-06 02:28:32 -07:00
continue;
}
2022-09-05 13:41:05 -07:00
2026-04-14 10:41:18 +08:00
FFGPUResult* gpu = FF_LIST_ADD(FFGPUResult, *gpus);
2025-03-25 09:19:32 +08:00
gpu->index = FF_GPU_INDEX_UNSET;
ffStrbufInit(&gpu->memoryType);
2023-02-11 12:34:57 +08:00
gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET;
2023-01-12 11:41:07 +01:00
gpu->type = FF_GPU_TYPE_UNKNOWN;
gpu->frequency = FF_GPU_FREQUENCY_UNSET;
2026-06-14 17:36:54 +08:00
gpu->pcieSpeed = FF_GPU_PCIE_SPEED_UNSET;
IORegistryEntryGetRegistryEntryID(registryEntry, &gpu->deviceId);
ffStrbufInitStatic(&gpu->platformApi, "IOKit");
2023-01-12 11:41:07 +01:00
ffStrbufInit(&gpu->driver); // Ok for both Apple and Intel
2022-09-17 02:17:27 +08:00
ffCfDictGetString(properties, CFSTR("CFBundleIdentifier"), &gpu->driver);
2022-09-16 18:11:39 +08:00
2026-03-29 09:28:49 +08:00
if (ffCfDictGetInt(properties, CFSTR("gpu-core-count"), &gpu->coreCount) != NULL) { // For Apple
gpu->coreCount = FF_GPU_CORE_COUNT_UNSET;
2026-03-29 09:28:49 +08:00
}
2025-02-16 21:50:19 +08:00
gpu->coreUsage = FF_GPU_CORE_USAGE_UNSET;
2024-08-01 13:54:03 +08:00
CFDictionaryRef perfStatistics = NULL;
uint64_t vramUsed = 0, vramTotal = 0;
2026-03-29 09:28:49 +08:00
if (ffCfDictGetDict(properties, CFSTR("PerformanceStatistics"), &perfStatistics) == NULL) {
2024-08-01 13:54:03 +08:00
int64_t utilization;
2026-03-29 09:28:49 +08:00
if (ffCfDictGetInt64(perfStatistics, CFSTR("Device Utilization %"), &utilization) == NULL) {
2024-08-01 13:54:03 +08:00
gpu->coreUsage = (double) utilization;
2026-03-29 09:28:49 +08:00
} else if (ffCfDictGetInt64(perfStatistics, CFSTR("GPU Core Utilization"), &utilization) == NULL) {
2024-08-01 13:54:03 +08:00
gpu->coreUsage = (double) utilization / 10000000.; // Nvidia?
2026-03-29 09:28:49 +08:00
}
2024-08-01 13:54:03 +08:00
2026-03-29 09:28:49 +08:00
if (ffCfDictGetInt64(perfStatistics, CFSTR("Alloc system memory"), (int64_t*) &vramTotal) == NULL) {
if (ffCfDictGetInt64(perfStatistics, CFSTR("In use system memory"), (int64_t*) &vramUsed) != NULL) {
2024-08-01 13:54:03 +08:00
vramTotal = 0;
2026-03-29 09:28:49 +08:00
}
} else if (ffCfDictGetInt64(perfStatistics, CFSTR("vramFreeBytes"), (int64_t*) &vramTotal) == NULL) {
if (ffCfDictGetInt64(perfStatistics, CFSTR("vramUsedBytes"), (int64_t*) &vramUsed) == NULL) {
2024-08-01 13:54:03 +08:00
vramTotal += vramUsed;
2026-03-29 09:28:49 +08:00
} else {
2024-08-01 13:54:03 +08:00
vramTotal = 0;
2026-03-29 09:28:49 +08:00
}
2024-08-01 13:54:03 +08:00
}
}
2022-09-17 00:35:07 +08:00
ffStrbufInit(&gpu->name);
2026-03-29 09:28:49 +08:00
// IOAccelerator returns model / vendor-id properties for Apple Silicon, but not for Intel Iris GPUs.
// Still needs testing for AMD's
if (ffCfDictGetString(properties, CFSTR("model"), &gpu->name) != NULL) {
2022-09-17 02:59:07 +08:00
CFRelease(properties);
2025-03-25 09:19:32 +08:00
properties = NULL;
2022-09-17 02:59:07 +08:00
2025-03-25 09:19:32 +08:00
FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t parentEntry = 0;
2026-03-29 09:28:49 +08:00
if (IORegistryEntryGetParentEntry(registryEntry, kIOServicePlane, &parentEntry) != kIOReturnSuccess ||
IORegistryEntryCreateCFProperties(parentEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) {
2022-09-17 02:59:07 +08:00
IOObjectRelease(registryEntry);
continue;
}
ffCfDictGetString(properties, CFSTR("model"), &gpu->name);
}
2022-09-17 00:35:07 +08:00
ffStrbufInit(&gpu->vendor);
int vendorId;
2026-03-29 09:28:49 +08:00
if (ffCfDictGetInt(properties, CFSTR("vendor-id"), &vendorId) == NULL) {
const char* vendorStr = ffGPUGetVendorString((unsigned) vendorId);
ffStrbufAppendS(&gpu->vendor, vendorStr);
2026-03-29 09:28:49 +08:00
if (vendorStr == FF_GPU_VENDOR_NAME_APPLE || vendorStr == FF_GPU_VENDOR_NAME_INTEL) {
gpu->type = FF_GPU_TYPE_INTEGRATED;
2026-03-29 09:28:49 +08:00
} else if (vendorStr == FF_GPU_VENDOR_NAME_NVIDIA || vendorStr == FF_GPU_VENDOR_NAME_AMD) {
gpu->type = FF_GPU_TYPE_DISCRETE;
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
#ifdef __aarch64__
if (vendorStr == FF_GPU_VENDOR_NAME_APPLE) {
detectFrequency(gpu);
2026-03-29 09:28:49 +08:00
}
#endif
2024-08-01 13:54:03 +08:00
2026-03-29 09:28:49 +08:00
if (gpu->type == FF_GPU_TYPE_INTEGRATED) {
2024-08-01 13:54:03 +08:00
gpu->shared.total = vramTotal;
gpu->shared.used = vramUsed;
2026-03-29 09:28:49 +08:00
} else if (gpu->type == FF_GPU_TYPE_DISCRETE) {
2024-08-01 13:54:03 +08:00
gpu->dedicated.total = vramTotal;
gpu->dedicated.used = vramUsed;
}
}
2022-09-17 00:35:07 +08:00
2023-08-07 10:15:08 +08:00
gpu->temperature = options->temp ? detectGpuTemp(&gpu->name) : FF_GPU_TEMP_UNSET;
2022-09-06 02:28:32 -07:00
2022-09-14 14:55:28 +02:00
CFRelease(properties);
IOObjectRelease(registryEntry);
2022-09-05 13:41:05 -07:00
}
ffGpuDetectMetal(gpus);
2026-03-29 09:28:49 +08:00
if (instance.config.general.detectVersion) {
2024-09-26 16:43:54 +08:00
ffGpuDetectDriverVersion(gpus);
2026-03-29 09:28:49 +08:00
}
2022-09-16 18:11:39 +08:00
return NULL;
2022-09-05 17:42:42 +02:00
}