mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2026-08-19 01:14:43 -05:00
Merge branch 'master' into display-info
This commit is contained in:
@@ -23,7 +23,7 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Docker Cache
|
||||
uses: AndreKurait/docker-cache@0.6.0
|
||||
uses: AndreKurait/docker-cache@eb0683e3972ed3066587b7199d3a0e9bd114bd3c #0.6.0
|
||||
with:
|
||||
key: docker-${{ matrix.target-os }}-${{ hashFiles('pkg/images/') }}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
- name: Install clippy
|
||||
run: rustup component add clippy
|
||||
- name: Run clippy
|
||||
run: cargo clippy --all --all-features --verbose
|
||||
run: cargo clippy --all --all-features -- --deny clippy::all
|
||||
|
||||
check-audit:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
Generated
+4
-4
@@ -31,9 +31,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "amdgpu-sysfs"
|
||||
version = "0.20.1"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e50f319907827f2c8dcd43fd1472e6103384bc3e8764a370db3db7a4e983817f"
|
||||
checksum = "4c67148270d0240773b7c69d6a98949a0f1e20fe91a756add562b5e6d3de3cf2"
|
||||
dependencies = [
|
||||
"enum_dispatch",
|
||||
"serde",
|
||||
@@ -1855,9 +1855,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",
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ members = [
|
||||
edition = "2024"
|
||||
|
||||
[workspace.dependencies]
|
||||
amdgpu-sysfs = { version = "0.20.1", features = ["serde"] }
|
||||
amdgpu-sysfs = { version = "0.21.0", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_with = { version = "3.18.0", default-features = false, features = [
|
||||
"macros",
|
||||
|
||||
@@ -54,14 +54,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/amdgpu-sysfs/amdgpu-sysfs-0.20.1.crate",
|
||||
"sha256": "e50f319907827f2c8dcd43fd1472e6103384bc3e8764a370db3db7a4e983817f",
|
||||
"dest": "cargo/vendor/amdgpu-sysfs-0.20.1"
|
||||
"url": "https://static.crates.io/crates/amdgpu-sysfs/amdgpu-sysfs-0.21.0.crate",
|
||||
"sha256": "4c67148270d0240773b7c69d6a98949a0f1e20fe91a756add562b5e6d3de3cf2",
|
||||
"dest": "cargo/vendor/amdgpu-sysfs-0.21.0"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"e50f319907827f2c8dcd43fd1472e6103384bc3e8764a370db3db7a4e983817f\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/amdgpu-sysfs-0.20.1",
|
||||
"contents": "{\"package\": \"4c67148270d0240773b7c69d6a98949a0f1e20fe91a756add562b5e6d3de3cf2\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/amdgpu-sysfs-0.21.0",
|
||||
"dest-filename": ".cargo-checksum.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"
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Environment=FLATPAK_INSTALL_USER=$USER
|
||||
ExecStart=$DAEMON_SH_PATH
|
||||
ExecStart=bash $DAEMON_SH_PATH
|
||||
Nice=-10
|
||||
Restart=on-failure
|
||||
|
||||
|
||||
@@ -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
@@ -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!(
|
||||
|
||||
@@ -37,7 +37,7 @@ nvml-wrapper = "0.12.1"
|
||||
bitflags = "2.11.1"
|
||||
pciid-parser = { version = "0.8", features = ["serde"] }
|
||||
zbus = { version = "5.14.0", default-features = false, features = ["tokio"] }
|
||||
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"
|
||||
|
||||
+15
-23
@@ -17,12 +17,12 @@ use server::{Server, handle_stream, handler::Handler};
|
||||
use std::sync::Arc;
|
||||
use std::{os::unix::net::UnixStream as StdUnixStream, time::Duration};
|
||||
use tokio::net::UnixStream;
|
||||
use tokio::runtime::LocalOptions;
|
||||
use tokio::sync::Notify;
|
||||
use tokio::time::timeout;
|
||||
use tokio::{
|
||||
runtime,
|
||||
signal::unix::{SignalKind, signal},
|
||||
task::LocalSet,
|
||||
};
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing::{Instrument, debug, debug_span, error, info, warn};
|
||||
@@ -46,7 +46,7 @@ const SHUTDOWN_SIGNALS: [SignalKind; 4] = [
|
||||
pub fn run() -> anyhow::Result<()> {
|
||||
let rt = runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.build_local(LocalOptions::default())
|
||||
.expect("Could not initialize tokio runtime");
|
||||
rt.block_on(async {
|
||||
let config = Config::load_or_create()?;
|
||||
@@ -59,20 +59,16 @@ pub fn run() -> anyhow::Result<()> {
|
||||
|
||||
ensure_sufficient_uptime().await;
|
||||
|
||||
LocalSet::new()
|
||||
.run_until(async move {
|
||||
let server = Server::new(config).await?;
|
||||
let handler = server.handler.clone();
|
||||
let server = Server::new(config).await?;
|
||||
let handler = server.handler.clone();
|
||||
|
||||
tokio::task::spawn_local(listen_config_changes(handler.clone()));
|
||||
tokio::task::spawn_local(listen_exit_signals(handler.clone()));
|
||||
tokio::task::spawn_local(listen_device_events(handler.clone()));
|
||||
tokio::task::spawn_local(suspend::listen_events(handler));
|
||||
tokio::task::spawn_local(listen_config_changes(handler.clone()));
|
||||
tokio::task::spawn_local(listen_exit_signals(handler.clone()));
|
||||
tokio::task::spawn_local(listen_device_events(handler.clone()));
|
||||
tokio::task::spawn_local(suspend::listen_events(handler));
|
||||
|
||||
server.run().await;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
server.run().await;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -84,18 +80,14 @@ pub fn run() -> anyhow::Result<()> {
|
||||
pub fn run_embedded(stream: StdUnixStream) -> anyhow::Result<()> {
|
||||
let rt = runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.build_local(LocalOptions::default())
|
||||
.expect("Could not initialize tokio runtime");
|
||||
rt.block_on(async {
|
||||
LocalSet::new()
|
||||
.run_until(async move {
|
||||
let config = Config::default();
|
||||
let handler = Handler::new(config).await?;
|
||||
let stream = UnixStream::try_from(stream)?;
|
||||
let config = Config::default();
|
||||
let handler = Handler::new(config).await?;
|
||||
let stream = UnixStream::try_from(stream)?;
|
||||
|
||||
handle_stream(stream, handler).await
|
||||
})
|
||||
.await
|
||||
handle_stream(stream, handler).await
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,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::DisplaysInfo { id } => ok_response(handler.get_displays_info(id).await?),
|
||||
|
||||
@@ -7,16 +7,20 @@ mod nvidia;
|
||||
|
||||
use amd::AmdGpuController;
|
||||
use intel::IntelGpuController;
|
||||
use lact_schema::DeviceApiInfo;
|
||||
use lact_schema::DeviceType;
|
||||
use lact_schema::DisplaysInfo;
|
||||
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;
|
||||
@@ -44,7 +48,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>;
|
||||
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
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,
|
||||
gpu_handle::{
|
||||
CommitHandle, GpuHandle, PerformanceLevel, PowerLevelKind,
|
||||
CommitHandle, GpuHandle, PerformanceLevel, PowerLevelId, PowerLevelKind,
|
||||
fan_control::FanCurve as PmfwCurve,
|
||||
overdrive::{ClocksTable, ClocksTableGen},
|
||||
power_profile_mode::PowerProfileModesTable,
|
||||
@@ -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::{
|
||||
AmdCacheInstance, AmdIpInfo, CacheInfo, CacheType, ClocksInfo, ClockspeedStats, DeviceFlag,
|
||||
DeviceInfo, DeviceStats, DeviceType, DisplayConnector, DisplaysInfo, DrmInfo, FanControlMode,
|
||||
FanStats, IntelDrmInfo, LinkInfo, PmfwInfo, PowerState, PowerStates, PowerStats, ProcessList,
|
||||
ProcessUtilizationType, RopInfo, TemperatureEntry, VoltageStats, VramStats,
|
||||
ActivePowerStates, AmdCacheInstance, AmdIpInfo, CacheInfo, CacheType, ClocksInfo,
|
||||
ClockspeedStats, DeviceApiInfo, DeviceFlag, DeviceInfo, DeviceStats, DeviceType,
|
||||
DisplayConnector, DisplaysInfo, 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};
|
||||
@@ -445,7 +442,9 @@ impl AmdGpuController {
|
||||
.unwrap_or_default();
|
||||
|
||||
if attempt < MAX_PSTATE_READ_ATTEMPTS
|
||||
&& levels.iter().any(|value| *value >= u64::from(u16::MAX))
|
||||
&& levels
|
||||
.iter()
|
||||
.any(|level| level.value >= u64::from(u16::MAX))
|
||||
{
|
||||
debug!("GPU reported nonsensical {kind:?} power state values, retrying: {levels:?}");
|
||||
return self.get_power_states_kind(gpu_config, kind, attempt + 1);
|
||||
@@ -453,15 +452,18 @@ impl AmdGpuController {
|
||||
|
||||
levels
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, value)| {
|
||||
let i = u8::try_from(i).unwrap();
|
||||
let enabled = enabled_states.is_none_or(|enabled| enabled.contains(&i));
|
||||
.map(|level| {
|
||||
let enabled = match level.id {
|
||||
PowerLevelId::Index(index) => {
|
||||
enabled_states.is_none_or(|enabled| enabled.contains(&index))
|
||||
}
|
||||
PowerLevelId::Sleep => true,
|
||||
};
|
||||
PowerState {
|
||||
enabled,
|
||||
min_value: None,
|
||||
value,
|
||||
index: Some(i),
|
||||
value: level.value,
|
||||
id: Some(level.id),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
@@ -734,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();
|
||||
@@ -782,11 +785,10 @@ impl GpuController for AmdGpuController {
|
||||
|
||||
DeviceInfo {
|
||||
pci_info,
|
||||
vulkan_instances,
|
||||
api_info,
|
||||
driver,
|
||||
vbios_version,
|
||||
link_info,
|
||||
opencl_instances,
|
||||
drm_info,
|
||||
flags,
|
||||
}
|
||||
@@ -1015,24 +1017,34 @@ impl GpuController for AmdGpuController {
|
||||
temps,
|
||||
busy_percent: self.handle.get_busy_percent().ok(),
|
||||
performance_level: self.handle.get_power_force_performance_level().ok(),
|
||||
core_power_state: self
|
||||
.handle
|
||||
.get_core_clock_levels()
|
||||
.inspect_err(|err| debug!("could not get active core power state: {err:#}"))
|
||||
.ok()
|
||||
.and_then(|levels| levels.active),
|
||||
memory_power_state: self
|
||||
.handle
|
||||
.get_memory_clock_levels()
|
||||
.inspect_err(|err| debug!("could not get active memory power state: {err:#}"))
|
||||
.ok()
|
||||
.and_then(|levels| levels.active),
|
||||
pcie_power_state: self
|
||||
.handle
|
||||
.get_pcie_clock_levels()
|
||||
.inspect_err(|err| debug!("could not get active PCIe power state: {err:#}"))
|
||||
.ok()
|
||||
.and_then(|levels| levels.active),
|
||||
active_power_states: {
|
||||
let active_power_states = ActivePowerStates {
|
||||
core: self
|
||||
.handle
|
||||
.get_core_clock_levels()
|
||||
.inspect_err(|err| debug!("could not get active core power state: {err:#}"))
|
||||
.ok()
|
||||
.and_then(|levels| levels.active),
|
||||
memory: self
|
||||
.handle
|
||||
.get_memory_clock_levels()
|
||||
.inspect_err(|err| {
|
||||
debug!("could not get active memory power state: {err:#}");
|
||||
})
|
||||
.ok()
|
||||
.and_then(|levels| levels.active),
|
||||
pcie: self
|
||||
.handle
|
||||
.get_pcie_clock_levels()
|
||||
.inspect_err(|err| debug!("could not get active PCIe power state: {err:#}"))
|
||||
.ok()
|
||||
.and_then(|levels| levels.active),
|
||||
};
|
||||
(active_power_states.core.is_some()
|
||||
|| active_power_states.memory.is_some()
|
||||
|| active_power_states.pcie.is_some())
|
||||
.then_some(active_power_states)
|
||||
},
|
||||
throttle_info,
|
||||
}
|
||||
}
|
||||
@@ -1162,7 +1174,7 @@ impl GpuController for AmdGpuController {
|
||||
.set_power_force_performance_level(performance_level)
|
||||
.context("Failed to set power performance level")?;
|
||||
}
|
||||
PerformanceLevel::High | PerformanceLevel::Low => {
|
||||
_ => {
|
||||
deferred_performance_level = Some(performance_level);
|
||||
}
|
||||
}
|
||||
@@ -1452,8 +1464,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)
|
||||
@@ -1461,7 +1471,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)
|
||||
}
|
||||
|
||||
@@ -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, DisplaysInfo,
|
||||
DrmInfo, DrmMemoryInfo, FanStats, IntelClocksTable, IntelDrmInfo, LinkInfo, PowerState,
|
||||
PowerStates, PowerStats, ProcessList, ProcessUtilizationType, TemperatureEntry, VoltageStats,
|
||||
VramStats, config::GpuConfig,
|
||||
ClocksInfo, ClocksTable, ClockspeedStats, DeviceApiInfo, DeviceInfo, DeviceStats, DeviceType,
|
||||
DisplaysInfo, 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![],
|
||||
}
|
||||
})
|
||||
@@ -772,7 +768,7 @@ impl GpuController for IntelGpuController {
|
||||
enabled: true,
|
||||
min_value: None,
|
||||
value,
|
||||
index: None,
|
||||
id: None,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -4,30 +4,26 @@ 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::{
|
||||
gpu_handle::{fan_control::FanInfo, power_profile_mode::PowerProfileModesTable},
|
||||
gpu_handle::{PowerLevelId, fan_control::FanInfo, power_profile_mode::PowerProfileModesTable},
|
||||
hw_mon::Temperature,
|
||||
};
|
||||
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::{
|
||||
CacheInfo, ClocksInfo, ClocksTable, ClockspeedStats, DeviceFlag, DeviceInfo, DeviceStats,
|
||||
DeviceType, DisplaysInfo, 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, DisplaysInfo, 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::{
|
||||
@@ -354,12 +350,12 @@ impl NvidiaGpuController {
|
||||
enabled: true,
|
||||
min_value: Some(u64::from(gpu_min)),
|
||||
value: u64::from(gpu_max),
|
||||
index: Some(
|
||||
id: Some(PowerLevelId::Index(
|
||||
pstate
|
||||
.as_c()
|
||||
.try_into()
|
||||
.expect("Power state always fits in u8"),
|
||||
),
|
||||
)),
|
||||
});
|
||||
|
||||
let (mem_min, mem_max) = device
|
||||
@@ -370,12 +366,12 @@ impl NvidiaGpuController {
|
||||
enabled: true,
|
||||
min_value: Some(u64::from(mem_min)),
|
||||
value: u64::from(mem_max),
|
||||
index: Some(
|
||||
id: Some(PowerLevelId::Index(
|
||||
pstate
|
||||
.as_c()
|
||||
.try_into()
|
||||
.expect("Power state always fits in u8"),
|
||||
),
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -753,7 +749,14 @@ impl GpuController for NvidiaGpuController {
|
||||
|
||||
let active_pstate = device
|
||||
.performance_state()
|
||||
.map(|pstate| pstate.as_c() as usize)
|
||||
.map(|pstate| {
|
||||
PowerLevelId::Index(
|
||||
pstate
|
||||
.as_c()
|
||||
.try_into()
|
||||
.expect("Power state always fits in u8"),
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
|
||||
let fan_range = device.min_max_fan_speed().ok();
|
||||
@@ -835,9 +838,11 @@ impl GpuController for NvidiaGpuController {
|
||||
..Default::default()
|
||||
},
|
||||
performance_level: None,
|
||||
core_power_state: active_pstate,
|
||||
memory_power_state: active_pstate,
|
||||
pcie_power_state: None,
|
||||
active_power_states: active_pstate.map(|active_pstate| ActivePowerStates {
|
||||
core: Some(active_pstate),
|
||||
memory: Some(active_pstate),
|
||||
pcie: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ use amdgpu_sysfs::gpu_handle::{
|
||||
};
|
||||
use anyhow::{Context, anyhow, bail};
|
||||
use lact_schema::{
|
||||
ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, DisplaysInfo, FanControlMode, FanOptions,
|
||||
PmfwOptions, PowerStates, ProcessList, ProfileRule, ProfileWatcherState, ProfilesInfo,
|
||||
ClocksInfo, DeviceApiInfo, DeviceInfo, DeviceListEntry, DeviceStats, DisplaysInfo,
|
||||
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> {
|
||||
@@ -785,7 +802,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(),
|
||||
@@ -1193,13 +1210,13 @@ async fn apply_config_to_controllers(
|
||||
|
||||
#[cfg(all(test, not(miri)))]
|
||||
pub(crate) fn read_pci_db() -> Database {
|
||||
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/tests/data/pci.ids");
|
||||
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../tests/snapshots/pci.ids");
|
||||
Database::read_from_file(&path).unwrap()
|
||||
}
|
||||
|
||||
#[cfg(all(test, miri))]
|
||||
pub(crate) fn read_pci_db() -> Database {
|
||||
let bytes = include_bytes!("../../src/tests/data/pci.ids");
|
||||
let bytes = include_bytes!("../../../tests/snapshots/pci.ids");
|
||||
Database::parse_db(std::io::Cursor::new(bytes)).unwrap()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
0: 500Mhz
|
||||
1: 16Mhz *
|
||||
2: 2400Mhz
|
||||
@@ -9,7 +9,6 @@ use lact_schema::config::GpuConfig;
|
||||
use mock_fs::MockSysfs;
|
||||
use std::{fs, path::PathBuf, sync::OnceLock};
|
||||
use tempfile::tempdir;
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
fn init_tracing() {
|
||||
static TRACING_LOCK: OnceLock<()> = OnceLock::new();
|
||||
@@ -25,7 +24,7 @@ fn init_tracing() {
|
||||
async fn snapshot_everything() {
|
||||
init_tracing();
|
||||
|
||||
let test_data_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/tests/data");
|
||||
let test_data_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../tests/snapshots");
|
||||
let pci_db = read_pci_db();
|
||||
|
||||
for vendor_dir in fs::read_dir(test_data_dir).unwrap().flatten() {
|
||||
@@ -55,78 +54,70 @@ async fn snapshot_everything() {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[tokio::test(flavor = "local")]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
async fn apply_settings() {
|
||||
init_tracing();
|
||||
|
||||
let local_set = LocalSet::new();
|
||||
local_set.spawn_local(async move {
|
||||
let test_data_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/tests/data");
|
||||
let pci_db = read_pci_db();
|
||||
let test_data_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../tests/snapshots");
|
||||
let pci_db = read_pci_db();
|
||||
|
||||
for vendor_dir in fs::read_dir(test_data_dir).unwrap().flatten() {
|
||||
if !vendor_dir.metadata().unwrap().is_dir() {
|
||||
continue;
|
||||
}
|
||||
for vendor_dir in fs::read_dir(test_data_dir).unwrap().flatten() {
|
||||
if !vendor_dir.metadata().unwrap().is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for device_dir in fs::read_dir(vendor_dir.path()).unwrap().flatten() {
|
||||
for entry in fs::read_dir(device_dir.path()).unwrap().flatten() {
|
||||
let name = entry.file_name();
|
||||
let name = name.to_str().unwrap();
|
||||
for device_dir in fs::read_dir(vendor_dir.path()).unwrap().flatten() {
|
||||
for entry in fs::read_dir(device_dir.path()).unwrap().flatten() {
|
||||
let name = entry.file_name();
|
||||
let name = name.to_str().unwrap();
|
||||
|
||||
#[allow(clippy::case_sensitive_file_extension_comparisons)]
|
||||
if name.starts_with("config") && name.ends_with(".yaml") {
|
||||
let test_key = format!(
|
||||
"apply_config/{}/{}/{name}",
|
||||
vendor_dir.file_name().to_string_lossy(),
|
||||
device_dir.file_name().to_string_lossy()
|
||||
);
|
||||
let raw_gpu_config = fs::read_to_string(entry.path()).unwrap();
|
||||
let gpu_config: GpuConfig =
|
||||
serde_norway::from_str(&raw_gpu_config).unwrap();
|
||||
#[allow(clippy::case_sensitive_file_extension_comparisons)]
|
||||
if name.starts_with("config") && name.ends_with(".yaml") {
|
||||
let test_key = format!(
|
||||
"apply_config/{}/{}/{name}",
|
||||
vendor_dir.file_name().to_string_lossy(),
|
||||
device_dir.file_name().to_string_lossy()
|
||||
);
|
||||
let raw_gpu_config = fs::read_to_string(entry.path()).unwrap();
|
||||
let gpu_config: GpuConfig = serde_norway::from_str(&raw_gpu_config).unwrap();
|
||||
|
||||
let mock_fs_dir = tempdir().unwrap();
|
||||
let mock_fs_dir = tempdir().unwrap();
|
||||
|
||||
let mock_fs = MockSysfs::new(device_dir.path());
|
||||
let writes = mock_fs.writes.clone();
|
||||
let mock_fs = MockSysfs::new(device_dir.path());
|
||||
let writes = mock_fs.writes.clone();
|
||||
|
||||
let mount = easy_fuser::spawn_mount(mock_fs, mock_fs_dir.path(), &[], 1)
|
||||
.expect("Could not mount mock fs");
|
||||
let mount = easy_fuser::spawn_mount(mock_fs, mock_fs_dir.path(), &[], 1)
|
||||
.expect("Could not mount mock fs");
|
||||
|
||||
let handler =
|
||||
Handler::with_base_path(mock_fs_dir.path(), Config::default(), &pci_db)
|
||||
.await
|
||||
.unwrap();
|
||||
let gpu_id = &handler.list_devices().await[0].id;
|
||||
|
||||
handler
|
||||
.config
|
||||
.write()
|
||||
let handler =
|
||||
Handler::with_base_path(mock_fs_dir.path(), Config::default(), &pci_db)
|
||||
.await
|
||||
.gpus_mut()
|
||||
.unwrap()
|
||||
.insert(gpu_id.clone(), gpu_config);
|
||||
.unwrap();
|
||||
let gpu_id = &handler.list_devices().await[0].id;
|
||||
|
||||
handler.apply_current_config().await.unwrap();
|
||||
handler
|
||||
.config
|
||||
.write()
|
||||
.await
|
||||
.gpus_mut()
|
||||
.unwrap()
|
||||
.insert(gpu_id.clone(), gpu_config);
|
||||
|
||||
mount.join();
|
||||
mock_fs_dir.close().unwrap();
|
||||
handler.apply_current_config().await.unwrap();
|
||||
|
||||
let write_commands = writes
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(name, contents)| {
|
||||
format!("{}: {contents}", name.to_str().unwrap())
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
assert_debug_snapshot!(test_key, write_commands);
|
||||
}
|
||||
mount.join();
|
||||
mock_fs_dir.close().unwrap();
|
||||
|
||||
let write_commands = writes
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(name, contents)| format!("{}: {contents}", name.to_str().unwrap()))
|
||||
.collect::<Vec<String>>();
|
||||
assert_debug_snapshot!(test_key, write_commands);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
local_set.await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,55 +143,75 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 800
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 1100
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 2700
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 400
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 800
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 800
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 800
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 0
|
||||
},
|
||||
"memory": {
|
||||
"Index": 0
|
||||
}
|
||||
},
|
||||
"busy_percent": 0,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 800,
|
||||
"sensors": {}
|
||||
},
|
||||
"core_power_state": 0,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {}
|
||||
},
|
||||
"memory_power_state": 0,
|
||||
"performance_level": "auto",
|
||||
"power": {
|
||||
"average": 3.103,
|
||||
|
||||
@@ -473,51 +473,75 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 300
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 1900
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 100
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 500
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 625
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 875
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 1
|
||||
},
|
||||
"memory": {
|
||||
"Index": 3
|
||||
},
|
||||
"pcie": {
|
||||
"Index": 1
|
||||
}
|
||||
},
|
||||
"busy_percent": 0,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 31,
|
||||
"sensors": {},
|
||||
"vram_clockspeed": 875
|
||||
},
|
||||
"core_power_state": 1,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {},
|
||||
@@ -528,8 +552,6 @@ expression: device_info
|
||||
"speed_max": 3200,
|
||||
"speed_min": 0
|
||||
},
|
||||
"memory_power_state": 3,
|
||||
"pcie_power_state": 1,
|
||||
"power": {
|
||||
"average": 16.0,
|
||||
"cap_current": 130.0,
|
||||
|
||||
@@ -473,39 +473,59 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 300
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 1900
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 100
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 500
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 625
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 875
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"memory": {
|
||||
"Index": 3
|
||||
},
|
||||
"pcie": {
|
||||
"Index": 1
|
||||
}
|
||||
},
|
||||
"busy_percent": 0,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 10,
|
||||
@@ -522,8 +542,6 @@ expression: device_info
|
||||
"speed_max": 3400,
|
||||
"speed_min": 0
|
||||
},
|
||||
"memory_power_state": 3,
|
||||
"pcie_power_state": 1,
|
||||
"power": {
|
||||
"average": 29.0,
|
||||
"cap_current": 220.0,
|
||||
|
||||
@@ -251,71 +251,103 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 300
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 600
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 900
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 1145
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 4,
|
||||
"id": {
|
||||
"Index": 4
|
||||
},
|
||||
"value": 1215
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 5,
|
||||
"id": {
|
||||
"Index": 5
|
||||
},
|
||||
"value": 1257
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 6,
|
||||
"id": {
|
||||
"Index": 6
|
||||
},
|
||||
"value": 1300
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 7,
|
||||
"id": {
|
||||
"Index": 7
|
||||
},
|
||||
"value": 1366
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 300
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 1000
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 1750
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 2
|
||||
},
|
||||
"memory": {
|
||||
"Index": 2
|
||||
},
|
||||
"pcie": {
|
||||
"Index": 1
|
||||
}
|
||||
},
|
||||
"busy_percent": 11,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 798,
|
||||
"sensors": {},
|
||||
"vram_clockspeed": 1750
|
||||
},
|
||||
"core_power_state": 2,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {},
|
||||
@@ -326,8 +358,6 @@ expression: device_info
|
||||
"speed_max": 3200,
|
||||
"speed_min": 0
|
||||
},
|
||||
"memory_power_state": 2,
|
||||
"pcie_power_state": 1,
|
||||
"performance_level": "auto",
|
||||
"power": {
|
||||
"average": 41.045,
|
||||
|
||||
@@ -433,51 +433,75 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 500
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 2605
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 2700
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 96
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 541
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 675
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 925
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 1
|
||||
},
|
||||
"memory": {
|
||||
"Index": 0
|
||||
},
|
||||
"pcie": {
|
||||
"Index": 1
|
||||
}
|
||||
},
|
||||
"busy_percent": 0,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 20,
|
||||
"sensors": {},
|
||||
"vram_clockspeed": 96
|
||||
},
|
||||
"core_power_state": 1,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {},
|
||||
@@ -488,8 +512,6 @@ expression: device_info
|
||||
"speed_max": 3100,
|
||||
"speed_min": 0
|
||||
},
|
||||
"memory_power_state": 0,
|
||||
"pcie_power_state": 1,
|
||||
"power": {
|
||||
"average": 3.0,
|
||||
"cap_current": 120.0,
|
||||
|
||||
@@ -395,39 +395,59 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 0
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 96
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 541
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 675
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 875
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"memory": {
|
||||
"Index": 0
|
||||
},
|
||||
"pcie": {
|
||||
"Index": 1
|
||||
}
|
||||
},
|
||||
"busy_percent": 0,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 0,
|
||||
@@ -444,8 +464,6 @@ expression: device_info
|
||||
"speed_max": 3300,
|
||||
"speed_min": 0
|
||||
},
|
||||
"memory_power_state": 0,
|
||||
"pcie_power_state": 1,
|
||||
"power": {
|
||||
"average": 3.0,
|
||||
"cap_current": 100.0,
|
||||
|
||||
@@ -435,39 +435,62 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 500
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 2660
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 96
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 456
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 673
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 1000
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 0
|
||||
},
|
||||
"memory": {
|
||||
"Index": 3
|
||||
},
|
||||
"pcie": {
|
||||
"Index": 1
|
||||
}
|
||||
},
|
||||
"busy_percent": 3,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 500,
|
||||
@@ -478,7 +501,6 @@ expression: device_info
|
||||
"target_gpu_clockspeed": 500,
|
||||
"vram_clockspeed": 1000
|
||||
},
|
||||
"core_power_state": 0,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {},
|
||||
@@ -489,8 +511,6 @@ expression: device_info
|
||||
"speed_max": 3400,
|
||||
"speed_min": 0
|
||||
},
|
||||
"memory_power_state": 3,
|
||||
"pcie_power_state": 1,
|
||||
"performance_level": "auto",
|
||||
"power": {
|
||||
"average": 36.0,
|
||||
|
||||
@@ -263,44 +263,66 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 255
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 1841
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 96
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 456
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 772
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 1000
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 1
|
||||
},
|
||||
"memory": {
|
||||
"Index": 0
|
||||
}
|
||||
},
|
||||
"busy_percent": 0,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 0,
|
||||
@@ -314,7 +336,6 @@ expression: device_info
|
||||
"target_gpu_clockspeed": 1200,
|
||||
"vram_clockspeed": 96
|
||||
},
|
||||
"core_power_state": 1,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {},
|
||||
@@ -325,7 +346,6 @@ expression: device_info
|
||||
"speed_max": 4900,
|
||||
"speed_min": 0
|
||||
},
|
||||
"memory_power_state": 0,
|
||||
"performance_level": "manual",
|
||||
"power": {
|
||||
"average": 1.0,
|
||||
|
||||
@@ -263,51 +263,72 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 255
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 2208
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 96
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 456
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 772
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 1124
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 1
|
||||
},
|
||||
"memory": {
|
||||
"Index": 0
|
||||
}
|
||||
},
|
||||
"busy_percent": 0,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 0,
|
||||
"sensors": {},
|
||||
"vram_clockspeed": 96
|
||||
},
|
||||
"core_power_state": 1,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {},
|
||||
@@ -318,7 +339,6 @@ expression: device_info
|
||||
"speed_max": 4900,
|
||||
"speed_min": 0
|
||||
},
|
||||
"memory_power_state": 0,
|
||||
"performance_level": "auto",
|
||||
"power": {
|
||||
"average": 1.0,
|
||||
|
||||
@@ -354,44 +354,69 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 500
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 77
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 2254
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 96
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 456
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 772
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 1218
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 1
|
||||
},
|
||||
"memory": {
|
||||
"Index": 1
|
||||
},
|
||||
"pcie": {
|
||||
"Index": 2
|
||||
}
|
||||
},
|
||||
"busy_percent": 2,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 2011,
|
||||
@@ -406,7 +431,6 @@ expression: device_info
|
||||
"target_gpu_clockspeed": 2011,
|
||||
"vram_clockspeed": 456
|
||||
},
|
||||
"core_power_state": 1,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {
|
||||
@@ -458,8 +482,6 @@ expression: device_info
|
||||
100
|
||||
]
|
||||
},
|
||||
"memory_power_state": 1,
|
||||
"pcie_power_state": 2,
|
||||
"performance_level": "manual",
|
||||
"power": {
|
||||
"average": 32.0,
|
||||
|
||||
@@ -354,51 +354,72 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 500
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 31
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 2219
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 96
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 456
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 772
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 1249
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 1
|
||||
},
|
||||
"memory": {
|
||||
"Index": 3
|
||||
}
|
||||
},
|
||||
"busy_percent": 3,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 31,
|
||||
"sensors": {},
|
||||
"vram_clockspeed": 1249
|
||||
},
|
||||
"core_power_state": 1,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {
|
||||
@@ -442,7 +463,6 @@ expression: device_info
|
||||
100
|
||||
]
|
||||
},
|
||||
"memory_power_state": 3,
|
||||
"performance_level": "auto",
|
||||
"power": {
|
||||
"average": 68.0,
|
||||
|
||||
@@ -353,54 +353,83 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 500
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 789
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 2070
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 96
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 456
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 772
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 875
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 4,
|
||||
"id": {
|
||||
"Index": 4
|
||||
},
|
||||
"value": 1124
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 5,
|
||||
"id": {
|
||||
"Index": 5
|
||||
},
|
||||
"value": 1258
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 1
|
||||
},
|
||||
"memory": {
|
||||
"Index": 4
|
||||
},
|
||||
"pcie": {
|
||||
"Index": 1
|
||||
}
|
||||
},
|
||||
"busy_percent": 25,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 900,
|
||||
@@ -415,7 +444,6 @@ expression: device_info
|
||||
"target_gpu_clockspeed": 900,
|
||||
"vram_clockspeed": 1124
|
||||
},
|
||||
"core_power_state": 1,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {
|
||||
@@ -460,8 +488,6 @@ expression: device_info
|
||||
100
|
||||
]
|
||||
},
|
||||
"memory_power_state": 4,
|
||||
"pcie_power_state": 1,
|
||||
"performance_level": "auto",
|
||||
"power": {
|
||||
"average": 49.0,
|
||||
|
||||
@@ -353,61 +353,99 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"value": 500
|
||||
"id": "Sleep",
|
||||
"value": 19
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"value": 16
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 615
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"value": 2400
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 800
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 888
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 1000
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 96
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 456
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 772
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 875
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 4,
|
||||
"id": {
|
||||
"Index": 4
|
||||
},
|
||||
"value": 1124
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 5,
|
||||
"id": {
|
||||
"Index": 5
|
||||
},
|
||||
"value": 1258
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": "Sleep",
|
||||
"memory": {
|
||||
"Index": 5
|
||||
},
|
||||
"pcie": {
|
||||
"Index": 1
|
||||
}
|
||||
},
|
||||
"busy_percent": 3,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 167,
|
||||
"sensors": {},
|
||||
"vram_clockspeed": 1258
|
||||
},
|
||||
"core_power_state": 1,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {
|
||||
@@ -459,8 +497,6 @@ expression: device_info
|
||||
100
|
||||
]
|
||||
},
|
||||
"memory_power_state": 5,
|
||||
"pcie_power_state": 1,
|
||||
"performance_level": "auto",
|
||||
"power": {
|
||||
"average": 55.0,
|
||||
|
||||
@@ -178,45 +178,61 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 600
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 848
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 2900
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 400
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 800
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 1000
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 1
|
||||
}
|
||||
},
|
||||
"busy_percent": 3,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 848,
|
||||
"sensors": {}
|
||||
},
|
||||
"core_power_state": 1,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {}
|
||||
|
||||
@@ -109,50 +109,68 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 200
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 1100
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 1600
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 400
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 600
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 687
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 687
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 0
|
||||
}
|
||||
},
|
||||
"busy_percent": 0,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 200,
|
||||
"sensors": {}
|
||||
},
|
||||
"core_power_state": 0,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {}
|
||||
|
||||
@@ -241,76 +241,107 @@ expression: device_info
|
||||
"core": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 852
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 991
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 1138
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 1269
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 4,
|
||||
"id": {
|
||||
"Index": 4
|
||||
},
|
||||
"value": 1312
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 5,
|
||||
"id": {
|
||||
"Index": 5
|
||||
},
|
||||
"value": 1474
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 6,
|
||||
"id": {
|
||||
"Index": 6
|
||||
},
|
||||
"value": 1538
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 7,
|
||||
"id": {
|
||||
"Index": 7
|
||||
},
|
||||
"value": 1590
|
||||
}
|
||||
],
|
||||
"vram": [
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 0,
|
||||
"id": {
|
||||
"Index": 0
|
||||
},
|
||||
"value": 167
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 1,
|
||||
"id": {
|
||||
"Index": 1
|
||||
},
|
||||
"value": 500
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 2,
|
||||
"id": {
|
||||
"Index": 2
|
||||
},
|
||||
"value": 700
|
||||
},
|
||||
{
|
||||
"enabled": true,
|
||||
"index": 3,
|
||||
"id": {
|
||||
"Index": 3
|
||||
},
|
||||
"value": 920
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"active_power_states": {
|
||||
"core": {
|
||||
"Index": 0
|
||||
},
|
||||
"memory": {
|
||||
"Index": 0
|
||||
}
|
||||
},
|
||||
"busy_percent": 0,
|
||||
"clockspeed": {
|
||||
"gpu_clockspeed": 26,
|
||||
"sensors": {},
|
||||
"vram_clockspeed": 167
|
||||
},
|
||||
"core_power_state": 0,
|
||||
"fan": {
|
||||
"control_enabled": false,
|
||||
"pmfw_info": {},
|
||||
@@ -321,7 +352,6 @@ expression: device_info
|
||||
"speed_max": 3500,
|
||||
"speed_min": 0
|
||||
},
|
||||
"memory_power_state": 0,
|
||||
"performance_level": "auto",
|
||||
"power": {
|
||||
"average": 8.0,
|
||||
|
||||
@@ -37,6 +37,7 @@ i18n-embed-fl = { workspace = true }
|
||||
rust-embed = { workspace = true }
|
||||
|
||||
plotters = { version = "0.3.7", default-features = false, features = [
|
||||
"area_series",
|
||||
"line_series",
|
||||
"full_palette",
|
||||
"point_series",
|
||||
|
||||
@@ -196,6 +196,7 @@ vram-pstates = VRAM Power States
|
||||
pstates-manual-needed = Performance level must be set to 'manual' to toggle power states
|
||||
enable-pstate-config = Enable power state configuration
|
||||
|
||||
menu = Menu
|
||||
show-historical-charts = Show Graphs
|
||||
show-process-monitor = Show Process Monitor
|
||||
generate-debug-snapshot = Generate Debug Snapshot
|
||||
|
||||
@@ -13,9 +13,3 @@
|
||||
.main-sidebar-container .sidebar-section {
|
||||
margin: 4px 8px 8px 8px;
|
||||
}
|
||||
|
||||
.bootstrap-spinner-large {
|
||||
min-width: 64px;
|
||||
min-height: 64px;
|
||||
-gtk-icon-size: 64px;
|
||||
}
|
||||
|
||||
+148
-121
@@ -6,6 +6,7 @@ pub mod graphs_window;
|
||||
mod info_dialog;
|
||||
mod info_row;
|
||||
mod info_row_level;
|
||||
mod loader;
|
||||
pub(crate) mod msg;
|
||||
mod overdrive_dialog;
|
||||
mod page_section;
|
||||
@@ -20,6 +21,7 @@ use crate::{
|
||||
APP_ID, CONFIG, GUI_VERSION, I18N,
|
||||
app::{
|
||||
about_dialog::{AboutDialog, AboutDialogMsg},
|
||||
ext::RelmLaunchable as _,
|
||||
gpu_selector::GpuSelector,
|
||||
info_dialog::{
|
||||
InfoDialog, InfoDialogConfirmation, InfoDialogData, InfoDialogId, InfoDialogMsg,
|
||||
@@ -45,7 +47,7 @@ use gtk::{
|
||||
use i18n_embed_fl::fl;
|
||||
use lact_client::{ConnectionStatusMsg, DaemonClient};
|
||||
use lact_schema::{
|
||||
DeviceFlag, DeviceListEntry, DeviceStats, DeviceType, GIT_COMMIT, SystemInfo,
|
||||
DeviceApiInfo, DeviceFlag, DeviceListEntry, DeviceStats, DeviceType, GIT_COMMIT, SystemInfo,
|
||||
args::GuiArgs,
|
||||
config::{GpuConfig, Profile},
|
||||
request::{ConfirmCommand, ProfileBase, SetClocksCommand},
|
||||
@@ -62,9 +64,11 @@ use pages::{
|
||||
use relm4::{
|
||||
AsyncComponentSender, Component, ComponentController, MessageBroker, RelmObjectExt,
|
||||
RelmWidgetExt,
|
||||
binding::BoolBinding,
|
||||
actions::{AccelsPlus, ActionGroupName, RelmAction, RelmActionGroup},
|
||||
binding::{Binding as _, BoolBinding},
|
||||
css,
|
||||
loading_widgets::LoadingWidgets,
|
||||
new_action_group, new_stateless_action,
|
||||
prelude::{AsyncComponent, AsyncComponentParts},
|
||||
tokio::{self, time::sleep},
|
||||
view,
|
||||
@@ -88,6 +92,14 @@ const DEFAULT_WINDOW_HEIGHT: i32 = 750;
|
||||
const CONFIRM_RESPONSE_APPLY: &str = "confirm";
|
||||
const CONFIRM_RESPONSE_REVERT: &str = "revert";
|
||||
|
||||
macro_rules! setup_actions {
|
||||
($(($grp:ident, $action:ty, $callback:expr),)*) => {
|
||||
$(
|
||||
$grp.add_action(RelmAction::<$action>::new_stateless(move |_| $callback));
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
pub struct AppModel {
|
||||
daemon_client: DaemonClient,
|
||||
graphs_window: relm4::Controller<GraphsWindow>,
|
||||
@@ -98,6 +110,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>,
|
||||
@@ -114,6 +127,10 @@ pub struct AppModel {
|
||||
system_info: SystemInfo,
|
||||
device_flags: Vec<DeviceFlag>,
|
||||
device_driver: String,
|
||||
|
||||
application: gtk::Application,
|
||||
|
||||
dump_vbios_action: gtk::gio::SimpleAction,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -130,6 +147,22 @@ impl AsyncComponent for AppModel {
|
||||
type Output = ();
|
||||
type CommandOutput = Option<CommandOutput>;
|
||||
|
||||
menu! {
|
||||
app_menu: {
|
||||
section! {
|
||||
&fl!(I18N, "show-process-monitor") => ProcessMonitorAction,
|
||||
},
|
||||
section! {
|
||||
&fl!(I18N, "generate-debug-snapshot") => GenerateDebugSnapshotAction,
|
||||
&fl!(I18N, "dump-vbios") => DumpVBiosAction,
|
||||
},
|
||||
section! {
|
||||
&fl!(I18N, "preferences") => PreferencesAction,
|
||||
&fl!(I18N, "about") => AboutAction,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
view! {
|
||||
#[root]
|
||||
adw::ApplicationWindow::builder()
|
||||
@@ -166,6 +199,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() {},
|
||||
|
||||
@@ -215,75 +249,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",
|
||||
|
||||
#[wrap(Some)]
|
||||
#[name = "header_menu_popover"]
|
||||
set_popover = >k::Popover {
|
||||
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
add_css_class: "header-settings-popover-container",
|
||||
|
||||
gtk::Button {
|
||||
set_label: &fl!(I18N, "show-process-monitor"),
|
||||
connect_clicked[header_menu_popover] => move |_| {
|
||||
header_menu_popover.popdown();
|
||||
APP_BROKER.send(AppMsg::ShowProcessMonitor);
|
||||
},
|
||||
add_css_class: "flat",
|
||||
},
|
||||
|
||||
gtk::Separator {},
|
||||
|
||||
gtk::Button {
|
||||
set_label: &fl!(I18N, "generate-debug-snapshot"),
|
||||
connect_clicked[header_menu_popover] => move |_| {
|
||||
header_menu_popover.popdown();
|
||||
APP_BROKER.send(AppMsg::DebugSnapshot);
|
||||
},
|
||||
add_css_class: "flat",
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_label: &fl!(I18N, "dump-vbios"),
|
||||
connect_clicked[header_menu_popover] => move |_| {
|
||||
header_menu_popover.popdown();
|
||||
APP_BROKER.send(AppMsg::DumpVBios);
|
||||
},
|
||||
add_css_class: "flat",
|
||||
#[watch]
|
||||
set_sensitive: model.device_flags.contains(&DeviceFlag::DumpableVBios),
|
||||
},
|
||||
|
||||
gtk::Separator {},
|
||||
|
||||
gtk::Button {
|
||||
set_label: &fl!(I18N, "preferences"),
|
||||
connect_clicked[header_menu_popover] => move |_| {
|
||||
header_menu_popover.popdown();
|
||||
APP_BROKER.send(AppMsg::ShowPreferencesDialog);
|
||||
},
|
||||
add_css_class: "flat",
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_label: &fl!(I18N, "about"),
|
||||
connect_clicked[header_menu_popover] => move |_| {
|
||||
header_menu_popover.popdown();
|
||||
APP_BROKER.send(AppMsg::ShowAboutDialog);
|
||||
},
|
||||
add_css_class: "flat",
|
||||
},
|
||||
}
|
||||
},
|
||||
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"),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -297,6 +273,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,
|
||||
@@ -341,23 +322,11 @@ 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<LoadingWidgets> {
|
||||
let loader_picture = loader::new();
|
||||
|
||||
view! {
|
||||
#[local]
|
||||
root {
|
||||
@@ -367,9 +336,8 @@ impl AsyncComponent for AppModel {
|
||||
set_valign: gtk::Align::Center,
|
||||
set_halign: gtk::Align::Center,
|
||||
|
||||
gtk::Spinner {
|
||||
add_css_class: "bootstrap-spinner-large",
|
||||
start: (),
|
||||
#[local_ref]
|
||||
loader_picture -> gtk::Picture {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,31 +431,23 @@ impl AsyncComponent for AppModel {
|
||||
|
||||
let info_page = InformationPage::detach_default();
|
||||
|
||||
let oc_page = OcPage::builder()
|
||||
.launch(settings_changed.clone())
|
||||
.forward(sender.input_sender(), |msg| msg);
|
||||
let thermals_page = ThermalsPage::builder().launch(()).detach();
|
||||
let oc_page =
|
||||
OcPage::launch(settings_changed.clone()).forward(sender.input_sender(), |msg| msg);
|
||||
let thermals_page = ThermalsPage::detach_default();
|
||||
|
||||
let software_page = SoftwarePage::builder()
|
||||
.launch((system_info.clone(), daemon_client.embedded))
|
||||
.detach();
|
||||
let software_page = SoftwarePage::detach((system_info.clone(), daemon_client.embedded));
|
||||
|
||||
let crash_page = CrashPage::builder()
|
||||
.launch(String::new())
|
||||
.forward(sender.input_sender(), |msg| msg);
|
||||
let crash_page = CrashPage::launch_default().forward(sender.input_sender(), |msg| msg);
|
||||
|
||||
let overdrive_dialog = OverdriveDialog::builder()
|
||||
.launch((system_info.clone(), root.clone().upcast()))
|
||||
.detach();
|
||||
let overdrive_dialog =
|
||||
OverdriveDialog::detach((system_info.clone(), root.clone().upcast()));
|
||||
|
||||
let preferences_dialog = PreferencesDialog::builder()
|
||||
.launch((system_info.clone(), root.clone()))
|
||||
.detach();
|
||||
let preferences_dialog = PreferencesDialog::detach((system_info.clone(), root.clone()));
|
||||
|
||||
let about_dialog = AboutDialog::builder().launch(root.clone()).detach();
|
||||
let info_dialog = InfoDialog::builder()
|
||||
.launch(root.clone())
|
||||
.forward(sender.input_sender(), |msg| msg);
|
||||
let about_dialog = AboutDialog::detach(root.clone());
|
||||
|
||||
let info_dialog =
|
||||
InfoDialog::launch(root.clone()).forward(sender.input_sender(), |msg| msg);
|
||||
|
||||
let graphs_window = GraphsWindow::detach_default();
|
||||
let process_monitor_window = ProcessMonitorWindow::detach_default();
|
||||
@@ -496,9 +456,36 @@ impl AsyncComponent for AppModel {
|
||||
.launch((devices, initial_gpu_id.clone()))
|
||||
.forward(sender.input_sender(), AppMsg::SelectGpu);
|
||||
|
||||
let profile_selector = ProfileSelector::builder()
|
||||
.launch(())
|
||||
.forward(sender.input_sender(), |msg| msg);
|
||||
let profile_selector =
|
||||
ProfileSelector::launch_default().forward(sender.input_sender(), |msg| msg);
|
||||
|
||||
// create action group and actions for app menu
|
||||
// action group and actions are declared at the bottom of the file
|
||||
let mut actions = RelmActionGroup::<AppActionGroup>::new();
|
||||
let application = root
|
||||
.application()
|
||||
.expect("Failed to get application from root window");
|
||||
setup_actions! {
|
||||
(actions, ProcessMonitorAction, APP_BROKER.send(AppMsg::ShowProcessMonitor)),
|
||||
(actions, GenerateDebugSnapshotAction, APP_BROKER.send(AppMsg::DebugSnapshot)),
|
||||
(actions, PreferencesAction, APP_BROKER.send(AppMsg::ShowPreferencesDialog)),
|
||||
(actions, AboutAction, APP_BROKER.send(AppMsg::ShowAboutDialog)),
|
||||
(actions, QuitAction, APP_BROKER.send(AppMsg::Quit)),
|
||||
}
|
||||
// this action is enabled/disabled conditionally in [Self::update_gpu_data_full]
|
||||
// when the device flags are set, only if the device contains the DumpableVBios flag
|
||||
let dump_vbios_action = {
|
||||
let action = RelmAction::<DumpVBiosAction>::new_stateless(move |_| {
|
||||
APP_BROKER.send(AppMsg::DumpVBios);
|
||||
});
|
||||
let gio_action = action.gio_action().clone();
|
||||
actions.add_action(action);
|
||||
gio_action.set_enabled(false);
|
||||
gio_action
|
||||
};
|
||||
application.set_accelerators_for_action::<PreferencesAction>(&["<Control>comma"]);
|
||||
application.set_accelerators_for_action::<QuitAction>(&["<Control>q"]);
|
||||
root.insert_action_group(AppActionGroup::NAME, Some(&actions.into_action_group()));
|
||||
|
||||
let mut model = AppModel {
|
||||
daemon_client,
|
||||
@@ -516,11 +503,14 @@ 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,
|
||||
device_flags: vec![],
|
||||
device_driver: String::new(),
|
||||
dump_vbios_action,
|
||||
application,
|
||||
};
|
||||
|
||||
if let Err(err) = model.reload_profiles(None).await {
|
||||
@@ -662,13 +652,28 @@ impl AppModel {
|
||||
AppMsg::ReloadData { full } => {
|
||||
self.settings_changed.set_value(false);
|
||||
|
||||
let gpu_id = self.get_selected_gpu_id()?;
|
||||
let gpu_id = Self::get_selected_gpu_id()?;
|
||||
if full {
|
||||
self.update_gpu_data_full(gpu_id, sender).await?;
|
||||
} else {
|
||||
self.update_gpu_data(gpu_id, sender).await?;
|
||||
}
|
||||
}
|
||||
AppMsg::ReloadApiInfo => {
|
||||
let gpu_id = Self::get_selected_gpu_id()?;
|
||||
match self.daemon_client.get_device_api_info(&gpu_id).await {
|
||||
Ok(api_info) => {
|
||||
self.software_page
|
||||
.emit(SoftwarePageMsg::DeviceApiInfo(Some(api_info)));
|
||||
}
|
||||
Err(err) => {
|
||||
error!("could not fetch API info: {err:#}");
|
||||
self.software_page.emit(SoftwarePageMsg::DeviceApiInfo(Some(
|
||||
DeviceApiInfo::default(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
AppMsg::ShowPreferencesDialog => {
|
||||
self.preferences_dialog.emit(PreferencesDialogMsg::Show);
|
||||
}
|
||||
@@ -733,7 +738,7 @@ impl AppModel {
|
||||
filters: vec![json_filter],
|
||||
..Default::default()
|
||||
};
|
||||
let file_picker = OpenDialog::builder().launch(settings);
|
||||
let file_picker = OpenDialog::launch(settings);
|
||||
file_picker.emit(OpenDialogMsg::Open);
|
||||
let stream = file_picker.into_stream();
|
||||
|
||||
@@ -752,7 +757,7 @@ impl AppModel {
|
||||
is_modal: true,
|
||||
..Default::default()
|
||||
};
|
||||
let diag = SaveDialog::builder().launch(settings);
|
||||
let diag = SaveDialog::launch(settings);
|
||||
diag.emit(SaveDialogMsg::SaveAs(format!(
|
||||
"LACT-profile-{}.json",
|
||||
name.as_deref().unwrap_or("default")
|
||||
@@ -791,7 +796,7 @@ impl AppModel {
|
||||
});
|
||||
}
|
||||
AppMsg::ApplyChanges => {
|
||||
self.apply_settings(self.get_selected_gpu_id()?, root, &sender)
|
||||
self.apply_settings(Self::get_selected_gpu_id()?, root, &sender)
|
||||
.await
|
||||
.inspect_err(|_| {
|
||||
sender.input(AppMsg::ReloadData { full: false });
|
||||
@@ -801,7 +806,7 @@ impl AppModel {
|
||||
sender.input(AppMsg::ReloadData { full: false });
|
||||
}
|
||||
AppMsg::ResetClocks => {
|
||||
let gpu_id = self.get_selected_gpu_id()?;
|
||||
let gpu_id = Self::get_selected_gpu_id()?;
|
||||
self.daemon_client
|
||||
.set_clocks_value(&gpu_id, SetClocksCommand::reset())
|
||||
.await?;
|
||||
@@ -811,7 +816,7 @@ impl AppModel {
|
||||
sender.input(AppMsg::ReloadData { full: false });
|
||||
}
|
||||
AppMsg::ResetPmfw => {
|
||||
let gpu_id = self.get_selected_gpu_id()?;
|
||||
let gpu_id = Self::get_selected_gpu_id()?;
|
||||
self.daemon_client.reset_pmfw(&gpu_id).await?;
|
||||
self.daemon_client
|
||||
.confirm_pending_config(ConfirmCommand::Confirm)
|
||||
@@ -826,7 +831,7 @@ impl AppModel {
|
||||
.emit(ProcessMonitorWindowMsg::Show);
|
||||
}
|
||||
AppMsg::DumpVBios => {
|
||||
self.dump_vbios(&self.get_selected_gpu_id()?, root, sender.clone())
|
||||
self.dump_vbios(&Self::get_selected_gpu_id()?, root, sender.clone())
|
||||
.await?;
|
||||
}
|
||||
AppMsg::DebugSnapshot => {
|
||||
@@ -867,7 +872,7 @@ impl AppModel {
|
||||
}
|
||||
AppMsg::FetchProcessList => {
|
||||
if self.process_monitor_window.widget().is_visible()
|
||||
&& let Ok(gpu_id) = self.get_selected_gpu_id()
|
||||
&& let Ok(gpu_id) = Self::get_selected_gpu_id()
|
||||
{
|
||||
match self.daemon_client.get_process_list(&gpu_id).await {
|
||||
Ok(process_list) => {
|
||||
@@ -882,9 +887,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 {
|
||||
@@ -918,6 +927,9 @@ impl AppModel {
|
||||
handle.abort();
|
||||
}
|
||||
}
|
||||
AppMsg::Quit => {
|
||||
self.application.quit();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -973,12 +985,14 @@ impl AppModel {
|
||||
}
|
||||
|
||||
fn set_selected_gpu_id(gpu_id: String) {
|
||||
CONFIG.write().edit(|config| {
|
||||
config.selected_gpu = Some(gpu_id);
|
||||
});
|
||||
if !Self::get_selected_gpu_id().is_ok_and(|current_id| current_id == gpu_id) {
|
||||
CONFIG.write().edit(|config| {
|
||||
config.selected_gpu = Some(gpu_id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn get_selected_gpu_id(&self) -> anyhow::Result<String> {
|
||||
fn get_selected_gpu_id() -> anyhow::Result<String> {
|
||||
CONFIG
|
||||
.read()
|
||||
.selected_gpu
|
||||
@@ -1013,10 +1027,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);
|
||||
@@ -1035,6 +1051,8 @@ impl AppModel {
|
||||
}
|
||||
|
||||
self.device_flags = info.flags.clone();
|
||||
self.dump_vbios_action
|
||||
.set_enabled(self.device_flags.contains(&DeviceFlag::DumpableVBios));
|
||||
self.device_driver = info.driver.clone();
|
||||
|
||||
let update = PageUpdate::Info(info.clone());
|
||||
@@ -1043,8 +1061,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,
|
||||
@@ -1419,3 +1438,11 @@ async fn create_connection() -> anyhow::Result<(DaemonClient, Option<anyhow::Err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_action_group!(pub AppActionGroup, "app");
|
||||
new_stateless_action!(pub ProcessMonitorAction, AppActionGroup, "show-process-monitor");
|
||||
new_stateless_action!(pub GenerateDebugSnapshotAction, AppActionGroup, "generate-debug-snapshot");
|
||||
new_stateless_action!(pub DumpVBiosAction, AppActionGroup, "dump-vbios");
|
||||
new_stateless_action!(pub PreferencesAction, AppActionGroup, "preferences");
|
||||
new_stateless_action!(pub AboutAction, AppActionGroup, "about");
|
||||
new_stateless_action!(pub QuitAction, AppActionGroup, "quit");
|
||||
|
||||
+18
-3
@@ -21,15 +21,30 @@ impl FlowBoxExt for FlowBox {
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
pub trait RelmDefaultLauchable: Component {
|
||||
fn detach_default() -> relm4::Controller<Self>;
|
||||
|
||||
pub trait RelmLaunchable: Component {
|
||||
fn launch(init: <Self as Component>::Init) -> relm4::component::Connector<Self>;
|
||||
|
||||
fn detach(init: <Self as Component>::Init) -> relm4::Controller<Self> {
|
||||
Self::launch(init).detach()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component> RelmLaunchable for T {
|
||||
fn launch(init: <Self as Component>::Init) -> relm4::component::Connector<Self> {
|
||||
Self::builder().launch(init)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RelmDefaultLauchable: RelmLaunchable {
|
||||
fn launch_default() -> relm4::component::Connector<Self>;
|
||||
|
||||
fn detach_default() -> relm4::Controller<Self>;
|
||||
}
|
||||
|
||||
impl<S: Default, T: Component<Init = S>> RelmDefaultLauchable for T {
|
||||
fn detach_default() -> relm4::Controller<Self> {
|
||||
Self::builder().launch(S::default()).detach()
|
||||
Self::detach(S::default())
|
||||
}
|
||||
|
||||
fn launch_default() -> relm4::component::Connector<Self> {
|
||||
|
||||
@@ -83,49 +83,51 @@ impl relm4::Component for GraphsWindow {
|
||||
},
|
||||
},
|
||||
|
||||
add_top_bar = >k::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_spacing: 10,
|
||||
set_margin_horizontal: 10,
|
||||
set_margin_top: 5,
|
||||
set_halign: gtk::Align::End,
|
||||
add_top_bar = >k::Revealer {
|
||||
#[watch]
|
||||
set_visible: model.edit_mode.value(),
|
||||
set_reveal_child: model.edit_mode.value(),
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_spacing: 10,
|
||||
set_margin_horizontal: 10,
|
||||
set_margin_top: 5,
|
||||
set_halign: gtk::Align::End,
|
||||
|
||||
gtk::Label {
|
||||
set_label: &fl!(I18N, "graphs-per-row"),
|
||||
},
|
||||
gtk::Label {
|
||||
set_label: &fl!(I18N, "graphs-per-row"),
|
||||
},
|
||||
|
||||
gtk::SpinButton {
|
||||
set_numeric: true,
|
||||
set_snap_to_ticks: true,
|
||||
set_range: (1.0, 5.0),
|
||||
set_increments: (1.0, 1.0),
|
||||
bind: &model.plots_per_row,
|
||||
connect_value_notify => GraphsWindowMsg::NotifyPlotsPerRow,
|
||||
},
|
||||
gtk::SpinButton {
|
||||
set_numeric: true,
|
||||
set_snap_to_ticks: true,
|
||||
set_range: (1.0, 5.0),
|
||||
set_increments: (1.0, 1.0),
|
||||
bind: &model.plots_per_row,
|
||||
connect_value_notify => GraphsWindowMsg::NotifyPlotsPerRow,
|
||||
},
|
||||
|
||||
gtk::Label {
|
||||
set_label: &fl!(I18N, "time-period-seconds"),
|
||||
},
|
||||
gtk::Label {
|
||||
set_label: &fl!(I18N, "time-period-seconds"),
|
||||
},
|
||||
|
||||
gtk::SpinButton {
|
||||
set_adjustment: &model.time_period_seconds_adj,
|
||||
connect_value_notify => GraphsWindowMsg::NotifyPlotsPerRow,
|
||||
},
|
||||
gtk::SpinButton {
|
||||
set_adjustment: &model.time_period_seconds_adj,
|
||||
connect_value_notify => GraphsWindowMsg::NotifyPlotsPerRow,
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_label: &fl!(I18N, "reset-button"),
|
||||
set_tooltip: &fl!(I18N, "reset-all-graphs-tooltip"),
|
||||
add_css_class: "destructive-action",
|
||||
connect_clicked => GraphsWindowMsg::SetConfig(default_plots()),
|
||||
},
|
||||
gtk::Button {
|
||||
set_label: &fl!(I18N, "reset-button"),
|
||||
set_tooltip: &fl!(I18N, "reset-all-graphs-tooltip"),
|
||||
add_css_class: "destructive-action",
|
||||
connect_clicked => GraphsWindowMsg::SetConfig(default_plots()),
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_icon_name: "list-add-symbolic",
|
||||
connect_clicked => GraphsWindowMsg::AddPlot,
|
||||
set_tooltip: &fl!(I18N, "add-graph"),
|
||||
},
|
||||
gtk::Button {
|
||||
set_icon_name: "list-add-symbolic",
|
||||
connect_clicked => GraphsWindowMsg::AddPlot,
|
||||
set_tooltip: &fl!(I18N, "add-graph"),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
#[wrap(Some)]
|
||||
|
||||
@@ -101,12 +101,15 @@ mod benches {
|
||||
},
|
||||
stat::{StatType, StatsData},
|
||||
};
|
||||
use amdgpu_sysfs::{gpu_handle::PerformanceLevel, hw_mon::Temperature};
|
||||
use amdgpu_sysfs::{
|
||||
gpu_handle::{PerformanceLevel, PowerLevelId},
|
||||
hw_mon::Temperature,
|
||||
};
|
||||
use divan::{Bencher, counter::ItemsCount};
|
||||
use jiff::Timestamp;
|
||||
use lact_schema::{
|
||||
ClockspeedStats, DeviceStats, FanStats, PmfwInfo, PowerStats, TemperatureEntry,
|
||||
VoltageStats, VramStats,
|
||||
ActivePowerStates, ClockspeedStats, DeviceStats, FanStats, PmfwInfo, PowerStats,
|
||||
TemperatureEntry, VoltageStats, VramStats,
|
||||
};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
@@ -158,7 +161,11 @@ mod benches {
|
||||
target_gpu_clockspeed: None,
|
||||
sensors: HashMap::new(),
|
||||
},
|
||||
core_power_state: Some(0),
|
||||
active_power_states: Some(ActivePowerStates {
|
||||
core: Some(PowerLevelId::Index(0)),
|
||||
memory: Some(PowerLevelId::Index(3)),
|
||||
pcie: Some(PowerLevelId::Index(1)),
|
||||
}),
|
||||
fan: FanStats {
|
||||
control_enabled: false,
|
||||
pmfw_info: PmfwInfo::default(),
|
||||
@@ -170,8 +177,6 @@ mod benches {
|
||||
speed_min: Some(0),
|
||||
..Default::default()
|
||||
},
|
||||
memory_power_state: Some(3),
|
||||
pcie_power_state: Some(1),
|
||||
performance_level: Some(PerformanceLevel::Auto),
|
||||
power: PowerStats {
|
||||
average: Some(36.0),
|
||||
|
||||
@@ -70,6 +70,8 @@ impl relm4::factory::FactoryComponent for PlotComponent {
|
||||
set_time_period_seconds: self.time_period.value() as i64,
|
||||
add_binding: (&self.print_extra_info, "print-extra-info"),
|
||||
|
||||
set_stats: self.selected_stats(),
|
||||
|
||||
connect_frame_rendered[sender] => move || {
|
||||
sender.input(PlotComponentMsg::FrameRendered);
|
||||
},
|
||||
@@ -193,8 +195,6 @@ impl relm4::factory::FactoryComponent for PlotComponent {
|
||||
}
|
||||
}
|
||||
|
||||
sender.input(PlotComponentMsg::UpdatedSelection);
|
||||
|
||||
let print_extra_info = BoolBinding::new(false);
|
||||
print_extra_info.connect_value_notify(move |_| {
|
||||
sender.input(PlotComponentMsg::Redraw);
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
use gtk::{
|
||||
glib::{self, ControlFlow},
|
||||
prelude::*,
|
||||
};
|
||||
use std::{cell::Cell, rc::Rc, time::Duration};
|
||||
|
||||
const APP_SVG: &[u8] = include_bytes!("../../../res/io.github.ilya_zlobintsev.LACT.svg");
|
||||
const FRAME_COUNT: usize = 12;
|
||||
const FRAME_INTERVAL: Duration = Duration::from_millis(65);
|
||||
const SIZE: i32 = 192;
|
||||
|
||||
pub(super) fn new() -> gtk::Picture {
|
||||
let textures = Rc::new(build_textures());
|
||||
let picture = gtk::Picture::new();
|
||||
|
||||
picture.set_size_request(SIZE, SIZE);
|
||||
picture.set_can_shrink(false);
|
||||
picture.set_keep_aspect_ratio(true);
|
||||
picture.set_paintable(Some(&textures[0]));
|
||||
|
||||
let frame = Rc::new(Cell::new(0));
|
||||
let picture_weak = picture.downgrade();
|
||||
glib::timeout_add_local(FRAME_INTERVAL, move || {
|
||||
let Some(picture) = picture_weak.upgrade() else {
|
||||
return ControlFlow::Break;
|
||||
};
|
||||
|
||||
let next_frame = (frame.get() + 1) % textures.len();
|
||||
frame.set(next_frame);
|
||||
picture.set_paintable(Some(&textures[next_frame]));
|
||||
|
||||
ControlFlow::Continue
|
||||
});
|
||||
|
||||
picture
|
||||
}
|
||||
|
||||
fn build_textures() -> Vec<gtk::gdk::Texture> {
|
||||
let svg = std::str::from_utf8(APP_SVG)
|
||||
.expect("app SVG should be valid UTF-8")
|
||||
.replace(
|
||||
"<svg height=\"128px\" viewBox=\"0 0 128 128\" width=\"128px\"",
|
||||
&format!("<svg height=\"{SIZE}px\" viewBox=\"0 0 128 128\" width=\"{SIZE}px\""),
|
||||
);
|
||||
|
||||
(0..FRAME_COUNT)
|
||||
.map(|frame| {
|
||||
let angle = -(frame as i32 * 360 / FRAME_COUNT as i32);
|
||||
let svg = svg
|
||||
.replace(
|
||||
"<g id=\"left-fan-blades\">",
|
||||
&format!(
|
||||
"<g id=\"left-fan-blades\" transform=\"rotate({angle} 41.4375 69.824219)\">"
|
||||
),
|
||||
)
|
||||
.replace(
|
||||
"<g id=\"right-fan-blades\">",
|
||||
&format!(
|
||||
"<g id=\"right-fan-blades\" transform=\"rotate({angle} 86.5625 69.824219)\">"
|
||||
),
|
||||
);
|
||||
|
||||
gtk::gdk::Texture::from_bytes(&glib::Bytes::from_owned(svg.into_bytes()))
|
||||
.expect("loader SVG frame should be loadable")
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -14,6 +14,7 @@ pub enum AppMsg {
|
||||
ReloadData {
|
||||
full: bool,
|
||||
},
|
||||
ReloadApiInfo,
|
||||
Stats(Arc<DeviceStats>),
|
||||
ProfilesPolled(Arc<ProfilesInfo>),
|
||||
ApplyChanges,
|
||||
@@ -54,4 +55,5 @@ pub enum AppMsg {
|
||||
ExportProfile(Option<String>),
|
||||
ConnectionStatus(ConnectionStatusMsg),
|
||||
Crash(String),
|
||||
Quit,
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ mod power_states;
|
||||
mod vf_curve;
|
||||
|
||||
use super::PageUpdate;
|
||||
use crate::app::ext::RelmLaunchable as _;
|
||||
use crate::app::pages::oc_page::gpu_stats_section::GpuStatsSectionMsg;
|
||||
use crate::app::pages::oc_page::vf_curve::{VfCurveEditor, VfCurveEditorMsg};
|
||||
use crate::app::{ext::RelmDefaultLauchable, msg::AppMsg};
|
||||
@@ -88,7 +89,7 @@ impl relm4::Component for OcPage {
|
||||
let performance_frame =
|
||||
PerformanceFrame::launch_default().forward(sender.input_sender(), |msg| msg);
|
||||
|
||||
let vf_curve_editor = VfCurveEditor::builder().launch(settings_changed).detach();
|
||||
let vf_curve_editor = VfCurveEditor::detach(settings_changed);
|
||||
|
||||
let model = Self {
|
||||
stats_section,
|
||||
|
||||
@@ -293,5 +293,13 @@ fn level_friendly_name(level: PerformanceLevel) -> String {
|
||||
PerformanceLevel::Low => fl!(I18N, "performance-level-low"),
|
||||
PerformanceLevel::High => fl!(I18N, "performance-level-high"),
|
||||
PerformanceLevel::Manual => fl!(I18N, "performance-level-manual"),
|
||||
PerformanceLevel::ProfileStandard
|
||||
| PerformanceLevel::ProfileMinSclk
|
||||
| PerformanceLevel::ProfileMinMclk
|
||||
| PerformanceLevel::ProfilePeak
|
||||
| PerformanceLevel::PerfDeterminism
|
||||
| PerformanceLevel::ProfileExit => {
|
||||
unreachable!("unsupported performance level in selector")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::power_states_list::PowerStatesList;
|
||||
use crate::{
|
||||
APP_BROKER, I18N,
|
||||
app::{
|
||||
ext::RelmLaunchable as _,
|
||||
msg::AppMsg,
|
||||
page_section_expander::PageSectionExpander,
|
||||
pages::oc_page::power_states::power_states_list::{
|
||||
@@ -18,7 +19,7 @@ use i18n_embed_fl::fl;
|
||||
use indexmap::IndexMap;
|
||||
use lact_schema::{DeviceStats, PowerStates};
|
||||
use relm4::{
|
||||
Component, ComponentController, ComponentParts, ComponentSender, RelmObjectExt,
|
||||
ComponentController, ComponentParts, ComponentSender, RelmObjectExt,
|
||||
binding::{Binding, BoolBinding},
|
||||
css,
|
||||
};
|
||||
@@ -101,18 +102,14 @@ impl relm4::SimpleComponent for PowerStatesFrame {
|
||||
root: Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let core_states_list = PowerStatesList::builder()
|
||||
.launch(PowerStatesListOptions {
|
||||
title: fl!(I18N, "gpu-pstates"),
|
||||
value_suffix: fl!(I18N, "mhz"),
|
||||
})
|
||||
.detach();
|
||||
let vram_states_list = PowerStatesList::builder()
|
||||
.launch(PowerStatesListOptions {
|
||||
title: fl!(I18N, "vram-pstates"),
|
||||
value_suffix: fl!(I18N, "mhz"),
|
||||
})
|
||||
.detach();
|
||||
let core_states_list = PowerStatesList::detach(PowerStatesListOptions {
|
||||
title: fl!(I18N, "gpu-pstates"),
|
||||
value_suffix: fl!(I18N, "mhz"),
|
||||
});
|
||||
let vram_states_list = PowerStatesList::detach(PowerStatesListOptions {
|
||||
title: fl!(I18N, "vram-pstates"),
|
||||
value_suffix: fl!(I18N, "mhz"),
|
||||
});
|
||||
|
||||
let states_configured = BoolBinding::new(false);
|
||||
|
||||
@@ -157,10 +154,12 @@ impl relm4::SimpleComponent for PowerStatesFrame {
|
||||
));
|
||||
}
|
||||
PowerStatesFrameMsg::Stats(stats) => {
|
||||
self.core_states_list
|
||||
.emit(PowerStatesListMsg::ActiveState(stats.core_power_state));
|
||||
self.vram_states_list
|
||||
.emit(PowerStatesListMsg::ActiveState(stats.memory_power_state));
|
||||
self.core_states_list.emit(PowerStatesListMsg::ActiveState(
|
||||
stats.active_power_states.and_then(|states| states.core),
|
||||
));
|
||||
self.vram_states_list.emit(PowerStatesListMsg::ActiveState(
|
||||
stats.active_power_states.and_then(|states| states.memory),
|
||||
));
|
||||
}
|
||||
PowerStatesFrameMsg::VramClockRatio(ratio) => {
|
||||
self.vram_clock_ratio = ratio;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::app::pages::oc_page::power_states::power_states_row::{
|
||||
PowerStateRow, PowerStateRowMsg, PowerStateRowOptions,
|
||||
};
|
||||
use amdgpu_sysfs::gpu_handle::PowerLevelId;
|
||||
use gtk::prelude::{FrameExt, WidgetExt};
|
||||
use lact_schema::PowerState;
|
||||
use relm4::{
|
||||
@@ -23,7 +24,7 @@ pub struct PowerStatesListOptions {
|
||||
#[derive(Debug)]
|
||||
pub enum PowerStatesListMsg {
|
||||
PowerStates(Vec<PowerState>, f64),
|
||||
ActiveState(Option<usize>),
|
||||
ActiveState(Option<PowerLevelId>),
|
||||
Configurable(bool),
|
||||
}
|
||||
|
||||
@@ -92,10 +93,7 @@ impl relm4::SimpleComponent for PowerStatesList {
|
||||
self.is_active_indicator_visible
|
||||
.set_value(active_idx.is_some());
|
||||
for (i, row) in self.states.iter().enumerate() {
|
||||
let is_active = row
|
||||
.power_state
|
||||
.index
|
||||
.is_some_and(|index| Some(usize::from(index)) == active_idx);
|
||||
let is_active = row.power_state.id == active_idx;
|
||||
|
||||
self.states.send(i, PowerStateRowMsg::Active(is_active));
|
||||
}
|
||||
@@ -112,7 +110,10 @@ impl PowerStatesList {
|
||||
self.states
|
||||
.iter()
|
||||
.filter(|row| row.enabled.value())
|
||||
.filter_map(|row| row.power_state.index)
|
||||
.filter_map(|row| match row.power_state.id? {
|
||||
PowerLevelId::Index(index) => Some(index),
|
||||
PowerLevelId::Sleep => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{APP_BROKER, app::msg::AppMsg};
|
||||
use amdgpu_sysfs::gpu_handle::PowerLevelId;
|
||||
use gtk::prelude::{BoxExt, OrientableExt, WidgetExt};
|
||||
use lact_schema::PowerState;
|
||||
use relm4::{RelmObjectExt, RelmWidgetExt, binding::BoolBinding, css};
|
||||
@@ -50,6 +51,7 @@ impl relm4::factory::FactoryComponent for PowerStateRow {
|
||||
append = >k::CheckButton {
|
||||
add_binding: (&self.enabled, "active"),
|
||||
add_binding: (&self.configurable, "visible"),
|
||||
set_sensitive: matches!(self.power_state.id, Some(PowerLevelId::Index(_))),
|
||||
},
|
||||
|
||||
append = >k::Label {
|
||||
@@ -57,11 +59,16 @@ impl relm4::factory::FactoryComponent for PowerStateRow {
|
||||
#[watch]
|
||||
set_class_active: (css::DIM_LABEL, !self.active.value()),
|
||||
set_label: &{
|
||||
let index_text = match self.power_state.id {
|
||||
Some(PowerLevelId::Index(index)) => index.to_string(),
|
||||
Some(PowerLevelId::Sleep) => "S".to_owned(),
|
||||
None => String::new(),
|
||||
};
|
||||
let value_text = match self.power_state.min_value {
|
||||
Some(min) if min != self.power_state.value => format!("{min}-{}", self.power_state.value),
|
||||
_ => self.power_state.value.to_string(),
|
||||
};
|
||||
format!("{}: {value_text} {}", index.current_index(), self.value_suffix)
|
||||
format!("{index_text}: {value_text} {}", self.value_suffix)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::{
|
||||
I18N,
|
||||
app::{APP_BROKER, graphs_window::plot::PlotColorScheme, msg::AppMsg},
|
||||
};
|
||||
use amdgpu_sysfs::gpu_handle::PowerLevelId;
|
||||
use gtk::{
|
||||
gdk,
|
||||
prelude::{
|
||||
@@ -16,7 +17,7 @@ use lact_schema::{ClocksTable, DeviceStats, NvidiaVfPoint, config};
|
||||
use plotters::{
|
||||
chart::{ChartBuilder, SeriesLabelPosition},
|
||||
prelude::{Circle, EmptyElement, IntoDrawingArea as _, Rectangle, Text},
|
||||
series::{DashedLineSeries, LineSeries, PointSeries},
|
||||
series::{AreaSeries, DashedLineSeries, LineSeries, PointSeries},
|
||||
style::{Color as _, ShapeStyle, TextStyle, text_anchor::Pos},
|
||||
};
|
||||
use plotters_cairo::CairoBackend;
|
||||
@@ -46,9 +47,13 @@ pub struct VfCurveEditor {
|
||||
global_settings_changed: BoolBinding,
|
||||
|
||||
cursor_position: Rc<Cell<Option<(f64, f64)>>>,
|
||||
hovered_coords: Rc<Cell<Option<(u32, u32)>>>,
|
||||
hovered_point: Rc<Cell<Option<usize>>>,
|
||||
dragging_point: Rc<Cell<Option<usize>>>,
|
||||
drag_modifiers: Rc<Cell<gdk::ModifierType>>,
|
||||
|
||||
selected_range_start: Rc<Cell<Option<usize>>>,
|
||||
selected_range_end: Rc<Cell<Option<usize>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -151,6 +156,8 @@ impl relm4::Component for VfCurveEditor {
|
||||
#[watch]
|
||||
set_cursor: if model.dragging_point.get().is_some() {
|
||||
gdk::Cursor::from_name("move", None)
|
||||
} else if model.selected_range_start.get().is_some() && model.selected_range_end.get().is_none() {
|
||||
gdk::Cursor::from_name("col-resize", None)
|
||||
} else {
|
||||
None
|
||||
}.as_ref(),
|
||||
@@ -196,8 +203,9 @@ impl relm4::Component for VfCurveEditor {
|
||||
add_css_class: "warning",
|
||||
add_binding: (&model.allow_editing, "active"),
|
||||
|
||||
connect_toggled => move |_| {
|
||||
connect_toggled[drawing_area] => move |_| {
|
||||
APP_BROKER.send(AppMsg::SettingsChanged);
|
||||
drawing_area.queue_draw();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -270,9 +278,12 @@ impl relm4::Component for VfCurveEditor {
|
||||
cursor_position: Rc::new(Cell::new(None)),
|
||||
visible_range_start: gtk::Adjustment::new(30.0, 0.0, 100.0, 1.0, 10.0, 0.0),
|
||||
visible_range_end: gtk::Adjustment::new(100.0, 0.0, 100.0, 1.0, 10.0, 0.0),
|
||||
hovered_coords: Rc::new(Cell::new(None)),
|
||||
hovered_point: Rc::new(Cell::new(None)),
|
||||
dragging_point: Rc::new(Cell::new(None)),
|
||||
drag_modifiers: Rc::new(Cell::new(gdk::ModifierType::empty())),
|
||||
selected_range_start: Rc::new(Cell::new(None)),
|
||||
selected_range_end: Rc::new(Cell::new(None)),
|
||||
};
|
||||
|
||||
let widgets = view_output!();
|
||||
@@ -313,15 +324,35 @@ impl relm4::Component for VfCurveEditor {
|
||||
self.drag_modifiers.set(modifiers);
|
||||
}
|
||||
VfCurveEditorMsg::DragStart => {
|
||||
if let Some(point) = self.hovered_point.get()
|
||||
&& self.allow_editing.value()
|
||||
{
|
||||
if !self.allow_editing.value() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(point) = self.hovered_point.get() {
|
||||
self.dragging_point.set(Some(point));
|
||||
} else {
|
||||
self.selected_range_start
|
||||
.set(self.hovered_coords.get().map(|(x, _)| x as usize));
|
||||
self.selected_range_end.set(None);
|
||||
}
|
||||
}
|
||||
VfCurveEditorMsg::DragEnd => {
|
||||
if self.dragging_point.take().is_some() {
|
||||
APP_BROKER.send(AppMsg::SettingsChanged);
|
||||
} else if self.allow_editing.value()
|
||||
&& let Some(selected_start) = self.selected_range_start.get()
|
||||
{
|
||||
if let Some(selected_end) = self.hovered_coords.get().map(|(x, _)| x as usize)
|
||||
&& self.points.borrow().iter().any(|p| {
|
||||
cmp::min(selected_start, selected_end) < (p.voltage as usize)
|
||||
&& (p.voltage as usize) < cmp::max(selected_start, selected_end)
|
||||
})
|
||||
{
|
||||
self.selected_range_end.set(Some(selected_end));
|
||||
} else {
|
||||
self.selected_range_start.set(None);
|
||||
self.selected_range_end.set(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
VfCurveEditorMsg::FlattenCurve => {
|
||||
@@ -458,7 +489,11 @@ impl VfCurveEditor {
|
||||
{
|
||||
let mut label = format!("Current: {current_clock} MHz @ {current_voltage} mV");
|
||||
|
||||
if stats.core_power_state != Some(0) {
|
||||
if stats
|
||||
.active_power_states
|
||||
.and_then(|states| states.core)
|
||||
.is_some_and(|state| state != PowerLevelId::Index(0))
|
||||
{
|
||||
label.push_str(" (Idle)");
|
||||
}
|
||||
|
||||
@@ -491,18 +526,43 @@ impl VfCurveEditor {
|
||||
|
||||
let active_style = colors.text;
|
||||
let hovered_style = colors.accent_bg;
|
||||
let selected_style = colors.accent_bg.mix(0.8);
|
||||
let selected_area_style = colors.text.mix(0.1);
|
||||
|
||||
let hovered_point = self
|
||||
.dragging_point
|
||||
.get()
|
||||
.or_else(|| self.hovered_point.get());
|
||||
|
||||
if !self.allow_editing.value() {
|
||||
self.selected_range_start.set(None);
|
||||
self.selected_range_end.set(None);
|
||||
}
|
||||
|
||||
if let Some((selected_start, selected_end)) = self.get_selected_range() {
|
||||
let x_values = [selected_start, selected_end];
|
||||
chart
|
||||
.draw_series(AreaSeries::new(
|
||||
x_values.map(|x| (x as u32, y_spec.end)),
|
||||
0,
|
||||
selected_area_style,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let main_series = chart
|
||||
.draw_series(PointSeries::of_element(
|
||||
points.iter().map(vf_point_coords).enumerate(),
|
||||
3,
|
||||
ShapeStyle::from(&colors.success).filled(),
|
||||
&|(i, coord), mut size, mut style| {
|
||||
if let Some((selected_start, selected_end)) = self.get_selected_range() {
|
||||
let voltage = coord.0 as usize;
|
||||
if selected_start < voltage && voltage < selected_end {
|
||||
style.color = selected_style.to_rgba();
|
||||
}
|
||||
}
|
||||
|
||||
let is_active = self.stats.borrow().voltage.gpu == Some(coord.0 as u64);
|
||||
if is_active {
|
||||
style.color = active_style.to_rgba();
|
||||
@@ -588,6 +648,7 @@ impl VfCurveEditor {
|
||||
.cursor_position
|
||||
.get()
|
||||
.and_then(|(x, y)| translate((x as i32, y as i32)));
|
||||
self.hovered_coords.set(hovered_coords);
|
||||
|
||||
let hovered_point = hovered_coords.and_then(|(voltage, freq)| {
|
||||
points
|
||||
@@ -606,6 +667,16 @@ impl VfCurveEditor {
|
||||
});
|
||||
self.hovered_point.set(hovered_point);
|
||||
|
||||
if let Some(point_idx) = self.dragging_point.get()
|
||||
&& let Some((selected_start, selected_end)) = self.get_selected_range()
|
||||
{
|
||||
let voltage = points[point_idx].voltage as usize;
|
||||
if voltage < selected_start || voltage > selected_end {
|
||||
self.selected_range_start.set(None);
|
||||
self.selected_range_end.set(None);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((_voltage, freq)) = hovered_coords
|
||||
&& let Some(point_idx) = self.dragging_point.get()
|
||||
{
|
||||
@@ -623,6 +694,16 @@ impl VfCurveEditor {
|
||||
point.freq = new_freq;
|
||||
}
|
||||
}
|
||||
} else if let Some((selected_start, selected_end)) = self.get_selected_range() {
|
||||
for point in points.iter_mut() {
|
||||
let voltage = point.voltage as usize;
|
||||
if selected_start < voltage && voltage < selected_end {
|
||||
let new_freq = (point.freq as i32 + drag_delta) as u32;
|
||||
if new_freq > 0 {
|
||||
point.freq = new_freq;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
points[point_idx].freq = new_freq;
|
||||
}
|
||||
@@ -642,6 +723,30 @@ impl VfCurveEditor {
|
||||
(cmp::min(start, end), cmp::max(end, start))
|
||||
}
|
||||
|
||||
fn get_selected_range(&self) -> Option<(usize, usize)> {
|
||||
match (
|
||||
self.selected_range_start.get(),
|
||||
self.selected_range_end.get(),
|
||||
) {
|
||||
(Some(selected_start), Some(selected_end)) => Some((
|
||||
cmp::min(selected_start, selected_end),
|
||||
cmp::max(selected_start, selected_end),
|
||||
)),
|
||||
(Some(selected_start), None) => {
|
||||
let selected_end = self
|
||||
.hovered_coords
|
||||
.get()
|
||||
.map(|(x, _)| x as usize)
|
||||
.unwrap_or(selected_start);
|
||||
Some((
|
||||
cmp::min(selected_start, selected_end),
|
||||
cmp::max(selected_start, selected_end),
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.points.borrow().is_empty()
|
||||
}
|
||||
|
||||
@@ -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 = >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 <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))
|
||||
})
|
||||
|
||||
@@ -467,6 +467,7 @@ impl relm4::Component for ThermalsPage {
|
||||
|
||||
let msg = CurveSetupMsg {
|
||||
curve: stats.fan.curve.clone().unwrap_or_else(default_fan_curve),
|
||||
hw_based: self.has_pmfw,
|
||||
current_temperatures: stats.temps.clone(),
|
||||
temperature_key: stats.fan.temperature_key.clone(),
|
||||
spindown_delay: stats.fan.spindown_delay_ms,
|
||||
|
||||
@@ -51,6 +51,8 @@ const PERCENTAGE_DRAG_MARGIN: f32 = 0.04;
|
||||
#[derive(Clone)]
|
||||
pub(super) struct FanCurveFrame {
|
||||
pmfw_options: PmfwOptions,
|
||||
/// PMFW fan control on AMD RDNA3+ with fixed length
|
||||
hw_based_fan_curve: Rc<AtomicBool>,
|
||||
|
||||
data: Rc<RefCell<Vec<(i32, f32)>>>,
|
||||
speed_range: Rc<RefCell<RangeInclusive<f32>>>,
|
||||
@@ -89,6 +91,7 @@ pub(super) enum FanCurveFrameMsg {
|
||||
#[derive(Debug)]
|
||||
pub(super) struct CurveSetupMsg {
|
||||
pub curve: FanCurveMap,
|
||||
pub hw_based: bool,
|
||||
pub current_temperatures: HashMap<String, TemperatureEntry>,
|
||||
pub temperature_key: Option<String>,
|
||||
pub speed_range: RangeInclusive<f32>,
|
||||
@@ -156,14 +159,14 @@ impl relm4::Component for FanCurveFrame {
|
||||
set_icon_name: "list-add-symbolic",
|
||||
connect_clicked => FanCurveFrameMsg::AddPoint,
|
||||
#[watch]
|
||||
set_visible: model.pmfw_options.is_empty(),
|
||||
set_visible: !model.hw_based_fan_curve.load(Ordering::SeqCst),
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_icon_name: "list-remove-symbolic",
|
||||
connect_clicked => FanCurveFrameMsg::RemovePoint,
|
||||
#[watch]
|
||||
set_visible: model.pmfw_options.is_empty(),
|
||||
set_visible: !model.hw_based_fan_curve.load(Ordering::SeqCst),
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
@@ -196,7 +199,7 @@ impl relm4::Component for FanCurveFrame {
|
||||
#[template]
|
||||
FanSettingRow {
|
||||
#[watch]
|
||||
set_visible: model.pmfw_options.is_empty(),
|
||||
set_visible: !model.hw_based_fan_curve.load(Ordering::SeqCst),
|
||||
|
||||
#[template_child]
|
||||
label {
|
||||
@@ -220,7 +223,7 @@ impl relm4::Component for FanCurveFrame {
|
||||
#[template]
|
||||
FanSettingRow {
|
||||
#[watch]
|
||||
set_visible: model.pmfw_options.is_empty(),
|
||||
set_visible: !model.hw_based_fan_curve.load(Ordering::SeqCst),
|
||||
|
||||
#[template_child]
|
||||
label {
|
||||
@@ -327,28 +330,9 @@ impl relm4::Component for FanCurveFrame {
|
||||
let temp_keys = gtk::StringList::default();
|
||||
let current_temp_key = U32Binding::new(0u32);
|
||||
|
||||
let change_signals = [
|
||||
&spindown_delay_adj,
|
||||
&change_threshold_adj,
|
||||
&auto_threshold_adj,
|
||||
]
|
||||
.into_iter()
|
||||
.map(|adj| {
|
||||
let signal = adj.connect_value_changed(|_| {
|
||||
APP_BROKER.send(AppMsg::SettingsChanged);
|
||||
});
|
||||
(adj.clone().upcast(), signal)
|
||||
})
|
||||
.chain([(
|
||||
current_temp_key.clone().upcast(),
|
||||
current_temp_key.connect_value_notify(|_| {
|
||||
APP_BROKER.send(AppMsg::SettingsChanged);
|
||||
}),
|
||||
)])
|
||||
.collect();
|
||||
|
||||
let model = Self {
|
||||
let mut model = Self {
|
||||
pmfw_options,
|
||||
hw_based_fan_curve: Rc::new(AtomicBool::new(false)),
|
||||
is_dragging: Rc::new(AtomicBool::new(false)),
|
||||
speed_range: Rc::new(RefCell::new(DEFAULT_SPEED_RANGE)),
|
||||
temperature_range: Rc::new(RefCell::new(DEFAULT_TEMP_RANGE)),
|
||||
@@ -357,7 +341,7 @@ impl relm4::Component for FanCurveFrame {
|
||||
auto_threshold_adj,
|
||||
temp_keys,
|
||||
current_temp_key,
|
||||
change_signals,
|
||||
change_signals: Rc::default(),
|
||||
data: Rc::default(),
|
||||
drag_coord: Rc::default(),
|
||||
drag_point: Rc::default(),
|
||||
@@ -367,8 +351,29 @@ impl relm4::Component for FanCurveFrame {
|
||||
|
||||
let label_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Horizontal);
|
||||
let spin_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Horizontal);
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
model.change_signals = [
|
||||
&model.spindown_delay_adj,
|
||||
&model.change_threshold_adj,
|
||||
&model.auto_threshold_adj,
|
||||
]
|
||||
.into_iter()
|
||||
.map(|adj| {
|
||||
let signal = adj.connect_value_changed(|_| {
|
||||
APP_BROKER.send(AppMsg::SettingsChanged);
|
||||
});
|
||||
(adj.clone().upcast(), signal)
|
||||
})
|
||||
.chain([(
|
||||
model.current_temp_key.clone().upcast(),
|
||||
model.current_temp_key.connect_value_notify(|_| {
|
||||
APP_BROKER.send(AppMsg::SettingsChanged);
|
||||
}),
|
||||
)])
|
||||
.collect();
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
@@ -386,6 +391,8 @@ impl relm4::Component for FanCurveFrame {
|
||||
.collect();
|
||||
*self.speed_range.borrow_mut() = msg.speed_range;
|
||||
*self.temperature_range.borrow_mut() = msg.temperature_range.clone();
|
||||
self.hw_based_fan_curve
|
||||
.store(msg.hw_based, Ordering::SeqCst);
|
||||
|
||||
for (adj, signal) in self.change_signals.iter() {
|
||||
adj.block_signal(signal);
|
||||
@@ -529,7 +536,7 @@ impl FanCurveFrame {
|
||||
}
|
||||
|
||||
fn temp_keys_available(&self) -> bool {
|
||||
self.pmfw_options.is_empty()
|
||||
!self.hw_based_fan_curve.load(Ordering::SeqCst)
|
||||
&& self.temp_keys.n_items() > 1
|
||||
&& (self.auto_threshold_adj.upper() == 0.0) // Disable key selection on nvidia
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ mod profile_row;
|
||||
pub mod profile_rule_window;
|
||||
|
||||
use crate::I18N;
|
||||
use crate::app::ext::RelmLaunchable as _;
|
||||
use crate::app::msg::AppMsg;
|
||||
use crate::app::profiles::new_profile_dialog::NewProfileDialog;
|
||||
use crate::app::profiles::profile_rename_dialog::ProfileRenameDialog;
|
||||
@@ -299,7 +300,7 @@ impl Component for ProfileSelector {
|
||||
auto_switch: *auto,
|
||||
parent: root.clone().upcast::<gtk::Widget>(),
|
||||
};
|
||||
let rule_window = ProfileRuleWindow::builder().launch(params).into_stream();
|
||||
let rule_window = ProfileRuleWindow::launch(params).into_stream();
|
||||
|
||||
sender.clone().oneshot_command(async move {
|
||||
if let Some((name, rule, hooks)) = rule_window.recv_one().await {
|
||||
|
||||
+21
-9
@@ -15,7 +15,7 @@ pub use response::Response;
|
||||
|
||||
use amdgpu_sysfs::{
|
||||
gpu_handle::{
|
||||
PerformanceLevel,
|
||||
PerformanceLevel, PowerLevelId,
|
||||
fan_control::FanInfo,
|
||||
overdrive::{ClocksTable as _, ClocksTableGen as AmdClocksTableGen},
|
||||
},
|
||||
@@ -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 {
|
||||
@@ -549,12 +555,18 @@ pub struct DeviceStats {
|
||||
pub temps: HashMap<String, TemperatureEntry>,
|
||||
pub busy_percent: Option<u8>,
|
||||
pub performance_level: Option<PerformanceLevel>,
|
||||
pub core_power_state: Option<usize>,
|
||||
pub memory_power_state: Option<usize>,
|
||||
pub pcie_power_state: Option<usize>,
|
||||
pub active_power_states: Option<ActivePowerStates>,
|
||||
pub throttle_info: Option<BTreeMap<String, Vec<String>>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)]
|
||||
pub struct ActivePowerStates {
|
||||
pub core: Option<PowerLevelId>,
|
||||
pub memory: Option<PowerLevelId>,
|
||||
pub pcie: Option<PowerLevelId>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TemperatureEntry {
|
||||
#[serde(flatten)]
|
||||
@@ -693,7 +705,7 @@ pub struct PowerState {
|
||||
pub enabled: bool,
|
||||
pub min_value: Option<u64>,
|
||||
pub value: u64,
|
||||
pub index: Option<u8>,
|
||||
pub id: Option<PowerLevelId>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
||||
@@ -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,
|
||||
|
||||
+7
-1
@@ -35,7 +35,8 @@
|
||||
},
|
||||
"1458": {
|
||||
"2424": "GIGABYTE RX 9070 XT Gaming OC",
|
||||
"2426": "GIGABYTE RX 9070 XT Elite"
|
||||
"2426": "GIGABYTE RX 9070 XT Elite",
|
||||
"2437": "GIGABYTE RX 9070 XT Gaming OC ICE"
|
||||
}
|
||||
},
|
||||
"744c": {
|
||||
@@ -392,6 +393,11 @@
|
||||
"f296": "Palit RTX 4090 GameRock OC"
|
||||
}
|
||||
},
|
||||
"2689": {
|
||||
"1458": {
|
||||
"4162": "GIGABYTE RTX 4070 Ti SUPER WindForce MAX OC"
|
||||
}
|
||||
},
|
||||
"2704": {
|
||||
"10de": {
|
||||
"167a": "NVIDIA RTX 4080 Founders Edition"
|
||||
|
||||
@@ -131,6 +131,7 @@
|
||||
<path d="m 61.417969 69.824219 c 0 11.035156 -8.945313 19.980469 -19.980469 19.980469 s -19.980469 -8.945313 -19.980469 -19.980469 c 0 -11.035157 8.945313 -19.980469 19.980469 -19.980469 s 19.980469 8.945312 19.980469 19.980469 z m 0 0" fill="#171420"/>
|
||||
<path d="m 108.523438 69.824219 c 0 12.128906 -9.832032 21.957031 -21.960938 21.957031 c -12.125 0 -21.957031 -9.828125 -21.957031 -21.957031 c 0 -12.128907 9.832031 -21.957031 21.957031 -21.957031 c 12.128906 0 21.960938 9.828124 21.960938 21.957031 z m 0 0" fill="url(#d)"/>
|
||||
<path d="m 106.542969 69.824219 c 0 11.035156 -8.945313 19.980469 -19.980469 19.980469 s -19.980469 -8.945313 -19.980469 -19.980469 c 0 -11.035157 8.945313 -19.980469 19.980469 -19.980469 s 19.980469 8.945312 19.980469 19.980469 z m 0 0" fill="#171420"/>
|
||||
<g id="left-fan-blades">
|
||||
<path d="m 59.769531 66.050781 c -0.105469 -0.503906 -0.320312 -0.863281 -0.71875 -1.191406 c -3.167969 -2.617187 -7.558593 -2.441406 -13.179687 0.527344 c -0.367188 -0.363281 -0.761719 -0.675781 -1.207032 -0.941407 c 3.871094 -3.472656 7.402344 -4.933593 10.589844 -4.375 c 0.9375 0.164063 1.589844 -0.914062 1.007813 -1.667968 c -1.292969 -1.679688 -2.761719 -3.042969 -4.53125 -4.207032 c -0.429688 -0.285156 -0.835938 -0.386718 -1.347657 -0.335937 c -4.089843 0.386719 -7.074218 3.617187 -8.945312 9.691406 c -0.519531 0 -1.019531 0.0625 -1.523438 0.1875 c 0.28125 -5.195312 1.746094 -8.722656 4.394532 -10.582031 c 0.78125 -0.546875 0.480468 -1.769531 -0.464844 -1.894531 c -2.101562 -0.269531 -4.105469 -0.195313 -6.179688 0.230469 c -0.503906 0.105468 -0.863281 0.320312 -1.191406 0.714843 c -2.617187 3.167969 -2.441406 7.5625 0.527344 13.179688 c -0.367188 0.367187 -0.675781 0.765625 -0.941406 1.210937 c -3.476563 -3.875 -4.933594 -7.402344 -4.378906 -10.589844 c 0.164062 -0.941406 -0.910157 -1.589843 -1.667969 -1.007812 c -1.675781 1.292969 -3.042969 2.761719 -4.207031 4.53125 c -0.28125 0.429688 -0.382813 0.835938 -0.335938 1.347656 c 0.390625 4.089844 3.621094 7.074219 9.695312 8.945313 c 0 0.519531 0.058594 1.019531 0.1875 1.523437 c -5.195312 -0.285156 -8.722656 -1.75 -10.582031 -4.394531 c -0.550781 -0.78125 -1.769531 -0.480469 -1.894531 0.464844 c -0.269531 2.101562 -0.195312 4.105469 0.230469 6.179687 c 0.101562 0.503906 0.320312 0.863282 0.714843 1.191406 c 3.167969 2.617188 7.5625 2.441407 13.179688 -0.527343 c 0.367188 0.363281 0.765625 0.675781 1.207031 0.941406 c -3.871093 3.472656 -7.402343 4.933594 -10.585937 4.375 c -0.941406 -0.164063 -1.59375 0.914063 -1.011719 1.667969 c 1.292969 1.679687 2.765625 3.042968 4.53125 4.207031 c 0.429687 0.285156 0.839844 0.386719 1.351563 0.335937 c 4.089843 -0.386718 7.070312 -3.617187 8.945312 -9.691406 c 0.515625 0 1.015625 -0.0625 1.519531 -0.1875 c -0.28125 5.195313 -1.746093 8.722656 -4.394531 10.582032 c -0.777344 0.546874 -0.476562 1.769531 0.46875 1.890624 c 2.097656 0.273438 4.105469 0.199219 6.179688 -0.226562 c 0.503906 -0.105469 0.863281 -0.320312 1.1875 -0.71875 c 2.621093 -3.164062 2.445312 -7.558594 -0.527344 -13.175781 c 0.367187 -0.367188 0.679687 -0.765625 0.945312 -1.210938 c 3.472656 3.875 4.933594 7.402344 4.375 10.589844 c -0.164062 0.941406 0.914063 1.589844 1.667969 1.007813 c 1.675781 -1.292969 3.042969 -2.761719 4.207031 -4.53125 c 0.285156 -0.429688 0.382813 -0.835938 0.335938 -1.347657 c -0.390625 -4.089843 -3.621094 -7.074219 -9.691406 -8.945312 c 0 -0.519531 -0.0625 -1.019531 -0.1875 -1.523438 c 5.195312 0.285157 8.722656 1.746094 10.582031 4.394531 c 0.546875 0.78125 1.769531 0.480469 1.890625 -0.464843 c 0.273437 -2.101563 0.199218 -4.105469 -0.226563 -6.179688 z m -18.332031 9 c -2.886719 0 -5.230469 -2.339843 -5.230469 -5.226562 s 2.34375 -5.226563 5.230469 -5.226563 s 5.226562 2.339844 5.226562 5.226563 s -2.339843 5.226562 -5.226562 5.226562 z m 0 0"/>
|
||||
<path d="m 39.914062 63.738281 c 0.28125 -5.195312 1.746094 -8.722656 4.394532 -10.582031 c 0.78125 -0.546875 0.480468 -1.769531 -0.464844 -1.894531 c -2.101562 -0.269531 -4.105469 -0.195313 -6.179688 0.230469 c -0.503906 0.105468 -0.863281 0.320312 -1.191406 0.714843 c -2.617187 3.167969 -2.441406 7.5625 0.527344 13.179688 z m 0 0" fill="url(#f)"/>
|
||||
<path d="m 44.664062 64.445312 c 3.871094 -3.472656 7.402344 -4.933593 10.589844 -4.375 c 0.9375 0.164063 1.589844 -0.914062 1.007813 -1.667968 c -1.292969 -1.679688 -2.761719 -3.042969 -4.53125 -4.207032 c -0.429688 -0.285156 -0.835938 -0.386718 -1.347657 -0.335937 c -4.089843 0.386719 -7.074218 3.617187 -8.945312 9.691406 z m 0 0" fill="url(#g)"/>
|
||||
@@ -140,8 +141,10 @@
|
||||
<path d="m 38.207031 75.203125 c -3.871093 3.472656 -7.402343 4.933594 -10.585937 4.375 c -0.941406 -0.164063 -1.59375 0.914063 -1.011719 1.667969 c 1.292969 1.679687 2.765625 3.042968 4.53125 4.207031 c 0.429687 0.285156 0.839844 0.386719 1.351563 0.335937 c 4.089843 -0.386718 7.070312 -3.617187 8.945312 -9.691406 z m 0 0" fill="url(#k)"/>
|
||||
<path d="m 35.351562 71.34375 c -5.195312 -0.28125 -8.722656 -1.746094 -10.582031 -4.390625 c -0.550781 -0.78125 -1.769531 -0.480469 -1.894531 0.464844 c -0.269531 2.101562 -0.195312 4.105469 0.230469 6.179687 c 0.101562 0.503906 0.320312 0.863282 0.714843 1.191406 c 3.167969 2.617188 7.5625 2.441407 13.179688 -0.527343 z m 0 0" fill="url(#l)"/>
|
||||
<path d="m 36.058594 66.597656 c -3.476563 -3.875 -4.933594 -7.402344 -4.378906 -10.589844 c 0.164062 -0.941406 -0.910157 -1.589843 -1.667969 -1.007812 c -1.675781 1.292969 -3.042969 2.761719 -4.207031 4.53125 c -0.28125 0.429688 -0.382813 0.835938 -0.335938 1.347656 c 0.390625 4.089844 3.621094 7.074219 9.695312 8.945313 z m 0 0" fill="url(#m)"/>
|
||||
</g>
|
||||
<path d="m 47.898438 69.824219 c 0 3.574219 -2.894532 6.46875 -6.460938 6.46875 c -3.570312 0 -6.464844 -2.894531 -6.464844 -6.46875 s 2.894532 -6.46875 6.464844 -6.46875 c 3.566406 0 6.460938 2.894531 6.460938 6.46875 z m 0 0" fill="url(#n)"/>
|
||||
<path d="m 46.957031 69.824219 c 0 3.050781 -2.472656 5.523437 -5.519531 5.523437 c -3.050781 0 -5.519531 -2.472656 -5.519531 -5.523437 s 2.46875 -5.523438 5.519531 -5.523438 c 3.046875 0 5.519531 2.472657 5.519531 5.523438 z m 0 0" fill="#342f3b"/>
|
||||
<g id="right-fan-blades">
|
||||
<path d="m 104.894531 66.050781 c -0.101562 -0.503906 -0.320312 -0.863281 -0.714843 -1.191406 c -3.167969 -2.617187 -7.5625 -2.441406 -13.179688 0.527344 c -0.367188 -0.363281 -0.765625 -0.675781 -1.207031 -0.941407 c 3.871093 -3.472656 7.402343 -4.933593 10.585937 -4.375 c 0.941406 0.164063 1.59375 -0.914062 1.011719 -1.667968 c -1.292969 -1.679688 -2.765625 -3.042969 -4.53125 -4.207032 c -0.429687 -0.285156 -0.839844 -0.386718 -1.351563 -0.335937 c -4.089843 0.386719 -7.070312 3.617187 -8.945312 9.691406 c -0.515625 0 -1.015625 0.0625 -1.519531 0.1875 c 0.28125 -5.195312 1.746093 -8.722656 4.394531 -10.582031 c 0.777344 -0.546875 0.476562 -1.769531 -0.46875 -1.894531 c -2.097656 -0.269531 -4.105469 -0.195313 -6.179688 0.230469 c -0.503906 0.105468 -0.863281 0.320312 -1.1875 0.714843 c -2.621093 3.167969 -2.445312 7.5625 0.527344 13.179688 c -0.367187 0.367187 -0.679687 0.765625 -0.945312 1.210937 c -3.472656 -3.875 -4.933594 -7.402344 -4.375 -10.589844 c 0.164062 -0.941406 -0.914063 -1.589843 -1.667969 -1.007812 c -1.675781 1.292969 -3.042969 2.761719 -4.207031 4.53125 c -0.285156 0.429688 -0.382813 0.835938 -0.335938 1.347656 c 0.390625 4.089844 3.621094 7.074219 9.691406 8.945313 c 0 0.519531 0.0625 1.019531 0.1875 1.523437 c -5.195312 -0.285156 -8.722656 -1.75 -10.582031 -4.394531 c -0.546875 -0.78125 -1.769531 -0.480469 -1.890625 0.464844 c -0.273437 2.101562 -0.199218 4.105469 0.226563 6.179687 c 0.105469 0.503906 0.320312 0.863282 0.71875 1.191406 c 3.167969 2.617188 7.558593 2.441407 13.179687 -0.527343 c 0.367188 0.363281 0.761719 0.675781 1.207032 0.941406 c -3.871094 3.472656 -7.402344 4.933594 -10.589844 4.375 c -0.9375 -0.164063 -1.589844 0.914063 -1.007813 1.667969 c 1.292969 1.679687 2.761719 3.042968 4.53125 4.207031 c 0.429688 0.285156 0.835938 0.386719 1.347657 0.335937 c 4.089843 -0.386718 7.074218 -3.617187 8.945312 -9.691406 c 0.519531 0 1.019531 -0.0625 1.523438 -0.1875 c -0.28125 5.195313 -1.746094 8.722656 -4.394532 10.582032 c -0.78125 0.546874 -0.480468 1.769531 0.464844 1.890624 c 2.101562 0.273438 4.105469 0.199219 6.179688 -0.226562 c 0.503906 -0.105469 0.863281 -0.320312 1.191406 -0.71875 c 2.617187 -3.164062 2.441406 -7.558594 -0.527344 -13.175781 c 0.367188 -0.367188 0.675781 -0.765625 0.941406 -1.210938 c 3.476563 3.875 4.933594 7.402344 4.378906 10.589844 c -0.164062 0.941406 0.910157 1.589844 1.667969 1.007813 c 1.675781 -1.292969 3.042969 -2.761719 4.207031 -4.53125 c 0.28125 -0.429688 0.382813 -0.835938 0.335938 -1.347657 c -0.390625 -4.089843 -3.621094 -7.074219 -9.695312 -8.945312 c 0 -0.519531 -0.058594 -1.019531 -0.1875 -1.523438 c 5.195312 0.285157 8.722656 1.746094 10.582031 4.394531 c 0.550781 0.78125 1.769531 0.480469 1.894531 -0.464843 c 0.269531 -2.101563 0.195312 -4.105469 -0.230469 -6.179688 z m -18.332031 9 c -2.886719 0 -5.226562 -2.339843 -5.226562 -5.226562 s 2.339843 -5.226563 5.226562 -5.226563 s 5.230469 2.339844 5.230469 5.226563 s -2.34375 5.226562 -5.230469 5.226562 z m 0 0"/>
|
||||
<path d="m 85.042969 63.738281 c 0.28125 -5.195312 1.746093 -8.722656 4.394531 -10.582031 c 0.777344 -0.546875 0.476562 -1.769531 -0.46875 -1.894531 c -2.097656 -0.269531 -4.105469 -0.195313 -6.179688 0.230469 c -0.503906 0.105468 -0.863281 0.320312 -1.1875 0.714843 c -2.621093 3.167969 -2.445312 7.5625 0.527344 13.179688 z m 0 0" fill="url(#o)"/>
|
||||
<path d="m 89.792969 64.445312 c 3.871093 -3.472656 7.402343 -4.933593 10.585937 -4.375 c 0.941406 0.164063 1.59375 -0.914062 1.011719 -1.667968 c -1.292969 -1.679688 -2.765625 -3.042969 -4.53125 -4.207032 c -0.429687 -0.285156 -0.839844 -0.386718 -1.351563 -0.335937 c -4.089843 0.386719 -7.070312 3.617187 -8.945312 9.691406 z m 0 0" fill="url(#p)"/>
|
||||
@@ -151,6 +154,7 @@
|
||||
<path d="m 83.335938 75.203125 c -3.871094 3.472656 -7.402344 4.933594 -10.589844 4.375 c -0.9375 -0.164063 -1.589844 0.914063 -1.007813 1.667969 c 1.292969 1.679687 2.761719 3.042968 4.53125 4.207031 c 0.429688 0.285156 0.835938 0.386719 1.347657 0.335937 c 4.089843 -0.386718 7.074218 -3.617187 8.945312 -9.691406 z m 0 0" fill="url(#t)"/>
|
||||
<path d="m 80.476562 71.34375 c -5.195312 -0.28125 -8.722656 -1.746094 -10.582031 -4.390625 c -0.546875 -0.78125 -1.769531 -0.480469 -1.890625 0.464844 c -0.273437 2.101562 -0.199218 4.105469 0.226563 6.179687 c 0.105469 0.503906 0.320312 0.863282 0.71875 1.191406 c 3.167969 2.617188 7.558593 2.441407 13.179687 -0.527343 z m 0 0" fill="url(#u)"/>
|
||||
<path d="m 81.183594 66.597656 c -3.472656 -3.875 -4.933594 -7.402344 -4.375 -10.589844 c 0.164062 -0.941406 -0.914063 -1.589843 -1.667969 -1.007812 c -1.675781 1.292969 -3.042969 2.761719 -4.207031 4.53125 c -0.285156 0.429688 -0.382813 0.835938 -0.335938 1.347656 c 0.390625 4.089844 3.621094 7.074219 9.691406 8.945313 z m 0 0" fill="url(#v)"/>
|
||||
</g>
|
||||
<path d="m 93.027344 69.824219 c 0 3.574219 -2.894532 6.46875 -6.464844 6.46875 c -3.566406 0 -6.460938 -2.894531 -6.460938 -6.46875 s 2.894532 -6.46875 6.460938 -6.46875 c 3.570312 0 6.464844 2.894531 6.464844 6.46875 z m 0 0" fill="url(#w)"/>
|
||||
<path d="m 92.082031 69.824219 c 0 3.050781 -2.46875 5.523437 -5.519531 5.523437 c -3.046875 0 -5.519531 -2.472656 -5.519531 -5.523437 s 2.472656 -5.523438 5.519531 -5.523438 c 3.050781 0 5.519531 2.472657 5.519531 5.523438 z m 0 0" fill="#342f3b"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user