feat: improve GPU selection

This commit is contained in:
Denis Benato
2026-01-25 03:48:17 +01:00
parent d890461777
commit 8867749e0e
4 changed files with 215 additions and 15 deletions

View File

@@ -1,6 +1,7 @@
pub mod setup_anime;
pub mod setup_aura;
pub mod setup_fans;
pub mod setup_gpu;
pub mod setup_system;
use std::sync::atomic::{AtomicU64, Ordering};
@@ -162,9 +163,12 @@ pub fn setup_window(
setup_anime_page(&ui, config.clone());
}
if available.contains(&"xyz.ljones.FanCurves".to_string()) {
setup_fan_curve_page(&ui, config);
setup_fan_curve_page(&ui, config.clone());
}
// Populate GPU page choices and callbacks
setup_gpu::setup_gpu_page(&ui);
ui
}

View File

@@ -0,0 +1,182 @@
use log::error;
use rog_platform::asus_armoury::{AttrValue, FirmwareAttributes};
use slint::{ComponentHandle, ModelRc, SharedString};
use crate::{GPUPageData, MainWindow};
// Populate GPU page choices and wire the `cb_set_gpu_mode` callback
pub fn setup_gpu_page(ui: &MainWindow) {
let handle = ui.as_weak();
tokio::spawn(async move {
// Read available attributes
let attrs = FirmwareAttributes::new();
let gpu_mux_available = attrs
.gpu_mux_mode()
.map(|a| a.base_path_exists())
.unwrap_or(false);
// Prepare choice strings
let mut choices: Vec<SharedString> = Vec::new();
choices.push(SharedString::from("Integrated"));
if gpu_mux_available {
choices.push(SharedString::from("Ultimate"));
}
choices.push(SharedString::from("Hybrid"));
// Read current attribute values to initialise UI state
let current_dgpu = attrs
.dgpu_disable()
.and_then(|a| a.current_value().ok())
.unwrap_or(AttrValue::Integer(0));
let current_mux = attrs
.gpu_mux_mode()
.and_then(|a| a.current_value().ok())
.unwrap_or(AttrValue::Integer(1));
// Convert to UI-able values
let dgpu_disabled = matches!(current_dgpu, AttrValue::Integer(v) if v == 1);
// Determine initial index for gpu_mux_mode property
let initial_index: i32 = if gpu_mux_available {
// If mux attr says 0 -> Ultimate, else try dgpu to refine
match current_mux {
AttrValue::Integer(0) => 1, // Ultimate
_ => {
match current_dgpu {
AttrValue::Integer(1) => 0, // Integrated
_ => 2, // Hybrid/Optimus fallback
}
}
}
} else {
// Only Integrated / Hybrid
match current_dgpu {
AttrValue::Integer(1) => 0,
_ => 1,
}
};
let handle_copy = handle.clone();
if let Err(e) = handle.upgrade_in_event_loop(move |handle| {
let global = handle.global::<GPUPageData>();
// set choices model
let model: ModelRc<SharedString> = choices.as_slice().into();
global.set_gpu_modes_choises(model);
global.set_gpu_mux_available(gpu_mux_available);
// set initial state
global.set_dgpu_disabled(if dgpu_disabled { 1 } else { 0 });
global.set_gpu_mux_mode(initial_index);
// register callback
let handle_cb = handle_copy.clone();
global.on_cb_set_gpu_mode(move |index: i32| {
// show a blue toast informing user a reboot is required (auto-clears)
let toast_handle = handle_cb.clone();
crate::ui::show_toast(
SharedString::from(
"GPU mode change scheduled — reboot required for changes to apply.",
),
SharedString::from("Failed to set GPU mode"),
toast_handle.clone(),
Ok(()),
);
let handle_next = handle_cb.clone();
tokio::spawn(async move {
let attrs = FirmwareAttributes::new();
let mux_avail = attrs
.gpu_mux_mode()
.map(|a| a.base_path_exists())
.unwrap_or(false);
// helper to set attribute ignoring errors
if mux_avail {
match index {
0 => {
// Integrated
if let Some(attr) = attrs.dgpu_disable() {
let _ = attr.set_current_value(&AttrValue::Integer(1));
}
if let Some(attr) = attrs.gpu_mux_mode() {
let _ = attr.set_current_value(&AttrValue::Integer(1));
}
}
1 => {
// Ultimate
if let Some(attr) = attrs.dgpu_disable() {
let _ = attr.set_current_value(&AttrValue::Integer(0));
}
if let Some(attr) = attrs.gpu_mux_mode() {
let _ = attr.set_current_value(&AttrValue::Integer(0));
}
}
2 => {
// Dynamic
if let Some(attr) = attrs.dgpu_disable() {
let _ = attr.set_current_value(&AttrValue::Integer(0));
}
if let Some(attr) = attrs.gpu_mux_mode() {
let _ = attr.set_current_value(&AttrValue::Integer(1));
}
}
_ => {}
}
} else {
match index {
0 => {
if let Some(attr) = attrs.dgpu_disable() {
let _ = attr.set_current_value(&AttrValue::Integer(1));
}
}
1 => {
if let Some(attr) = attrs.dgpu_disable() {
let _ = attr.set_current_value(&AttrValue::Integer(0));
}
}
_ => {}
}
};
// After attempting write(s), refresh UI from attributes
let attrs2 = FirmwareAttributes::new();
let cur_dgpu = attrs2
.dgpu_disable()
.and_then(|a| a.current_value().ok())
.unwrap_or(AttrValue::Integer(0));
let cur_mux = attrs2
.gpu_mux_mode()
.and_then(|a| a.current_value().ok())
.unwrap_or(AttrValue::Integer(1));
let dgpu_disabled = matches!(cur_dgpu, AttrValue::Integer(v) if v == 1);
let new_index: i32 = if mux_avail {
match cur_mux {
AttrValue::Integer(0) => 1,
_ => match cur_dgpu {
AttrValue::Integer(1) => 0,
_ => 2,
},
}
} else {
match cur_dgpu {
AttrValue::Integer(1) => 0,
_ => 1,
}
};
if let Err(e) = handle_next.upgrade_in_event_loop(move |h| {
let g = h.global::<GPUPageData>();
g.set_dgpu_disabled(if dgpu_disabled { 1 } else { 0 });
g.set_gpu_mux_mode(new_index);
}) {
error!("setup_gpu callback: upgrade_in_event_loop: {e:?}");
}
});
});
}) {
error!("setup_gpu_page: upgrade_in_event_loop: {e:?}");
}
});
}

View File

@@ -7,7 +7,7 @@ import { PageFans } from "pages/fans.slint";
import { PageAnime, AnimePageData } from "pages/anime.slint";
import { RogItem } from "widgets/common.slint";
import { PageAura } from "pages/aura.slint";
import { PageGPU } from "pages/gpu.slint";
import { PageGPU, GPUPageData } from "pages/gpu.slint";
import { Node } from "widgets/graph.slint";
export { Node }
import { FanPageData, FanType, Profile } from "types/fan_types.slint";
@@ -16,7 +16,7 @@ import { AuraPageData, AuraDevType, LaptopAuraPower, AuraPowerState, PowerZones,
export { AuraPageData, AuraDevType, LaptopAuraPower, AuraPowerState, PowerZones, AuraEffect }
import { PageAppSettings, AppSettingsPageData } from "pages/app_settings.slint";
export { AppSize, AttrMinMax, SystemPageData, AnimePageData, AppSettingsPageData }
export { AppSize, AttrMinMax, SystemPageData, AnimePageData, AppSettingsPageData, GPUPageData }
export component MainWindow inherits Window {
title: "ROG Control";

View File

@@ -8,10 +8,9 @@ export global GPUPageData {
in-out property <int> gpu_mux_mode: 1; // 0 = Ultra/Discreet, 1 = Integrated/Optimus
in-out property <int> dgpu_disabled: 0; // 1 == dGPU disabled
in-out property <int> egpu_enabled: 0; // 1 == eGPU (XGMobile) enabled
in-out property <[string]> gpu_modes_choises: [@tr("Ultra"), @tr("Integrated")];
callback cb_gpu_mux_mode(int);
callback cb_dgpu_disabled(int);
callback cb_egpu_enabled(int);
in-out property <[string]> gpu_modes_choises: [@tr("Integrated"), @tr("Hybrid")];
in-out property <bool> gpu_mux_available: false;
callback cb_set_gpu_mode(int);
}
export component PageGPU inherits Rectangle {
@@ -21,14 +20,29 @@ export component PageGPU inherits Rectangle {
VerticalLayout {
padding: 10px;
spacing: 10px;
HorizontalLayout {
padding-right: 10px;
padding-left: 10px;
alignment: LayoutAlignment.space-between;
Rectangle {
height: 32px;
Text {
font-size: 16px;
text: @tr("Note: Changes take effect after a reboot. The dropdown always shows the current mode.");
}
}
}
HorizontalLayout {
SystemDropdown {
text: @tr("GPU mode");
model <=> GPUPageData.gpu_modes_choises;
current_index <=> GPUPageData.gpu_mux_mode;
current_value: GPUPageData.gpu_modes_choises[GPUPageData.gpu_mux_mode];
model <=> GPUPageData.gpu_modes_choises;
selected => {
GPUPageData.cb_gpu_mux_mode(0);
GPUPageData.cb_gpu_mux_mode(1);
GPUPageData.cb_set_gpu_mode(GPUPageData.gpu_mux_mode)
}
}
}
}