mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2026-08-17 16:34:54 -05:00
feat: fmt gpu name in gpu-picker (#1157)
* feat: fmt gpu name in gpu-picker * move implementation to daemon * move to schema * add Radeon/Geforce prefix back * remove from info_elements
This commit is contained in:
@@ -265,7 +265,7 @@ There is also a cli available.
|
|||||||
Example output:
|
Example output:
|
||||||
|
|
||||||
```
|
```
|
||||||
10DE:2704-1462:5110-0000:09:00.0 (AD103 [GeForce RTX 4080])
|
0: 10DE:2704-1462:5110-0000:09:00.0 (GeForce RTX 4080) [Dedicated]
|
||||||
```
|
```
|
||||||
- Getting GPU information:
|
- Getting GPU information:
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ There is also a cli available.
|
|||||||
$ lact cli info
|
$ lact cli info
|
||||||
GPU 10DE:2704-1462:5110-0000:09:00.0:
|
GPU 10DE:2704-1462:5110-0000:09:00.0:
|
||||||
=====================================
|
=====================================
|
||||||
GPU Model: NVIDIA GeForce RTX 4080 (0x10DE:0x2704)
|
GPU Model: GeForce RTX 4080 (0x10DE:0x2704)
|
||||||
Card Manufacturer: Micro-Star International Co., Ltd. [MSI] (0x1462)
|
Card Manufacturer: Micro-Star International Co., Ltd. [MSI] (0x1462)
|
||||||
Card Model: Unknown (0x5110)
|
Card Model: Unknown (0x5110)
|
||||||
Driver Used: nvidia 570.124.04
|
Driver Used: nvidia 570.124.04
|
||||||
|
|||||||
+8
-2
@@ -8,6 +8,7 @@ use anyhow::{Context, Result, bail};
|
|||||||
use lact_client::DaemonClient;
|
use lact_client::DaemonClient;
|
||||||
use lact_schema::{
|
use lact_schema::{
|
||||||
args::cli::{CliArgs, CliCommand, ProfileAutoSwitchCommand, ProfileCommand},
|
args::cli::{CliArgs, CliCommand, ProfileAutoSwitchCommand, ProfileCommand},
|
||||||
|
clean_gpu_name,
|
||||||
config::GpuConfig,
|
config::GpuConfig,
|
||||||
request::ConfirmCommand,
|
request::ConfirmCommand,
|
||||||
};
|
};
|
||||||
@@ -100,7 +101,11 @@ impl CliContext<'_> {
|
|||||||
if entries.len() > 1 {
|
if entries.len() > 1 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"GPU id not specified, selecting {}",
|
"GPU id not specified, selecting {}",
|
||||||
first_entry.name.as_deref().unwrap_or("<Unknown>")
|
first_entry
|
||||||
|
.name
|
||||||
|
.as_deref()
|
||||||
|
.map(clean_gpu_name)
|
||||||
|
.unwrap_or("<Unknown>")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(first_entry.id.clone())
|
Ok(first_entry.id.clone())
|
||||||
@@ -141,6 +146,7 @@ impl CliContext<'_> {
|
|||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find(|entry| entry.id == gpu_id)
|
.find(|entry| entry.id == gpu_id)
|
||||||
.and_then(|entry| entry.name))
|
.and_then(|entry| entry.name)
|
||||||
|
.map(|name| clean_gpu_name(&name).to_owned()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,10 @@ const PROFILE_DEFAULT: &str = "Default";
|
|||||||
pub async fn list_gpus(ctx: CliContext<'_>) -> Result<()> {
|
pub async fn list_gpus(ctx: CliContext<'_>) -> Result<()> {
|
||||||
let entries = ctx.client.list_devices().await?;
|
let entries = ctx.client.list_devices().await?;
|
||||||
for (i, entry) in entries.into_iter().enumerate() {
|
for (i, entry) in entries.into_iter().enumerate() {
|
||||||
let id = entry.id;
|
if entry.name.is_some() {
|
||||||
let device_type = entry.device_type;
|
println!("{i}: {} ({entry}) [{}]", entry.id, entry.device_type);
|
||||||
|
|
||||||
if let Some(name) = entry.name {
|
|
||||||
println!("{i}: {id} ({name}) [{device_type}]");
|
|
||||||
} else {
|
} else {
|
||||||
println!("{i}: {id} [{device_type}]");
|
println!("{i}: {} [{}]", entry.id, entry.device_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use jiff::Zoned;
|
use jiff::Zoned;
|
||||||
use lact_schema::DeviceStats;
|
use lact_schema::{DeviceStats, clean_gpu_name};
|
||||||
use schema::{
|
use schema::{
|
||||||
Attribute, Gauge, GaugeDataPoint, Metric, MetricsPayload, Resource, ResourceMetric, Scope,
|
Attribute, Gauge, GaugeDataPoint, Metric, MetricsPayload, Resource, ResourceMetric, Scope,
|
||||||
ScopeMetric, Value,
|
ScopeMetric, Value,
|
||||||
@@ -335,7 +335,15 @@ async fn get_stats(handler: &Handler) -> anyhow::Result<IndexMap<String, (String
|
|||||||
for device in device_list {
|
for device in device_list {
|
||||||
let stats = handler.get_gpu_stats(&device.id).await?;
|
let stats = handler.get_gpu_stats(&device.id).await?;
|
||||||
|
|
||||||
devices.insert(device.id, (device.name.unwrap_or_default(), stats));
|
devices.insert(
|
||||||
|
device.id,
|
||||||
|
(
|
||||||
|
device
|
||||||
|
.name
|
||||||
|
.map_or_else(String::new, |name| clean_gpu_name(&name).to_owned()),
|
||||||
|
stats,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(devices)
|
Ok(devices)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use crate::app::{
|
|||||||
use gtk::pango::AttrList;
|
use gtk::pango::AttrList;
|
||||||
use gtk::prelude::{BoxExt, OrientableExt, PopoverExt as _, WidgetExt};
|
use gtk::prelude::{BoxExt, OrientableExt, PopoverExt as _, WidgetExt};
|
||||||
use i18n_embed_fl::fl;
|
use i18n_embed_fl::fl;
|
||||||
use lact_schema::{DeviceInfo, DeviceStats, PowerStates, PowerStats};
|
use lact_schema::{DeviceInfo, DeviceStats, PowerStates, PowerStats, clean_gpu_name};
|
||||||
use relm4::{ComponentParts, ComponentSender, RelmWidgetExt as _};
|
use relm4::{ComponentParts, ComponentSender, RelmWidgetExt as _};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::str::FromStr as _;
|
use std::str::FromStr as _;
|
||||||
@@ -360,13 +360,14 @@ impl relm4::SimpleComponent for GpuStatsSection {
|
|||||||
GpuStatsSectionMsg::Info(info) => {
|
GpuStatsSectionMsg::Info(info) => {
|
||||||
self.vram_clock_ratio = info.vram_clock_ratio();
|
self.vram_clock_ratio = info.vram_clock_ratio();
|
||||||
if let Some(pci_info) = &info.pci_info {
|
if let Some(pci_info) = &info.pci_info {
|
||||||
self.gpu_model = info
|
self.gpu_model = clean_gpu_name(
|
||||||
.drm_info
|
info.drm_info
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|drm| drm.device_name.as_deref())
|
.and_then(|drm| drm.device_name.as_deref())
|
||||||
.or(pci_info.device_pci_info.model.as_deref())
|
.or(pci_info.device_pci_info.model.as_deref())
|
||||||
.unwrap_or("Unknown")
|
.unwrap_or("Unknown"),
|
||||||
.to_owned();
|
)
|
||||||
|
.to_owned();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GpuStatsSectionMsg::Stats(stats) => {
|
GpuStatsSectionMsg::Stats(stats) => {
|
||||||
|
|||||||
+28
-1
@@ -67,6 +67,33 @@ pub fn bytes_to_mib(bytes: u64) -> f64 {
|
|||||||
bytes as f64 / 1024.0 / 1024.0
|
bytes as f64 / 1024.0 / 1024.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GPU_VENDOR_PREFIXES: &[&str] = &["AMD ", "NVIDIA ", "Intel "];
|
||||||
|
|
||||||
|
pub fn clean_gpu_name(name: &str) -> &str {
|
||||||
|
let mut short = name.trim();
|
||||||
|
|
||||||
|
if let Some(marketing_name) = GPU_VENDOR_PREFIXES
|
||||||
|
.iter()
|
||||||
|
.find_map(|&prefix| short.strip_prefix(prefix))
|
||||||
|
{
|
||||||
|
short = marketing_name
|
||||||
|
.split_once('[')
|
||||||
|
.map_or(marketing_name, |(model, _)| model.trim_end());
|
||||||
|
} else if let Some((_, bracketed)) = short.split_once('[')
|
||||||
|
&& let Some((model, _)) = bracketed.split_once(']')
|
||||||
|
{
|
||||||
|
// PCI device names carry the marketing name in brackets, e.g. "DG2 [Arc A770]"
|
||||||
|
short = model.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
short = GPU_VENDOR_PREFIXES
|
||||||
|
.iter()
|
||||||
|
.find_map(|&prefix| short.strip_prefix(prefix))
|
||||||
|
.unwrap_or(short);
|
||||||
|
|
||||||
|
short
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Pong;
|
pub struct Pong;
|
||||||
|
|
||||||
@@ -121,7 +148,7 @@ pub struct DeviceListEntry {
|
|||||||
impl Display for DeviceListEntry {
|
impl Display for DeviceListEntry {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match &self.name {
|
match &self.name {
|
||||||
Some(name) => Display::fmt(name, f),
|
Some(name) => Display::fmt(clean_gpu_name(name), f),
|
||||||
None => Display::fmt(&self.id, f),
|
None => Display::fmt(&self.id, f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{FanControlMode, FanOptions, PmfwOptions, Pong, Request, Response};
|
use crate::{FanControlMode, FanOptions, PmfwOptions, Pong, Request, Response, clean_gpu_name};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@@ -86,3 +86,55 @@ fn set_fan_clocks() {
|
|||||||
});
|
});
|
||||||
assert_eq!(expected_request, request);
|
assert_eq!(expected_request, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clean_gpu_name_removes_vendor_prefixes() {
|
||||||
|
assert_eq!(clean_gpu_name("AMD Radeon RX 9070 XT"), "Radeon RX 9070 XT");
|
||||||
|
assert_eq!(
|
||||||
|
clean_gpu_name("NVIDIA GeForce RTX 5090"),
|
||||||
|
"GeForce RTX 5090"
|
||||||
|
);
|
||||||
|
assert_eq!(clean_gpu_name("NVIDIA GeForce MX450"), "GeForce MX450");
|
||||||
|
assert_eq!(
|
||||||
|
clean_gpu_name("NVIDIA GeForce RTX 5090 [Founders Edition]"),
|
||||||
|
"GeForce RTX 5090"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
clean_gpu_name("NVIDIA GeForce RTX 4070 Super"),
|
||||||
|
"GeForce RTX 4070 Super"
|
||||||
|
);
|
||||||
|
assert_eq!(clean_gpu_name("Intel Arc A380"), "Arc A380");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clean_gpu_name_unwraps_pci_names() {
|
||||||
|
assert_eq!(
|
||||||
|
clean_gpu_name("Pitcairn XT [Radeon HD 7870 GHz Edition]"),
|
||||||
|
"Radeon HD 7870 GHz Edition"
|
||||||
|
);
|
||||||
|
assert_eq!(clean_gpu_name("DG2 [Arc A380]"), "Arc A380");
|
||||||
|
assert_eq!(clean_gpu_name("GK107M [GeForce 710A]"), "GeForce 710A");
|
||||||
|
assert_eq!(clean_gpu_name("GK107M [GeForce 820M]"), "GeForce 820M");
|
||||||
|
assert_eq!(clean_gpu_name("TU117M [GeForce MX450]"), "GeForce MX450");
|
||||||
|
assert_eq!(
|
||||||
|
clean_gpu_name("TigerLake-LP GT2 [Iris Xe Graphics]"),
|
||||||
|
"Iris Xe Graphics"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clean_gpu_name_keeps_consumer_brands_and_unrecognized_names() {
|
||||||
|
assert_eq!(
|
||||||
|
clean_gpu_name("AMD Radeon 780M Graphics"),
|
||||||
|
"Radeon 780M Graphics"
|
||||||
|
);
|
||||||
|
assert_eq!(clean_gpu_name("AMD Radeon VII"), "Radeon VII");
|
||||||
|
assert_eq!(clean_gpu_name("Phoenix1"), "Phoenix1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clean_gpu_name_keeps_professional_product_brands() {
|
||||||
|
assert_eq!(clean_gpu_name("GK107GL [Quadro K600]"), "Quadro K600");
|
||||||
|
assert_eq!(clean_gpu_name("GK110GL [Tesla K20]"), "Tesla K20");
|
||||||
|
assert_eq!(clean_gpu_name("NVIDIA Quadro RTX 6000"), "Quadro RTX 6000");
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user