feat: add UMA carveout option for AMD iGPUs (#1219)

* feat: UMA carveout daemon option

* feat: working UI

* chore: switch to released lib
This commit is contained in:
Ilya Zlobintsev authored and GitHub committed 2026-10-04 13:44:54 +03:00
1 parent 6e46792968
commit bd9aa0c1df
20 files changed
+337 -55

No files matched your search

Generated
+3 -2
View File
@@ -31,8 +31,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "amdgpu-sysfs"
version = "0.21.0"
source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs.git?rev=215eafbcbca0fe28f66f8ba0cbcec0f547baaba1#215eafbcbca0fe28f66f8ba0cbcec0f547baaba1"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb53ee2a9c2f4a63b349793a8c247500016e0a23d674226c44f1a6916582e866"
dependencies = [
"enum_dispatch",
"serde",
+1 -1
View File
@@ -13,7 +13,7 @@ members = [
edition = "2024"
[workspace.dependencies]
amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs.git", rev = "215eafbcbca0fe28f66f8ba0cbcec0f547baaba1", features = ["serde"] }
amdgpu-sysfs = { version = "0.22.0", features = ["serde"] }
nvml-wrapper = { git = "https://github.com/rust-nvml/nvml-wrapper.git", rev = "eb47417eede43443f139053479618e96ad32893d" }
serde = { version = "1.0", features = ["derive"] }
serde_with = { version = "3.21.0", default-features = false, features = [
+4
View File
@@ -213,6 +213,10 @@ gpus:
mem_clock_offsets:
0: 200
# Index of the UMA (dedicated VRAM) configuration that should be used.
# Needs a reboot to actually take effect. Applicable to certain AMD iGPUs.
uma_carveout: 1
# Settings profiles
profiles:
# Name of the profile
+10 -21
View File
@@ -1,10 +1,4 @@
[
{
"type": "git",
"url": "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs",
"commit": "215eafbcbca0fe28f66f8ba0cbcec0f547baaba1",
"dest": "flatpak-cargo/git/amdgpu-sysfs-rs-215eafb"
},
{
"type": "git",
"url": "https://github.com/rust-nvml/nvml-wrapper",
@@ -64,21 +58,16 @@
"dest-filename": ".cargo-checksum.json"
},
{
"type": "shell",
"commands": [
"cp -r --reflink=auto \"flatpak-cargo/git/amdgpu-sysfs-rs-215eafb/.\" \"cargo/vendor/amdgpu-sysfs\""
]
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/amdgpu-sysfs/amdgpu-sysfs-0.22.0.crate",
"sha256": "cb53ee2a9c2f4a63b349793a8c247500016e0a23d674226c44f1a6916582e866",
"dest": "cargo/vendor/amdgpu-sysfs-0.22.0"
},
{
"type": "inline",
"contents": "[package]\nname = \"amdgpu-sysfs\"\nversion = \"0.21.0\"\nauthors = [ \"Ilya Zlobintsev <ilya.zl@protonmail.com>\",]\nedition = \"2021\"\nlicense = \"LGPL-3.0-or-later\"\nrepository = \"https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs/\"\ndescription = \"Library for interacting with the Linux Kernel SysFS interface for GPUs (mainly targeted at the AMDGPU driver).\"\n\n[features]\ndefault = [ \"overdrive\",]\noverdrive = [ \"enum_dispatch\",]\n\n[dev-dependencies]\npretty_assertions = \"1.4.1\"\nrust-embed = \"8.7.2\"\ntempfile = \"3.22.0\"\n\n[dependencies.enum_dispatch]\nversion = \"0.3.8\"\noptional = true\n\n[dependencies.serde]\nversion = \"1\"\nfeatures = [ \"derive\",]\noptional = true\n\n[dev-dependencies.amdgpu-sysfs]\npath = \".\"\nfeatures = [ \"serde\",]\n\n[dev-dependencies.insta]\nversion = \"1.43.2\"\nfeatures = [ \"yaml\", \"redactions\",]\n",
"dest": "cargo/vendor/amdgpu-sysfs",
"dest-filename": "Cargo.toml"
},
{
"type": "inline",
"contents": "{\"package\": null, \"files\": {}}",
"dest": "cargo/vendor/amdgpu-sysfs",
"contents": "{\"package\": \"cb53ee2a9c2f4a63b349793a8c247500016e0a23d674226c44f1a6916582e866\", \"files\": {}}",
"dest": "cargo/vendor/amdgpu-sysfs-0.22.0",
"dest-filename": ".cargo-checksum.json"
},
{
@@ -2663,7 +2652,7 @@
},
{
"type": "inline",
"contents": "[package]\nname = \"nvml-wrapper\"\nversion = \"0.12.1\"\nauthors = [ \"Cldfire\",]\ndescription = \"A safe and ergonomic Rust wrapper for the NVIDIA Management Library\"\nreadme = \"../README.md\"\ndocumentation = \"https://docs.rs/nvml-wrapper\"\nrepository = \"https://github.com/Cldfire/nvml-wrapper\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\nrust-version = \"1.60.0\"\nkeywords = [ \"nvidia\", \"gpu\", \"managment\", \"monitoring\", \"hardware\",]\ncategories = [ \"api-bindings\", \"hardware-support\",]\n\n[features]\ndefault = []\nlegacy-functions = [ \"nvml-wrapper-sys/legacy-functions\",]\nserde = [ \"dep:serde\", \"dep:serde_derive\", \"bitflags/serde\",]\n\n[dependencies]\nthiserror = \"1.0\"\nbitflags = \"2.4.0\"\nwrapcenum-derive = \"0.4.1\"\nlibloading = \"0.8.1\"\nstatic_assertions = \"1.1\"\n\n[dev-dependencies]\npretty-bytes = \"0.2\"\n\n[dependencies.serde]\nversion = \"1.0\"\noptional = true\n\n[dependencies.serde_derive]\nversion = \"1.0\"\noptional = true\n\n[dependencies.nvml-wrapper-sys]\nversion = \"0.9.1\"\npath = \"../nvml-wrapper-sys\"\n",
"contents": "[package]\nname = \"nvml-wrapper\"\nversion = \"0.12.1\"\nauthors = [\"Cldfire\"]\ndescription = \"A safe and ergonomic Rust wrapper for the NVIDIA Management Library\"\nreadme = \"../README.md\"\ndocumentation = \"https://docs.rs/nvml-wrapper\"\nrepository = \"https://github.com/Cldfire/nvml-wrapper\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\nrust-version = \"1.60.0\"\nkeywords = [\"nvidia\", \"gpu\", \"managment\", \"monitoring\", \"hardware\"]\ncategories = [\"api-bindings\", \"hardware-support\"]\n\n[features]\ndefault = []\nlegacy-functions = [\"nvml-wrapper-sys/legacy-functions\"]\nserde = [\"dep:serde\", \"dep:serde_derive\", \"bitflags/serde\"]\n\n[dependencies]\nthiserror = \"1.0\"\nbitflags = \"2.4.0\"\nwrapcenum-derive = \"0.4.1\"\nlibloading = \"0.8.1\"\nstatic_assertions = \"1.1\"\n\n[dependencies.serde]\nversion = \"1.0\"\noptional = true\n\n[dependencies.serde_derive]\nversion = \"1.0\"\noptional = true\n\n[dependencies.nvml-wrapper-sys]\nversion = \"0.9.1\"\npath = \"../nvml-wrapper-sys\"\n\n[dev-dependencies]\npretty-bytes = \"0.2\"\n",
"dest": "cargo/vendor/nvml-wrapper",
"dest-filename": "Cargo.toml"
},
@@ -2681,7 +2670,7 @@
},
{
"type": "inline",
"contents": "[package]\nname = \"nvml-wrapper-sys\"\nversion = \"0.9.1\"\nauthors = [ \"Cldfire\",]\ndescription = \"Generated bindings to the NVIDIA Management Library.\"\nreadme = \"README.md\"\ndocumentation = \"https://docs.rs/nvml-wrapper-sys\"\nrepository = \"https://github.com/Cldfire/nvml-wrapper\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\nrust-version = \"1.60.0\"\nkeywords = [ \"nvidia\", \"gpu\", \"managment\", \"monitoring\", \"hardware\",]\ncategories = [ \"external-ffi-bindings\", \"hardware-support\",]\nexclude = [ \"nvml.h\",]\n\n[dependencies]\nlibloading = \"0.8.1\"\n\n[features]\ndefault = []\nlegacy-functions = []\n",
"contents": "[package]\nname = \"nvml-wrapper-sys\"\nversion = \"0.9.1\"\nauthors = [\"Cldfire\"]\ndescription = \"Generated bindings to the NVIDIA Management Library.\"\nreadme = \"README.md\"\ndocumentation = \"https://docs.rs/nvml-wrapper-sys\"\nrepository = \"https://github.com/Cldfire/nvml-wrapper\"\nlicense = \"MIT OR Apache-2.0\"\nedition = \"2021\"\nrust-version = \"1.60.0\"\nkeywords = [\"nvidia\", \"gpu\", \"managment\", \"monitoring\", \"hardware\"]\ncategories = [\"external-ffi-bindings\", \"hardware-support\"]\nexclude = [\"nvml.h\"]\n\n[dependencies]\nlibloading = \"0.8.1\"\n\n[features]\ndefault = []\nlegacy-functions = []\n",
"dest": "cargo/vendor/nvml-wrapper-sys",
"dest-filename": "Cargo.toml"
},
@@ -5384,7 +5373,7 @@
},
{
"type": "inline",
"contents": "[source.vendored-sources]\ndirectory = \"cargo/vendor\"\n\n[source.crates-io]\nreplace-with = \"vendored-sources\"\n\n[source.\"https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs\"]\ngit = \"https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs\"\nreplace-with = \"vendored-sources\"\nrev = \"215eafbcbca0fe28f66f8ba0cbcec0f547baaba1\"\n\n[source.\"https://github.com/rust-nvml/nvml-wrapper\"]\ngit = \"https://github.com/rust-nvml/nvml-wrapper\"\nreplace-with = \"vendored-sources\"\nrev = \"eb47417eede43443f139053479618e96ad32893d\"\n",
"contents": "[source.vendored-sources]\ndirectory = \"cargo/vendor\"\n\n[source.crates-io]\nreplace-with = \"vendored-sources\"\n\n[source.\"https://github.com/rust-nvml/nvml-wrapper\"]\ngit = \"https://github.com/rust-nvml/nvml-wrapper\"\nreplace-with = \"vendored-sources\"\nrev = \"eb47417eede43443f139053479618e96ad32893d\"\n",
"dest": "cargo",
"dest-filename": "config"
}
+2 -15
View File
@@ -495,7 +495,7 @@ mod tests {
use indexmap::IndexMap;
use insta::assert_yaml_snapshot;
use lact_schema::{
FanControlMode, NvidiaThermalOptions, NvidiaVfPoint, PmfwOptions,
FanControlMode, NvidiaVfPoint, PmfwOptions,
config::{
ClocksConfiguration, CurvePoint, FanControlSettings, FanCurve, GpuConfig,
NvidiaCurvePoint,
@@ -601,20 +601,7 @@ mod tests {
#[test]
fn clocks_configuration_applied() {
let mut gpu = GpuConfig {
fan_control_enabled: false,
fan_control_settings: None,
pmfw_options: PmfwOptions::default(),
nvidia_thermal_options: NvidiaThermalOptions::default(),
power_mizer_mode: None,
power_cap: None,
performance_level: None,
clocks_configuration: ClocksConfiguration::default(),
power_profile_mode_index: None,
custom_power_profile_mode_hueristics: vec![],
power_states: IndexMap::new(),
};
let mut gpu = GpuConfig::default();
assert!(!gpu.is_core_clocks_used());
gpu.clocks_configuration.voltage_offset = Some(10);
assert!(gpu.is_core_clocks_used());
+31 -5
View File
@@ -23,10 +23,10 @@ use drm::{DrmProvider, amdgpu::AmdGpuDrmProvider};
use futures::{FutureExt, future::LocalBoxFuture};
use indexmap::IndexMap;
use lact_schema::{
ActivePowerStates, AmdCacheInstance, CacheInfo, CacheType, ClocksInfo, ClockspeedStats,
DeviceApiInfo, DeviceFlag, DeviceInfo, DeviceStats, DeviceType, DrmInfo, FanControlMode,
FanStats, LinkInfo, NvidiaThermalInfo, PmfwInfo, PowerState, PowerStates, PowerStats,
ProcessList, ProcessUtilizationType, TemperatureEntry, VoltageStats, VramStats,
ActivePowerStates, AmdCacheInstance, AmdUmaCarveout, CacheInfo, CacheType, ClocksInfo,
ClockspeedStats, DeviceApiInfo, DeviceFlag, DeviceInfo, DeviceStats, DeviceType, DrmInfo,
FanControlMode, FanStats, LinkInfo, NvidiaThermalInfo, PmfwInfo, PowerState, PowerStates,
PowerStats, ProcessList, ProcessUtilizationType, TemperatureEntry, VoltageStats, VramStats,
config::{ClocksConfiguration, FanControlSettings, FanCurve, GpuConfig},
};
#[cfg(feature = "display-info")]
@@ -567,14 +567,27 @@ impl AmdGpuController {
.ok()
});
let uma_carveout = self
.handle
.get_uma_carveout_options()
.and_then(|options| {
let current = self.handle.get_current_uma_carveout()?;
Ok(AmdUmaCarveout {
options: options.options,
current,
})
})
.ok();
trace!("Reading DRM info");
let drm_handle = self.drm_handle.as_ref();
match drm_handle {
Some(drm_handle) => drm_handle.get_drm_info(&self.handle, cache_info),
Some(drm_handle) => drm_handle.get_drm_info(&self.handle, cache_info, uma_carveout),
None => Some(DrmInfo {
cache_info,
vram_clock_ratio: 1.0,
amd_uma_carveout: uma_carveout,
..Default::default()
}),
}
@@ -1385,6 +1398,19 @@ impl GpuController for AmdGpuController {
.with_context(|| format!("Could not set {kind:?} power states"))?;
}
if let Some(uma_carveout) = config.uma_carveout {
let current_carveout = self
.handle
.get_current_uma_carveout()
.context("Could not get current UMA carveout")?;
if uma_carveout != current_carveout {
self.handle
.set_uma_carveout(uma_carveout)
.context("Could not apply UMA carveout")?;
}
}
Ok(())
})
}
@@ -3,11 +3,16 @@ pub mod amdgpu;
pub mod mock;
use amdgpu_sysfs::gpu_handle::GpuHandle;
use lact_schema::{CacheInfo, DeviceType, DrmInfo};
use lact_schema::{AmdUmaCarveout, CacheInfo, DeviceType, DrmInfo};
use libdrm_amdgpu_sys::AMDGPU::VBIOS::VbiosInfo;
pub trait DrmProvider {
fn get_drm_info(&self, handle: &GpuHandle, cache_info: Option<CacheInfo>) -> Option<DrmInfo>;
fn get_drm_info(
&self,
handle: &GpuHandle,
cache_info: Option<CacheInfo>,
uma_carveout: Option<AmdUmaCarveout>,
) -> Option<DrmInfo>;
fn get_device_type(&self) -> Option<DeviceType>;
@@ -1,7 +1,9 @@
use super::DrmProvider;
use amdgpu_sysfs::gpu_handle::GpuHandle;
use anyhow::{Context as _, anyhow};
use lact_schema::{AmdIpInfo, CacheInfo, DeviceType, DrmInfo, DrmMemoryInfo, RopInfo};
use lact_schema::{
AmdIpInfo, AmdUmaCarveout, CacheInfo, DeviceType, DrmInfo, DrmMemoryInfo, RopInfo,
};
use libdrm_amdgpu_sys::{
AMDGPU::{
GPU_INFO as _, HW_IP::HW_IP_TYPE, SENSOR_INFO::SENSOR_TYPE, VBIOS::VbiosInfo, VRAM_TYPE,
@@ -50,7 +52,12 @@ impl AmdGpuDrmProvider {
}
impl DrmProvider for AmdGpuDrmProvider {
fn get_drm_info(&self, handle: &GpuHandle, cache_info: Option<CacheInfo>) -> Option<DrmInfo> {
fn get_drm_info(
&self,
handle: &GpuHandle,
cache_info: Option<CacheInfo>,
amd_uma_carveout: Option<AmdUmaCarveout>,
) -> Option<DrmInfo> {
let drm_memory_info = self.0.memory_info().ok().map(|memory_info| DrmMemoryInfo {
resizeable_bar: Some(memory_info.check_resizable_bar()),
cpu_accessible_used: memory_info.cpu_accessible_vram.heap_usage,
@@ -108,6 +115,7 @@ impl DrmProvider for AmdGpuDrmProvider {
},
operations_count: drm_info.calc_rop_count(),
}),
amd_uma_carveout,
..Default::default()
})
}
@@ -1,6 +1,6 @@
use super::DrmProvider;
use crate::server::{gpu_controller::read_mock_snapshot, handler::SnapshotDeviceInfo};
use lact_schema::{DeviceType, DrmInfo};
use lact_schema::{AmdUmaCarveout, DeviceType, DrmInfo};
use libdrm_amdgpu_sys::AMDGPU::VBIOS::VbiosInfo;
use std::path::Path;
@@ -21,12 +21,13 @@ impl DrmProvider for MockDrmProvider {
&self,
_handle: &amdgpu_sysfs::gpu_handle::GpuHandle,
cache_info: Option<lact_schema::CacheInfo>,
amd_uma_carveout: Option<AmdUmaCarveout>,
) -> Option<DrmInfo> {
self.snapshot
.info
.drm_info
.clone()
.map(|info| DrmInfo { cache_info, ..info })
self.snapshot.info.drm_info.clone().map(|info| DrmInfo {
cache_info,
amd_uma_carveout,
..info
})
}
fn get_device_type(&self) -> Option<DeviceType> {
@@ -860,6 +860,7 @@ impl GpuController for NvidiaGpuController {
.ok(),
amd_ip_info: vec![],
intel: IntelDrmInfo::default(),
..Default::default()
}),
flags: vec![
DeviceFlag::ConfigurableFanControl,
@@ -103,6 +103,7 @@ gpus:
memory_clock:
- 0
- 1
uma_carveout: 1
profiles:
vkcube:
rule:
@@ -39,6 +39,43 @@ expression: device_info
"info": {
"driver": "amdgpu",
"drm_info": {
"amd_uma_carveout": {
"current": 0,
"options": [
{
"name": "Minimum",
"size_bytes": 536870912
},
{
"name": null,
"size_bytes": 1073741824
},
{
"name": null,
"size_bytes": 2147483648
},
{
"name": null,
"size_bytes": 4294967296
},
{
"name": null,
"size_bytes": 6442450944
},
{
"name": "Medium",
"size_bytes": 8589934592
},
{
"name": null,
"size_bytes": 12884901888
},
{
"name": "High",
"size_bytes": 17179869184
}
]
},
"cache_info": {
"Amd": [
[
+4
View File
@@ -154,6 +154,10 @@ missing-stat = N/A
vram-usage = VRAM Usage:
gtt-usage = GTT Usage:
igpu-section = Integrated Graphics
uma-carveout = UMA Carveout Size
uma-carveout-caption = Amount of memory reserved as dedicated VRAM. Requires a reboot to take effect.
performance-level-auto = Automatic
performance-level-high = Highest Clocks
performance-level-low = Lowest Clocks
+20
View File
@@ -1154,6 +1154,18 @@ impl AppModel {
.ok()
.flatten();
let info = self
.daemon_client
.get_device_info(&gpu_id, Some(false))
.await
.context("Could not fetch info")?;
let info = Arc::new(info);
self.oc_page.emit(OcPageMsg::Update {
update: PageUpdate::Info(info),
initial: true,
});
let stats = self
.daemon_client
.get_device_stats(&gpu_id)
@@ -1270,6 +1282,14 @@ impl AppModel {
let enabled_power_states = self.oc_page.model().get_enabled_power_states();
gpu_config.power_states = enabled_power_states;
// Avoids applying the setting if it's the default but doesn't wipe it if it's unchanged in the UI
gpu_config.uma_carveout = self
.oc_page
.model()
.get_uma_carveout()
.map(|value| value as usize)
.or(gpu_config.uma_carveout);
let delay = self
.daemon_client
.set_gpu_config(&gpu_id, gpu_config)
+22
View File
@@ -1,5 +1,6 @@
mod card_layout;
mod clocks_frame;
mod igpu_frame;
mod performance_frame;
mod power_frame;
mod power_states;
@@ -10,6 +11,7 @@ use crate::app::components::gpu_stats_section::{
GpuStat, GpuStatsSection, GpuStatsSectionConfig, GpuStatsSectionMsg,
};
use crate::app::pages::PageUpdate;
use crate::app::pages::oc_page::igpu_frame::{IGpuFrame, IGpuFrameMsg};
use crate::app::utils::ext::RelmLaunchable as _;
use crate::app::{msg::AppMsg, utils::ext::RelmDefaultLauchable};
use adw::prelude::*;
@@ -40,6 +42,7 @@ pub struct OcPage {
power_states_dialog: relm4::Controller<PowerStatesDialog>,
gpu_clocks_frame: relm4::Controller<ClocksFrame>,
vram_clocks_frame: relm4::Controller<ClocksFrame>,
igpu_frame: relm4::Controller<IGpuFrame>,
vf_curve_editor: relm4::Controller<VfCurveEditor>,
}
@@ -88,6 +91,8 @@ impl relm4::Component for OcPage {
ColumnBias::Left, // Core
ColumnBias::Right, // VRAM
ColumnBias::Left, // Power
ColumnBias::Right, // Integrated Graphics
])),
set_valign: gtk::Align::Start,
@@ -102,6 +107,10 @@ impl relm4::Component for OcPage {
model.power_frame.widget() {
add_css_class: "oc-page-section",
},
model.igpu_frame.widget() {
add_css_class: "oc-page-section",
},
},
},
}
@@ -143,6 +152,7 @@ impl relm4::Component for OcPage {
let power_states_dialog =
PowerStatesDialog::launch_default().forward(sender.input_sender(), |msg| msg);
let power_frame = PowerFrame::launch_default().forward(sender.input_sender(), |msg| msg);
let igpu_frame = IGpuFrame::launch_default().forward(sender.input_sender(), |msg| msg);
let vf_curve_editor = VfCurveEditor::detach(VfCurveEditorInit {
global_settings_changed: settings_changed,
@@ -156,6 +166,7 @@ impl relm4::Component for OcPage {
power_states_dialog,
gpu_clocks_frame,
vram_clocks_frame,
igpu_frame,
vf_curve_editor,
};
@@ -211,6 +222,13 @@ impl relm4::Component for OcPage {
.emit(PowerStatesDialogMsg::VramClockRatio(vram_clock_ratio));
self.vram_clocks_frame
.emit(ClocksFrameMsg::VramRatio(vram_clock_ratio));
let uma_carveout = info
.drm_info
.as_ref()
.and_then(|info| info.amd_uma_carveout.clone());
self.igpu_frame
.emit(IGpuFrameMsg::UmaCarveout(uma_carveout));
}
},
OcPageMsg::ClocksTable {
@@ -347,4 +365,8 @@ impl OcPage {
IndexMap::new()
}
}
pub fn get_uma_carveout(&self) -> Option<u32> {
self.igpu_frame.model().get_uma_carveout()
}
}
@@ -0,0 +1,157 @@
use super::OcPageMsg;
use crate::I18N;
use crate::app::APP_BROKER;
use crate::app::components::{adjustment_card::AdjustmentCard, page_section::PageSection};
use crate::app::msg::AppMsg;
use crate::app::utils::formatting;
use gtk::gio::prelude::ListModelExt as _;
use gtk::prelude::{BoxExt as _, ListBoxRowExt as _, OrientableExt as _, WidgetExt as _};
use i18n_embed_fl::fl;
use lact_schema::AmdUmaCarveout;
use relm4::{ComponentParts, ComponentSender, WidgetTemplate as _, css};
pub struct IGpuFrame {
// options: Option<UmaCarveoutOptions>,
// combo_row: relm4::Controller<SimpleComboRow<String>>,
uma_options: gtk::StringList,
uma_current: u32,
uma_selected: u32,
}
#[derive(Debug)]
pub enum IGpuFrameMsg {
UmaCarveout(Option<AmdUmaCarveout>),
UmaSelected(u32),
}
#[relm4::component(pub)]
impl relm4::Component for IGpuFrame {
type Init = ();
type Input = IGpuFrameMsg;
type Output = OcPageMsg;
type CommandOutput = ();
view! {
PageSection {
set_hide_visible_container: true,
#[watch]
set_name: fl!(I18N, "igpu-section"),
#[watch]
set_visible: model.uma_options.n_items() != 0,
#[template]
#[local]
append_child = &card -> AdjustmentCard {
#[template_child]
content {
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 10,
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_hexpand: true,
set_valign: gtk::Align::Center,
gtk::Label {
set_label: &fl!(I18N, "uma-carveout"),
set_halign: gtk::Align::Start,
},
gtk::Label {
set_label: &fl!(I18N, "uma-carveout-caption"),
set_halign: gtk::Align::Start,
set_xalign: 0.0,
set_wrap: true,
add_css_class: css::DIM_LABEL,
add_css_class: css::CAPTION,
},
},
gtk::ListBoxRow {
set_activatable: false,
set_selectable: false,
#[name = "uma_dropdown"]
gtk::DropDown {
set_valign: gtk::Align::Center,
#[watch]
#[block_signal(uma_mode_select_handler)]
set_model: Some(&model.uma_options),
#[watch]
#[block_signal(uma_mode_select_handler)]
set_selected: model.uma_selected,
connect_selected_notify[sender] => move |dropdown| {
sender.input(IGpuFrameMsg::UmaSelected(dropdown.selected()));
} @ uma_mode_select_handler,
},
},
},
},
},
}
}
fn init(
_: Self::Init,
root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let card = AdjustmentCard::init(());
let model = Self {
uma_options: gtk::StringList::default(),
uma_selected: 0,
uma_current: 0,
};
let widgets = view_output!();
ComponentParts { model, widgets }
}
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>, _root: &Self::Root) {
match msg {
IGpuFrameMsg::UmaCarveout(carveout) => {
let uma_options = gtk::StringList::default();
match carveout {
Some(carveout) => {
for option in carveout.options {
let size = formatting::fmt_human_bytes(option.size_bytes, None);
let text = if let Some(name) = option.name {
format!("{size} ({name})")
} else {
size
};
uma_options.append(&text);
}
self.uma_current = carveout.current as u32;
self.uma_selected = self.uma_current;
}
None => {
self.uma_current = 0;
self.uma_selected = 0;
}
}
self.uma_options = uma_options;
}
IGpuFrameMsg::UmaSelected(selected) => {
self.uma_selected = selected;
APP_BROKER.send(AppMsg::SettingsChanged);
}
}
}
}
impl IGpuFrame {
pub fn get_uma_carveout(&self) -> Option<u32> {
if self.uma_current != self.uma_selected {
Some(self.uma_selected)
} else {
None
}
}
}
+1
View File
@@ -53,6 +53,7 @@ pub struct GpuConfig {
pub custom_power_profile_mode_hueristics: Vec<Vec<Option<i32>>>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub power_states: IndexMap<PowerLevelKind, Vec<u8>>,
pub uma_carveout: Option<usize>,
}
#[skip_serializing_none]
+10 -1
View File
@@ -15,7 +15,7 @@ pub use response::Response;
use amdgpu_sysfs::{
gpu_handle::{
PerformanceLevel, PowerLevelId,
PerformanceLevel, PowerLevelId, UmaCarveoutOption,
fan_control::FanInfo,
overdrive::{ClocksTable as _, ClocksTableGen as AmdClocksTableGen},
},
@@ -415,10 +415,19 @@ pub struct DrmInfo {
pub memory_info: Option<DrmMemoryInfo>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub amd_ip_info: Vec<AmdIpInfo>,
pub amd_uma_carveout: Option<AmdUmaCarveout>,
#[serde(flatten)]
pub intel: IntelDrmInfo,
}
/// Options for iGPU UMA allocation
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct AmdUmaCarveout {
pub options: Vec<UmaCarveoutOption>,
pub current: usize,
}
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct AmdIpInfo {
pub ip_type: String,
@@ -0,0 +1 @@
0
@@ -0,0 +1,8 @@
0: Minimum (512 MB)
1: (1 GB)
2: (2 GB)
3: (4 GB)
4: (6 GB)
5: Medium (8 GB)
6: (12 GB)
7: High (16 GB)