diff --git a/Cargo.lock b/Cargo.lock index bff50bb4..e8278627 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/flatpak/generated-sources.json b/flatpak/generated-sources.json index 6ffe0a4f..13f3f9f6 100644 --- a/flatpak/generated-sources.json +++ b/flatpak/generated-sources.json @@ -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" } -] +] \ No newline at end of file diff --git a/lact-cli/src/subcommands.rs b/lact-cli/src/subcommands.rs index 0fb8fbb3..1a8c96f3 100644 --- a/lact-cli/src/subcommands.rs +++ b/lact-cli/src/subcommands.rs @@ -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)); diff --git a/lact-client/src/lib.rs b/lact-client/src/lib.rs index 47fae295..2c163b6e 100644 --- a/lact-client/src/lib.rs +++ b/lact-client/src/lib.rs @@ -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, + ) -> anyhow::Result { + 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!( diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index 92babe2e..f0ab9caa 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -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" diff --git a/lact-daemon/src/server.rs b/lact-daemon/src/server.rs index 1113fe23..fb179ec6 100644 --- a/lact-daemon/src/server.rs +++ b/lact-daemon/src/server.rs @@ -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 } => { diff --git a/lact-daemon/src/server/gpu_controller.rs b/lact-daemon/src/server/gpu_controller.rs index 95a3f059..41a56aa8 100644 --- a/lact-daemon/src/server/gpu_controller.rs +++ b/lact-daemon/src/server/gpu_controller.rs @@ -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; diff --git a/lact-daemon/src/server/gpu_controller/amd.rs b/lact-daemon/src/server/gpu_controller/amd.rs index 8a6b1b68..24f8d568 100644 --- a/lact-daemon/src/server/gpu_controller/amd.rs +++ b/lact-daemon/src/server/gpu_controller/amd.rs @@ -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 { - 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) } diff --git a/lact-daemon/src/server/gpu_controller/intel.rs b/lact-daemon/src/server/gpu_controller/intel.rs index 37ee89e3..2b6c3eb3 100644 --- a/lact-daemon/src/server/gpu_controller/intel.rs +++ b/lact-daemon/src/server/gpu_controller/intel.rs @@ -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![], } }) diff --git a/lact-daemon/src/server/gpu_controller/nvidia.rs b/lact-daemon/src/server/gpu_controller/nvidia.rs index dbd5884c..9a2f4df9 100644 --- a/lact-daemon/src/server/gpu_controller/nvidia.rs +++ b/lact-daemon/src/server/gpu_controller/nvidia.rs @@ -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, diff --git a/lact-daemon/src/server/handler.rs b/lact-daemon/src/server/handler.rs index c45c625f..d569cdf6 100644 --- a/lact-daemon/src/server/handler.rs +++ b/lact-daemon/src/server/handler.rs @@ -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 { + pub async fn get_device_info( + &'a self, + id: &str, + include_api_info: Option, + ) -> anyhow::Result { + 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 { 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 { @@ -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(), diff --git a/lact-gui/src/app.rs b/lact-gui/src/app.rs index 5e86241d..92fb447c 100644 --- a/lact-gui/src/app.rs +++ b/lact-gui/src/app.rs @@ -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, ui_sensitive: BoolBinding, + is_reconnecting: BoolBinding, info_page: relm4::Controller, oc_page: relm4::Controller, @@ -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 = >k::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 = >k::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 = >k::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 = >k::Label { - set_margin_all: 10, - set_label: &fl!(I18N, "reconnecting-to-daemon"), - } - }, } fn init_loading_widgets(root: Self::Root) -> Option { @@ -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, ) -> 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, diff --git a/lact-gui/src/app/msg.rs b/lact-gui/src/app/msg.rs index c0a506b3..47a626c3 100644 --- a/lact-gui/src/app/msg.rs +++ b/lact-gui/src/app/msg.rs @@ -14,6 +14,7 @@ pub enum AppMsg { ReloadData { full: bool, }, + ReloadApiInfo, Stats(Arc), ProfilesPolled(Arc), ApplyChanges, diff --git a/lact-gui/src/app/pages/software_page.rs b/lact-gui/src/app/pages/software_page.rs index 674a4feb..56f4db7a 100644 --- a/lact-gui/src/app/pages/software_page.rs +++ b/lact-gui/src/app/pages/software_page.rs @@ -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>, + device_api_info: Option, vulkan_driver_selector: relm4::Controller>, opencl_platform_selector: relm4::Controller>, @@ -30,7 +31,7 @@ pub struct SoftwarePage { #[derive(Debug)] pub enum SoftwarePageMsg { - DeviceInfo(Arc), + DeviceApiInfo(Option), 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 = >k::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 = >k::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 = >k::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 = >k::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 = >k::Label { - set_label: &fl!(I18N, "device-not-found", kind = "OpenCL"), - set_halign: gtk::Align::Start, - }, + None => { + PageSection::new("Vulkan") { + append_child = >k::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 = >k::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 = >k::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 {GIT_COMMIT})" ); + 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) { 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)) }) diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index 40eccd03..585cb37e 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -133,10 +133,8 @@ pub enum DeviceFlag { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DeviceInfo { pub pci_info: Option, - #[serde(default)] - pub vulkan_instances: Vec, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub opencl_instances: Vec, + #[serde(flatten)] + pub api_info: DeviceApiInfo, pub driver: String, pub vbios_version: Option, 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, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub opencl_instances: Vec, +} + #[skip_serializing_none] #[derive(Serialize, Deserialize, Debug, Clone, Default)] pub struct DrmInfo { diff --git a/lact-schema/src/request.rs b/lact-schema/src/request.rs index a32b7b3e..05364bd3 100644 --- a/lact-schema/src/request.rs +++ b/lact-schema/src/request.rs @@ -15,6 +15,11 @@ pub enum Request<'a> { SystemInfo, DeviceInfo { id: &'a str, + #[serde(skip_serializing_if = "Option::is_none")] + include_api_info: Option, + }, + DeviceApiInfo { + id: &'a str, }, DeviceStats { id: &'a str,