From f1185039038d14cf7b9489e486ce972d6d428e75 Mon Sep 17 00:00:00 2001 From: ferreo Date: Sat, 30 Nov 2024 19:44:28 +0000 Subject: [PATCH] Update GPU detection logic - Enhanced GPU detection functions to support multiple GPU vendors (NVIDIA, AMD, Intel) and improved memory reporting. --- .github/release-nest-v3 | 2 +- pikafetch/debian/changelog | 2 +- pikafetch/src/system/hardware/gpu.zig | 389 ++++++++++++++++++-------- 3 files changed, 274 insertions(+), 119 deletions(-) diff --git a/.github/release-nest-v3 b/.github/release-nest-v3 index d8263ee..e440e5c 100644 --- a/.github/release-nest-v3 +++ b/.github/release-nest-v3 @@ -1 +1 @@ -2 \ No newline at end of file +3 \ No newline at end of file diff --git a/pikafetch/debian/changelog b/pikafetch/debian/changelog index 2e28ae9..67ef038 100644 --- a/pikafetch/debian/changelog +++ b/pikafetch/debian/changelog @@ -1,4 +1,4 @@ -pikafetch (0.1.0-101pika2) pika; urgency=medium +pikafetch (0.1.0-101pika3) pika; urgency=medium * Initial release. diff --git a/pikafetch/src/system/hardware/gpu.zig b/pikafetch/src/system/hardware/gpu.zig index c1cdc3c..e4d4534 100644 --- a/pikafetch/src/system/hardware/gpu.zig +++ b/pikafetch/src/system/hardware/gpu.zig @@ -1,7 +1,62 @@ const std = @import("std"); +const mem = std.mem; +const fs = std.fs; +const ArrayList = std.ArrayList; +const allocPrint = std.fmt.allocPrint; -pub fn getGPUInfo(allocator: std.mem.Allocator) ![][]const u8 { - var gpus = std.ArrayList([]const u8).init(allocator); +const VendorId = struct { + const APPLE = 0x106b; + const AMD = 0x1002; + const AMD_2 = 0x1022; + const INTEL = 0x8086; + const INTEL_2 = 0x8087; + const INTEL_3 = 0x03e7; + const NVIDIA = 0x10de; + const NVIDIA_2 = 0x12d2; + const NVIDIA_3 = 0x0955; + const MTHREADS = 0x1ed5; + const QUALCOMM = 0x5143; + const MTK = 0x14c3; + const VMWARE = 0x15ad; + const REDHAT = 0x1af4; + const PARALLEL = 0x1ab8; + const MICROSOFT = 0x1414; + const ORACLE = 0x108e; +}; + +const GPUInfo = struct { + vendor: []const u8, + model: []const u8, + driver: ?[]const u8, + memory: ?u64, + + pub fn format(self: GPUInfo, allocator: mem.Allocator) ![]const u8 { + if (self.memory) |memm| { + if (self.driver) |drv| { + return allocPrint(allocator, "{s} {s} ({s}, {d}MB)", .{ + self.vendor, self.model, drv, memm, + }); + } else { + return allocPrint(allocator, "{s} {s} ({d}MB)", .{ + self.vendor, self.model, memm, + }); + } + } else { + if (self.driver) |drv| { + return allocPrint(allocator, "{s} {s} ({s})", .{ + self.vendor, self.model, drv, + }); + } else { + return allocPrint(allocator, "{s} {s}", .{ + self.vendor, self.model, + }); + } + } + } +}; + +pub fn getGPUInfo(allocator: mem.Allocator) ![][]const u8 { + var gpus = ArrayList([]const u8).init(allocator); errdefer { for (gpus.items) |gpu| { allocator.free(gpu); @@ -9,115 +64,55 @@ pub fn getGPUInfo(allocator: std.mem.Allocator) ![][]const u8 { gpus.deinit(); } - var card_num: u32 = 0; - while (true) : (card_num += 1) { - const card_path = try std.fmt.allocPrint(allocator, "/sys/class/drm/card{d}/device", .{card_num}); - defer allocator.free(card_path); + var dir = fs.openDirAbsolute("/sys/class/drm", .{ .iterate = true }) catch { + const unknown = try allocator.dupe(u8, "Unknown GPU"); + try gpus.append(unknown); + return gpus.toOwnedSlice(); + }; + defer dir.close(); - const vendor_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "{s}/vendor", .{card_path}), .{}) catch |err| switch (err) { - error.FileNotFound => break, // No more cards found - else => return err, - }; + var iter = dir.iterate(); + while (try iter.next()) |entry| { + if (!mem.startsWith(u8, entry.name, "card")) continue; + if (mem.indexOf(u8, entry.name, "-")) |_| continue; + + const vendor_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/vendor", .{entry.name}); + defer allocator.free(vendor_path); + + const vendor_file = fs.openFileAbsolute(vendor_path, .{}) catch continue; defer vendor_file.close(); - const device_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "{s}/device", .{card_path}), .{}) catch |err| switch (err) { - error.FileNotFound => continue, - else => return err, - }; - defer device_file.close(); - var vendor_buf: [32]u8 = undefined; - var device_buf: [32]u8 = undefined; + const vendor_len = vendor_file.readAll(&vendor_buf) catch continue; + const vendor_str = mem.trim(u8, vendor_buf[0..vendor_len], "\n\r \t0x"); - const vendor_bytes = try vendor_file.readAll(&vendor_buf); - const device_bytes = try device_file.readAll(&device_buf); + const vendor_id = std.fmt.parseInt(u32, vendor_str, 16) catch continue; - const vendor_str = std.mem.trim(u8, vendor_buf[0..vendor_bytes], "\n\r \t0x"); - const device_str = std.mem.trim(u8, device_buf[0..device_bytes], "\n\r \t0x"); - - const vendor_id = try std.fmt.parseInt(u32, vendor_str, 16); - const device_id = try std.fmt.parseInt(u32, device_str, 16); - - const modalias_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "/sys/class/drm/card{d}/device/modalias", .{card_num}), .{}) catch |err| switch (err) { - error.FileNotFound => null, - else => return err, - }; - defer if (modalias_file) |f| f.close(); - - const name_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "/sys/class/drm/card{d}/device/product", .{card_num}), .{}) catch |err| switch (err) { - error.FileNotFound => null, - else => return err, - }; - defer if (name_file) |f| f.close(); - - const model_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "/sys/class/drm/card{d}/device/model", .{card_num}), .{}) catch |err| switch (err) { - error.FileNotFound => null, - else => return err, - }; - defer if (model_file) |f| f.close(); - - const subsystem_file = std.fs.openFileAbsolute(try std.fmt.allocPrint(allocator, "/sys/class/drm/card{d}/device/subsystem_device", .{card_num}), .{}) catch |err| switch (err) { - error.FileNotFound => null, - else => return err, - }; - defer if (subsystem_file) |f| f.close(); - - var name_buf: [256]u8 = undefined; - var gpu_name: []const u8 = ""; - - if (name_file) |f| { - const name_bytes = try f.readAll(&name_buf); - const name = std.mem.trim(u8, name_buf[0..name_bytes], "\n\r \t"); - if (name.len > 0) { - gpu_name = name; - } - } - - if (gpu_name.len == 0) { - if (model_file) |f| { - const name_bytes = try f.readAll(&name_buf); - const name = std.mem.trim(u8, name_buf[0..name_bytes], "\n\r \t"); - if (name.len > 0) { - gpu_name = name; + switch (vendor_id) { + VendorId.NVIDIA, VendorId.NVIDIA_2, VendorId.NVIDIA_3 => { + if (try detectNvidiaGPU(allocator, entry.name)) |gpu| { + try gpus.append(gpu); + continue; } - } + }, + VendorId.AMD, VendorId.AMD_2 => { + if (try detectAMDGPU(allocator, entry.name)) |gpu| { + try gpus.append(gpu); + continue; + } + }, + VendorId.INTEL, VendorId.INTEL_2, VendorId.INTEL_3 => { + if (try detectIntelGPU(allocator, entry.name)) |gpu| { + try gpus.append(gpu); + continue; + } + }, + else => {}, } - // Declare and initialize the driver variable - var dir_handle = std.fs.openDirAbsolute(card_path, .{}) catch null; - const driver = if (dir_handle) |*dir| blk: { - defer dir.close(); - var path_buf: [256]u8 = undefined; - if (dir.readLink("driver", &path_buf)) |path| { - break :blk std.fs.path.basename(path); - } else |_| { - break :blk null; - } - } else null; - - const gpu_info = if (findPCIName(vendor_id, device_id)) |model| - try std.fmt.allocPrint(allocator, "{s} {s}", .{ - switch (vendor_id) { - 0x1002 => "AMD", - 0x10de => "NVIDIA", - 0x8086 => "Intel", - else => "Unknown", - }, - model, - }) - else - try std.fmt.allocPrint(allocator, "{s} GPU (0x{x:0>4}){s}", .{ - switch (vendor_id) { - 0x1002 => "AMD", - 0x10de => "NVIDIA", - 0x8086 => "Intel", - else => "Unknown", - }, - device_id, - if (driver) |d| try std.fmt.allocPrint(allocator, ", {s}", .{d}) else "", - }); - - try gpus.append(gpu_info); + if (try detectGenericGPU(allocator, entry.name, vendor_id)) |gpu| { + try gpus.append(gpu); + } } if (gpus.items.len == 0) { @@ -128,33 +123,193 @@ pub fn getGPUInfo(allocator: std.mem.Allocator) ![][]const u8 { return gpus.toOwnedSlice(); } -const pci_ids = @embedFile("../../data/pci.ids"); +fn detectNvidiaGPU(allocator: mem.Allocator, card_name: []const u8) !?[]const u8 { + const proc_path = try allocPrint(allocator, "/proc/driver/nvidia/gpus/{s}/information", .{card_name}); + defer allocator.free(proc_path); + + const info_file = fs.openFileAbsolute(proc_path, .{}) catch return null; + defer info_file.close(); + + var buf: [1024]u8 = undefined; + const bytes_read = info_file.readAll(&buf) catch return null; + const content = buf[0..bytes_read]; + + var model_name: ?[]const u8 = null; + var vbios_version: ?[]const u8 = null; + var memory_size: ?u64 = null; + + var lines = mem.split(u8, content, "\n"); + while (lines.next()) |line| { + if (mem.indexOf(u8, line, "Model:")) |_| { + const start = mem.indexOf(u8, line, ":").? + 1; + model_name = mem.trim(u8, line[start..], " \t\r\n"); + } else if (mem.indexOf(u8, line, "Video BIOS:")) |_| { + const start = mem.indexOf(u8, line, ":").? + 1; + vbios_version = mem.trim(u8, line[start..], " \t\r\n"); + } else if (mem.indexOf(u8, line, "Video Memory:")) |_| { + const start = mem.indexOf(u8, line, ":").? + 1; + const mem_str = mem.trim(u8, line[start..], " \t\r\nMB"); + memory_size = std.fmt.parseInt(u64, mem_str, 10) catch null; + } + } + + if (model_name) |model| { + const gpu_info = GPUInfo{ + .vendor = "NVIDIA", + .model = model, + .driver = vbios_version, + .memory = memory_size, + }; + const formatted = try gpu_info.format(allocator); + return formatted; + } + + return null; +} + +fn detectAMDGPU(allocator: mem.Allocator, card_name: []const u8) !?[]const u8 { + const product_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/product_name", .{card_name}); + defer allocator.free(product_path); + + var product_name: []const u8 = undefined; + if (fs.openFileAbsolute(product_path, .{})) |product_file| { + defer product_file.close(); + var product_buf: [256]u8 = undefined; + const product_len = product_file.readAll(&product_buf) catch return null; + product_name = mem.trim(u8, product_buf[0..product_len], "\n\r \t"); + } else |_| { + return null; + } + + const driver_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/driver", .{card_name}); + defer allocator.free(driver_path); + + const driver = blk: { + var dir_handle = fs.openDirAbsolute(driver_path, .{}) catch break :blk null; + defer dir_handle.close(); + + var path_buf: [256]u8 = undefined; + const path = dir_handle.readLink("driver", &path_buf) catch break :blk null; + break :blk std.fs.path.basename(path); + }; + + const gpu_info = GPUInfo{ + .vendor = "AMD", + .model = product_name, + .driver = driver, + .memory = null, + }; + + const formatted = try gpu_info.format(allocator); + return formatted; +} + +fn detectIntelGPU(allocator: mem.Allocator, card_name: []const u8) !?[]const u8 { + const product_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/product_name", .{card_name}); + defer allocator.free(product_path); + + var product_name: []const u8 = undefined; + if (fs.openFileAbsolute(product_path, .{})) |product_file| { + defer product_file.close(); + var product_buf: [256]u8 = undefined; + const product_len = product_file.readAll(&product_buf) catch return null; + product_name = mem.trim(u8, product_buf[0..product_len], "\n\r \t"); + } else |_| { + return null; + } + + const driver_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/driver", .{card_name}); + defer allocator.free(driver_path); + + const driver = blk: { + var dir_handle = fs.openDirAbsolute(driver_path, .{}) catch break :blk null; + defer dir_handle.close(); + + var path_buf: [256]u8 = undefined; + const path = dir_handle.readLink("driver", &path_buf) catch break :blk null; + break :blk std.fs.path.basename(path); + }; + + const gpu_info = GPUInfo{ + .vendor = "Intel", + .model = product_name, + .driver = driver, + .memory = null, + }; + + const formatted = try gpu_info.format(allocator); + return formatted; +} fn findPCIName(vendor_id: u32, device_id: u32) ?[]const u8 { - var lines = std.mem.splitSequence(u8, pci_ids, "\n"); + const pci_data = @embedFile("../../data/pci.ids"); + var lines = mem.split(u8, pci_data, "\n"); var current_vendor: ?u32 = null; while (lines.next()) |line| { - if (line.len == 0 or line[0] == '#') continue; + const trimmed = mem.trim(u8, line, " \t\r"); + if (trimmed.len == 0 or trimmed[0] == '#') continue; - if (line[0] != '\t') { - if (line.len < 4) continue; - const vid = std.fmt.parseInt(u32, line[0..4], 16) catch continue; - current_vendor = vid; + // Check if this is a vendor line (no tabs at start) + if (trimmed[0] != '\t') { + // Parse vendor ID + const space_idx = mem.indexOf(u8, trimmed, " ") orelse continue; + const vendor_str = trimmed[0..space_idx]; + current_vendor = std.fmt.parseInt(u32, vendor_str, 16) catch continue; continue; } - if (current_vendor) |vid| { - if (vid != vendor_id) continue; + // If we found our vendor, look for the device + if (current_vendor) |vendor| { + if (vendor != vendor_id) continue; - if (line[0] == '\t' and line[1] != '\t') { - if (line.len < 5) continue; - const did = std.fmt.parseInt(u32, std.mem.trim(u8, line[1..5], " "), 16) catch continue; - if (did == device_id) { - return std.mem.trim(u8, line[6..], " \t"); - } + // Device lines have one tab + if (trimmed[0] != '\t' or trimmed.len < 2) continue; + const device_line = mem.trim(u8, trimmed[1..], " \t"); + + const space_idx = mem.indexOf(u8, device_line, " ") orelse continue; + const device_str = device_line[0..space_idx]; + const found_device = std.fmt.parseInt(u32, device_str, 16) catch continue; + + if (found_device == device_id) { + return device_line[space_idx + 1 ..]; } } } + return null; } + +fn detectGenericGPU(allocator: mem.Allocator, card_name: []const u8, vendor_id: u32) !?[]const u8 { + const device_path = try allocPrint(allocator, "/sys/class/drm/{s}/device/device", .{card_name}); + defer allocator.free(device_path); + + const device_file = fs.openFileAbsolute(device_path, .{}) catch return null; + defer device_file.close(); + + var device_buf: [32]u8 = undefined; + const device_len = device_file.readAll(&device_buf) catch return null; + const device_str = mem.trim(u8, device_buf[0..device_len], "\n\r \t0x"); + + const device_id = std.fmt.parseInt(u32, device_str, 16) catch return null; + + const vendor_name = switch (vendor_id) { + VendorId.AMD, VendorId.AMD_2 => "AMD", + VendorId.INTEL, VendorId.INTEL_2, VendorId.INTEL_3 => "Intel", + VendorId.NVIDIA, VendorId.NVIDIA_2, VendorId.NVIDIA_3 => "NVIDIA", + else => "Unknown", + }; + + const gpu_info = GPUInfo{ + .vendor = vendor_name, + .model = if (findPCIName(vendor_id, device_id)) |name| + name + else + try allocPrint(allocator, "GPU (0x{x:0>4})", .{device_id}), + .driver = null, + .memory = null, + }; + + const formatted = try gpu_info.format(allocator); + return formatted; +}