From f8cbe4c0082139ce8e50622d97a7d59040e6c63c Mon Sep 17 00:00:00 2001 From: Carter Li Date: Thu, 18 Apr 2024 16:41:19 +0800 Subject: [PATCH] GPU (Linux): improve detection performance of Apple Silicon --- src/detection/cpu/cpu.c | 21 +++ src/detection/cpu/cpu.h | 1 + src/detection/cpu/cpu_linux.c | 19 +-- src/detection/gpu/gpu_linux.c | 269 +++++++++++++++++++++------------- 4 files changed, 192 insertions(+), 118 deletions(-) diff --git a/src/detection/cpu/cpu.c b/src/detection/cpu/cpu.c index 5edc2f434..68d23ac88 100644 --- a/src/detection/cpu/cpu.c +++ b/src/detection/cpu/cpu.c @@ -18,3 +18,24 @@ const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu) ffStrbufTrimRight(&cpu->name, ' '); //If we removed the @ in previous step there was most likely a space before it return NULL; } + +const char* ffCPUAppleCodeToName(uint32_t code) +{ + // https://github.com/AsahiLinux/docs/wiki/Codenames + switch (code) + { + case 8103: return "Apple M1"; + case 6000: return "Apple M1 Pro"; + case 6001: return "Apple M1 Max"; + case 6002: return "Apple M1 Ultra"; + case 8112: return "Apple M2"; + case 6020: return "Apple M2 Pro"; + case 6021: return "Apple M2 Max"; + case 6022: return "Apple M2 Ultra"; + case 8122: return "Apple M3"; + case 6030: return "Apple M3 Pro"; + case 6031: + case 6034: return "Apple M3 Max"; + default: return "Apple Silicon"; + } +} diff --git a/src/detection/cpu/cpu.h b/src/detection/cpu/cpu.h index 628698e4b..bba2aba47 100644 --- a/src/detection/cpu/cpu.h +++ b/src/detection/cpu/cpu.h @@ -22,3 +22,4 @@ typedef struct FFCPUResult const char* ffCPUDetectByCpuid(FFCPUResult* cpu); const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu); +const char* ffCPUAppleCodeToName(uint32_t code); diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index acce51ca8..e100eb60c 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -210,23 +210,8 @@ void detectAsahi(FFCPUResult* cpu) char* modelName = memchr(content, '\0', (size_t) length) + 1; if (modelName - content < length && ffStrStartsWith(modelName, "apple,t")) { - // https://github.com/AsahiLinux/docs/wiki/Codenames - switch (strtoul(modelName + strlen("apple,t"), NULL, 10)) - { - case 8103: ffStrbufSetStatic(&cpu->name, "Apple M1"); break; - case 6000: ffStrbufSetStatic(&cpu->name, "Apple M1 Pro"); break; - case 6001: ffStrbufSetStatic(&cpu->name, "Apple M1 Max"); break; - case 6002: ffStrbufSetStatic(&cpu->name, "Apple M1 Ultra"); break; - case 8112: ffStrbufSetStatic(&cpu->name, "Apple M2"); break; - case 6020: ffStrbufSetStatic(&cpu->name, "Apple M2 Pro"); break; - case 6021: ffStrbufSetStatic(&cpu->name, "Apple M2 Max"); break; - case 6022: ffStrbufSetStatic(&cpu->name, "Apple M2 Ultra"); break; - case 8122: ffStrbufSetStatic(&cpu->name, "Apple M3"); break; - case 6030: ffStrbufSetStatic(&cpu->name, "Apple M3 Pro"); break; - case 6031: - case 6034: ffStrbufSetStatic(&cpu->name, "Apple M3 Max"); break; - default: ffStrbufSetStatic(&cpu->name, "Apple Silicon"); break; - } + uint32_t deviceId = (uint32_t) strtoul(modelName + strlen("apple,t"), NULL, 10); + ffStrbufSetStatic(&cpu->name, ffCPUAppleCodeToName(deviceId)); } } } diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 27a7d64a0..cba6aa3f8 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -1,6 +1,7 @@ #include "detection/gpu/gpu.h" #include "detection/vulkan/vulkan.h" #include "detection/temps/temps_linux.h" +#include "detection/cpu/cpu.h" #include "common/io/io.h" #include "common/properties.h" #include "util/stringUtils.h" @@ -57,6 +58,7 @@ static void pciDetectDriver(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer static void pciDetectVmem(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) { + // Works for AMD GPUs // https://www.kernel.org/doc/html/v5.10/gpu/amdgpu.html#mem-info-vis-vram-total ffStrbufAppendS(pciDir, "/mem_info_vis_vram_total"); uint64_t size = 0; @@ -80,6 +82,21 @@ static void pciDetectVmem(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) } } +static void pciDetectVfreq(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* buffer) +{ + // Works for Intel GPUs + // https://patchwork.kernel.org/project/intel-gfx/patch/1422039866-11572-3-git-send-email-ville.syrjala@linux.intel.com/ + ffStrbufSetNS(buffer, ffStrbufLastIndexC(pciDir, '/'), pciDir->chars); + ffStrbufAppendS(buffer, "/gt_cur_freq_mhz"); + char str[16]; + ssize_t len = ffReadFileData(buffer->chars, sizeof(str) - 1, str); + if (len > 1) + { + str[len] = '\0'; + gpu->frequency = (double) strtoul(str, NULL, 10) / 1000.0; + } +} + static bool loadPciIds(FFstrbuf* pciids) { #ifdef FF_CUSTOM_PCI_IDS_PATH @@ -99,121 +116,171 @@ static bool loadPciIds(FFstrbuf* pciids) return false; } -static const char* pciDetectGPUs(const FFGPUOptions* options, FFlist* gpus) +static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir) { - //https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci - const char* pciDirPath = "/sys/bus/pci/devices/"; + const uint32_t drmDirPathLength = drmDir->length; + uint32_t vendorId, deviceId, subVendorId, subDeviceId; + uint8_t classId, subclassId; + if (sscanf(buffer->chars + strlen("pci:"), "v%8" SCNx32 "d%8" SCNx32 "sv%8" SCNx32 "sd%8" SCNx32 "bc%2" SCNx8 "sc%2" SCNx8, &vendorId, &deviceId, &subVendorId, &subDeviceId, &classId, &subclassId) != 6) + return "Invalid modalias string"; - FF_AUTO_CLOSE_DIR DIR* dirp = opendir(pciDirPath); - if(dirp == NULL) - return "Failed to open `/sys/bus/pci/devices/`"; + if (classId != 0x03 /*PCI_BASE_CLASS_DISPLAY*/) + return "Should not happen"; - FF_STRBUF_AUTO_DESTROY pciDir = ffStrbufCreateA(64); - ffStrbufAppendS(&pciDir, pciDirPath); + char pciPath[PATH_MAX]; + ssize_t pathLength = readlink(drmDir->chars, pciPath, sizeof(pciPath) - 1); + if (pathLength <= 0) + return "Unable to get PCI device path"; + pciPath[pathLength] = '\0'; + const char* pPciPath = strrchr(pciPath, '/'); + if (pPciPath) + pPciPath++; + else + pPciPath = pciPath; - const uint32_t pciBaseDirLength = pciDir.length; + uint32_t pciDomain, pciBus, pciDevice, pciFunc; + if (sscanf(pPciPath, "%" SCNx32 ":%" SCNx32 ":%" SCNx32 ".%" SCNx32, &pciDomain, &pciBus, &pciDevice, &pciFunc) != 4) + return "Invalid PCI device path"; + + FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); + ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString((uint16_t) vendorId)); + ffStrbufInit(&gpu->name); + ffStrbufInit(&gpu->driver); + ffStrbufInit(&gpu->platformApi); + gpu->temperature = FF_GPU_TEMP_UNSET; + gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->type = FF_GPU_TYPE_UNKNOWN; + gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; + gpu->deviceId = ((uint64_t) pciDomain << 6) | ((uint64_t) pciBus << 4) | (deviceId << 2) | pciFunc; + gpu->frequency = FF_GPU_FREQUENCY_UNSET; + + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) + { + ffStrbufAppendS(drmDir, "/revision"); + if (ffReadFileBuffer(drmDir->chars, buffer)) + { + char* pend; + uint64_t revision = strtoul(buffer->chars, &pend, 16); + if (pend != buffer->chars) + { + char query[32]; + snprintf(query, sizeof(query), "%X,\t%X,", (unsigned) deviceId, (unsigned) revision); + ffParsePropFileData("libdrm/amdgpu.ids", query, &gpu->name); + } + } + ffStrbufSubstrBefore(drmDir, drmDirPathLength); + } + + if (gpu->name.length == 0) + { + static FFstrbuf pciids; + if (pciids.chars == NULL) + { + ffStrbufInit(&pciids); + loadPciIds(&pciids); + } + if (pciids.length) + ffGPUParsePciIds(&pciids, subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); + } + + pciDetectVmem(gpu, drmDir, buffer); + ffStrbufSubstrBefore(drmDir, drmDirPathLength); + + pciDetectVfreq(gpu, drmDir, buffer); + ffStrbufSubstrBefore(drmDir, drmDirPathLength); + + pciDetectDriver(gpu, drmDir, buffer); + ffStrbufSubstrBefore(drmDir, drmDirPathLength); + + #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && (options->temp || options->driverSpecific)) + { + ffDetectNvidiaGpuInfo(&(FFGpuDriverCondition) { + .type = FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID, + .pciBusId = { + .domain = pciDomain, + .bus = pciBus, + .device = pciDevice, + .func = pciFunc, + }, + }, (FFGpuDriverResult) { + .temp = options->temp ? &gpu->temperature : NULL, + .memory = options->driverSpecific ? &gpu->dedicated : NULL, + .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, + .type = &gpu->type, + .frequency = &gpu->frequency, + }, "libnvidia-ml.so"); + + if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) + gpu->type = gpu->dedicated.total > (uint64_t)1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; + } + #endif // FF_USE_PROPRIETARY_GPU_DRIVER_API + + #ifdef __linux__ + if(options->temp && gpu->temperature != gpu->temperature) + pciDetectTemp(gpu, ((uint32_t) classId << 8) + subclassId); + #endif + + return NULL; +} + +FF_MAYBE_UNUSED static const char* detectAsahi(FFlist* gpus, FFstrbuf* buffer, FFstrbuf* drmDir) +{ + uint32_t index = ffStrbufFirstIndexS(buffer, "apple,agx-t"); + if (index == buffer->length) return "display-subsystem?"; + index += strlen("apple,agx-t"); + + FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); + gpu->deviceId = strtoul(buffer->chars + index, NULL, 10); + ffStrbufInitStatic(&gpu->name, ffCPUAppleCodeToName(gpu->deviceId)); + ffStrbufInitStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE); + ffStrbufInit(&gpu->driver); + ffStrbufInit(&gpu->platformApi); + gpu->temperature = FF_GPU_TEMP_UNSET; + gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + gpu->type = FF_GPU_TYPE_INTEGRATED; + gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; + gpu->frequency = FF_GPU_FREQUENCY_UNSET; + + pciDetectDriver(gpu, drmDir, buffer); + + return NULL; +} + +static const char* drmDetectGPUs(const FFGPUOptions* options, FFlist* gpus) +{ + FF_STRBUF_AUTO_DESTROY drmDir = ffStrbufCreateA(64); + ffStrbufAppendS(&drmDir, "/sys/class/drm/"); + const uint32_t drmDirLength = drmDir.length; + + FF_AUTO_CLOSE_DIR DIR* dir = opendir(drmDir.chars); + if(dir == NULL) + return "/sys/class/drm doesn't exist"; FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); - FF_STRBUF_AUTO_DESTROY pciids = ffStrbufCreate(); struct dirent* entry; - while((entry = readdir(dirp)) != NULL) + while ((entry = readdir(dir)) != NULL) { - if(entry->d_name[0] == '.') + if (!ffStrStartsWith(entry->d_name, "card") || + strchr(entry->d_name + 4, '-') != NULL) continue; - ffStrbufSubstrBefore(&pciDir, pciBaseDirLength); - ffStrbufAppendS(&pciDir, entry->d_name); + ffStrbufAppendS(&drmDir, entry->d_name); - const uint32_t pciDevDirLength = pciDir.length; - - ffStrbufAppendS(&pciDir, "/modalias"); - if (!ffReadFileBuffer(pciDir.chars, &buffer)) + ffStrbufAppendS(&drmDir, "/device/modalias"); + if (!ffReadFileBuffer(drmDir.chars, &buffer)) continue; - ffStrbufSubstrBefore(&pciDir, pciDevDirLength); + ffStrbufSubstrBefore(&drmDir, drmDir.length - (uint32_t) strlen("/modalias")); - uint32_t vendorId, deviceId, subVendorId, subDeviceId; - uint8_t classId, subclassId; - if (sscanf(buffer.chars, "pci:v%8" SCNx32 "d%8" SCNx32 "sv%8" SCNx32 "sd%8" SCNx32 "bc%2" SCNx8 "sc%2" SCNx8, &vendorId, &deviceId, &subVendorId, &subDeviceId, &classId, &subclassId) != 6) - continue; - - if (classId != 0x03 /*PCI_BASE_CLASS_DISPLAY*/) - continue; - - uint32_t pciDomain, pciBus, pciDevice, pciFunc; - if (sscanf(entry->d_name, "%" SCNx32 ":%" SCNx32 ":%" SCNx32 ".%" SCNx32, &pciDomain, &pciBus, &pciDevice, &pciFunc) != 4) - continue; - - FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); - ffStrbufInitStatic(&gpu->vendor, ffGetGPUVendorString((uint16_t) vendorId)); - ffStrbufInit(&gpu->name); - ffStrbufInit(&gpu->driver); - ffStrbufInit(&gpu->platformApi); - gpu->temperature = FF_GPU_TEMP_UNSET; - gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; - gpu->type = FF_GPU_TYPE_UNKNOWN; - gpu->dedicated.total = gpu->dedicated.used = gpu->shared.total = gpu->shared.used = FF_GPU_VMEM_SIZE_UNSET; - gpu->deviceId = ((uint64_t) pciDomain << 6) | ((uint64_t) pciBus << 4) | (deviceId << 2) | pciFunc; - gpu->frequency = FF_GPU_FREQUENCY_UNSET; - - if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_AMD) - { - ffStrbufAppendS(&pciDir, "/revision"); - if (ffReadFileBuffer(pciDir.chars, &buffer)) - { - char* pend; - uint64_t revision = strtoul(buffer.chars, &pend, 16); - if (pend != buffer.chars) - { - char query[32]; - snprintf(query, sizeof(query), "%X,\t%X,", (unsigned) deviceId, (unsigned) revision); - ffParsePropFileData("libdrm/amdgpu.ids", query, &gpu->name); - } - } - ffStrbufSubstrBefore(&pciDir, pciDevDirLength); - } - - if (gpu->name.length == 0) - { - if (!pciids.length) - loadPciIds(&pciids); - ffGPUParsePciIds(&pciids, subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); - } - - pciDetectVmem(gpu, &pciDir, &buffer); - ffStrbufSubstrBefore(&pciDir, pciDevDirLength); - - pciDetectDriver(gpu, &pciDir, &buffer); - ffStrbufSubstrBefore(&pciDir, pciDevDirLength); - - #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API - if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && (options->temp || options->driverSpecific)) - { - ffDetectNvidiaGpuInfo(&(FFGpuDriverCondition) { - .type = FF_GPU_DRIVER_CONDITION_TYPE_BUS_ID, - .pciBusId = { - .domain = pciDomain, - .bus = pciBus, - .device = pciDevice, - .func = pciFunc, - }, - }, (FFGpuDriverResult) { - .temp = options->temp ? &gpu->temperature : NULL, - .memory = options->driverSpecific ? &gpu->dedicated : NULL, - .coreCount = options->driverSpecific ? (uint32_t*) &gpu->coreCount : NULL, - .type = &gpu->type, - .frequency = &gpu->frequency, - }, "libnvidia-ml.so"); - - if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) - gpu->type = gpu->dedicated.total > (uint64_t)1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; - } - #endif // FF_USE_PROPRIETARY_GPU_DRIVER_API - - #ifdef __linux__ - if(options->temp && gpu->temperature != gpu->temperature) - pciDetectTemp(gpu, ((uint32_t) classId << 8) + subclassId); + if (ffStrbufStartsWithS(&buffer, "pci:")) + detectPci(options, gpus, &buffer, &drmDir); + #ifdef __aarch64__ + else if (ffStrbufStartsWithS(&buffer, "of:")) + detectAsahi(gpus, &buffer, &drmDir); #endif + + ffStrbufSubstrBefore(&drmDir, drmDirLength); } return NULL; @@ -227,5 +294,5 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) return NULL; #endif - return pciDetectGPUs(options, gpus); + return drmDetectGPUs(options, gpus); }