Merge branch 'master' into service-setup-gui-2

This commit is contained in:
Ilya Zlobintsev
2026-05-28 09:27:17 +03:00
16 changed files with 365 additions and 274 deletions
Generated
+2 -2
View File
@@ -1850,9 +1850,9 @@ dependencies = [
[[package]]
name = "libdrm_amdgpu_sys"
version = "0.8.14"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b63f219bdee8484c65e0f54f81b6c671f1ab433f460e40c3174dea688abf31b"
checksum = "1e11704169b7b5a043ec5627aa0433019cf67d07f233002e1233edf67c2e662b"
dependencies = [
"libc",
"libloading",
+6 -6
View File
@@ -2173,14 +2173,14 @@
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/libdrm_amdgpu_sys/libdrm_amdgpu_sys-0.8.14.crate",
"sha256": "3b63f219bdee8484c65e0f54f81b6c671f1ab433f460e40c3174dea688abf31b",
"dest": "cargo/vendor/libdrm_amdgpu_sys-0.8.14"
"url": "https://static.crates.io/crates/libdrm_amdgpu_sys/libdrm_amdgpu_sys-0.8.16.crate",
"sha256": "1e11704169b7b5a043ec5627aa0433019cf67d07f233002e1233edf67c2e662b",
"dest": "cargo/vendor/libdrm_amdgpu_sys-0.8.16"
},
{
"type": "inline",
"contents": "{\"package\": \"3b63f219bdee8484c65e0f54f81b6c671f1ab433f460e40c3174dea688abf31b\", \"files\": {}}",
"dest": "cargo/vendor/libdrm_amdgpu_sys-0.8.14",
"contents": "{\"package\": \"1e11704169b7b5a043ec5627aa0433019cf67d07f233002e1233edf67c2e662b\", \"files\": {}}",
"dest": "cargo/vendor/libdrm_amdgpu_sys-0.8.16",
"dest-filename": ".cargo-checksum.json"
},
{
@@ -5361,4 +5361,4 @@
"dest": "cargo",
"dest-filename": "config"
}
]
]
+1 -1
View File
@@ -30,7 +30,7 @@ pub async fn info(ctx: CliContext<'_>) -> Result<()> {
println!("{gpu_line}");
println!("{}", "=".repeat(gpu_line.len()));
let info = ctx.client.get_device_info(&id).await?;
let info = ctx.client.get_device_info(&id, Some(false)).await?;
let stats = ctx.client.get_device_stats(&id).await?;
let elements = info.info_elements(Some(&stats));
+14 -2
View File
@@ -4,7 +4,7 @@ mod macros;
pub use lact_schema as schema;
use lact_schema::{
ProcessList, ProfileRule,
DeviceApiInfo, ProcessList, ProfileRule,
config::{GpuConfig, Profile, ProfileHooks},
};
@@ -126,12 +126,24 @@ impl DaemonClient {
self.make_request(Request::ListDevices).await
}
pub async fn get_device_info(
&self,
id: &str,
include_api_info: Option<bool>,
) -> anyhow::Result<DeviceInfo> {
self.make_request(Request::DeviceInfo {
id,
include_api_info,
})
.await
}
request_plain!(get_system_info, SystemInfo, SystemInfo);
request_plain!(enable_overdrive, EnableOverdrive, String);
request_plain!(disable_overdrive, DisableOverdrive, String);
request_plain!(generate_debug_snapshot, GenerateSnapshot, String);
request_plain!(reset_config, RestConfig, ());
request_with_id!(get_device_info, DeviceInfo, DeviceInfo);
request_with_id!(get_device_api_info, DeviceApiInfo, DeviceApiInfo);
request_with_id!(get_device_stats, DeviceStats, DeviceStats);
request_with_id!(get_device_clocks_info, DeviceClocksInfo, ClocksInfo);
request_with_id!(
+1 -1
View File
@@ -37,7 +37,7 @@ nvml-wrapper = "0.12.1"
bitflags = "2.11.1"
pciid-parser = { version = "0.8", features = ["serde"] }
zbus = { workspace = true }
libdrm_amdgpu_sys = { version = "0.8.13", default-features = false, features = [
libdrm_amdgpu_sys = { version = "0.8.16", default-features = false, features = [
"dynamic_loading",
] }
tar = "0.4.45"
+5 -1
View File
@@ -157,7 +157,11 @@ async fn handle_request<'a>(
Request::Ping => ok_response(ping()),
Request::SystemInfo => ok_response(system::info().await?),
Request::ListDevices => ok_response(handler.list_devices().await),
Request::DeviceInfo { id } => ok_response(handler.get_device_info(id).await?),
Request::DeviceInfo {
id,
include_api_info,
} => ok_response(handler.get_device_info(id, include_api_info).await?),
Request::DeviceApiInfo { id } => ok_response(handler.get_device_api_info(id).await?),
Request::DeviceStats { id } => ok_response(handler.get_gpu_stats(id).await?),
Request::DeviceClocksInfo { id } => ok_response(handler.get_clocks_info(id).await?),
Request::DevicePowerProfileModes { id } => {
+30 -1
View File
@@ -7,15 +7,19 @@ mod nvidia;
use amd::AmdGpuController;
use intel::IntelGpuController;
use lact_schema::DeviceApiInfo;
use lact_schema::DeviceType;
use lact_schema::ProcessList;
#[cfg(feature = "nvidia")]
use nvidia::NvidiaGpuController;
use tokio::join;
pub const VENDOR_AMD: &str = "1002";
pub const VENDOR_NVIDIA: &str = "10DE";
use crate::server::handler::{AMD_DRM, INTEL_DRM, NVML};
use crate::server::opencl::get_opencl_info;
use crate::server::vulkan::get_vulkan_info;
use amdgpu_sysfs::gpu_handle::power_profile_mode::PowerProfileModesTable;
use anyhow::Context;
use anyhow::anyhow;
@@ -43,7 +47,32 @@ pub trait GpuController {
fn device_type(&self) -> DeviceType;
fn get_info(&self, unique_vendor: bool) -> LocalBoxFuture<'_, DeviceInfo>;
fn get_info(
&self,
unique_vendor: bool,
include_api_info: bool,
) -> LocalBoxFuture<'_, DeviceInfo>;
fn get_api_info(&self, unique_vendor: bool) -> LocalBoxFuture<'_, DeviceApiInfo> {
async move {
let common = self.controller_info();
let (vulkan_result, opencl_instances) = join!(
get_vulkan_info(common),
get_opencl_info(common, unique_vendor)
);
let vulkan_instances = vulkan_result.unwrap_or_else(|err| {
warn!("could not load vulkan info: {err:#}");
vec![]
});
DeviceApiInfo {
vulkan_instances,
opencl_instances,
}
}
.boxed_local()
}
fn friendly_name(&self) -> Option<String>;
+20 -25
View File
@@ -1,11 +1,7 @@
use super::{CommonControllerInfo, FanControlHandle, GpuController, VENDOR_AMD};
use crate::server::{
gpu_controller::common::{
fan_control::FanCurveExt,
fdinfo::{self, DrmUtilMap},
},
opencl::get_opencl_info,
vulkan::get_vulkan_info,
use crate::server::gpu_controller::common::{
fan_control::FanCurveExt,
fdinfo::{self, DrmUtilMap},
};
use amdgpu_sysfs::{
error::Error,
@@ -19,12 +15,13 @@ use amdgpu_sysfs::{
sysfs::SysFS,
};
use anyhow::{Context, anyhow, bail};
use futures::{FutureExt, future::LocalBoxFuture, join};
use futures::{FutureExt, future::LocalBoxFuture};
use lact_schema::{
ActivePowerStates, AmdCacheInstance, AmdIpInfo, CacheInfo, CacheType, ClocksInfo,
ClockspeedStats, DeviceFlag, DeviceInfo, DeviceStats, DeviceType, DrmInfo, FanControlMode,
FanStats, IntelDrmInfo, LinkInfo, PmfwInfo, PowerState, PowerStates, PowerStats, ProcessList,
ProcessUtilizationType, RopInfo, TemperatureEntry, VoltageStats, VramStats,
ClockspeedStats, DeviceApiInfo, DeviceFlag, DeviceInfo, DeviceStats, DeviceType, DrmInfo,
FanControlMode, FanStats, IntelDrmInfo, LinkInfo, PmfwInfo, PowerState, PowerStates,
PowerStats, ProcessList, ProcessUtilizationType, RopInfo, TemperatureEntry, VoltageStats,
VramStats,
config::{ClocksConfiguration, FanControlSettings, FanCurve, GpuConfig},
};
use libdrm_amdgpu_sys::AMDGPU::{GpuMetrics, HW_IP::HW_IP_TYPE, ThrottlerBit, ThrottlerType};
@@ -739,16 +736,17 @@ impl GpuController for AmdGpuController {
})
}
fn get_info(&self, unique_vendor: bool) -> LocalBoxFuture<'_, DeviceInfo> {
fn get_info(
&self,
unique_vendor: bool,
include_api_info: bool,
) -> LocalBoxFuture<'_, DeviceInfo> {
Box::pin(async move {
let (vulkan_result, opencl_instances) = join!(
get_vulkan_info(&self.common),
get_opencl_info(&self.common, unique_vendor)
);
let vulkan_instances = vulkan_result.unwrap_or_else(|err| {
warn!("could not load vulkan info: {err:#}");
vec![]
});
let api_info = if include_api_info {
self.get_api_info(unique_vendor).await
} else {
DeviceApiInfo::default()
};
let pci_info = Some(self.common.pci_info.clone());
let driver = self.handle.get_driver().to_owned();
@@ -787,11 +785,10 @@ impl GpuController for AmdGpuController {
DeviceInfo {
pci_info,
vulkan_instances,
api_info,
driver,
vbios_version,
link_info,
opencl_instances,
drm_info,
flags,
}
@@ -1427,8 +1424,6 @@ fn get_drm_handle(
common: &CommonControllerInfo,
libdrm_amdgpu: &LibDrmAmdgpu,
) -> anyhow::Result<DrmHandle> {
use std::os::unix::io::AsRawFd;
let path = common.get_drm_render()?;
let drm_file = fs::OpenOptions::new()
.read(true)
@@ -1436,7 +1431,7 @@ fn get_drm_handle(
.open(&path)
.with_context(|| format!("Could not open drm file at {}", path.display()))?;
let (handle, _, _) = libdrm_amdgpu
.init_device_handle(drm_file.as_raw_fd())
.init_device_handle_with_fd(drm_file)
.map_err(|err| anyhow!("Could not open drm handle, error code {err}"))?;
Ok(handle)
}
+17 -21
View File
@@ -6,20 +6,16 @@ use crate::{
IntelDrm, drm_i915_gem_memory_class_I915_MEMORY_CLASS_DEVICE,
drm_xe_memory_class_DRM_XE_MEM_REGION_CLASS_VRAM,
},
server::{
gpu_controller::common::fdinfo::{self, DrmUtilMap},
opencl::get_opencl_info,
vulkan::get_vulkan_info,
},
server::gpu_controller::common::fdinfo::{self, DrmUtilMap},
};
use amdgpu_sysfs::{gpu_handle::power_profile_mode::PowerProfileModesTable, hw_mon::Temperature};
use anyhow::{Context, anyhow, bail};
use futures::{future::LocalBoxFuture, join};
use futures::future::LocalBoxFuture;
use lact_schema::{
ClocksInfo, ClocksTable, ClockspeedStats, DeviceInfo, DeviceStats, DeviceType, DrmInfo,
DrmMemoryInfo, FanStats, IntelClocksTable, IntelDrmInfo, LinkInfo, PowerState, PowerStates,
PowerStats, ProcessList, ProcessUtilizationType, TemperatureEntry, VoltageStats, VramStats,
config::GpuConfig,
ClocksInfo, ClocksTable, ClockspeedStats, DeviceApiInfo, DeviceInfo, DeviceStats, DeviceType,
DrmInfo, DrmMemoryInfo, FanStats, IntelClocksTable, IntelDrmInfo, LinkInfo, PowerState,
PowerStates, PowerStats, ProcessList, ProcessUtilizationType, TemperatureEntry, VoltageStats,
VramStats, config::GpuConfig,
};
use std::{
borrow::Cow,
@@ -592,16 +588,17 @@ impl GpuController for IntelGpuController {
self.common.pci_info.device_pci_info.model.clone()
}
fn get_info(&self, unique_vendor: bool) -> LocalBoxFuture<'_, DeviceInfo> {
fn get_info(
&self,
unique_vendor: bool,
include_api_info: bool,
) -> LocalBoxFuture<'_, DeviceInfo> {
Box::pin(async move {
let (vulkan_result, opencl_instances) = join!(
get_vulkan_info(&self.common),
get_opencl_info(&self.common, unique_vendor)
);
let vulkan_instances = vulkan_result.unwrap_or_else(|err| {
warn!("could not load vulkan info: {err:#}");
vec![]
});
let api_info = if include_api_info {
self.get_api_info(unique_vendor).await
} else {
DeviceApiInfo::default()
};
let vram_info = self.get_vram_info();
@@ -617,12 +614,11 @@ impl GpuController for IntelGpuController {
DeviceInfo {
pci_info: Some(self.common.pci_info.clone()),
vulkan_instances,
api_info,
driver: self.common.driver.clone(),
vbios_version: None,
link_info: LinkInfo::default(),
drm_info: Some(drm_info),
opencl_instances,
flags: vec![],
}
})
+21 -25
View File
@@ -4,14 +4,10 @@ pub mod nvapi;
use super::{CommonControllerInfo, FanControlHandle, GpuController};
use crate::{
bindings::nvidia::NvPhysicalGpuHandle,
server::{
gpu_controller::{
NvApi,
common::{fan_control::FanCurveExt, resolve_process_name},
nvidia::nvapi::{CLOCK_CLIENT_CLK_VF_POINT_TYPE_PROG, ClockClientClkVfPointInfoV1},
},
opencl::get_opencl_info,
vulkan::get_vulkan_info,
server::gpu_controller::{
NvApi,
common::{fan_control::FanCurveExt, resolve_process_name},
nvidia::nvapi::{CLOCK_CLIENT_CLK_VF_POINT_TYPE_PROG, ClockClientClkVfPointInfoV1},
},
};
use amdgpu_sysfs::{
@@ -20,14 +16,14 @@ use amdgpu_sysfs::{
};
use anyhow::{Context, anyhow, bail};
use driver::DriverHandle;
use futures::{FutureExt, future::LocalBoxFuture, join};
use futures::{FutureExt, future::LocalBoxFuture};
use indexmap::IndexMap;
use lact_schema::{
ActivePowerStates, CacheInfo, ClocksInfo, ClocksTable, ClockspeedStats, DeviceFlag, DeviceInfo,
DeviceStats, DeviceType, DrmInfo, DrmMemoryInfo, FanControlMode, FanStats, IntelDrmInfo,
LinkInfo, NvidiaClockOffset, NvidiaClocksTable, NvidiaVfPoint, PmfwInfo, PowerState,
PowerStates, PowerStats, ProcessInfo, ProcessList, ProcessType, ProcessUtilizationType,
TemperatureEntry, VoltageStats, VramStats,
ActivePowerStates, CacheInfo, ClocksInfo, ClocksTable, ClockspeedStats, DeviceApiInfo,
DeviceFlag, DeviceInfo, DeviceStats, DeviceType, DrmInfo, DrmMemoryInfo, FanControlMode,
FanStats, IntelDrmInfo, LinkInfo, NvidiaClockOffset, NvidiaClocksTable, NvidiaVfPoint,
PmfwInfo, PowerState, PowerStates, PowerStats, ProcessInfo, ProcessList, ProcessType,
ProcessUtilizationType, TemperatureEntry, VoltageStats, VramStats,
config::{CurvePoint, FanControlSettings, FanCurve, GpuConfig},
};
use nvml_wrapper::{
@@ -535,23 +531,24 @@ impl GpuController for NvidiaGpuController {
.or_else(|| self.common.pci_info.device_pci_info.model.clone())
}
fn get_info(&self, unique_vendor: bool) -> LocalBoxFuture<'_, DeviceInfo> {
fn get_info(
&self,
unique_vendor: bool,
include_api_info: bool,
) -> LocalBoxFuture<'_, DeviceInfo> {
Box::pin(async move {
let (vulkan_result, opencl_instances) = join!(
get_vulkan_info(&self.common),
get_opencl_info(&self.common, unique_vendor)
);
let vulkan_instances = vulkan_result.unwrap_or_else(|err| {
warn!("could not load vulkan info: {err:#}");
vec![]
});
let api_info = if include_api_info {
self.get_api_info(unique_vendor).await
} else {
DeviceApiInfo::default()
};
let device = self.device();
let driver_handle = self.driver_handle.as_ref();
DeviceInfo {
pci_info: Some(self.common.pci_info.clone()),
vulkan_instances,
api_info,
driver: format!(
"nvidia {}",
self.nvml.sys_driver_version().unwrap_or_default()
@@ -585,7 +582,6 @@ impl GpuController for NvidiaGpuController {
output
}),
},
opencl_instances,
drm_info: Some(DrmInfo {
device_name: device.name().ok(),
pci_revision_id: None,
+22 -5
View File
@@ -14,8 +14,9 @@ use amdgpu_sysfs::gpu_handle::{
};
use anyhow::{Context, anyhow, bail};
use lact_schema::{
ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanControlMode, FanOptions, PmfwOptions,
PowerStates, ProcessList, ProfileRule, ProfileWatcherState, ProfilesInfo,
ClocksInfo, DeviceApiInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanControlMode,
FanOptions, PmfwOptions, PowerStates, ProcessList, ProfileRule, ProfileWatcherState,
ProfilesInfo,
config::{
FanControlSettings, FanCurve, GpuConfig, Profile, ProfileHooks, default_fan_static_speed,
},
@@ -423,7 +424,23 @@ impl<'a> Handler {
entries
}
pub async fn get_device_info(&'a self, id: &str) -> anyhow::Result<DeviceInfo> {
pub async fn get_device_info(
&'a self,
id: &str,
include_api_info: Option<bool>,
) -> anyhow::Result<DeviceInfo> {
let controllers = self.gpu_controllers.read().await;
let controller = controllers
.get(id)
.ok_or_else(|| anyhow!("Controller '{id}' not found"))?;
let unique_vendor = controller_vendor_is_unique(controller, id, &controllers);
let include_api_info = include_api_info.unwrap_or(true);
Ok(controller.get_info(unique_vendor, include_api_info).await)
}
pub async fn get_device_api_info(&'a self, id: &str) -> anyhow::Result<DeviceApiInfo> {
let controllers = self.gpu_controllers.read().await;
let controller = controllers
.get(id)
@@ -431,7 +448,7 @@ impl<'a> Handler {
let unique_vendor = controller_vendor_is_unique(controller, id, &controllers);
Ok(controller.get_info(unique_vendor).await)
Ok(controller.get_api_info(unique_vendor).await)
}
pub async fn get_gpu_stats(&'a self, id: &str) -> anyhow::Result<DeviceStats> {
@@ -773,7 +790,7 @@ impl<'a> Handler {
let data = json!({
"pci_info": controller.controller_info().pci_info.clone(),
"info": controller.get_info(unique_vendor).await,
"info": controller.get_info(unique_vendor, true).await,
"stats": controller.get_stats(gpu_config),
"clocks_info": controller.get_clocks_info(gpu_config).ok(),
"power_profile_modes": controller.get_power_profile_modes().ok(),
+29 -21
View File
@@ -69,7 +69,7 @@ use relm4::{
AsyncComponentSender, Component, ComponentController, MessageBroker, RelmObjectExt,
RelmWidgetExt,
actions::{AccelsPlus, ActionGroupName, RelmAction, RelmActionGroup},
binding::BoolBinding,
binding::{Binding as _, BoolBinding},
css,
loading_widgets::LoadingWidgets,
new_action_group, new_stateless_action,
@@ -114,6 +114,7 @@ pub struct AppModel {
info_dialog: relm4::Controller<InfoDialog>,
ui_sensitive: BoolBinding,
is_reconnecting: BoolBinding,
info_page: relm4::Controller<InformationPage>,
oc_page: relm4::Controller<OcPage>,
@@ -203,6 +204,7 @@ impl AsyncComponent for AppModel {
set_orientation: gtk::Orientation::Vertical,
set_vexpand: true,
add_css_class: "main-sidebar-container",
add_binding: (&model.ui_sensitive, "sensitive"),
model.gpu_selector.widget().clone() {},
@@ -252,16 +254,17 @@ impl AsyncComponent for AppModel {
set_child = &adw::ToolbarView {
#[name = "content_header"]
add_top_bar = &adw::HeaderBar {
pack_end = &gtk::MenuButton {
set_icon_name: "open-menu-symbolic",
set_tooltip_text: Some(&fl!(I18N, "menu")),
set_menu_model: Some(&app_menu),
add_binding: (&model.ui_sensitive, "sensitive"),
},
pack_end = &gtk::Button {
set_label: &fl!(I18N, "show-historical-charts"),
connect_clicked => move |_| APP_BROKER.send(AppMsg::ShowGraphsWindow),
add_binding: (&model.ui_sensitive, "sensitive"),
},
},
@@ -275,6 +278,11 @@ impl AsyncComponent for AppModel {
connect_button_clicked => AppMsg::ShowOverdriveDialog,
},
add_top_bar = &adw::Banner {
set_title: &fl!(I18N, "reconnecting-to-daemon"),
add_binding: (&model.is_reconnecting, "revealed"),
},
#[wrap(Some)]
set_content = &gtk::ScrolledWindow {
set_hscrollbar_policy: gtk::PolicyType::Never,
@@ -319,20 +327,6 @@ impl AsyncComponent for AppModel {
}
}
},
#[name = "reconnecting_dialog"]
adw::Dialog {
set_title: &fl!(I18N, "daemon-connection-lost"),
set_content_width: 300,
set_content_height: 80,
set_can_close: false,
#[wrap(Some)]
set_child = &gtk::Label {
set_margin_all: 10,
set_label: &fl!(I18N, "reconnecting-to-daemon"),
}
},
}
fn init_loading_widgets(root: Self::Root) -> Option<LoadingWidgets> {
@@ -549,6 +543,7 @@ impl AsyncComponent for AppModel {
gpu_selector,
profile_selector,
ui_sensitive: BoolBinding::new(false),
is_reconnecting: BoolBinding::new(false),
stats_task_handle: None,
settings_changed,
system_info,
@@ -704,6 +699,12 @@ impl AppModel {
self.update_gpu_data(gpu_id, sender).await?;
}
}
AppMsg::ReloadApiInfo => {
let gpu_id = Self::get_selected_gpu_id()?;
let api_info = self.daemon_client.get_device_api_info(&gpu_id).await?;
self.software_page
.emit(SoftwarePageMsg::DeviceApiInfo(Some(api_info)));
}
AppMsg::ShowPreferencesDialog => {
self.preferences_dialog.emit(PreferencesDialogMsg::Show);
}
@@ -926,9 +927,13 @@ impl AppModel {
}
AppMsg::ConnectionStatus(status) => match status {
ConnectionStatusMsg::Disconnected => {
// widgets.reconnecting_dialog.present(Some(root))
self.ui_sensitive.set(false);
self.is_reconnecting.set(true);
}
ConnectionStatusMsg::Reconnected => {
self.ui_sensitive.set(true);
self.is_reconnecting.set(false);
}
ConnectionStatusMsg::Reconnected => widgets.reconnecting_dialog.force_close(),
},
AppMsg::EvaluateProfile(rule, sender) => {
match self.daemon_client.evaluate_profile_rule(rule).await {
@@ -1062,10 +1067,12 @@ impl AppModel {
sender: AsyncComponentSender<AppModel>,
) -> anyhow::Result<()> {
self.ui_sensitive.set_value(false);
self.software_page
.emit(SoftwarePageMsg::DeviceApiInfo(None));
let daemon_client = self.daemon_client.clone();
let info_buf = daemon_client
.get_device_info(&gpu_id)
.get_device_info(&gpu_id, Some(false))
.await
.context("Could not fetch info")?;
let info = Arc::new(info_buf);
@@ -1094,8 +1101,9 @@ impl AppModel {
update: update.clone(),
initial: true,
});
self.software_page
.emit(SoftwarePageMsg::DeviceInfo(info.clone()));
sender.input(AppMsg::ReloadApiInfo);
self.thermals_page.emit(ThermalsPageMsg::Update {
update: update.clone(),
initial: true,
+1
View File
@@ -14,6 +14,7 @@ pub enum AppMsg {
ReloadData {
full: bool,
},
ReloadApiInfo,
Stats(Arc<DeviceStats>),
ProfilesPolled(Arc<ProfilesInfo>),
ApplyChanges,
+181 -159
View File
@@ -1,6 +1,7 @@
use crate::app::ext::FlowBoxExt;
mod vulkan;
use crate::app::loader;
use crate::{
GUI_VERSION, I18N, REPO_URL,
app::{
@@ -13,14 +14,14 @@ use gtk::prelude::*;
use i18n_embed_fl::fl;
use indexmap::IndexMap;
use lact_client::schema::{GIT_COMMIT, SystemInfo};
use lact_schema::{DeviceInfo, OpenCLInfo, VulkanInfo};
use lact_schema::{DeviceApiInfo, OpenCLInfo, VulkanInfo};
use relm4::{Component, ComponentController, ComponentParts, ComponentSender, RelmWidgetExt};
use relm4_components::simple_combo_box::{SimpleComboBox, SimpleComboBoxMsg};
use std::{fmt::Write, sync::Arc};
use std::fmt::Write as _;
use vulkan::feature_window::{VulkanFeature, VulkanFeaturesWindow};
pub struct SoftwarePage {
device_info: Option<Arc<DeviceInfo>>,
device_api_info: Option<DeviceApiInfo>,
vulkan_driver_selector: relm4::Controller<SimpleComboBox<String>>,
opencl_platform_selector: relm4::Controller<SimpleComboBox<String>>,
@@ -30,7 +31,7 @@ pub struct SoftwarePage {
#[derive(Debug)]
pub enum SoftwarePageMsg {
DeviceInfo(Arc<DeviceInfo>),
DeviceApiInfo(Option<DeviceApiInfo>),
ShowVulkanFeatures,
ShowVulkanExtensions,
SelectionChanged,
@@ -63,163 +64,179 @@ impl relm4::SimpleComponent for SoftwarePage {
},
},
#[name = "vulkan_stack"]
match model.selected_vulkan_info() {
Some(info) => {
PageSection::new("Vulkan") {
append_child = &gtk::FlowBox {
set_orientation: gtk::Orientation::Horizontal,
set_column_spacing: 10,
set_homogeneous: true,
set_min_children_per_line: 2,
set_max_children_per_line: 4,
set_selection_mode: gtk::SelectionMode::None,
append_child = &InfoRow {
set_name: fl!(I18N, "instance"),
append_child = model.vulkan_driver_selector.widget(),
} -> vulkan_instance_item: gtk::FlowBoxChild {
#[watch]
set_visible: model.vulkan_driver_selector.model().variants.len() > 1,
},
append_child = &InfoRow {
set_name: fl!(I18N, "device-name"),
#[watch]
set_value: info.device_name.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "api-version"),
#[watch]
set_value: info.api_version.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "driver-name"),
#[watch]
set_value: info.driver.name.as_deref().unwrap_or_default(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "driver-version"),
#[watch]
set_value: info.driver.info.as_deref().unwrap_or_default(),
set_selectable: true,
},
append_child = &InfoRow {
set_value: fl!(I18N, "features"),
set_icon: "go-next-symbolic".to_string(),
connect_clicked => SoftwarePageMsg::ShowVulkanFeatures,
},
append_child = &InfoRow {
set_value: fl!(I18N, "extensions"),
set_icon: "go-next-symbolic".to_string(),
connect_clicked => SoftwarePageMsg::ShowVulkanExtensions,
},
},
}
}
None => {
PageSection::new("Vulkan") {
append_child = &gtk::Label {
set_label: &fl!(I18N, "device-not-found", kind = "Vulkan"),
set_halign: gtk::Align::Start,
},
}
}
#[local_ref]
loader_picture -> gtk::Picture {
set_halign: gtk::Align::Center,
set_valign: gtk::Align::Start,
#[watch]
set_visible: model.device_api_info.is_none(),
},
#[name = "opencl_stack"]
match model.selected_opencl_info() {
Some(info) => {
PageSection::new("OpenCL") {
append_child = &gtk::FlowBox {
set_orientation: gtk::Orientation::Horizontal,
set_column_spacing: 10,
set_homogeneous: true,
set_min_children_per_line: 2,
set_max_children_per_line: 4,
set_selection_mode: gtk::SelectionMode::None,
gtk::Box {
set_spacing: 15,
set_orientation: gtk::Orientation::Vertical,
#[watch]
set_visible: model.device_api_info.is_some(),
set_visible: false,
append_child = &InfoRow {
set_name: fl!(I18N, "platform-name"),
append_child = model.opencl_platform_selector.widget(),
} -> opencl_platform_item: gtk::FlowBoxChild {
#[watch]
set_visible: model.opencl_platform_selector.model().variants.len() > 1,
},
#[name = "vulkan_stack"]
match model.selected_vulkan_info() {
Some(info) => {
PageSection::new("Vulkan") {
append_child = &gtk::FlowBox {
set_orientation: gtk::Orientation::Horizontal,
set_column_spacing: 10,
set_homogeneous: true,
set_min_children_per_line: 2,
set_max_children_per_line: 4,
set_selection_mode: gtk::SelectionMode::None,
append_child = &InfoRow {
set_name: fl!(I18N, "platform-name"),
#[watch]
set_value: info.platform_name.as_str(),
set_selectable: true,
} -> opencl_platform_name_item: gtk::FlowBoxChild {
#[watch]
set_visible: model.opencl_platform_selector.model().variants.len() == 1,
append_child = &InfoRow {
set_name: fl!(I18N, "instance"),
append_child = model.vulkan_driver_selector.widget(),
} -> vulkan_instance_item: gtk::FlowBoxChild {
#[watch]
set_visible: model.vulkan_driver_selector.model().variants.len() > 1,
},
append_child = &InfoRow {
set_name: fl!(I18N, "device-name"),
#[watch]
set_value: info.device_name.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "api-version"),
#[watch]
set_value: info.api_version.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "driver-name"),
#[watch]
set_value: info.driver.name.as_deref().unwrap_or_default(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "driver-version"),
#[watch]
set_value: info.driver.info.as_deref().unwrap_or_default(),
set_selectable: true,
},
append_child = &InfoRow {
set_value: fl!(I18N, "features"),
set_icon: "go-next-symbolic".to_string(),
connect_clicked => SoftwarePageMsg::ShowVulkanFeatures,
},
append_child = &InfoRow {
set_value: fl!(I18N, "extensions"),
set_icon: "go-next-symbolic".to_string(),
connect_clicked => SoftwarePageMsg::ShowVulkanExtensions,
},
},
append_child = &InfoRow {
set_name: fl!(I18N, "device-name"),
#[watch]
set_value: info.device_name.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "version"),
#[watch]
set_value: info.version.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "driver-version"),
#[watch]
set_value: info.driver_version.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "cl-c-version"),
#[watch]
set_value: info.c_version.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "compute-units"),
#[watch]
set_value: info.compute_units.to_string(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "workgroup-size"),
#[watch]
set_value: info.workgroup_size.to_string(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "global-memory"),
#[watch]
set_value: formatting::fmt_human_bytes(info.global_memory, None),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "local-memory"),
#[watch]
set_value: formatting::fmt_human_bytes(info.local_memory, None),
set_selectable: true,
},
},
}
}
}
None => {
PageSection::new("OpenCL") {
append_child = &gtk::Label {
set_label: &fl!(I18N, "device-not-found", kind = "OpenCL"),
set_halign: gtk::Align::Start,
},
None => {
PageSection::new("Vulkan") {
append_child = &gtk::Label {
set_label: &fl!(I18N, "device-not-found", kind = "Vulkan"),
set_halign: gtk::Align::Start,
},
}
}
}
},
#[name = "opencl_stack"]
match model.selected_opencl_info() {
Some(info) => {
PageSection::new("OpenCL") {
append_child = &gtk::FlowBox {
set_orientation: gtk::Orientation::Horizontal,
set_column_spacing: 10,
set_homogeneous: true,
set_min_children_per_line: 2,
set_max_children_per_line: 4,
set_selection_mode: gtk::SelectionMode::None,
append_child = &InfoRow {
set_name: fl!(I18N, "platform-name"),
append_child = model.opencl_platform_selector.widget(),
} -> opencl_platform_item: gtk::FlowBoxChild {
#[watch]
set_visible: model.opencl_platform_selector.model().variants.len() > 1,
},
append_child = &InfoRow {
set_name: fl!(I18N, "platform-name"),
#[watch]
set_value: info.platform_name.as_str(),
set_selectable: true,
} -> opencl_platform_name_item: gtk::FlowBoxChild {
#[watch]
set_visible: model.opencl_platform_selector.model().variants.len() == 1,
},
append_child = &InfoRow {
set_name: fl!(I18N, "device-name"),
#[watch]
set_value: info.device_name.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "version"),
#[watch]
set_value: info.version.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "driver-version"),
#[watch]
set_value: info.driver_version.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "cl-c-version"),
#[watch]
set_value: info.c_version.as_str(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "compute-units"),
#[watch]
set_value: info.compute_units.to_string(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "workgroup-size"),
#[watch]
set_value: info.workgroup_size.to_string(),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "global-memory"),
#[watch]
set_value: formatting::fmt_human_bytes(info.global_memory, None),
set_selectable: true,
},
append_child = &InfoRow {
set_name: fl!(I18N, "local-memory"),
#[watch]
set_value: formatting::fmt_human_bytes(info.local_memory, None),
set_selectable: true,
},
},
}
}
None => {
PageSection::new("OpenCL") {
append_child = &gtk::Label {
set_label: &fl!(I18N, "device-not-found", kind = "OpenCL"),
set_halign: gtk::Align::Start,
},
}
}
},
},
}
}
@@ -244,10 +261,10 @@ impl relm4::SimpleComponent for SoftwarePage {
.forward(sender.input_sender(), |_| SoftwarePageMsg::SelectionChanged);
let model = Self {
device_info: None,
vulkan_driver_selector,
opencl_platform_selector,
vulkan_window: None,
device_api_info: None,
};
let mut daemon_version = format!("{}-{}", system_info.version, system_info.profile);
@@ -273,6 +290,8 @@ impl relm4::SimpleComponent for SoftwarePage {
"{GUI_VERSION}-{gui_profile} (commit <a href=\"{gui_commit_link}\">{GIT_COMMIT}</a>)"
);
let loader_picture = loader::new();
let widgets = view_output!();
widgets.vulkan_stack.set_vhomogeneous(false);
@@ -283,7 +302,7 @@ impl relm4::SimpleComponent for SoftwarePage {
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
match msg {
SoftwarePageMsg::DeviceInfo(info) => {
SoftwarePageMsg::DeviceApiInfo(Some(info)) => {
let mut vulkan_drivers = Vec::new();
for info in &info.vulkan_instances {
@@ -323,7 +342,10 @@ impl relm4::SimpleComponent for SoftwarePage {
active_index: selected_platform,
}));
self.device_info = Some(info);
self.device_api_info = Some(info);
}
SoftwarePageMsg::DeviceApiInfo(None) => {
self.device_api_info = None;
}
SoftwarePageMsg::ShowVulkanFeatures => {
if let Some(vulkan_info) = &self.selected_vulkan_info() {
@@ -352,7 +374,7 @@ impl SoftwarePage {
.model()
.active_index
.and_then(|idx| {
self.device_info
self.device_api_info
.as_ref()
.and_then(|info| info.vulkan_instances.get(idx))
})
@@ -363,7 +385,7 @@ impl SoftwarePage {
.model()
.active_index
.and_then(|idx| {
self.device_info
self.device_api_info
.as_ref()
.and_then(|info| info.opencl_instances.get(idx))
})
+10 -4
View File
@@ -133,10 +133,8 @@ pub enum DeviceFlag {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DeviceInfo {
pub pci_info: Option<GpuPciInfo>,
#[serde(default)]
pub vulkan_instances: Vec<VulkanInfo>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub opencl_instances: Vec<OpenCLInfo>,
#[serde(flatten)]
pub api_info: DeviceApiInfo,
pub driver: String,
pub vbios_version: Option<String>,
pub link_info: LinkInfo,
@@ -330,6 +328,14 @@ impl DeviceInfo {
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct DeviceApiInfo {
#[serde(default)]
pub vulkan_instances: Vec<VulkanInfo>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub opencl_instances: Vec<OpenCLInfo>,
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct DrmInfo {
+5
View File
@@ -15,6 +15,11 @@ pub enum Request<'a> {
SystemInfo,
DeviceInfo {
id: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
include_api_info: Option<bool>,
},
DeviceApiInfo {
id: &'a str,
},
DeviceStats {
id: &'a str,