diff --git a/Cargo.lock b/Cargo.lock index b4da6c2..a60111c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "amdgpu-sysfs" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae160ad9551e11cf5aa19fc78b590ee5f8ccfe07f983a99bead7a6680907da30" +checksum = "6667682d11c8face0277986d0cfc0862399febf666e0d9a9d8b12fa0362373ba" dependencies = [ "enum_dispatch", "serde", diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index 2f31d67..0eb8834 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -amdgpu-sysfs = { version = "0.9.5", features = ["serde"] } +amdgpu-sysfs = { version = "0.9.7", features = ["serde"] } anyhow = "1.0" bincode = "1.3" nix = "0.26" diff --git a/lact-daemon/src/config.rs b/lact-daemon/src/config.rs index 1e12162..3aad909 100644 --- a/lact-daemon/src/config.rs +++ b/lact-daemon/src/config.rs @@ -41,6 +41,7 @@ pub struct Gpu { pub max_core_clock: Option, pub max_memory_clock: Option, pub max_voltage: Option, + pub voltage_offset: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index 1844e34..6e443cc 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -11,8 +11,8 @@ use amdgpu_sysfs::{ }; use anyhow::{anyhow, Context}; use lact_schema::{ - ClocksInfo, ClocksTable, ClockspeedStats, DeviceInfo, DeviceStats, FanStats, GpuPciInfo, - LinkInfo, PciInfo, PerformanceLevel, PowerStats, VoltageStats, VramStats, + ClocksInfo, ClocksTable, ClocksTableGen, ClockspeedStats, DeviceInfo, DeviceStats, FanStats, + GpuPciInfo, LinkInfo, PciInfo, PerformanceLevel, PowerStats, VoltageStats, VramStats, }; use pciid_parser::Database; use std::{ @@ -347,6 +347,15 @@ impl GpuController { { let mut table = self.handle.get_clocks_table()?; + if let ClocksTableGen::Vega20(ref mut table) = table { + // Avoid writing settings to the clocks table except the user-specified ones + // There is an issue on some GPU models where the default values are actually outside of the allowed range + // See https://github.com/sibradzic/amdgpu-clocks/issues/32#issuecomment-829953519 (part 2) for an example + table.clear(); + + table.voltage_offset = config.voltage_offset; + } + if let Some(clockspeed) = config.max_core_clock { table.set_max_sclk(clockspeed)?; } diff --git a/lact-daemon/src/server/handler.rs b/lact-daemon/src/server/handler.rs index c5f84a9..c8d78cd 100644 --- a/lact-daemon/src/server/handler.rs +++ b/lact-daemon/src/server/handler.rs @@ -214,6 +214,7 @@ impl<'a> Handler { SetClocksCommand::MaxCoreClock(clock) => gpu_config.max_core_clock = Some(clock), SetClocksCommand::MaxMemoryClock(clock) => gpu_config.max_memory_clock = Some(clock), SetClocksCommand::MaxVoltage(voltage) => gpu_config.max_voltage = Some(voltage), + SetClocksCommand::VoltageOffset(offset) => gpu_config.voltage_offset = Some(offset), SetClocksCommand::Reset => { gpu_config.max_core_clock = None; gpu_config.max_memory_clock = None; diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs index 6e47122..bea8646 100644 --- a/lact-gui/src/app/mod.rs +++ b/lact-gui/src/app/mod.rs @@ -297,6 +297,12 @@ impl App { .context("Could not set the maximum voltage")?; } + if let Some(offset) = clocks_settings.voltage_offset { + self.daemon_client + .set_clocks_value(&gpu_id, SetClocksCommand::VoltageOffset(offset)) + .context("Could not set the voltage offset")?; + } + self.set_initial(&gpu_id); Ok(()) diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs index 4775901..2bd70e8 100644 --- a/lact-gui/src/app/root_stack/info_page/mod.rs +++ b/lact-gui/src/app/root_stack/info_page/mod.rs @@ -34,7 +34,7 @@ impl InformationPage { let gpu_name_label = label_row("GPU Model:", &values_grid, 0, 0, true); let gpu_manufacturer_label = label_row("GPU Manufacturer:", &values_grid, 1, 0, true); let vbios_version_label = label_row("VBIOS Version:", &values_grid, 2, 0, true); - let driver_label = label_row("Driver Version:", &values_grid, 3, 0, true); + let driver_label = label_row("Driver Used:", &values_grid, 3, 0, true); let vram_size_label = label_row("VRAM Size:", &values_grid, 4, 0, true); let link_speed_label = label_row("Link Speed:", &values_grid, 5, 0, true); diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs index 28ef3d0..5f57a66 100644 --- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs +++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs @@ -4,6 +4,9 @@ use gtk::prelude::*; use gtk::*; use lact_client::schema::{ClocksTable, ClocksTableGen}; +const VOLTAGE_OFFSET_RANGE: f64 = 250.0; +const WARNING_TEXT: &str = "Warning: changing these values may lead to system instability and potentially damage your hardware!"; + #[derive(Clone)] pub struct ClocksFrame { pub container: Box, @@ -11,6 +14,7 @@ pub struct ClocksFrame { max_sclk_adjustment: Adjustment, max_mclk_adjustment: Adjustment, max_voltage_adjustment: Adjustment, + voltage_offset_adjustment: Adjustment, reset_button: Button, clocks_data_unavailable_label: Label, } @@ -20,15 +24,29 @@ impl ClocksFrame { let container = section_box("Maximum Clocks"); let tweaking_grid = Grid::builder().row_spacing(5).build(); - let max_sclk_adjustment = oc_adjustment("GPU Clock (MHz)", &tweaking_grid, 0); - let max_voltage_adjustment = oc_adjustment("GPU voltage (mV)", &tweaking_grid, 1); - let max_mclk_adjustment = oc_adjustment("VRAM Clock (MHz)", &tweaking_grid, 2); + + let warning_label = Label::builder() + .label(WARNING_TEXT) + .wrap_mode(pango::WrapMode::Word) + .halign(Align::Start) + .hexpand(true) + .margin_top(5) + .margin_bottom(5) + .build(); + tweaking_grid.attach(&warning_label, 0, 0, 7, 1); + + let max_sclk_adjustment = oc_adjustment("GPU Clock (MHz)", &tweaking_grid, 1); + let max_voltage_adjustment = oc_adjustment("GPU voltage (mV)", &tweaking_grid, 2); + let max_mclk_adjustment = oc_adjustment("VRAM Clock (MHz)", &tweaking_grid, 3); + let voltage_offset_adjustment = oc_adjustment("GPU voltage offset (mV)", &tweaking_grid, 4); let reset_button = Button::builder() - .label("Defaults") + .label("Reset") .halign(Align::End) + .tooltip_text("Warning: this resets all clock settings to defaults!") + .css_classes(["destructive-action"]) .build(); - tweaking_grid.attach(&reset_button, 6, 3, 1, 1); + tweaking_grid.attach(&reset_button, 6, 5, 1, 1); let clocks_data_unavailable_label = Label::new(Some("No clocks data available")); @@ -43,10 +61,12 @@ impl ClocksFrame { max_voltage_adjustment, reset_button, clocks_data_unavailable_label, + voltage_offset_adjustment, } } pub fn set_table(&self, table: ClocksTableGen) -> anyhow::Result<()> { + // The upper value "0.0" is used to hide the adjustment when info is not available if let Some((current_sclk_max, sclk_min, sclk_max)) = extract_value_and_range(&table, |table| { (table.get_max_sclk(), table.get_max_sclk_range()) @@ -55,6 +75,8 @@ impl ClocksFrame { self.max_sclk_adjustment.set_lower(sclk_min.into()); self.max_sclk_adjustment.set_upper(sclk_max.into()); self.max_sclk_adjustment.set_value(current_sclk_max.into()); + } else { + self.max_sclk_adjustment.set_upper(0.0); } if let Some((current_mclk_max, mclk_min, mclk_max)) = @@ -65,6 +87,8 @@ impl ClocksFrame { self.max_mclk_adjustment.set_lower(mclk_min.into()); self.max_mclk_adjustment.set_upper(mclk_max.into()); self.max_mclk_adjustment.set_value(current_mclk_max.into()); + } else { + self.max_mclk_adjustment.set_upper(0.0); } if let Some((current_voltage_max, voltage_min, voltage_max)) = @@ -76,8 +100,30 @@ impl ClocksFrame { self.max_voltage_adjustment.set_upper(voltage_max.into()); self.max_voltage_adjustment .set_value(current_voltage_max.into()); + } else { + self.max_voltage_adjustment.set_upper(0.0); } + if let ClocksTableGen::Vega20(table) = table { + if let Some(offset) = table.voltage_offset { + // TODO: check this + self.voltage_offset_adjustment + .set_lower(VOLTAGE_OFFSET_RANGE * -1.0); + self.voltage_offset_adjustment + .set_upper(VOLTAGE_OFFSET_RANGE); + self.voltage_offset_adjustment.set_value(offset.into()); + } else { + self.voltage_offset_adjustment.set_upper(0.0); + } + } else { + self.voltage_offset_adjustment.set_upper(0.0); + } + + emit_changed(&self.max_sclk_adjustment); + emit_changed(&self.max_mclk_adjustment); + emit_changed(&self.max_voltage_adjustment); + emit_changed(&self.voltage_offset_adjustment); + Ok(()) } @@ -95,7 +141,8 @@ impl ClocksFrame { let f = clone!(@strong f => move |_: &Adjustment| f()); self.max_sclk_adjustment.connect_value_changed(f.clone()); self.max_mclk_adjustment.connect_value_changed(f.clone()); - self.max_voltage_adjustment.connect_value_changed(f); + self.max_voltage_adjustment.connect_value_changed(f.clone()); + self.voltage_offset_adjustment.connect_value_changed(f); } pub fn connect_clocks_reset(&self, f: F) { @@ -108,10 +155,17 @@ impl ClocksFrame { let max_memory_clock = zero_to_option(self.max_mclk_adjustment.value()); let max_voltage = zero_to_option(self.max_voltage_adjustment.value()); + let voltage_offset = if self.voltage_offset_adjustment.upper() == 0.0 { + None + } else { + Some(self.voltage_offset_adjustment.value() as i32) + }; + ClocksSettings { max_core_clock, max_memory_clock, max_voltage, + voltage_offset, } } else { ClocksSettings::default() @@ -130,7 +184,7 @@ fn extract_value_and_range( } fn oc_adjustment(title: &'static str, grid: &Grid, row: i32) -> Adjustment { - let label = Label::new(Some(title)); + let label = Label::builder().label(title).halign(Align::Start).build(); let adjustment = Adjustment::new(0.0, 0.0, 0.0, 1.0, 10.0, 0.0); @@ -148,17 +202,36 @@ fn oc_adjustment(title: &'static str, grid: &Grid, row: i32) -> Adjustment { let value_selector = SpinButton::new(Some(&adjustment), 1.0, 0); let value_label = Label::new(None); - adjustment.connect_value_changed(clone!(@strong value_label => move |adjustment| { - let value = adjustment.value(); - value_label.set_text(&value.to_string()); - })); - let popover = Popover::builder().child(&value_selector).build(); let value_button = MenuButton::builder() .popover(&popover) .child(&value_label) .build(); + adjustment.connect_value_changed(clone!(@strong value_label => move |adjustment| { + let value = adjustment.value(); + value_label.set_text(&value.to_string()); + })); + + adjustment.connect_changed( + clone!(@strong label, @strong value_label, @strong scale, @strong value_button => move |adjustment| { + let value = adjustment.value(); + value_label.set_text(&value.to_string()); + + if adjustment.upper() == 0.0 { + label.hide(); + value_label.hide(); + scale.hide(); + value_button.hide(); + } else { + label.show(); + value_label.show(); + scale.show(); + value_button.show(); + } + } + )); + grid.attach(&label, 0, row, 1, 1); grid.attach(&scale, 1, row, 4, 1); grid.attach(&value_button, 6, row, 4, 1); @@ -171,6 +244,7 @@ pub struct ClocksSettings { pub max_core_clock: Option, pub max_memory_clock: Option, pub max_voltage: Option, + pub voltage_offset: Option, } fn zero_to_option(value: f64) -> Option { @@ -180,3 +254,7 @@ fn zero_to_option(value: f64) -> Option { Some(value as u32) } } + +fn emit_changed(adjustment: &Adjustment) { + adjustment.emit_by_name::<()>("changed", &[]); +} diff --git a/lact-schema/Cargo.toml b/lact-schema/Cargo.toml index 50ec65f..f6b4def 100644 --- a/lact-schema/Cargo.toml +++ b/lact-schema/Cargo.toml @@ -4,7 +4,7 @@ version = "0.2.2" edition = "2021" [dependencies] -amdgpu-sysfs = { version = "0.9.5", features = ["serde"] } +amdgpu-sysfs = { version = "0.9.7", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } indexmap = { version = "*", features = ["serde"] } diff --git a/lact-schema/src/request.rs b/lact-schema/src/request.rs index f53c986..8b85164 100644 --- a/lact-schema/src/request.rs +++ b/lact-schema/src/request.rs @@ -42,5 +42,6 @@ pub enum SetClocksCommand { MaxCoreClock(u32), MaxMemoryClock(u32), MaxVoltage(u32), + VoltageOffset(i32), Reset, }