Improve memory clock selection for #140, fix dpm_performance enforcement check for GPU

This commit is contained in:
NGnius (Graham) 2024-01-27 18:44:43 -05:00
parent 622f161560
commit a1c44cdea7
19 changed files with 285 additions and 113 deletions

View file

@ -43,5 +43,5 @@ pub struct Gpu {
pub tdp: Option<u64>, pub tdp: Option<u64>,
pub tdp_boost: Option<u64>, pub tdp_boost: Option<u64>,
pub clock_limits: Option<MinMax<u64>>, pub clock_limits: Option<MinMax<u64>>,
pub slow_memory: bool, pub memory_clock: Option<u64>,
} }

View file

@ -16,35 +16,11 @@ impl Default for Base {
fn default() -> Self { fn default() -> Self {
Base { Base {
configs: vec![ 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 { super::Config {
name: "Steam Deck".to_owned(), name: "Steam Deck".to_owned(),
conditions: super::Conditions { conditions: super::Conditions {
dmi: None, 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, os: None,
command: None, command: None,
file_exists: 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 { super::Config {
name: "AMD R3 2300U".to_owned(), name: "AMD R3 2300U".to_owned(),
conditions: super::Conditions { conditions: super::Conditions {

View file

@ -8,6 +8,8 @@ pub enum GpuLimitType {
SteamDeck, SteamDeck,
#[serde(rename = "GabeBoyAdvance", alias = "SteamDeckAdvance")] #[serde(rename = "GabeBoyAdvance", alias = "SteamDeckAdvance")]
SteamDeckAdvance, SteamDeckAdvance,
#[serde(rename = "GabeBoy101", alias = "SteamDeckOLED")]
SteamDeckOLED,
Generic, Generic,
GenericAMD, GenericAMD,
Unknown, Unknown,
@ -27,6 +29,8 @@ pub struct GenericGpuLimit {
pub clock_min: Option<RangeLimit<u64>>, pub clock_min: Option<RangeLimit<u64>>,
pub clock_max: Option<RangeLimit<u64>>, pub clock_max: Option<RangeLimit<u64>>,
pub clock_step: Option<u64>, pub clock_step: Option<u64>,
pub memory_clock: Option<RangeLimit<u64>>,
pub memory_clock_step: Option<u64>,
pub skip_resume_reclock: bool, pub skip_resume_reclock: bool,
} }
@ -34,6 +38,7 @@ impl GenericGpuLimit {
pub fn default_for(t: GpuLimitType) -> Self { pub fn default_for(t: GpuLimitType) -> Self {
match t { match t {
GpuLimitType::SteamDeck | GpuLimitType::SteamDeckAdvance => Self::default_steam_deck(), GpuLimitType::SteamDeck | GpuLimitType::SteamDeckAdvance => Self::default_steam_deck(),
GpuLimitType::SteamDeckOLED => Self::default_steam_deck_oled(),
_t => Self::default(), _t => Self::default(),
} }
} }
@ -64,10 +69,24 @@ impl GenericGpuLimit {
max: Some(1600), max: Some(1600),
}), }),
clock_step: Some(100), 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, 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) { pub fn apply_override(&mut self, limit_override: Self) {
if let Some(range) = limit_override.fast_ppt { if let Some(range) = limit_override.fast_ppt {
if range.min.is_none() && range.max.is_none() { if range.min.is_none() && range.max.is_none() {

View file

@ -59,7 +59,8 @@ pub struct GpuLimits {
pub clock_min_limits: Option<RangeLimit<u64>>, pub clock_min_limits: Option<RangeLimit<u64>>,
pub clock_max_limits: Option<RangeLimit<u64>>, pub clock_max_limits: Option<RangeLimit<u64>>,
pub clock_step: u64, pub clock_step: u64,
pub memory_control_capable: bool, pub memory_control: Option<RangeLimit<u64>>,
pub memory_step: u64,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]

View file

@ -160,17 +160,17 @@ pub fn set_slow_memory(
sender: Sender<ApiMessage>, sender: Sender<ApiMessage>,
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType { ) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
let sender = Mutex::new(sender); // Sender is not Sync; this is required for safety 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 sender
.lock() .lock()
.unwrap() .unwrap()
.send(ApiMessage::Gpu(GpuMessage::SetSlowMemory(value))) .send(ApiMessage::Gpu(GpuMessage::SetMemoryClock(Some(value))))
.expect("unset_clock_limits send failed") .expect("unset_clock_limits send failed")
}; };
move |params_in: super::ApiParameterType| { move |params_in: super::ApiParameterType| {
if let Some(&Primitive::Bool(memory_is_slow)) = params_in.get(0) { if let Some(&Primitive::F64(mem_clock)) = params_in.get(0) {
setter(memory_is_slow); setter(mem_clock as _);
vec![memory_is_slow.into()] vec![mem_clock.into()]
} else { } else {
vec!["set_slow_memory missing parameter 0".into()] vec!["set_slow_memory missing parameter 0".into()]
} }
@ -183,14 +183,14 @@ pub fn get_slow_memory(sender: Sender<ApiMessage>) -> impl AsyncCallable {
let sender2 = sender.clone(); let sender2 = sender.clone();
move || { move || {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let callback = move |value: bool| { let callback = move |value: Option<u64>| {
tx.send(value) tx.send(value)
.expect("get_slow_memory callback send failed") .expect("get_slow_memory callback send failed")
}; };
sender2 sender2
.lock() .lock()
.unwrap() .unwrap()
.send(ApiMessage::Gpu(GpuMessage::GetSlowMemory(Box::new( .send(ApiMessage::Gpu(GpuMessage::GetMemoryClock(Box::new(
callback, callback,
)))) ))))
.expect("get_slow_memory send failed"); .expect("get_slow_memory send failed");
@ -199,6 +199,23 @@ pub fn get_slow_memory(sender: Sender<ApiMessage>) -> impl AsyncCallable {
}; };
super::async_utils::AsyncIshGetter { super::async_utils::AsyncIshGetter {
set_get: getter, set_get: getter,
trans_getter: |value: bool| vec![value.into()], trans_getter: |value: Option<u64>| vec![super::utility::map_optional(value)],
}
}
pub fn unset_slow_memory(
sender: Sender<ApiMessage>,
) -> 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()]
} }
} }

View file

@ -198,8 +198,8 @@ pub enum GpuMessage {
GetPpt(Callback<(Option<u64>, Option<u64>)>), GetPpt(Callback<(Option<u64>, Option<u64>)>),
SetClockLimits(Option<MinMax<u64>>), SetClockLimits(Option<MinMax<u64>>),
GetClockLimits(Callback<Option<MinMax<u64>>>), GetClockLimits(Callback<Option<MinMax<u64>>>),
SetSlowMemory(bool), SetMemoryClock(Option<u64>),
GetSlowMemory(Callback<bool>), GetMemoryClock(Callback<Option<u64>>),
} }
impl GpuMessage { impl GpuMessage {
@ -210,8 +210,8 @@ impl GpuMessage {
Self::GetPpt(cb) => cb(settings.get_ppt()), Self::GetPpt(cb) => cb(settings.get_ppt()),
Self::SetClockLimits(clocks) => settings.clock_limits(clocks), Self::SetClockLimits(clocks) => settings.clock_limits(clocks),
Self::GetClockLimits(cb) => cb(settings.get_clock_limits().map(|x| x.to_owned())), Self::GetClockLimits(cb) => cb(settings.get_clock_limits().map(|x| x.to_owned())),
Self::SetSlowMemory(val) => *settings.slow_memory() = val, Self::SetMemoryClock(val) => settings.memory_clock(val),
Self::GetSlowMemory(cb) => cb(*settings.slow_memory()), Self::GetMemoryClock(cb) => cb(settings.get_memory_clock()),
} }
dirty dirty
} }
@ -219,7 +219,7 @@ impl GpuMessage {
fn is_modify(&self) -> bool { fn is_modify(&self) -> bool {
matches!( matches!(
self, self,
Self::SetPpt(_, _) | Self::SetClockLimits(_) | Self::SetSlowMemory(_) Self::SetPpt(_, _) | Self::SetClockLimits(_) | Self::SetMemoryClock(_)
) )
} }
} }

View file

@ -70,7 +70,7 @@ fn web_config_to_settings_json(meta: community_settings_core::v1::Metadata) -> c
min: lim.min, min: lim.min,
max: lim.max, max: lim.max,
}), }),
slow_memory: meta.config.gpu.slow_memory, memory_clock: meta.config.gpu.memory_clock,
root: None, root: None,
}, },
battery: crate::persist::BatteryJson { 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, min: lim.min,
max: lim.max, max: lim.max,
}), }),
slow_memory: settings.gpu.slow_memory, memory_clock: settings.gpu.memory_clock,
}, },
battery: community_settings_core::v1::Battery { battery: community_settings_core::v1::Battery {
charge_rate: settings.battery.charge_rate, charge_rate: settings.battery.charge_rate,

View file

@ -243,6 +243,10 @@ fn main() -> Result<(), ()> {
"GPU_get_slow_memory", "GPU_get_slow_memory",
api::gpu::get_slow_memory(api_sender.clone()), api::gpu::get_slow_memory(api_sender.clone()),
) )
.register(
"GPU_unset_slow_memory",
api::gpu::unset_slow_memory(api_sender.clone()),
)
// general API functions // general API functions
.register( .register(
"GENERAL_set_persistent", "GENERAL_set_persistent",

View file

@ -11,7 +11,7 @@ pub struct GpuJson {
pub tdp: Option<u64>, pub tdp: Option<u64>,
pub tdp_boost: Option<u64>, pub tdp_boost: Option<u64>,
pub clock_limits: Option<MinMaxJson<u64>>, pub clock_limits: Option<MinMaxJson<u64>>,
pub slow_memory: bool, pub memory_clock: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub root: Option<String>, pub root: Option<String>,
} }
@ -24,7 +24,7 @@ impl Default for GpuJson {
tdp: None, tdp: None,
tdp_boost: None, tdp_boost: None,
clock_limits: None, clock_limits: None,
slow_memory: false, memory_clock: None,
root: None, root: None,
} }
} }

View file

@ -203,6 +203,13 @@ pub fn auto_detect0(
relevant_limits.gpu.limits, 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 => { GpuLimitType::Generic => {
Box::new(crate::settings::generic::Gpu::from_json_and_limits( Box::new(crate::settings::generic::Gpu::from_json_and_limits(
settings.gpu.clone(), settings.gpu.clone(),
@ -289,6 +296,9 @@ pub fn auto_detect0(
GpuLimitType::SteamDeckAdvance => { GpuLimitType::SteamDeckAdvance => {
Box::new(crate::settings::steam_deck::Gpu::from_limits(relevant_limits.gpu.limits)) 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 => { GpuLimitType::Generic => {
Box::new(crate::settings::generic::Gpu::from_limits(relevant_limits.gpu.limits)) Box::new(crate::settings::generic::Gpu::from_limits(relevant_limits.gpu.limits))
} }

View file

@ -111,7 +111,7 @@ impl Into<GpuJson> for Gpu {
tdp: self.tdp, tdp: self.tdp,
tdp_boost: self.tdp_boost, tdp_boost: self.tdp_boost,
clock_limits: self.clock_limits.map(|x| x.into()), 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())) 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() .clone()
.map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(3_000))), .map(|x| RangeLimit::new(x.min.unwrap_or(0), x.max.unwrap_or(3_000))),
clock_step: self.limits.clock_step.unwrap_or(100), 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() self.clock_limits.as_ref()
} }
fn slow_memory(&mut self) -> &mut bool { fn memory_clock(&mut self, _speed: Option<u64>) {}
&mut self.slow_memory
fn get_memory_clock(&self) -> Option<u64> {
None
} }
fn provider(&self) -> crate::persist::DriverJson { fn provider(&self) -> crate::persist::DriverJson {

View file

@ -299,7 +299,8 @@ fn bad_gpu_limits() -> crate::api::GpuLimits {
clock_min_limits: None, clock_min_limits: None,
clock_max_limits: None, clock_max_limits: None,
clock_step: 100, 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() self.generic.get_clock_limits()
} }
fn slow_memory(&mut self) -> &mut bool { fn memory_clock(&mut self, speed: Option<u64>) {
self.generic.slow_memory() self.generic.memory_clock(speed)
}
fn get_memory_clock(&self) -> Option<u64> {
self.generic.get_memory_clock()
} }
fn provider(&self) -> crate::persist::DriverJson { fn provider(&self) -> crate::persist::DriverJson {

View file

@ -1,6 +1,6 @@
use std::convert::Into; 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; use limits_core::json_v2::GenericGpuLimit;
@ -20,7 +20,7 @@ pub struct Gpu {
pub fast_ppt: Option<u64>, pub fast_ppt: Option<u64>,
pub slow_ppt: Option<u64>, pub slow_ppt: Option<u64>,
pub clock_limits: Option<MinMax<u64>>, pub clock_limits: Option<MinMax<u64>>,
pub slow_memory: bool, pub memory_clock: Option<u64>,
limits: GenericGpuLimit, limits: GenericGpuLimit,
state: crate::state::steam_deck::Gpu, state: crate::state::steam_deck::Gpu,
sysfs_card: BasicEntityPath, sysfs_card: BasicEntityPath,
@ -47,6 +47,8 @@ enum ClockType {
const MAX_CLOCK: u64 = 1600; const MAX_CLOCK: u64 = 1600;
const MIN_CLOCK: u64 = 200; 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 MAX_FAST_PPT: u64 = 30_000_000;
const MIN_FAST_PPT: u64 = 1_000_000; const MIN_FAST_PPT: u64 = 1_000_000;
const MAX_SLOW_PPT: u64 = 29_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<SettingError>> { fn set_clocks(&mut self) -> Result<(), Vec<SettingError>> {
let mut errors = Vec::new(); let mut errors = Vec::new();
if let Some(clock_limits) = &self.clock_limits { if let Some(clock_limits) = &self.clock_limits {
@ -130,7 +189,7 @@ impl Gpu {
|| POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() || POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual()
{ {
self.state.clock_limits_set = false; 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() { if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() {
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs_card)?; POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level(&self.sysfs_card)?;
// disable manual clock limits // 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); let path = GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.path(&self.sysfs_card);
if slow { let payload = self.build_memory_clock_payload(clock);
self.sysfs_card.set(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned(), slow as u8).map_err(|e| { log::debug!("Generated payload for gpu fclk (memory): `{}` (is maxed? {})", payload, self.is_memory_clock_maxed());
SettingError { self.sysfs_card.set(GPU_MEMORY_DOWNCLOCK_ATTRIBUTE.to_owned(), payload).map_err(|e| {
msg: format!("Failed to write to `{}`: {}", path.display(), e), SettingError {
setting: crate::settings::SettingVariant::Gpu, 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,
}
})
}
} }
fn set_force_performance_related(&mut self) -> Result<(), Vec<SettingError>> { fn set_force_performance_related(&mut self) -> Result<(), Vec<SettingError>> {
let mut errors = Vec::new(); 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?) // enable/disable downclock of GPU memory (to 400Mhz?)
if self.slow_memory { self.set_memory_speed(
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(true); self.memory_clock
POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT .or_else(|| self.limits.memory_clock
.enforce_level(&self.sysfs_card) .map(|lim| lim.max.unwrap_or(MAX_MEMORY_CLOCK))
.unwrap_or_else(|mut e| errors.append(&mut e)); ).unwrap_or(MAX_MEMORY_CLOCK)
self.set_slow_memory(self.slow_memory).unwrap_or_else(|e| errors.push(e)); ).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_clocks() self.set_clocks()
.unwrap_or_else(|mut e| errors.append(&mut e)); .unwrap_or_else(|mut e| errors.append(&mut e));
// commit changes (if no errors have already occured) // commit changes (if no errors have already occured)
if errors.is_empty() { 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| { self.set_confirm().map_err(|e| {
errors.push(e); errors.push(e);
errors 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))); 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<GpuJson> for Gpu {
tdp: None, tdp: None,
tdp_boost: None, tdp_boost: None,
clock_limits: self.clock_limits.map(|x| x.into()), 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())) 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<GpuJson, GenericGpuLimit> for Gpu {
fast_ppt: persistent.fast_ppt, fast_ppt: persistent.fast_ppt,
slow_ppt: persistent.slow_ppt, slow_ppt: persistent.slow_ppt,
clock_limits: persistent.clock_limits.map(|x| min_max_from_json(x, version)), 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, limits: limits,
state: crate::state::steam_deck::Gpu::default(), state: crate::state::steam_deck::Gpu::default(),
sysfs_card: Self::find_card_sysfs(persistent.root.clone()), sysfs_card: Self::find_card_sysfs(persistent.root.clone()),
@ -329,7 +380,7 @@ impl ProviderBuilder<GpuJson, GenericGpuLimit> for Gpu {
fast_ppt: persistent.fast_ppt, fast_ppt: persistent.fast_ppt,
slow_ppt: persistent.slow_ppt, slow_ppt: persistent.slow_ppt,
clock_limits: persistent.clock_limits.map(|x| min_max_from_json(x, version)), 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, limits: limits,
state: crate::state::steam_deck::Gpu::default(), state: crate::state::steam_deck::Gpu::default(),
sysfs_card: Self::find_card_sysfs(persistent.root.clone()), sysfs_card: Self::find_card_sysfs(persistent.root.clone()),
@ -343,7 +394,7 @@ impl ProviderBuilder<GpuJson, GenericGpuLimit> for Gpu {
fast_ppt: None, fast_ppt: None,
slow_ppt: None, slow_ppt: None,
clock_limits: None, clock_limits: None,
slow_memory: false, memory_clock: None,
limits: limits, limits: limits,
state: crate::state::steam_deck::Gpu::default(), state: crate::state::steam_deck::Gpu::default(),
sysfs_card: Self::find_card_sysfs(None::<&'static str>), 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), max: super::util::range_max_or_fallback(&self.limits.clock_max, MAX_CLOCK),
}), }),
clock_step: self.limits.clock_step.unwrap_or(100), 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() self.clock_limits.as_ref()
} }
fn slow_memory(&mut self) -> &mut bool { fn memory_clock(&mut self, speed: Option<u64>) {
&mut self.slow_memory self.memory_clock = speed;
}
fn get_memory_clock(&self) -> Option<u64> {
self.memory_clock
} }
fn provider(&self) -> crate::persist::DriverJson { fn provider(&self) -> crate::persist::DriverJson {
crate::persist::DriverJson::SteamDeck 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::<usize>() {
if let Some((freq, _unit)) = freq_mess.trim().split_once(|c: char| !c.is_digit(10)) {
if let Ok(freq) = freq.parse::<usize>() {
result.push((val, freq));
}
}
}
}
} else {
break;
}
}
result
}

View file

@ -96,7 +96,7 @@ impl PDFPLManager {
if let Ok(mode_now) = if let Ok(mode_now) =
entity.attribute::<String, _>(DPM_FORCE_LIMITS_ATTRIBUTE.to_owned()) entity.attribute::<String, _>(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 { } else {
log::debug!("Error getting new mode for debugging purposes"); log::debug!("Error getting new mode for debugging purposes");
} }

View file

@ -59,7 +59,9 @@ pub trait TGpu: OnSet + OnResume + OnPowerEvent + Debug + Send {
fn get_clock_limits(&self) -> Option<&MinMax<u64>>; fn get_clock_limits(&self) -> Option<&MinMax<u64>>;
fn slow_memory(&mut self) -> &mut bool; fn memory_clock(&mut self, speed: Option<u64>);
fn get_memory_clock(&self) -> Option<u64>;
fn provider(&self) -> crate::persist::DriverJson { fn provider(&self) -> crate::persist::DriverJson {
crate::persist::DriverJson::AutoDetect crate::persist::DriverJson::AutoDetect

View file

@ -8,13 +8,11 @@ use crate::settings::{TGpu, ProviderBuilder};
use crate::settings::{OnResume, OnSet, SettingError}; use crate::settings::{OnResume, OnSet, SettingError};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Gpu { pub struct Gpu {}
slow_memory: bool, // ignored
}
impl Gpu { impl Gpu {
pub fn system_default() -> Self { pub fn system_default() -> Self {
Self { slow_memory: false } Self { }
} }
} }
@ -37,7 +35,7 @@ impl Into<GpuJson> for Gpu {
tdp: None, tdp: None,
tdp_boost: None, tdp_boost: None,
clock_limits: None, clock_limits: None,
slow_memory: false, memory_clock: None,
root: None, root: None,
} }
} }
@ -69,7 +67,8 @@ impl TGpu for Gpu {
clock_min_limits: None, clock_min_limits: None,
clock_max_limits: None, clock_max_limits: None,
clock_step: 100, clock_step: 100,
memory_control_capable: false, memory_control: None,
memory_step: 400,
} }
} }
@ -89,8 +88,10 @@ impl TGpu for Gpu {
None None
} }
fn slow_memory(&mut self) -> &mut bool { fn memory_clock(&mut self, _speed: Option<u64>) {}
&mut self.slow_memory
fn get_memory_clock(&self) -> Option<u64> {
None
} }
fn provider(&self) -> crate::persist::DriverJson { fn provider(&self) -> crate::persist::DriverJson {

View file

@ -90,7 +90,8 @@ export type GpuLimits = {
clock_min_limits: RangeLimit | null; clock_min_limits: RangeLimit | null;
clock_max_limits: RangeLimit | null; clock_max_limits: RangeLimit | null;
clock_step: number; clock_step: number;
memory_control_capable: boolean; memory_control: RangeLimit | null,
memory_step: number,
}; };
// API // API
@ -229,14 +230,18 @@ export async function unsetGpuClockLimits(): Promise<any[]> {
return (await call_backend("GPU_unset_clock_limits", [])); return (await call_backend("GPU_unset_clock_limits", []));
} }
export async function setGpuSlowMemory(val: boolean): Promise<boolean> { export async function setGpuSlowMemory(clock: number): Promise<number> {
return (await call_backend("GPU_set_slow_memory", [val]))[0]; return (await call_backend("GPU_set_slow_memory", [clock]))[0];
} }
export async function getGpuSlowMemory(): Promise<boolean> { export async function getGpuSlowMemory(): Promise<number> {
return (await call_backend("GPU_get_slow_memory", []))[0]; return (await call_backend("GPU_get_slow_memory", []))[0];
} }
export async function unsetGpuSlowMemory(): Promise<any[]> {
return (await call_backend("GPU_unset_slow_memory", []));
}
// general // general
export async function setGeneralPersistent(val: boolean): Promise<boolean> { export async function setGeneralPersistent(val: boolean): Promise<boolean> {

View file

@ -180,16 +180,42 @@ export class Gpu extends Component<backend.IdcProps> {
}} }}
/>} />}
</PanelSectionRow> </PanelSectionRow>
{(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_control_capable && <PanelSectionRow> {((get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_control) && <PanelSectionRow>
<ToggleField <ToggleField
checked={get_value(SLOW_MEMORY_GPU)} checked={get_value(SLOW_MEMORY_GPU) != null}
label={tr("Downclock Memory")} label={tr("Downclock Memory")}
description={tr("Force RAM into low-power mode")} description={tr("Force RAM into low-power mode")}
onChange={(value: boolean) => { onChange={(value: boolean) => {
backend.resolve(backend.setGpuSlowMemory(value), (val: boolean) => { if (value) {
set_value(SLOW_MEMORY_GPU, val); set_value(SLOW_MEMORY_GPU, (get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_control!.max);
reloadGUI("GPUSlowMemory"); reloadGUI("GPUMemFreqToggle");
}) } else {
set_value(SLOW_MEMORY_GPU, null);
backend.resolve(backend.unsetGpuSlowMemory(), (_: any[]) => {
reloadGUI("GPUUnsetMemFreq");
});
}
}}
/>
</PanelSectionRow>}
{get_value(SLOW_MEMORY_GPU) != null && <PanelSectionRow>
<SliderField
label={tr("Maximum (MHz)")}
value={get_value(SLOW_MEMORY_GPU)}
max={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_control!.max}
min={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_control!.min}
step={(get_value(LIMITS_INFO) as backend.SettingsLimits).gpu.memory_step}
showValue={true}
disabled={get_value(SLOW_MEMORY_GPU) == null}
onChange={(val: number) => {
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");
}
);
}} }}
/> />
</PanelSectionRow>} </PanelSectionRow>}

View file

@ -170,7 +170,7 @@ const reload = function() {
set_value(CLOCK_MIN_GPU, limits[0]); set_value(CLOCK_MIN_GPU, limits[0]);
set_value(CLOCK_MAX_GPU, limits[1]); 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.getGeneralPersistent(), (value: boolean) => { set_value(PERSISTENT_GEN, value) });
backend.resolve(backend.getGeneralSettingsName(), (name: string) => { set_value(NAME_GEN, name) }); backend.resolve(backend.getGeneralSettingsName(), (name: string) => { set_value(NAME_GEN, name) });