diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 160e108..94a2e37 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -150,20 +150,27 @@ gpus: memory_clock: - 0 - 1 - # Minimum GPU clockspeed in MHz. + + # Minimum GPU clockspeed in MHz. Applicable to AMD and Intel. min_core_clock: 300 - # Minimum VRAM clockspeed in MHz. + # Minimum VRAM clockspeed in MHz. Applicable to AMD only. min_memory_clock: 500 - # Minimum GPU voltage in mV. + # Minimum GPU voltage in mV. Applicable to AMD only. min_voltage: 900 - # Maximum GPU clockspeed in MHz. + # Maximum GPU clockspeed in MHz. Applicable to AMD and Intel. max_core_clock: 1630 - # Maximum VRAM clockspeed in MHz. + # Maximum VRAM clockspeed in MHz. Applicable to AMD only. max_memory_clock: 800 # Maximum GPU voltage in mV. max_voltage: 1200 # Voltage offset value in mV for RDNA and newer AMD GPUs. voltage_offset: 0 + + # GPU and VRAM clockspeed offset values, per-pstate. Only applicable on Nvidia. + gpu_clock_offsets: + 0: -100 + mem_clock_offsets: + 0: 200 # Settings profiles profiles: diff --git a/lact-daemon/src/config.rs b/lact-daemon/src/config.rs index 7d441c7..9b1f4ab 100644 --- a/lact-daemon/src/config.rs +++ b/lact-daemon/src/config.rs @@ -108,7 +108,7 @@ pub struct Gpu { } #[skip_serializing_none] -#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)] pub struct ClocksConfiguration { pub min_core_clock: Option, pub min_memory_clock: Option, @@ -116,6 +116,10 @@ pub struct ClocksConfiguration { pub max_core_clock: Option, pub max_memory_clock: Option, pub max_voltage: Option, + #[serde(default, skip_serializing_if = "IndexMap::is_empty")] + pub gpu_clock_offsets: IndexMap, + #[serde(default, skip_serializing_if = "IndexMap::is_empty")] + pub mem_clock_offsets: IndexMap, pub voltage_offset: Option, } @@ -135,6 +139,22 @@ impl Gpu { ClockspeedType::MinMemoryClock => clocks.min_memory_clock = value, ClockspeedType::MinVoltage => clocks.min_voltage = value, ClockspeedType::VoltageOffset => clocks.voltage_offset = value, + ClockspeedType::GpuClockOffset(pstate) => match value { + Some(value) => { + clocks.gpu_clock_offsets.insert(pstate, value); + } + None => { + clocks.gpu_clock_offsets.shift_remove(&pstate); + } + }, + ClockspeedType::MemClockOffset(pstate) => match value { + Some(value) => { + clocks.mem_clock_offsets.insert(pstate, value); + } + None => { + clocks.mem_clock_offsets.shift_remove(&pstate); + } + }, ClockspeedType::Reset => { *clocks = ClocksConfiguration::default(); assert!(!self.is_core_clocks_used()); @@ -234,6 +254,23 @@ impl Config { } } } + 2 => { + for (id, gpu) in &mut self.gpus { + if id.starts_with(VENDOR_NVIDIA) { + gpu.clocks_configuration.max_core_clock = None; + gpu.clocks_configuration.max_memory_clock = None; + } + } + + for profile in &mut self.profiles.values_mut() { + for (id, gpu) in &mut profile.gpus { + if id.starts_with(VENDOR_NVIDIA) { + gpu.clocks_configuration.max_core_clock = None; + gpu.clocks_configuration.max_memory_clock = None; + } + } + } + } _ => break, } info!("migrated config version {} to {next_version}", self.version); @@ -511,7 +548,7 @@ mod tests { .unwrap() .clocks_configuration .max_core_clock, - Some(3000) + None, ); assert_eq!( config diff --git a/lact-daemon/src/server/gpu_controller/nvidia.rs b/lact-daemon/src/server/gpu_controller/nvidia.rs index 3a79bab..b7e8e28 100644 --- a/lact-daemon/src/server/gpu_controller/nvidia.rs +++ b/lact-daemon/src/server/gpu_controller/nvidia.rs @@ -7,19 +7,19 @@ use super::{fan_control::FanCurve, CommonControllerInfo, FanControlHandle, GpuCo use amdgpu_sysfs::{gpu_handle::power_profile_mode::PowerProfileModesTable, hw_mon::Temperature}; use anyhow::{anyhow, Context}; use futures::future::LocalBoxFuture; +use indexmap::IndexMap; use lact_schema::{ ClocksInfo, ClocksTable, ClockspeedStats, DeviceInfo, DeviceStats, DrmInfo, DrmMemoryInfo, - FanControlMode, FanStats, IntelDrmInfo, LinkInfo, NvidiaClockInfo, NvidiaClocksTable, PmfwInfo, - PowerState, PowerStates, PowerStats, VoltageStats, VramStats, + FanControlMode, FanStats, IntelDrmInfo, LinkInfo, NvidiaClockOffset, NvidiaClocksTable, + PmfwInfo, PowerState, PowerStates, PowerStats, VoltageStats, VramStats, }; use nvml_wrapper::{ bitmasks::device::ThrottleReasons, - enum_wrappers::device::{Brand, Clock, TemperatureSensor, TemperatureThreshold}, - enums::device::DeviceArchitecture, + enum_wrappers::device::{Clock, PerformanceState, TemperatureSensor, TemperatureThreshold}, Device, Nvml, }; use std::{ - cell::{Cell, RefCell}, + cell::RefCell, collections::HashMap, fmt::Write, rc::Rc, @@ -33,8 +33,8 @@ pub struct NvidiaGpuController { common: CommonControllerInfo, fan_control_handle: RefCell>, - last_applied_gpc_offset: Cell>, - last_applied_mem_offset: Cell>, + // Store last applied offsets as a workaround when the driver doesn't tell us the current offset + last_applied_offsets: RefCell>>, } impl NvidiaGpuController { @@ -50,8 +50,7 @@ impl NvidiaGpuController { nvml, common, fan_control_handle: RefCell::new(None), - last_applied_gpc_offset: Cell::new(None), - last_applied_mem_offset: Cell::new(None), + last_applied_offsets: RefCell::new(HashMap::new()), }) } @@ -243,20 +242,6 @@ impl NvidiaGpuController { Ok(power_states) } - - // See https://github.com/ilya-zlobintsev/LACT/issues/418 - fn vram_offset_ratio(&self) -> i32 { - let device = self.device(); - if let (Ok(brand), Ok(architecture)) = (device.brand(), device.architecture()) { - let ratio = match (brand, architecture) { - (Brand::GeForce, DeviceArchitecture::Ada) => 2, - // TODO: check others - _ => 1, - }; - return ratio; - } - 1 - } } impl GpuController for NvidiaGpuController { @@ -461,47 +446,46 @@ impl GpuController for NvidiaGpuController { fn get_clocks_info(&self) -> anyhow::Result { let device = self.device(); - let mut gpc = None; - let mut mem = None; + let mut gpu_offsets = IndexMap::new(); + let mut mem_offsets = IndexMap::new(); - // Negative offset values are not correctly reported by NVML, so we have to use the last known applied value - // instead of the actual read when an unreasonable value appears. + let supported_pstates = device.supported_performance_states()?; - if let Ok(max) = device.max_clock_info(Clock::Graphics) { - if let Ok(offset_range) = device.gpc_clk_min_max_vf_offset() { - if let Some(offset) = self - .last_applied_gpc_offset - .get() - .or_else(|| device.gpc_clk_vf_offset().ok()) - { - gpc = Some(NvidiaClockInfo { - max: max as i32, - offset, - offset_ratio: 1, - offset_range, - }); + let clock_types = [ + (Clock::Graphics, &mut gpu_offsets), + (Clock::Memory, &mut mem_offsets), + ]; + + for (clock_type, offsets) in clock_types { + for pstate in supported_pstates.iter().rev() { + if let Ok(offset) = device.clock_offset(clock_type, *pstate) { + let mut offset = NvidiaClockOffset { + current: offset.clock_offset_mhz, + min: offset.min_clock_offset_mhz, + max: offset.max_clock_offset_mhz, + }; + + // On some driver versions, the applied offset values are not reported. + // In these scenarios we must store them manually for reporting. + if offset.current == 0 { + if let Some(applied_offsets) = + self.last_applied_offsets.borrow().get(&clock_type) + { + if let Some(applied_offset) = applied_offsets.get(pstate) { + offset.current = *applied_offset; + } + } + } + + offsets.insert(pstate.as_c(), offset); } } } - if let Ok(max) = device.max_clock_info(Clock::Memory) { - if let Ok(offset_range) = device.mem_clk_min_max_vf_offset() { - if let Some(offset) = self - .last_applied_mem_offset - .get() - .or_else(|| device.mem_clk_vf_offset().ok()) - { - mem = Some(NvidiaClockInfo { - max: max as i32, - offset, - offset_ratio: self.vram_offset_ratio(), - offset_range, - }); - } - } - } - - let table = NvidiaClocksTable { gpc, mem }; + let table = NvidiaClocksTable { + gpu_offsets, + mem_offsets, + }; Ok(ClocksInfo { table: Some(ClocksTable::Nvidia(table)), @@ -564,34 +548,38 @@ impl GpuController for NvidiaGpuController { self.cleanup_clocks()?; - if let Some(max_gpu_clock) = config.clocks_configuration.max_core_clock { - let default_max_clock = device - .max_clock_info(Clock::Graphics) - .context("Could not read max graphics clock")?; - let offset = max_gpu_clock - default_max_clock as i32; - debug!( - "Using graphics clock offset {offset} (default max clock: {default_max_clock})" - ); - + for (pstate, offset) in &config.clocks_configuration.gpu_clock_offsets { + let pstate = PerformanceState::try_from(*pstate) + .map_err(|_| anyhow!("Invalid pstate '{pstate}'"))?; + debug!("applying offset {offset} for GPU pstate {pstate:?}"); device - .set_gpc_clk_vf_offset(offset) - .context("Could not set graphics clock offset")?; + .set_clock_offset(Clock::Graphics, pstate, *offset) + .with_context(|| { + format!("Could not set clock offset {offset} for GPU pstate {pstate:?}") + })?; - self.last_applied_gpc_offset.set(Some(offset)); + self.last_applied_offsets + .borrow_mut() + .entry(Clock::Graphics) + .or_default() + .insert(pstate, *offset); } - if let Some(max_mem_clock) = config.clocks_configuration.max_memory_clock { - let default_max_clock = device - .max_clock_info(Clock::Memory) - .context("Could not read max memory clock")?; - let offset = (max_mem_clock - default_max_clock as i32) * self.vram_offset_ratio(); - debug!("Using mem clock offset {offset} (default max clock: {default_max_clock})"); - + for (pstate, offset) in &config.clocks_configuration.mem_clock_offsets { + let pstate = PerformanceState::try_from(*pstate) + .map_err(|_| anyhow!("Invalid pstate '{pstate}'"))?; + debug!("applying offset {offset} for VRAM pstate {pstate:?}"); device - .set_mem_clk_vf_offset(offset) - .context("Could not set memory clock offset")?; + .set_clock_offset(Clock::Memory, pstate, *offset) + .with_context(|| { + format!("Could not set clock offset {offset} for VRAM pstate {pstate:?}") + })?; - self.last_applied_mem_offset.set(Some(offset)); + self.last_applied_offsets + .borrow_mut() + .entry(Clock::Memory) + .or_default() + .insert(pstate, *offset); } if config.fan_control_enabled { @@ -633,23 +621,33 @@ impl GpuController for NvidiaGpuController { fn cleanup_clocks(&self) -> anyhow::Result<()> { let device = self.device(); - if let Ok(current_offset) = device.gpc_clk_vf_offset() { - if current_offset != 0 { - device - .set_gpc_clk_vf_offset(0) - .context("Could not reset graphics clock offset")?; + if let Ok(supported_pstates) = device.supported_performance_states() { + for pstate in supported_pstates { + for clock_type in [Clock::Graphics, Clock::Memory] { + if let Ok(current_offset) = device.clock_offset(clock_type, pstate) { + if current_offset.clock_offset_mhz != 0 + || self + .last_applied_offsets + .borrow() + .get(&clock_type) + .and_then(|applied_offsets| applied_offsets.get(&pstate)) + .is_some_and(|offset| *offset != 0) + { + debug!("resetting clock offset for {clock_type:?} pstate {pstate:?}"); + device + .set_clock_offset(clock_type, pstate, 0) + .with_context(|| { + format!("Could not reset {clock_type:?} pstate {pstate:?}") + })?; + } + } - self.last_applied_gpc_offset.set(None); - } - } - - if let Ok(current_offset) = device.mem_clk_vf_offset() { - if current_offset != 0 { - device - .set_mem_clk_vf_offset(0) - .context("Could not reset memory clock offset")?; - - self.last_applied_mem_offset.set(None); + if let Some(applied_offsets) = + self.last_applied_offsets.borrow_mut().get_mut(&clock_type) + { + applied_offsets.remove(&pstate); + } + } } } diff --git a/lact-daemon/src/snapshots/lact_daemon__config__tests__parse_doc.snap b/lact-daemon/src/snapshots/lact_daemon__config__tests__parse_doc.snap index 686b8e4..fd6cc5d 100644 --- a/lact-daemon/src/snapshots/lact_daemon__config__tests__parse_doc.snap +++ b/lact-daemon/src/snapshots/lact_daemon__config__tests__parse_doc.snap @@ -42,6 +42,10 @@ gpus: max_core_clock: 1630 max_memory_clock: 800 max_voltage: 1200 + gpu_clock_offsets: + 0: -100 + mem_clock_offsets: + 0: 200 voltage_offset: 0 power_profile_mode_index: 0 custom_power_profile_mode_hueristics: diff --git a/lact-gui/src/app/pages/oc_page/clocks_frame.rs b/lact-gui/src/app/pages/oc_page/clocks_frame.rs index 66892c5..6b84eaa 100644 --- a/lact-gui/src/app/pages/oc_page/clocks_frame.rs +++ b/lact-gui/src/app/pages/oc_page/clocks_frame.rs @@ -8,13 +8,16 @@ use adjustment_row::{ClockAdjustmentRow, ClockAdjustmentRowMsg, ClocksData}; use amdgpu_sysfs::gpu_handle::overdrive::{ClocksTable as _, ClocksTableGen as AmdClocksTable}; use gtk::{ pango, - prelude::{BoxExt, ButtonExt, OrientableExt, WidgetExt}, + prelude::{BoxExt, ButtonExt, CheckButtonExt, OrientableExt, WidgetExt}, }; use lact_schema::{ request::{ClockspeedType, SetClocksCommand}, - ClocksTable, IntelClocksTable, NvidiaClockInfo, NvidiaClocksTable, + ClocksTable, IntelClocksTable, NvidiaClockOffset, NvidiaClocksTable, +}; +use relm4::{ + binding::BoolBinding, factory::FactoryHashMap, ComponentParts, ComponentSender, RelmObjectExt, + RelmWidgetExt, }; -use relm4::{factory::FactoryHashMap, ComponentParts, ComponentSender, RelmWidgetExt}; // This is only used on RDNA1 in practice const DEFAULT_VOLTAGE_OFFSET_RANGE: i32 = 250; @@ -23,12 +26,15 @@ const WARNING_TEXT: &str = "Warning: changing these values may lead to system in pub struct ClocksFrame { clocks: FactoryHashMap, vram_clock_ratio: f64, + show_nvidia_pstate_info: bool, + show_all_pstates: BoolBinding, } #[derive(Debug)] pub enum ClocksFrameMsg { Clocks(Option), VramRatio(f64), + TogglePStatesVisibility, } #[relm4::component(pub)] @@ -43,12 +49,36 @@ impl relm4::SimpleComponent for ClocksFrame { set_label: WARNING_TEXT, set_wrap_mode: pango::WrapMode::Word, set_halign: gtk::Align::Start, - set_margin_vertical: 5, + set_margin_horizontal: 5, + }, + + append = >k::Box { + set_orientation: gtk::Orientation::Vertical, + set_spacing: 5, + + #[watch] + set_visible: model.show_nvidia_pstate_info, + + append = >k::CheckButton { + set_label: Some("Show all P-States"), + add_binding["active"]: &model.show_all_pstates, + }, + + append = >k::Label { + add_binding["visible"]: &model.show_all_pstates, + + set_margin_horizontal: 5, + set_markup: "The following values are clock offsets for each P-State, going from highest to lowest.", + set_wrap_mode: pango::WrapMode::Word, + set_halign: gtk::Align::Start, + }, + }, append = model.clocks.widget() { set_orientation: gtk::Orientation::Vertical, set_spacing: 5, + set_margin_horizontal: 5, }, append = >k::Label { @@ -62,7 +92,7 @@ impl relm4::SimpleComponent for ClocksFrame { append = >k::Button { set_label: "Reset", set_halign: gtk::Align::End, - set_width_request: 75, + set_margin_horizontal: 5, set_tooltip_text: Some("Warning: this resets all clock settings to defaults!"), set_css_classes: &["destructive-action"], #[watch] @@ -78,15 +108,21 @@ impl relm4::SimpleComponent for ClocksFrame { fn init( _init: Self::Init, root: Self::Root, - _sender: ComponentSender, + sender: ComponentSender, ) -> ComponentParts { let clocks = FactoryHashMap::builder().launch_default().detach(); let model = Self { clocks, vram_clock_ratio: 1.0, + show_nvidia_pstate_info: false, + show_all_pstates: BoolBinding::new(false), }; + model + .show_all_pstates + .connect_value_notify(move |_| sender.input(ClocksFrameMsg::TogglePStatesVisibility)); + let widgets = view_output!(); ComponentParts { model, widgets } @@ -96,6 +132,8 @@ impl relm4::SimpleComponent for ClocksFrame { match msg { ClocksFrameMsg::Clocks(clocks_table) => { self.clocks.clear(); + self.show_nvidia_pstate_info = false; + self.show_all_pstates.set_value(false); if let Some(table) = clocks_table { match table { @@ -104,10 +142,31 @@ impl relm4::SimpleComponent for ClocksFrame { ClocksTable::Intel(table) => self.set_intel_table(table), } } + + // Make sure the width of all the labels is the same + let label_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Horizontal); + let input_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Horizontal); + + for clockspeed_type in self.clocks.keys() { + self.clocks.send( + clockspeed_type, + ClockAdjustmentRowMsg::AddSizeGroup { + label_group: label_size_group.clone(), + input_group: input_size_group.clone(), + }, + ); + } } ClocksFrameMsg::VramRatio(vram_ratio) => { self.vram_clock_ratio = vram_ratio; } + ClocksFrameMsg::TogglePStatesVisibility => { + let show = self.show_all_pstates.value(); + for key in self.clocks.keys() { + self.clocks + .send(key, ClockAdjustmentRowMsg::ShowSecondaryPStates(show)); + } + } } self.update_vram_clock_ratio(); } @@ -190,16 +249,18 @@ impl ClocksFrame { } fn set_nvidia_table(&mut self, table: NvidiaClocksTable) { - if let Some(gpc_info) = &table.gpc { + self.show_nvidia_pstate_info = true; + + for (pstate, offset) in table.gpu_offsets { self.clocks.insert( - ClockspeedType::MaxCoreClock, - nvidia_clock_offset_to_data(gpc_info), + ClockspeedType::GpuClockOffset(pstate), + nvidia_clock_offset_to_data(&offset), ); } - if let Some(mem_info) = &table.mem { + for (pstate, offset) in table.mem_offsets { self.clocks.insert( - ClockspeedType::MaxMemoryClock, - nvidia_clock_offset_to_data(mem_info), + ClockspeedType::MemClockOffset(pstate), + nvidia_clock_offset_to_data(&offset), ); } } @@ -241,10 +302,10 @@ impl ClocksFrame { } } -fn nvidia_clock_offset_to_data(clock_info: &NvidiaClockInfo) -> ClocksData { +fn nvidia_clock_offset_to_data(clock_info: &NvidiaClockOffset) -> ClocksData { ClocksData { - current: clock_info.max + (clock_info.offset / clock_info.offset_ratio), - min: clock_info.max + clock_info.offset_range.0, - max: clock_info.max + clock_info.offset_range.1, + current: clock_info.current, + min: clock_info.min, + max: clock_info.max, } } diff --git a/lact-gui/src/app/pages/oc_page/clocks_frame/adjustment_row.rs b/lact-gui/src/app/pages/oc_page/clocks_frame/adjustment_row.rs index ea50fe4..07f1726 100644 --- a/lact-gui/src/app/pages/oc_page/clocks_frame/adjustment_row.rs +++ b/lact-gui/src/app/pages/oc_page/clocks_frame/adjustment_row.rs @@ -25,6 +25,11 @@ pub struct ClocksData { #[derive(Debug)] pub enum ClockAdjustmentRowMsg { ValueRatio(f64), + ShowSecondaryPStates(bool), + AddSizeGroup { + label_group: gtk::SizeGroup, + input_group: gtk::SizeGroup, + }, } #[relm4::factory(pub)] @@ -37,20 +42,24 @@ impl FactoryComponent for ClockAdjustmentRow { type Index = ClockspeedType; view! { + #[name = "root_box"] gtk::Box { + #[name = "title_label"] gtk::Label { - set_width_request: 185, set_xalign: 0.0, - set_label: match self.clock_type { - ClockspeedType::MaxCoreClock => "Maximum GPU Clock (MHz)", - ClockspeedType::MaxMemoryClock => "Maximum VRAM Clock (MHz)", - ClockspeedType::MaxVoltage => "Maximum GPU voltage (mV)", - ClockspeedType::MinCoreClock => "Minimum GPU Clock (MHz)", - ClockspeedType::MinMemoryClock => "Minimum VRAM Clock (MHz)", - ClockspeedType::MinVoltage => "Minimum GPU voltage (mV)", - ClockspeedType::VoltageOffset => "GPU voltage offset (mV)", + #[watch] + set_markup: &match self.clock_type { + ClockspeedType::MaxCoreClock => "Maximum GPU Clock (MHz)".to_owned(), + ClockspeedType::MaxMemoryClock => "Maximum VRAM Clock (MHz)".to_owned(), + ClockspeedType::MaxVoltage => "Maximum GPU voltage (mV)".to_owned(), + ClockspeedType::MinCoreClock => "Minimum GPU Clock (MHz)".to_owned(), + ClockspeedType::MinMemoryClock => "Minimum VRAM Clock (MHz)".to_owned(), + ClockspeedType::MinVoltage => "Minimum GPU voltage (mV)".to_owned(), + ClockspeedType::VoltageOffset => "GPU voltage offset (mV)".to_owned(), + ClockspeedType::GpuClockOffset(pstate) => format!("GPU P-State {pstate} Clock Offset (MHz)"), + ClockspeedType::MemClockOffset(pstate) => format!("VRAM P-State {pstate} Clock Offset (MHz)"), ClockspeedType::Reset => unreachable!(), - }, + } }, gtk::Scale { @@ -63,9 +72,9 @@ impl FactoryComponent for ClockAdjustmentRow { set_margin_horizontal: 5, }, + #[name = "input_button"] gtk::SpinButton { set_adjustment: &self.adjustment, - set_width_request: 120, }, } } @@ -96,7 +105,12 @@ impl FactoryComponent for ClockAdjustmentRow { } } - fn update(&mut self, msg: Self::Input, _sender: relm4::FactorySender) { + fn update_with_view( + &mut self, + widgets: &mut Self::Widgets, + msg: Self::Input, + sender: relm4::FactorySender, + ) { match msg { ClockAdjustmentRowMsg::ValueRatio(ratio) => { self.adjustment.block_signal(&self.change_signal); @@ -113,7 +127,20 @@ impl FactoryComponent for ClockAdjustmentRow { self.adjustment.unblock_signal(&self.change_signal); } + ClockAdjustmentRowMsg::AddSizeGroup { + label_group, + input_group, + } => { + label_group.add_widget(&widgets.title_label); + input_group.add_widget(&widgets.input_button); + } + ClockAdjustmentRowMsg::ShowSecondaryPStates(show_secondary) => { + let show_current = show_secondary || !clock_type_is_secondary(&self.clock_type); + widgets.root_box.set_visible(show_current); + } } + + self.update_view(widgets, sender); } } @@ -124,3 +151,11 @@ impl ClockAdjustmentRow { .map(|value| (value / self.value_ratio) as i32) } } + +fn clock_type_is_secondary(clock_type: &ClockspeedType) -> bool { + match clock_type { + ClockspeedType::GpuClockOffset(pstate) => *pstate > 0, + ClockspeedType::MemClockOffset(pstate) => *pstate > 0, + _ => false, + } +} diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index 3905818..8b4b94f 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -167,11 +167,12 @@ pub enum ClocksTable { Intel(IntelClocksTable), } -#[skip_serializing_none] #[derive(Serialize, Deserialize, Default, Debug, Clone)] pub struct NvidiaClocksTable { - pub gpc: Option, - pub mem: Option, + #[serde(default, skip_serializing_if = "IndexMap::is_empty")] + pub gpu_offsets: IndexMap, + #[serde(default, skip_serializing_if = "IndexMap::is_empty")] + pub mem_offsets: IndexMap, } /// Doc from `xe_gt_freq.c` @@ -188,11 +189,10 @@ pub struct IntelClocksTable { } #[derive(Serialize, Deserialize, Default, Debug, Clone)] -pub struct NvidiaClockInfo { +pub struct NvidiaClockOffset { + pub current: i32, + pub min: i32, pub max: i32, - pub offset: i32, - pub offset_ratio: i32, - pub offset_range: (i32, i32), } impl From for ClocksInfo { diff --git a/lact-schema/src/request.rs b/lact-schema/src/request.rs index af15522..08dcc60 100644 --- a/lact-schema/src/request.rs +++ b/lact-schema/src/request.rs @@ -125,6 +125,8 @@ pub enum ClockspeedType { MinMemoryClock, MinVoltage, VoltageOffset, + GpuClockOffset(u32), + MemClockOffset(u32), Reset, }