From a1c44cdea7ae27f7a09fd194c580c79b291d25d1 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sat, 27 Jan 2024 18:44:43 -0500 Subject: [PATCH] Improve memory clock selection for #140, fix dpm_performance enforcement check for GPU --- .../community_settings_core/src/v1/setting.rs | 2 +- backend/limits_core/src/json_v2/base.rs | 50 +++--- backend/limits_core/src/json_v2/gpu_limit.rs | 19 +++ backend/src/api/api_types.rs | 3 +- backend/src/api/gpu.rs | 33 +++- backend/src/api/handler.rs | 10 +- backend/src/api/web.rs | 4 +- backend/src/main.rs | 4 + backend/src/persist/gpu.rs | 4 +- backend/src/settings/detect/auto_detect.rs | 10 ++ backend/src/settings/generic/gpu.rs | 11 +- backend/src/settings/generic_amd/gpu.rs | 11 +- backend/src/settings/steam_deck/gpu.rs | 161 +++++++++++++----- .../settings/steam_deck/power_dpm_force.rs | 2 +- backend/src/settings/traits.rs | 4 +- backend/src/settings/unknown/gpu.rs | 17 +- src/backend.ts | 13 +- src/components/gpu.tsx | 38 ++++- src/index.tsx | 2 +- 19 files changed, 285 insertions(+), 113 deletions(-) diff --git a/backend/community_settings_core/src/v1/setting.rs b/backend/community_settings_core/src/v1/setting.rs index 259a7b5..8d2ea03 100644 --- a/backend/community_settings_core/src/v1/setting.rs +++ b/backend/community_settings_core/src/v1/setting.rs @@ -43,5 +43,5 @@ pub struct Gpu { pub tdp: Option, pub tdp_boost: Option, pub clock_limits: Option>, - pub slow_memory: bool, + pub memory_clock: Option, } diff --git a/backend/limits_core/src/json_v2/base.rs b/backend/limits_core/src/json_v2/base.rs index 23c0717..57abec8 100644 --- a/backend/limits_core/src/json_v2/base.rs +++ b/backend/limits_core/src/json_v2/base.rs @@ -16,35 +16,11 @@ impl Default for Base { fn default() -> Self { Base { configs: vec![ - super::Config { - name: "Steam Deck Custom".to_owned(), - conditions: super::Conditions { - dmi: None, - cpuinfo: Some("model name\t: AMD Custom APU (0405)|(0932)\n".to_owned()), - os: None, - command: None, - file_exists: Some("./limits_override.json".into()), - }, - limits: super::Limits { - cpu: super::Limit { - provider: super::CpuLimitType::SteamDeckAdvance, - limits: super::GenericCpusLimit::default_for(super::CpuLimitType::SteamDeckAdvance), - }, - gpu: super::Limit { - provider: super::GpuLimitType::SteamDeckAdvance, - limits: super::GenericGpuLimit::default_for(super::GpuLimitType::SteamDeckAdvance), - }, - battery: super::Limit { - provider: super::BatteryLimitType::SteamDeckAdvance, - limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::SteamDeckAdvance), - }, - } - }, super::Config { name: "Steam Deck".to_owned(), conditions: super::Conditions { dmi: None, - cpuinfo: Some("model name\t: AMD Custom APU (0405)|(0932)\n".to_owned()), + cpuinfo: Some("model name\t: AMD Custom APU 0405\n".to_owned()), os: None, command: None, file_exists: None, @@ -64,6 +40,30 @@ impl Default for Base { }, } }, + super::Config { + name: "Steam Deck OLED".to_owned(), + conditions: super::Conditions { + dmi: None, + cpuinfo: Some("model name\t: AMD Custom APU 0932\n".to_owned()), + os: None, + command: None, + file_exists: None, + }, + limits: super::Limits { + cpu: super::Limit { + provider: super::CpuLimitType::SteamDeck, + limits: super::GenericCpusLimit::default_for(super::CpuLimitType::SteamDeck), + }, + gpu: super::Limit { + provider: super::GpuLimitType::SteamDeck, + limits: super::GenericGpuLimit::default_for(super::GpuLimitType::SteamDeckOLED), + }, + battery: super::Limit { + provider: super::BatteryLimitType::SteamDeck, + limits: super::GenericBatteryLimit::default_for(super::BatteryLimitType::SteamDeck), + }, + } + }, super::Config { name: "AMD R3 2300U".to_owned(), conditions: super::Conditions { diff --git a/backend/limits_core/src/json_v2/gpu_limit.rs b/backend/limits_core/src/json_v2/gpu_limit.rs index 32e47f6..39643ff 100644 --- a/backend/limits_core/src/json_v2/gpu_limit.rs +++ b/backend/limits_core/src/json_v2/gpu_limit.rs @@ -8,6 +8,8 @@ pub enum GpuLimitType { SteamDeck, #[serde(rename = "GabeBoyAdvance", alias = "SteamDeckAdvance")] SteamDeckAdvance, + #[serde(rename = "GabeBoy101", alias = "SteamDeckOLED")] + SteamDeckOLED, Generic, GenericAMD, Unknown, @@ -27,6 +29,8 @@ pub struct GenericGpuLimit { pub clock_min: Option>, pub clock_max: Option>, pub clock_step: Option, + pub memory_clock: Option>, + pub memory_clock_step: Option, pub skip_resume_reclock: bool, } @@ -34,6 +38,7 @@ impl GenericGpuLimit { pub fn default_for(t: GpuLimitType) -> Self { match t { GpuLimitType::SteamDeck | GpuLimitType::SteamDeckAdvance => Self::default_steam_deck(), + GpuLimitType::SteamDeckOLED => Self::default_steam_deck_oled(), _t => Self::default(), } } @@ -64,10 +69,24 @@ impl GenericGpuLimit { max: Some(1600), }), clock_step: Some(100), + // Disabled for now since LCD version is a bit broken on sysfs right now + /*memory_clock: Some(RangeLimit { + min: Some(400), + max: Some(800), + }), + memory_clock_step: Some(400),*/ + memory_clock: None, + memory_clock_step: None, skip_resume_reclock: false, } } + fn default_steam_deck_oled() -> Self { + let mut sd = Self::default_steam_deck(); + sd.memory_clock_step = Some(200); + sd + } + pub fn apply_override(&mut self, limit_override: Self) { if let Some(range) = limit_override.fast_ppt { if range.min.is_none() && range.max.is_none() { diff --git a/backend/src/api/api_types.rs b/backend/src/api/api_types.rs index cf889d3..8854656 100644 --- a/backend/src/api/api_types.rs +++ b/backend/src/api/api_types.rs @@ -59,7 +59,8 @@ pub struct GpuLimits { pub clock_min_limits: Option>, pub clock_max_limits: Option>, pub clock_step: u64, - pub memory_control_capable: bool, + pub memory_control: Option>, + pub memory_step: u64, } #[derive(Serialize, Deserialize)] diff --git a/backend/src/api/gpu.rs b/backend/src/api/gpu.rs index 320a331..f8b178d 100644 --- a/backend/src/api/gpu.rs +++ b/backend/src/api/gpu.rs @@ -160,17 +160,17 @@ pub fn set_slow_memory( sender: Sender, ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety - let setter = move |value: bool| { + let setter = move |value: u64| { sender .lock() .unwrap() - .send(ApiMessage::Gpu(GpuMessage::SetSlowMemory(value))) + .send(ApiMessage::Gpu(GpuMessage::SetMemoryClock(Some(value)))) .expect("unset_clock_limits send failed") }; move |params_in: super::ApiParameterType| { - if let Some(&Primitive::Bool(memory_is_slow)) = params_in.get(0) { - setter(memory_is_slow); - vec![memory_is_slow.into()] + if let Some(&Primitive::F64(mem_clock)) = params_in.get(0) { + setter(mem_clock as _); + vec![mem_clock.into()] } else { vec!["set_slow_memory missing parameter 0".into()] } @@ -183,14 +183,14 @@ pub fn get_slow_memory(sender: Sender) -> impl AsyncCallable { let sender2 = sender.clone(); move || { let (tx, rx) = mpsc::channel(); - let callback = move |value: bool| { + let callback = move |value: Option| { tx.send(value) .expect("get_slow_memory callback send failed") }; sender2 .lock() .unwrap() - .send(ApiMessage::Gpu(GpuMessage::GetSlowMemory(Box::new( + .send(ApiMessage::Gpu(GpuMessage::GetMemoryClock(Box::new( callback, )))) .expect("get_slow_memory send failed"); @@ -199,6 +199,23 @@ pub fn get_slow_memory(sender: Sender) -> impl AsyncCallable { }; super::async_utils::AsyncIshGetter { set_get: getter, - trans_getter: |value: bool| vec![value.into()], + trans_getter: |value: Option| vec![super::utility::map_optional(value)], + } +} + +pub fn unset_slow_memory( + sender: Sender, +) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { + let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety + let setter = move || { + sender + .lock() + .unwrap() + .send(ApiMessage::Gpu(GpuMessage::SetMemoryClock(None))) + .expect("unset_slow_memory send failed") + }; + move |_: super::ApiParameterType| { + setter(); + vec![true.into()] } } diff --git a/backend/src/api/handler.rs b/backend/src/api/handler.rs index 62c433f..f07ce58 100644 --- a/backend/src/api/handler.rs +++ b/backend/src/api/handler.rs @@ -198,8 +198,8 @@ pub enum GpuMessage { GetPpt(Callback<(Option, Option)>), SetClockLimits(Option>), GetClockLimits(Callback>>), - SetSlowMemory(bool), - GetSlowMemory(Callback), + SetMemoryClock(Option), + GetMemoryClock(Callback>), } impl GpuMessage { @@ -210,8 +210,8 @@ impl GpuMessage { Self::GetPpt(cb) => cb(settings.get_ppt()), Self::SetClockLimits(clocks) => settings.clock_limits(clocks), Self::GetClockLimits(cb) => cb(settings.get_clock_limits().map(|x| x.to_owned())), - Self::SetSlowMemory(val) => *settings.slow_memory() = val, - Self::GetSlowMemory(cb) => cb(*settings.slow_memory()), + Self::SetMemoryClock(val) => settings.memory_clock(val), + Self::GetMemoryClock(cb) => cb(settings.get_memory_clock()), } dirty } @@ -219,7 +219,7 @@ impl GpuMessage { fn is_modify(&self) -> bool { matches!( self, - Self::SetPpt(_, _) | Self::SetClockLimits(_) | Self::SetSlowMemory(_) + Self::SetPpt(_, _) | Self::SetClockLimits(_) | Self::SetMemoryClock(_) ) } } diff --git a/backend/src/api/web.rs b/backend/src/api/web.rs index 08b9eab..81d5cb0 100644 --- a/backend/src/api/web.rs +++ b/backend/src/api/web.rs @@ -70,7 +70,7 @@ fn web_config_to_settings_json(meta: community_settings_core::v1::Metadata) -> c min: lim.min, max: lim.max, }), - slow_memory: meta.config.gpu.slow_memory, + memory_clock: meta.config.gpu.memory_clock, root: None, }, battery: crate::persist::BatteryJson { @@ -138,7 +138,7 @@ fn settings_to_web_config(app_id: u32, user_id: u64, username: String, settings: min: lim.min, max: lim.max, }), - slow_memory: settings.gpu.slow_memory, + memory_clock: settings.gpu.memory_clock, }, battery: community_settings_core::v1::Battery { charge_rate: settings.battery.charge_rate, diff --git a/backend/src/main.rs b/backend/src/main.rs index e39bec6..f2f683b 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -243,6 +243,10 @@ fn main() -> Result<(), ()> { "GPU_get_slow_memory", api::gpu::get_slow_memory(api_sender.clone()), ) + .register( + "GPU_unset_slow_memory", + api::gpu::unset_slow_memory(api_sender.clone()), + ) // general API functions .register( "GENERAL_set_persistent", diff --git a/backend/src/persist/gpu.rs b/backend/src/persist/gpu.rs index a91f9de..57df0f2 100644 --- a/backend/src/persist/gpu.rs +++ b/backend/src/persist/gpu.rs @@ -11,7 +11,7 @@ pub struct GpuJson { pub tdp: Option, pub tdp_boost: Option, pub clock_limits: Option>, - pub slow_memory: bool, + pub memory_clock: Option, #[serde(skip_serializing_if = "Option::is_none")] pub root: Option, } @@ -24,7 +24,7 @@ impl Default for GpuJson { tdp: None, tdp_boost: None, clock_limits: None, - slow_memory: false, + memory_clock: None, root: None, } } diff --git a/backend/src/settings/detect/auto_detect.rs b/backend/src/settings/detect/auto_detect.rs index 9556d42..d21e502 100644 --- a/backend/src/settings/detect/auto_detect.rs +++ b/backend/src/settings/detect/auto_detect.rs @@ -203,6 +203,13 @@ pub fn auto_detect0( relevant_limits.gpu.limits, )) } + GpuLimitType::SteamDeckOLED => { + Box::new(crate::settings::steam_deck::Gpu::from_json_and_limits( + settings.gpu.clone(), + settings.version, + relevant_limits.gpu.limits, + )) + } GpuLimitType::Generic => { Box::new(crate::settings::generic::Gpu::from_json_and_limits( settings.gpu.clone(), @@ -289,6 +296,9 @@ pub fn auto_detect0( GpuLimitType::SteamDeckAdvance => { Box::new(crate::settings::steam_deck::Gpu::from_limits(relevant_limits.gpu.limits)) } + GpuLimitType::SteamDeckOLED => { + Box::new(crate::settings::steam_deck::Gpu::from_limits(relevant_limits.gpu.limits)) + } GpuLimitType::Generic => { Box::new(crate::settings::generic::Gpu::from_limits(relevant_limits.gpu.limits)) } diff --git a/backend/src/settings/generic/gpu.rs b/backend/src/settings/generic/gpu.rs index 27c4322..3e2b5cf 100644 --- a/backend/src/settings/generic/gpu.rs +++ b/backend/src/settings/generic/gpu.rs @@ -111,7 +111,7 @@ impl Into for Gpu { tdp: self.tdp, tdp_boost: self.tdp_boost, clock_limits: self.clock_limits.map(|x| x.into()), - slow_memory: false, + memory_clock: None, root: self.sysfs.root().and_then(|p| p.as_ref().to_str().map(|s| s.to_owned())) } } @@ -177,7 +177,8 @@ impl TGpu for Gpu { .clone() .map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(3_000))), clock_step: self.limits.clock_step.unwrap_or(100), - memory_control_capable: false, + memory_control: None, + memory_step: 100, } } @@ -227,8 +228,10 @@ impl TGpu for Gpu { self.clock_limits.as_ref() } - fn slow_memory(&mut self) -> &mut bool { - &mut self.slow_memory + fn memory_clock(&mut self, _speed: Option) {} + + fn get_memory_clock(&self) -> Option { + None } fn provider(&self) -> crate::persist::DriverJson { diff --git a/backend/src/settings/generic_amd/gpu.rs b/backend/src/settings/generic_amd/gpu.rs index baf1508..0a1331a 100644 --- a/backend/src/settings/generic_amd/gpu.rs +++ b/backend/src/settings/generic_amd/gpu.rs @@ -299,7 +299,8 @@ fn bad_gpu_limits() -> crate::api::GpuLimits { clock_min_limits: None, clock_max_limits: None, clock_step: 100, - memory_control_capable: false, + memory_control: None, + memory_step: 400, } } @@ -333,8 +334,12 @@ impl TGpu for Gpu { self.generic.get_clock_limits() } - fn slow_memory(&mut self) -> &mut bool { - self.generic.slow_memory() + fn memory_clock(&mut self, speed: Option) { + self.generic.memory_clock(speed) + } + + fn get_memory_clock(&self) -> Option { + self.generic.get_memory_clock() } fn provider(&self) -> crate::persist::DriverJson { diff --git a/backend/src/settings/steam_deck/gpu.rs b/backend/src/settings/steam_deck/gpu.rs index 7b06454..fe918eb 100644 --- a/backend/src/settings/steam_deck/gpu.rs +++ b/backend/src/settings/steam_deck/gpu.rs @@ -1,6 +1,6 @@ use std::convert::Into; -use sysfuss::{BasicEntityPath, HwMonPath, SysEntity, capability::attributes, SysEntityAttributesExt, SysAttribute}; +use sysfuss::{BasicEntityPath, HwMonPath, SysEntity, capability::attributes, SysEntityAttributes, SysEntityAttributesExt, SysAttribute}; use limits_core::json_v2::GenericGpuLimit; @@ -20,7 +20,7 @@ pub struct Gpu { pub fast_ppt: Option, pub slow_ppt: Option, pub clock_limits: Option>, - pub slow_memory: bool, + pub memory_clock: Option, limits: GenericGpuLimit, state: crate::state::steam_deck::Gpu, sysfs_card: BasicEntityPath, @@ -47,6 +47,8 @@ enum ClockType { const MAX_CLOCK: u64 = 1600; const MIN_CLOCK: u64 = 200; +const MAX_MEMORY_CLOCK: u64 = 800; +const MIN_MEMORY_CLOCK: u64 = 400; const MAX_FAST_PPT: u64 = 30_000_000; const MIN_FAST_PPT: u64 = 1_000_000; const MAX_SLOW_PPT: u64 = 29_000_000; @@ -108,6 +110,63 @@ impl Gpu { }) } + fn is_memory_clock_maxed(&self) -> bool { + if let Some(clock) = &self.memory_clock { + if let Some(limit) = &self.limits.memory_clock { + if let Some(limit) = &limit.max { + if let Some(step) = &self.limits.memory_clock_step { + log::debug!("chosen_clock: {}, limit_clock: {}, step: {}", clock, limit, step); + return clock > &(limit - step); + } else { + log::debug!("chosen_clock: {}, limit_clock: {}", clock, limit); + return clock == limit; + } + } + } + } + true + } + + fn quantize_memory_clock(&self, clock: u64) -> u64 { + if let Ok(f) = self.sysfs_card.read_value(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned()) { + let options = parse_pp_dpm_fclk(&String::from_utf8_lossy(&f)); + // round (and find) nearest valid clock step + // roughly price is right strategy (clock step will always be lower or equal to chosen) + for i in 0..options.len() { + let (current_val_opt, current_speed_opt) = &options[i]; + let current_speed_opt = *current_speed_opt as u64; + if clock == current_speed_opt { + return *current_val_opt as _; + } else if current_speed_opt > clock { + if i == 0 { + return *current_val_opt as _; + } else { + return options[i-1].0 as _; + } + } + } + options[options.len() - 1].0 as _ + } else { + self.is_memory_clock_maxed() as u64 + } + } + + fn build_memory_clock_payload(&self, clock: u64) -> String { + let max_val = self.quantize_memory_clock(clock); + match max_val { + 0 => "0\n".to_owned(), + max_val => { + use std::fmt::Write; + let mut payload = String::from("0"); + for i in 1..max_val { + write!(payload, " {}", i).expect("Failed to write to memory payload (should be infallible!?)"); + } + write!(payload, " {}\n", max_val).expect("Failed to write to memory payload (should be infallible!?)"); + payload + } + } + } + fn set_clocks(&mut self) -> Result<(), Vec> { let mut errors = Vec::new(); if let Some(clock_limits) = &self.clock_limits { @@ -130,7 +189,7 @@ impl Gpu { || POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { self.state.clock_limits_set = false; - POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(self.slow_memory); + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(!self.is_memory_clock_maxed()); if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs_card)?; // disable manual clock limits @@ -155,47 +214,36 @@ impl Gpu { } } - fn set_slow_memory(&self, slow: bool) -> Result<(), SettingError> { + fn set_memory_speed(&self, clock: u64) -> Result<(), SettingError> { let path = GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.path(&self.sysfs_card); - if slow { - self.sysfs_card.set(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned(), slow as u8).map_err(|e| { - SettingError { - msg: format!("Failed to write to `{}`: {}", path.display(), e), - setting: crate::settings::SettingVariant::Gpu, - } - }) - } else { - // NOTE: there is a GPU driver/hardware bug that prevents this from working - self.sysfs_card.set(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned(), "0 1\n").map_err(|e| { - SettingError { - msg: format!("Failed to write to `{}`: {}", path.display(), e), - setting: crate::settings::SettingVariant::Gpu, - } - }) - } + let payload = self.build_memory_clock_payload(clock); + log::debug!("Generated payload for gpu fclk (memory): `{}` (is maxed? {})", payload, self.is_memory_clock_maxed()); + self.sysfs_card.set(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned(), payload).map_err(|e| { + SettingError { + msg: format!("Failed to write to `{}`: {}", path.display(), e), + setting: crate::settings::SettingVariant::Gpu, + } + }) } fn set_force_performance_related(&mut self) -> Result<(), Vec> { let mut errors = Vec::new(); + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(!self.is_memory_clock_maxed() || self.clock_limits.is_some()); + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT + .enforce_level(&self.sysfs_card) + .unwrap_or_else(|mut e| errors.append(&mut e)); // enable/disable downclock of GPU memory (to 400Mhz?) - if self.slow_memory { - POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(true); - POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT - .enforce_level(&self.sysfs_card) - .unwrap_or_else(|mut e| errors.append(&mut e)); - self.set_slow_memory(self.slow_memory).unwrap_or_else(|e| errors.push(e)); - } else if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { - self.set_slow_memory(self.slow_memory).unwrap_or_else(|e| errors.push(e)); - POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(self.clock_limits.is_some()); - POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT - .enforce_level(&self.sysfs_card) - .unwrap_or_else(|mut e| errors.append(&mut e)); - } + self.set_memory_speed( + self.memory_clock + .or_else(|| self.limits.memory_clock + .map(|lim| lim.max.unwrap_or(MAX_MEMORY_CLOCK)) + ).unwrap_or(MAX_MEMORY_CLOCK) + ).unwrap_or_else(|e| errors.push(e)); self.set_clocks() .unwrap_or_else(|mut e| errors.append(&mut e)); // commit changes (if no errors have already occured) if errors.is_empty() { - if self.slow_memory || self.clock_limits.is_some() { + if !self.is_memory_clock_maxed() || self.clock_limits.is_some() { self.set_confirm().map_err(|e| { errors.push(e); errors @@ -294,6 +342,9 @@ impl Gpu { Some(max.clamp(self.limits.clock_max.and_then(|lim| lim.min).unwrap_or(MIN_CLOCK), self.limits.clock_max.and_then(|lim| lim.max).unwrap_or(MAX_CLOCK))); } } + if let Some(mem_clock) = self.memory_clock { + self.memory_clock = Some(mem_clock.clamp(self.limits.memory_clock.and_then(|lim| lim.min).unwrap_or(MIN_MEMORY_CLOCK), self.limits.memory_clock.and_then(|lim| lim.max).unwrap_or(MAX_MEMORY_CLOCK))); + } } } @@ -306,7 +357,7 @@ impl Into for Gpu { tdp: None, tdp_boost: None, clock_limits: self.clock_limits.map(|x| x.into()), - slow_memory: self.slow_memory, + memory_clock: self.memory_clock, root: self.sysfs_card.root().or(self.sysfs_hwmon.root()).and_then(|p| p.as_ref().to_str().map(|r| r.to_owned())) } } @@ -319,7 +370,7 @@ impl ProviderBuilder for Gpu { fast_ppt: persistent.fast_ppt, slow_ppt: persistent.slow_ppt, clock_limits: persistent.clock_limits.map(|x| min_max_from_json(x, version)), - slow_memory: persistent.slow_memory, + memory_clock: persistent.memory_clock, limits: limits, state: crate::state::steam_deck::Gpu::default(), sysfs_card: Self::find_card_sysfs(persistent.root.clone()), @@ -329,7 +380,7 @@ impl ProviderBuilder for Gpu { fast_ppt: persistent.fast_ppt, slow_ppt: persistent.slow_ppt, clock_limits: persistent.clock_limits.map(|x| min_max_from_json(x, version)), - slow_memory: persistent.slow_memory, + memory_clock: persistent.memory_clock, limits: limits, state: crate::state::steam_deck::Gpu::default(), sysfs_card: Self::find_card_sysfs(persistent.root.clone()), @@ -343,7 +394,7 @@ impl ProviderBuilder for Gpu { fast_ppt: None, slow_ppt: None, clock_limits: None, - slow_memory: false, + memory_clock: None, limits: limits, state: crate::state::steam_deck::Gpu::default(), sysfs_card: Self::find_card_sysfs(None::<&'static str>), @@ -393,7 +444,11 @@ impl TGpu for Gpu { max: super::util::range_max_or_fallback(&self.limits.clock_max, MAX_CLOCK), }), clock_step: self.limits.clock_step.unwrap_or(100), - memory_control_capable: true, + memory_control: Some(RangeLimit { + min: super::util::range_min_or_fallback(&self.limits.memory_clock, MIN_MEMORY_CLOCK), + max: super::util::range_max_or_fallback(&self.limits.memory_clock, MAX_MEMORY_CLOCK), + }), + memory_step: self.limits.memory_clock_step.unwrap_or(400), } } @@ -421,11 +476,35 @@ impl TGpu for Gpu { self.clock_limits.as_ref() } - fn slow_memory(&mut self) -> &mut bool { - &mut self.slow_memory + fn memory_clock(&mut self, speed: Option) { + self.memory_clock = speed; + } + + fn get_memory_clock(&self) -> Option { + self.memory_clock } fn provider(&self) -> crate::persist::DriverJson { crate::persist::DriverJson::SteamDeck } } + +fn parse_pp_dpm_fclk(s: &str) -> Vec<(usize, usize)> { // (value, MHz) + let mut result = Vec::new(); + for line in s.split('\n') { + if !line.is_empty() { + if let Some((val, freq_mess)) = line.split_once(':') { + if let Ok(val) = val.parse::() { + if let Some((freq, _unit)) = freq_mess.trim().split_once(|c: char| !c.is_digit(10)) { + if let Ok(freq) = freq.parse::() { + result.push((val, freq)); + } + } + } + } + } else { + break; + } + } + result +} diff --git a/backend/src/settings/steam_deck/power_dpm_force.rs b/backend/src/settings/steam_deck/power_dpm_force.rs index 72404cc..d804a18 100644 --- a/backend/src/settings/steam_deck/power_dpm_force.rs +++ b/backend/src/settings/steam_deck/power_dpm_force.rs @@ -96,7 +96,7 @@ impl PDFPLManager { if let Ok(mode_now) = entity.attribute::(DPM_FORCE_LIMITS_ATTRIBUTE.to_owned()) { - log::debug!("Mode for `{}` is now `{}`", path.display(), mode_now); + log::debug!("Mode for `{}` is now `{}` ({:#b})", path.display(), mode_now, self.get()); } else { log::debug!("Error getting new mode for debugging purposes"); } diff --git a/backend/src/settings/traits.rs b/backend/src/settings/traits.rs index dd0fc69..5f993b2 100644 --- a/backend/src/settings/traits.rs +++ b/backend/src/settings/traits.rs @@ -59,7 +59,9 @@ pub trait TGpu: OnSet + OnResume + OnPowerEvent + Debug + Send { fn get_clock_limits(&self) -> Option<&MinMax>; - fn slow_memory(&mut self) -> &mut bool; + fn memory_clock(&mut self, speed: Option); + + fn get_memory_clock(&self) -> Option; fn provider(&self) -> crate::persist::DriverJson { crate::persist::DriverJson::AutoDetect diff --git a/backend/src/settings/unknown/gpu.rs b/backend/src/settings/unknown/gpu.rs index f7b5b36..c676cda 100644 --- a/backend/src/settings/unknown/gpu.rs +++ b/backend/src/settings/unknown/gpu.rs @@ -8,13 +8,11 @@ use crate::settings::{TGpu, ProviderBuilder}; use crate::settings::{OnResume, OnSet, SettingError}; #[derive(Debug, Clone)] -pub struct Gpu { - slow_memory: bool, // ignored -} +pub struct Gpu {} impl Gpu { pub fn system_default() -> Self { - Self { slow_memory: false } + Self { } } } @@ -37,7 +35,7 @@ impl Into for Gpu { tdp: None, tdp_boost: None, clock_limits: None, - slow_memory: false, + memory_clock: None, root: None, } } @@ -69,7 +67,8 @@ impl TGpu for Gpu { clock_min_limits: None, clock_max_limits: None, clock_step: 100, - memory_control_capable: false, + memory_control: None, + memory_step: 400, } } @@ -89,8 +88,10 @@ impl TGpu for Gpu { None } - fn slow_memory(&mut self) -> &mut bool { - &mut self.slow_memory + fn memory_clock(&mut self, _speed: Option) {} + + fn get_memory_clock(&self) -> Option { + None } fn provider(&self) -> crate::persist::DriverJson { diff --git a/src/backend.ts b/src/backend.ts index a04a79a..bff625a 100644 --- a/src/backend.ts +++ b/src/backend.ts @@ -90,7 +90,8 @@ export type GpuLimits = { clock_min_limits: RangeLimit | null; clock_max_limits: RangeLimit | null; clock_step: number; - memory_control_capable: boolean; + memory_control: RangeLimit | null, + memory_step: number, }; // API @@ -229,14 +230,18 @@ export async function unsetGpuClockLimits(): Promise { return (await call_backend("GPU_unset_clock_limits", [])); } -export async function setGpuSlowMemory(val: boolean): Promise { - return (await call_backend("GPU_set_slow_memory", [val]))[0]; +export async function setGpuSlowMemory(clock: number): Promise { + return (await call_backend("GPU_set_slow_memory", [clock]))[0]; } -export async function getGpuSlowMemory(): Promise { +export async function getGpuSlowMemory(): Promise { return (await call_backend("GPU_get_slow_memory", []))[0]; } +export async function unsetGpuSlowMemory(): Promise { + return (await call_backend("GPU_unset_slow_memory", [])); +} + // general export async function setGeneralPersistent(val: boolean): Promise { diff --git a/src/components/gpu.tsx b/src/components/gpu.tsx index a8fdd52..fb8dfc6 100644 --- a/src/components/gpu.tsx +++ b/src/components/gpu.tsx @@ -180,16 +180,42 @@ export class Gpu extends Component { }} />} - {(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_control_capable && + {((get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_control) && { - backend.resolve(backend.setGpuSlowMemory(value), (val: boolean) => { - set_value(SLOW_MEMORY_GPU, val); - reloadGUI("GPUSlowMemory"); - }) + if (value) { + set_value(SLOW_MEMORY_GPU, (get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_control!.max); + reloadGUI("GPUMemFreqToggle"); + } else { + set_value(SLOW_MEMORY_GPU, null); + backend.resolve(backend.unsetGpuSlowMemory(), (_: any[]) => { + reloadGUI("GPUUnsetMemFreq"); + }); + } + }} + /> + } + {get_value(SLOW_MEMORY_GPU) != null && + { + backend.log(backend.LogLevel.Debug, "GPU memory clock Max is now " + val.toString()); + backend.resolve( + backend.setGpuSlowMemory(val), + (val: number) => { + set_value(SLOW_MEMORY_GPU, val); + reloadGUI("GPUSetMemFreq"); + } + ); }} /> } diff --git a/src/index.tsx b/src/index.tsx index 3348742..1c8bef7 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -170,7 +170,7 @@ const reload = function() { set_value(CLOCK_MIN_GPU, limits[0]); set_value(CLOCK_MAX_GPU, limits[1]); }); - backend.resolve(backend.getGpuSlowMemory(), (status: boolean) => { set_value(SLOW_MEMORY_GPU, status) }); + backend.resolve(backend.getGpuSlowMemory(), (status: number) => { set_value(SLOW_MEMORY_GPU, status) }); backend.resolve(backend.getGeneralPersistent(), (value: boolean) => { set_value(PERSISTENT_GEN, value) }); backend.resolve(backend.getGeneralSettingsName(), (name: string) => { set_value(NAME_GEN, name) });