feat: initial power states gui

This commit is contained in:
Ilya Zlobintsev
2023-11-19 15:03:19 +02:00
parent e692ecde99
commit 6372f0059b
14 changed files with 371 additions and 19 deletions

View File

@@ -8,8 +8,8 @@ use nix::unistd::getuid;
use schema::{
amdgpu_sysfs::gpu_handle::{power_profile_mode::PowerProfileModesTable, PerformanceLevel},
request::{ConfirmCommand, SetClocksCommand},
ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanControlMode, FanCurveMap, Request,
Response, SystemInfo,
ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanControlMode, FanCurveMap, PowerStates,
Request, Response, SystemInfo,
};
use serde::Deserialize;
use std::{
@@ -134,6 +134,7 @@ impl DaemonClient {
DevicePowerProfileModes,
PowerProfileModesTable
);
request_with_id!(get_power_states, GetPowerStates, PowerStates);
pub fn set_performance_level(
&self,

View File

@@ -66,6 +66,7 @@ pub struct Gpu {
pub max_voltage: Option<i32>,
pub voltage_offset: Option<i32>,
pub power_profile_mode_index: Option<u16>,
#[serde(default)]
pub power_states: HashMap<PowerLevelKind, Vec<u8>>,
}

View File

@@ -413,8 +413,8 @@ impl GpuController {
pub fn get_power_states(&self, gpu_config: Option<&config::Gpu>) -> PowerStates {
let core = self.get_power_states_kind(gpu_config, PowerLevelKind::CoreClock);
let memory = self.get_power_states_kind(gpu_config, PowerLevelKind::MemoryClock);
PowerStates { core, memory }
let vram = self.get_power_states_kind(gpu_config, PowerLevelKind::MemoryClock);
PowerStates { core, vram }
}
fn get_power_states_kind<T>(

View File

@@ -256,6 +256,20 @@ impl App {
.performance_frame
.set_power_profile_modes(maybe_modes_table);
match self
.daemon_client
.get_power_states(gpu_id)
.and_then(|states| states.inner())
{
Ok(power_states) => {
self.root_stack
.oc_page
.power_states_frame
.set_power_states(power_states);
}
Err(err) => warn!("could not get power states: {err:?}"),
}
// Show apply button on setting changes
// This is done here because new widgets may appear after applying settings (like fan curve points) which should be connected
let show_revealer = clone!(@strong self.apply_revealer as apply_revealer => move || {
@@ -424,9 +438,17 @@ impl App {
.daemon_client
.batch_set_clocks_value(&gpu_id, clocks_commands)
.context("Could not commit clocks settins")?;
self.ask_confirmation(gpu_id.clone(), delay);
}
let enabled_states = self
.root_stack
.oc_page
.power_states_frame
.get_enabled_power_states();
println!("{:?}", enabled_states);
self.set_initial(&gpu_id);
Ok(())

View File

@@ -68,18 +68,6 @@ mod imp {
}
}
// impl Default for VulkanFeaturesWindow {
// fn default() -> Self {
// Self {
// model: RefCell::new(gio::ListStore::new::<VulkanFeature>().into()),
// features_factory: Default::default(),
// filter_model: Default::default(),
// search_filter: Default::default(),
// search_entry: Default::default(),
// }
// }
// }
#[glib::derived_properties]
impl ObjectImpl for VulkanFeaturesWindow {
fn constructed(&self) {

View File

@@ -3,11 +3,13 @@ mod gpu_stats_section;
mod oc_adjustment;
mod performance_frame;
mod power_cap_frame;
mod power_states;
use self::power_states::power_states_frame::PowerStatesFrame;
use clocks_frame::ClocksFrame;
use gpu_stats_section::GpuStatsSection;
use gtk::prelude::*;
use gtk::*;
use gtk::{glib::clone, prelude::*};
use lact_client::schema::{
amdgpu_sysfs::gpu_handle::{overdrive::ClocksTableGen, PerformanceLevel},
DeviceStats, SystemInfo,
@@ -25,6 +27,7 @@ pub struct OcPage {
stats_section: GpuStatsSection,
pub performance_frame: PerformanceFrame,
power_cap_frame: PowerCapFrame,
pub power_states_frame: PowerStatesFrame,
pub clocks_frame: ClocksFrame,
pub enable_overclocking_button: Option<Button>,
}
@@ -54,9 +57,18 @@ impl OcPage {
let power_cap_frame = PowerCapFrame::new();
let performance_level_frame = PerformanceFrame::new();
let clocks_frame = ClocksFrame::new();
let power_states_frame = PowerStatesFrame::new();
performance_level_frame.connect_settings_changed(
clone!(@strong performance_level_frame, @strong power_states_frame => move || {
let level = performance_level_frame.get_selected_performance_level();
power_states_frame.set_sensitive(level == PerformanceLevel::Manual);
}),
);
vbox.append(&power_cap_frame.container);
vbox.append(&performance_level_frame.container);
vbox.append(&power_states_frame);
vbox.append(&clocks_frame.container);
container.set_child(Some(&vbox));
@@ -68,6 +80,7 @@ impl OcPage {
clocks_frame,
power_cap_frame,
enable_overclocking_button,
power_states_frame,
}
}
@@ -103,7 +116,8 @@ impl OcPage {
pub fn connect_settings_changed<F: Fn() + 'static + Clone>(&self, f: F) {
self.performance_frame.connect_settings_changed(f.clone());
self.power_cap_frame.connect_cap_changed(f.clone());
self.clocks_frame.connect_clocks_changed(f);
self.clocks_frame.connect_clocks_changed(f.clone());
self.power_states_frame.connect_values_changed(f);
}
pub fn set_performance_level(&self, profile: Option<PerformanceLevel>) {

View File

@@ -0,0 +1,3 @@
mod power_state_row;
pub mod power_states_frame;
mod power_states_list;

View File

@@ -0,0 +1,69 @@
use gtk::glib::{self, Object};
use lact_client::schema::PowerState;
use std::fmt::Display;
glib::wrapper! {
pub struct PowerStateRow(ObjectSubclass<imp::PowerStateRow>)
@extends gtk::Widget,
@implements gtk::Accessible, gtk::Buildable;
}
impl PowerStateRow {
pub fn new<T: Display>(power_state: PowerState<T>, index: usize, value_suffix: &str) -> Self {
let title = format!("{}: {} {}", index, power_state.value, value_suffix);
Object::builder()
.property("enabled", power_state.enabled)
.property("title", title)
.property("index", index as u64)
.build()
}
}
mod imp {
use gtk::{
glib::{self, subclass::InitializingObject, Properties},
prelude::ObjectExt,
subclass::{
prelude::*,
widget::{CompositeTemplateClass, WidgetImpl},
},
CompositeTemplate,
};
use std::{
cell::RefCell,
sync::atomic::{AtomicBool, AtomicU64},
};
#[derive(CompositeTemplate, Default, Properties)]
#[properties(wrapper_type = super::PowerStateRow)]
#[template(file = "ui/oc_page/power_state_row.blp")]
pub struct PowerStateRow {
#[property(get, set)]
title: RefCell<String>,
#[property(get, set)]
enabled: AtomicBool,
#[property(get, set)]
index: AtomicU64,
}
#[glib::object_subclass]
impl ObjectSubclass for PowerStateRow {
const NAME: &'static str = "PowerStateRow";
type Type = super::PowerStateRow;
type ParentType = gtk::Box;
fn class_init(class: &mut Self::Class) {
class.bind_template();
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
#[glib::derived_properties]
impl ObjectImpl for PowerStateRow {}
impl WidgetImpl for PowerStateRow {}
impl BoxImpl for PowerStateRow {}
}

View File

@@ -0,0 +1,90 @@
use std::collections::HashMap;
use gtk::glib::{self, subclass::types::ObjectSubclassIsExt, Object};
use lact_client::schema::{amdgpu_sysfs::gpu_handle::PowerLevelKind, PowerStates};
glib::wrapper! {
pub struct PowerStatesFrame(ObjectSubclass<imp::PowerStatesFrame>)
@extends gtk::Widget,
@implements gtk::Accessible, gtk::Buildable;
}
impl PowerStatesFrame {
pub fn new() -> Self {
Object::builder().build()
}
pub fn set_power_states(&self, states: PowerStates) {
let imp = self.imp();
imp.core_states_list.set_power_states(states.core, "MHz");
imp.vram_states_list.set_power_states(states.vram, "MHz");
}
pub fn connect_values_changed<F: Fn() + 'static + Clone>(&self, f: F) {
let imp = self.imp();
imp.core_states_list.connect_values_changed(f.clone());
imp.vram_states_list.connect_values_changed(f);
}
pub fn get_enabled_power_states(&self) -> HashMap<PowerLevelKind, Vec<usize>> {
let imp = self.imp();
let core_states = imp.core_states_list.get_enabled_power_states();
let vram_states = imp.vram_states_list.get_enabled_power_states();
[
(PowerLevelKind::CoreClock, core_states),
(PowerLevelKind::MemoryClock, vram_states),
]
.into_iter()
.collect()
}
}
impl Default for PowerStatesFrame {
fn default() -> Self {
Self::new()
}
}
mod imp {
use crate::app::root_stack::oc_page::power_states::power_states_list::PowerStatesList;
use gtk::{
glib::{self, subclass::InitializingObject, StaticTypeExt},
subclass::{
prelude::*,
widget::{CompositeTemplateClass, WidgetImpl},
},
CompositeTemplate,
};
#[derive(CompositeTemplate, Default)]
#[template(file = "ui/oc_page/power_states_frame.blp")]
pub struct PowerStatesFrame {
#[template_child]
pub core_states_list: TemplateChild<PowerStatesList>,
#[template_child]
pub vram_states_list: TemplateChild<PowerStatesList>,
}
#[glib::object_subclass]
impl ObjectSubclass for PowerStatesFrame {
const NAME: &'static str = "PowerStatesFrame";
type Type = super::PowerStatesFrame;
type ParentType = gtk::Box;
fn class_init(class: &mut Self::Class) {
PowerStatesList::ensure_type();
class.bind_template();
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
// #[glib::derived_properties]
impl ObjectImpl for PowerStatesFrame {}
impl WidgetImpl for PowerStatesFrame {}
impl BoxImpl for PowerStatesFrame {}
}

View File

@@ -0,0 +1,112 @@
use crate::app::root_stack::oc_page::power_states::power_state_row::PowerStateRow;
use gtk::{
gio,
glib::{self, clone, subclass::types::ObjectSubclassIsExt, Cast, Object},
prelude::{ListBoxRowExt, WidgetExt},
ListBoxRow, Widget,
};
use lact_client::schema::PowerState;
use std::fmt::Display;
glib::wrapper! {
pub struct PowerStatesList(ObjectSubclass<imp::PowerStatesList>)
@extends gtk::Widget,
@implements gtk::Accessible, gtk::Buildable;
}
impl PowerStatesList {
pub fn new(title: &str) -> Self {
Object::builder().property("title", title).build()
}
pub fn get_enabled_power_states(&self) -> Vec<usize> {
self.rows()
.iter()
.filter(|row| row.enabled())
.map(|row| row.index() as usize)
.collect()
}
pub fn set_power_states<T: Display>(
&self,
power_states: Vec<PowerState<T>>,
value_suffix: &str,
) {
let store = gio::ListStore::new::<PowerStateRow>();
for (i, state) in power_states.into_iter().enumerate() {
let row = PowerStateRow::new(state, i, value_suffix);
store.append(&row);
}
self.imp().states_listbox.bind_model(Some(&store), |obj| {
obj.clone().downcast::<Widget>().unwrap()
});
}
pub fn connect_values_changed<F: Fn() + 'static + Clone>(&self, f: F) {
for row in self.rows() {
row.connect_enabled_notify(clone!(@strong f => move |_| f()));
}
}
fn rows(&self) -> Vec<PowerStateRow> {
let children = self.imp().states_listbox.observe_children();
children
.into_iter()
.flatten()
.filter_map(|object| {
let item = object.downcast::<ListBoxRow>().unwrap();
let child = item.child()?;
let row = child
.downcast::<PowerStateRow>()
.expect("ListBoxRow child must be a PowerStateRow");
Some(row)
})
.collect()
}
}
mod imp {
use gtk::{
glib::{self, subclass::InitializingObject, Properties},
prelude::ObjectExt,
subclass::{
prelude::*,
widget::{CompositeTemplateClass, WidgetImpl},
},
CompositeTemplate, ListBox,
};
use std::cell::RefCell;
#[derive(CompositeTemplate, Default, Properties)]
#[properties(wrapper_type = super::PowerStatesList)]
#[template(file = "ui/oc_page/power_states_list.blp")]
pub struct PowerStatesList {
#[property(get, set)]
title: RefCell<String>,
#[template_child]
pub states_listbox: TemplateChild<ListBox>,
}
#[glib::object_subclass]
impl ObjectSubclass for PowerStatesList {
const NAME: &'static str = "PowerStatesList";
type Type = super::PowerStatesList;
type ParentType = gtk::Frame;
fn class_init(class: &mut Self::Class) {
class.bind_template();
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
#[glib::derived_properties]
impl ObjectImpl for PowerStatesList {}
impl WidgetImpl for PowerStatesList {}
impl FrameImpl for PowerStatesList {}
}

View File

@@ -0,0 +1,12 @@
using Gtk 4.0;
template $PowerStateRow: Box {
orientation: horizontal;
spacing: 5;
CheckButton {
active: bind template.enabled bidirectional;
label: bind template.title;
hexpand: true;
}
}

View File

@@ -0,0 +1,24 @@
using Gtk 4.0;
template $PowerStatesFrame: Box {
Expander {
label: "Power states";
Box {
margin-start: 10;
margin-end: 10;
margin-top: 10;
margin-bottom: 10;
spacing: 10;
orientation: horizontal;
$PowerStatesList core_states_list {
title: "GPU power states";
}
$PowerStatesList vram_states_list {
title: "VRAM power states";
}
}
}
}

View File

@@ -0,0 +1,16 @@
using Gtk 4.0;
template $PowerStatesList: Frame {
hexpand: true;
[label]
Label {
label: bind template.title;
margin-start: 5;
margin-end: 5;
}
ListBox states_listbox {
selection-mode: none;
}
}

View File

@@ -228,7 +228,7 @@ pub struct PowerStats {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PowerStates {
pub core: Vec<PowerState<u64>>,
pub memory: Vec<PowerState<u64>>,
pub vram: Vec<PowerState<u64>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]