master ohmu/fastfetch-openbsd / src / detection / gpu / gpu_intel.c
ohmu  ·  2026-10-15
  1#include "gpu_driver_specific.h"
  2
  3#include "common/library.h"
  4#include "common/mallocHelper.h"
  5#include "igcl.h"
  6
  7struct FFIgclData {
  8    FF_LIBRARY_SYMBOL(ctlClose)
  9
 10    FF_LIBRARY_SYMBOL(ctlEnumerateDevices)
 11    FF_LIBRARY_SYMBOL(ctlGetDeviceProperties)
 12    FF_LIBRARY_SYMBOL(ctlEnumTemperatureSensors)
 13    FF_LIBRARY_SYMBOL(ctlTemperatureGetProperties)
 14    FF_LIBRARY_SYMBOL(ctlEnumMemoryModules)
 15    FF_LIBRARY_SYMBOL(ctlMemoryGetProperties)
 16    FF_LIBRARY_SYMBOL(ctlMemoryGetState)
 17    FF_LIBRARY_SYMBOL(ctlEnumFrequencyDomains)
 18    FF_LIBRARY_SYMBOL(ctlFrequencyGetProperties)
 19    FF_LIBRARY_SYMBOL(ctlPciGetProperties)
 20    FF_LIBRARY_SYMBOL(ctlPciGetState)
 21
 22    bool inited;
 23    ctl_api_handle_t apiHandle;
 24} igclData;
 25
 26static void shutdownIgcl() {
 27    if (igclData.apiHandle) {
 28        igclData.ffctlClose(igclData.apiHandle);
 29        igclData.apiHandle = nullptr;
 30    }
 31}
 32
 33const char* ffDetectIntelGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverResult result, const char* soName) {
 34    if (!igclData.inited) {
 35        igclData.inited = true;
 36        FF_LIBRARY_LOAD(libigcl, "dlopen igcl (ControlLib) failed", soName, 1);
 37        FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libigcl, ctlInit)
 38        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlClose)
 39        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumerateDevices)
 40        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlGetDeviceProperties)
 41        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumTemperatureSensors)
 42        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlTemperatureGetProperties)
 43        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumMemoryModules)
 44        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlMemoryGetProperties)
 45        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlMemoryGetState)
 46        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlEnumFrequencyDomains)
 47        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlFrequencyGetProperties)
 48        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlPciGetProperties)
 49        FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libigcl, igclData, ctlPciGetState)
 50
 51        if (ffctlInit(&(ctl_init_args_t) {
 52                          .AppVersion = CTL_IMPL_VERSION,
 53                          .flags = CTL_INIT_FLAG_USE_LEVEL_ZERO,
 54                          .Size = sizeof(ctl_init_args_t),
 55                          .Version = 0,
 56                      },
 57                &igclData.apiHandle) != CTL_RESULT_SUCCESS) {
 58            return "loading igcl library failed";
 59        }
 60        atexit(shutdownIgcl);
 61        libigcl = nullptr; // don't close igcl
 62    }
 63
 64    if (!igclData.apiHandle) {
 65        return "loading igcl library failed";
 66    }
 67
 68    uint32_t deviceCount = 0;
 69    // ctlEnumerateDevices MUST be called with deviceCount = 0 and devices = nullptr first,
 70    // otherwise it will be silently ignored and return CTL_RESULT_SUCCESS with deviceCount and devices unmodified.
 71    // VERY STRANGE BEHAVIOR
 72    if (igclData.ffctlEnumerateDevices(igclData.apiHandle, &deviceCount, nullptr) != CTL_RESULT_SUCCESS) {
 73        return "ctlEnumerateDevices(nullptr) failed";
 74    }
 75    if (deviceCount == 0) {
 76        return "No Intel graphics adapter found";
 77    }
 78
 79    FF_AUTO_FREE ctl_device_adapter_handle_t* devices = malloc(deviceCount * sizeof(*devices));
 80    if (igclData.ffctlEnumerateDevices(igclData.apiHandle, &deviceCount, devices) != CTL_RESULT_SUCCESS) {
 81        return "ctlEnumerateDevices(devices) failed";
 82    }
 83
 84    ctl_device_adapter_handle_t device = nullptr;
 85
 86    uint64_t /* LUID */ deviceId = 0;
 87    ctl_device_adapter_properties_t properties = {
 88        .Size = sizeof(properties),
 89        .pDeviceID = &deviceId,
 90        .device_id_size = sizeof(deviceId),
 91        .Version = 2,
 92    };
 93    for (uint32_t iDev = 0; iDev < deviceCount; iDev++) {
 94        if (igclData.ffctlGetDeviceProperties(devices[iDev], &properties) != CTL_RESULT_SUCCESS) {
 95            continue;
 96        }
 97
 98        if (properties.device_type != CTL_DEVICE_TYPE_GRAPHICS) {
 99            continue;
100        }
101
102        if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID) {
103            if (cond->pciBusId.bus == properties.adapter_bdf.bus &&
104                cond->pciBusId.device == properties.adapter_bdf.device &&
105                cond->pciBusId.func == properties.adapter_bdf.function) {
106                device = devices[iDev];
107                break;
108            }
109        } else if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_LUID) {
110            if (cond->luid == deviceId) {
111                device = devices[iDev];
112                break;
113            }
114        } else if (cond->type & FF_GPU_DRIVER_CONDITION_TYPE_DEVICE_ID) {
115            if (
116                cond->pciDeviceId.deviceId == properties.pci_device_id &&
117                cond->pciDeviceId.vendorId == properties.pci_vendor_id &&
118                cond->pciDeviceId.subSystemId == (uint32_t) ((properties.pci_subsys_id << 16u) | properties.pci_subsys_vendor_id) &&
119                cond->pciDeviceId.revId == properties.rev_id) {
120                device = devices[iDev];
121                break;
122            }
123        }
124    }
125
126    if (!device) {
127        return "Device not found";
128    }
129
130    if (result.coreCount) {
131        *result.coreCount = properties.num_slices * properties.num_sub_slices_per_slice * properties.num_eus_per_sub_slice;
132    }
133
134    if (result.memory) {
135        ctl_mem_handle_t memoryModules[16];
136        uint32_t memoryCount = ARRAY_SIZE(memoryModules);
137        if (igclData.ffctlEnumMemoryModules(device, &memoryCount, memoryModules) == CTL_RESULT_SUCCESS && memoryCount > 0) {
138            result.memory->used = 0;
139            result.memory->total = 0;
140            for (uint32_t iMem = 0; iMem < memoryCount; iMem++) {
141                ctl_mem_properties_t memoryProperties = {
142                    .Size = sizeof(memoryProperties),
143                    .Version = 0,
144                };
145                if (igclData.ffctlMemoryGetProperties(memoryModules[iMem], &memoryProperties) == CTL_RESULT_SUCCESS) {
146                    if (memoryProperties.location == CTL_MEM_LOC_DEVICE && result.memoryType) {
147                        switch (memoryProperties.type) {
148#define FF_ICTL_MEM_TYPE_CASE(type)                  \
149    case CTL_MEM_TYPE_##type:                        \
150        ffStrbufSetStatic(result.memoryType, #type); \
151        break
152                            FF_ICTL_MEM_TYPE_CASE(HBM);
153                            FF_ICTL_MEM_TYPE_CASE(DDR);
154                            FF_ICTL_MEM_TYPE_CASE(DDR3);
155                            FF_ICTL_MEM_TYPE_CASE(DDR4);
156                            FF_ICTL_MEM_TYPE_CASE(DDR5);
157                            FF_ICTL_MEM_TYPE_CASE(LPDDR);
158                            FF_ICTL_MEM_TYPE_CASE(LPDDR3);
159                            FF_ICTL_MEM_TYPE_CASE(LPDDR4);
160                            FF_ICTL_MEM_TYPE_CASE(LPDDR5);
161                            FF_ICTL_MEM_TYPE_CASE(GDDR4);
162                            FF_ICTL_MEM_TYPE_CASE(GDDR5);
163                            FF_ICTL_MEM_TYPE_CASE(GDDR5X);
164                            FF_ICTL_MEM_TYPE_CASE(GDDR6);
165                            FF_ICTL_MEM_TYPE_CASE(GDDR6X);
166                            FF_ICTL_MEM_TYPE_CASE(GDDR7);
167#undef FF_ICTL_MEM_TYPE_CASE
168                            default:
169                                ffStrbufSetF(result.memoryType, "Unknown (%u)", memoryProperties.type);
170                                break;
171                        }
172                    }
173
174                    ctl_mem_state_t memoryState = {
175                        .Size = sizeof(ctl_mem_state_t),
176                        .Version = 0,
177                    };
178                    if (igclData.ffctlMemoryGetState(memoryModules[iMem], &memoryState) == CTL_RESULT_SUCCESS) {
179                        if (memoryProperties.location == CTL_MEM_LOC_DEVICE) {
180                            result.memory->total += memoryState.size;
181                            result.memory->used += memoryState.size - memoryState.free;
182                        } else if (result.sharedMemory && memoryProperties.location == CTL_MEM_LOC_SYSTEM) {
183                            result.sharedMemory->total += memoryState.size;
184                            result.sharedMemory->used += memoryState.size - memoryState.free;
185                        }
186                    }
187                }
188            }
189        }
190    }
191
192    if (result.type) {
193        *result.type = properties.graphics_adapter_properties & CTL_ADAPTER_PROPERTIES_FLAG_INTEGRATED
194            ? FF_GPU_TYPE_INTEGRATED
195            : FF_GPU_TYPE_DISCRETE;
196    }
197
198    if (result.temp) {
199        ctl_temp_handle_t sensors[16];
200        uint32_t sensorCount = ARRAY_SIZE(sensors);
201        if (igclData.ffctlEnumTemperatureSensors(device, &sensorCount, sensors) == CTL_RESULT_SUCCESS && sensorCount > 0) {
202            for (uint32_t iSensor = 0; iSensor < sensorCount; iSensor++) {
203                ctl_temp_properties_t props = { .Size = sizeof(props) };
204                // The official sample code does not set Version
205                // https://github.com/intel/drivers.gpu.control-library/blob/1bbacbf3814f2fd0d2b930cdf42fad83f3628db9/Samples/Telemetry_Samples/Sample_TelemetryAPP.cpp#L256
206                if (igclData.ffctlTemperatureGetProperties(sensors[iSensor], &props) == CTL_RESULT_SUCCESS) {
207                    if (props.type == CTL_TEMP_SENSORS_GPU) {
208                        *result.temp = props.maxTemperature;
209                        break;
210                    }
211                }
212            }
213        }
214    }
215
216    if (result.frequency) {
217        ctl_freq_handle_t domains[16];
218        uint32_t domainCount = ARRAY_SIZE(domains);
219        if (igclData.ffctlEnumFrequencyDomains(device, &domainCount, domains) == CTL_RESULT_SUCCESS && domainCount > 0) {
220            double maxValue = 0;
221            ctl_freq_properties_t props = { .Size = sizeof(props), .Version = 0 };
222            for (uint32_t iDomain = 0; iDomain < domainCount; iDomain++) {
223                if (igclData.ffctlFrequencyGetProperties(domains[iDomain], &props) == CTL_RESULT_SUCCESS) {
224                    if (props.type == CTL_FREQ_DOMAIN_GPU && props.max > maxValue) {
225                        maxValue = props.max;
226                    }
227                }
228            }
229            *result.frequency = (uint32_t) (maxValue + 0.5);
230        }
231    }
232
233    if (result.name) {
234        ffStrbufSetS(result.name, properties.name);
235    }
236
237    if (result.psMax) {
238        ctl_pci_properties_t pciProps = { .Size = sizeof(pciProps), .Version = 0 };
239        if (igclData.ffctlPciGetProperties(device, &pciProps) == CTL_RESULT_SUCCESS) {
240            if (pciProps.maxSpeed.gen > 0) {
241                result.psMax->gen = (uint16_t) pciProps.maxSpeed.gen;
242            }
243            if (pciProps.maxSpeed.width > 0) {
244                result.psMax->lanes = (uint16_t) pciProps.maxSpeed.width;
245            }
246        }
247    }
248    if (result.psCurr) {
249        ctl_pci_state_t pciState = { .Size = sizeof(pciState), .Version = 0 };
250        if (igclData.ffctlPciGetState(device, &pciState) == CTL_RESULT_SUCCESS) {
251            if (pciState.speed.gen > 0) {
252                result.psCurr->gen = (uint16_t) pciState.speed.gen;
253            }
254            if (pciState.speed.width > 0) {
255                result.psCurr->lanes = (uint16_t) pciState.speed.width;
256            }
257        }
258    }
259
260    return nullptr;
261}