From 1a5e456c5b10fb453cf406f36fa5ba87dff379c1 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Wed, 16 Nov 2022 22:46:18 +0200 Subject: [PATCH 001/113] wip everything --- Cargo.lock | 1247 ++++------------- Cargo.toml | 2 +- cli/Cargo.toml | 14 - cli/src/main.rs | 236 ---- daemon/Cargo.toml | 20 - daemon/src/config.rs | 118 -- daemon/src/daemon_connection.rs | 228 --- daemon/src/gpu_controller.rs | 1120 --------------- daemon/src/hw_mon.rs | 306 ---- daemon/src/lib.rs | 498 ------- daemon/src/main.rs | 21 - deb/DEBIAN/conffiles | 0 deb/DEBIAN/control | 9 - deb/usr/lib/systemd/system/lactd.service | 9 - deb/usr/share/applications/lact.desktop | 5 - gui/Cargo.toml | 17 - gui/src/main.rs | 78 -- lact-daemon/Cargo.toml | 31 + lact-daemon/src/config.rs | 115 ++ lact-daemon/src/fork.rs | 103 ++ lact-daemon/src/main.rs | 37 + .../src/server/gpu_controller/fan_control.rs | 134 ++ lact-daemon/src/server/gpu_controller/mod.rs | 236 ++++ lact-daemon/src/server/handler.rs | 153 ++ lact-daemon/src/server/mod.rs | 94 ++ lact-daemon/src/server/vulkan.rs | 78 ++ lact-daemon/src/socket.rs | 64 + lact-gui/Cargo.toml | 20 + {gui => lact-gui}/src/app.rs | 135 +- {gui => lact-gui}/src/app/apply_revealer.rs | 0 {gui => lact-gui}/src/app/header.rs | 12 +- {gui => lact-gui}/src/app/root_stack.rs | 0 .../src/app/root_stack/info_page.rs | 66 +- .../app/root_stack/info_page/vulkan_info.rs | 10 +- .../src/app/root_stack/oc_page.rs | 41 +- .../app/root_stack/oc_page/clocks_frame.rs | 0 .../oc_page/performance_level_frame.rs | 0 .../app/root_stack/oc_page/power_cap_frame.rs | 0 .../src/app/root_stack/oc_page/stats_grid.rs | 4 +- .../app/root_stack/oc_page/warning_frame.rs | 0 .../src/app/root_stack/software_page.rs | 0 .../src/app/root_stack/thermals_page.rs | 61 +- .../thermals_page/fan_curve_frame.rs | 0 lact-gui/src/client.rs | 84 ++ lact-gui/src/main.rs | 27 + lact-schema/Cargo.toml | 13 + lact-schema/src/lib.rs | 68 + lact-schema/src/request.rs | 11 + lact-schema/src/response.rs | 11 + lact-schema/src/tests.rs | 48 + 50 files changed, 1813 insertions(+), 3771 deletions(-) delete mode 100644 cli/Cargo.toml delete mode 100644 cli/src/main.rs delete mode 100644 daemon/Cargo.toml delete mode 100644 daemon/src/config.rs delete mode 100644 daemon/src/daemon_connection.rs delete mode 100644 daemon/src/gpu_controller.rs delete mode 100644 daemon/src/hw_mon.rs delete mode 100644 daemon/src/lib.rs delete mode 100644 daemon/src/main.rs delete mode 100644 deb/DEBIAN/conffiles delete mode 100644 deb/DEBIAN/control delete mode 100755 deb/usr/lib/systemd/system/lactd.service delete mode 100644 deb/usr/share/applications/lact.desktop delete mode 100644 gui/Cargo.toml delete mode 100644 gui/src/main.rs create mode 100644 lact-daemon/Cargo.toml create mode 100644 lact-daemon/src/config.rs create mode 100644 lact-daemon/src/fork.rs create mode 100644 lact-daemon/src/main.rs create mode 100644 lact-daemon/src/server/gpu_controller/fan_control.rs create mode 100644 lact-daemon/src/server/gpu_controller/mod.rs create mode 100644 lact-daemon/src/server/handler.rs create mode 100644 lact-daemon/src/server/mod.rs create mode 100644 lact-daemon/src/server/vulkan.rs create mode 100644 lact-daemon/src/socket.rs create mode 100644 lact-gui/Cargo.toml rename {gui => lact-gui}/src/app.rs (65%) rename {gui => lact-gui}/src/app/apply_revealer.rs (100%) rename {gui => lact-gui}/src/app/header.rs (79%) rename {gui => lact-gui}/src/app/root_stack.rs (100%) rename {gui => lact-gui}/src/app/root_stack/info_page.rs (70%) rename {gui => lact-gui}/src/app/root_stack/info_page/vulkan_info.rs (93%) rename {gui => lact-gui}/src/app/root_stack/oc_page.rs (74%) rename {gui => lact-gui}/src/app/root_stack/oc_page/clocks_frame.rs (100%) rename gui/src/app/root_stack/oc_page/power_profile_frame.rs => lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs (100%) rename {gui => lact-gui}/src/app/root_stack/oc_page/power_cap_frame.rs (100%) rename {gui => lact-gui}/src/app/root_stack/oc_page/stats_grid.rs (98%) rename {gui => lact-gui}/src/app/root_stack/oc_page/warning_frame.rs (100%) rename {gui => lact-gui}/src/app/root_stack/software_page.rs (100%) rename {gui => lact-gui}/src/app/root_stack/thermals_page.rs (79%) rename {gui => lact-gui}/src/app/root_stack/thermals_page/fan_curve_frame.rs (100%) create mode 100644 lact-gui/src/client.rs create mode 100644 lact-gui/src/main.rs create mode 100644 lact-schema/Cargo.toml create mode 100644 lact-schema/src/lib.rs create mode 100644 lact-schema/src/request.rs create mode 100644 lact-schema/src/response.rs create mode 100644 lact-schema/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 7951fc12..97853084 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,10 +3,16 @@ version = 3 [[package]] -name = "adler" -version = "1.0.2" +name = "ahash" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", +] [[package]] name = "aho-corasick" @@ -18,25 +24,24 @@ dependencies = [ ] [[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +name = "amdgpu-sysfs" +version = "0.5.0" +source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=blocking#55ddcc95cddde27e5c5147d9b5ee4831b5d7aaf8" dependencies = [ - "winapi", + "serde", ] [[package]] name = "anyhow" -version = "1.0.62" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" [[package]] name = "ash" -version = "0.33.3+1.2.191" +version = "0.37.0+1.3.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219" +checksum = "006ca68e0f2b03f22d6fa9f2860f85aed430d257fec20f8879b2145e7c7ae1a6" dependencies = [ "libloading", ] @@ -65,29 +70,12 @@ dependencies = [ "system-deps", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - [[package]] name = "bincode" version = "1.3.3" @@ -104,10 +92,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "bumpalo" -version = "3.11.0" +name = "bytemuck" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "5fe233b960f12f8007e3db2d136e3cb1c291bfd7396e384ee76025fc1a3932b4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "bytes" @@ -139,12 +141,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - [[package]] name = "cfg-expr" version = "0.8.1" @@ -160,49 +156,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chunked_transfer" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "cli" -version = "0.1.0" -dependencies = [ - "colored", - "daemon", - "env_logger 0.8.4", - "log", - "structopt", -] - -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - [[package]] name = "core-foundation" version = "0.9.3" @@ -220,12 +173,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] -name = "crc32fast" -version = "1.3.2" +name = "core-graphics-types" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ - "cfg-if", + "bitflags", + "core-foundation", + "foreign-types", + "libc", ] [[package]] @@ -249,21 +205,10 @@ dependencies = [ ] [[package]] -name = "daemon" -version = "0.1.0" -dependencies = [ - "bincode", - "env_logger 0.9.0", - "log", - "nix", - "pciid-parser", - "rand", - "reqwest", - "serde", - "serde_json", - "signal-hook", - "vulkano", -] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "either" @@ -271,50 +216,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" -[[package]] -name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "env_logger" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - [[package]] name = "field-offset" version = "0.3.4" @@ -325,22 +226,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "flate2" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foreign-types" version = "0.3.2" @@ -356,36 +241,26 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-executor" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ "futures-core", "futures-task", @@ -394,32 +269,24 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" - -[[package]] -name = "futures-sink" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-core", - "futures-io", "futures-task", - "memchr", "pin-project-lite", "pin-utils", "slab", @@ -550,7 +417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aad66361f66796bfc73f530c51ef123970eb895ffba991a234fcf7bea89e518" dependencies = [ "anyhow", - "heck", + "heck 0.3.3", "proc-macro-crate", "proc-macro-error", "proc-macro2", @@ -627,7 +494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21de1da96dc117443fb03c2e270b2d34b7de98d0a79a19bbb689476173745b79" dependencies = [ "anyhow", - "heck", + "heck 0.3.3", "proc-macro-crate", "proc-macro-error", "proc-macro2", @@ -636,42 +503,14 @@ dependencies = [ ] [[package]] -name = "gui" -version = "0.1.0" -dependencies = [ - "daemon", - "env_logger 0.9.0", - "glib", - "gtk", - "log", - "pango", -] - -[[package]] -name = "h2" -version = "0.3.14" +name = "half" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "ad6a9459c9c30b177b925162351f97e7d967c7ea8bab3b8352805327daf45554" dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", + "crunchy", ] -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - [[package]] name = "hashbrown" version = "0.12.3" @@ -688,101 +527,10 @@ dependencies = [ ] [[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "http" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "idna" -version = "0.2.3" +name = "heck" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "indexmap" @@ -795,42 +543,63 @@ dependencies = [ ] [[package]] -name = "instant" -version = "0.1.12" +name = "itertools" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ - "cfg-if", + "either", ] [[package]] -name = "ipnet" -version = "2.5.0" +name = "itoa" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] -name = "itertools" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +name = "lact-daemon" +version = "0.1.0" dependencies = [ - "either", + "amdgpu-sysfs", + "anyhow", + "bincode", + "lact-schema", + "nix", + "pciid-parser", + "serde", + "serde_json", + "serde_yaml", + "tokio", + "tracing", + "tracing-subscriber", + "vulkano", ] [[package]] -name = "itoa" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +name = "lact-gui" +version = "0.1.0" +dependencies = [ + "amdgpu-sysfs", + "anyhow", + "glib", + "gtk", + "lact-schema", + "nix", + "pango", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] [[package]] -name = "js-sys" -version = "0.3.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +name = "lact-schema" +version = "0.1.0" dependencies = [ - "wasm-bindgen", + "amdgpu-sysfs", + "serde", + "serde_json", ] [[package]] @@ -875,10 +644,22 @@ dependencies = [ ] [[package]] -name = "matches" -version = "0.1.9" +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] [[package]] name = "memchr" @@ -896,123 +677,61 @@ dependencies = [ ] [[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.4" +name = "mio" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", "wasi", - "windows-sys", -] - -[[package]] -name = "native-tls" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", + "windows-sys 0.36.1", ] [[package]] name = "nix" -version = "0.23.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" dependencies = [ + "autocfg", "bitflags", - "cc", "cfg-if", "libc", "memoffset", + "pin-utils", ] [[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" - -[[package]] -name = "openssl" -version = "0.10.41" +name = "nu-ansi-term" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", + "overload", + "winapi", ] [[package]] -name = "openssl-macros" -version = "0.1.0" +name = "objc" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ - "proc-macro2", - "quote", - "syn", + "malloc_buf", ] [[package]] -name = "openssl-probe" -version = "0.1.5" +name = "once_cell" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" [[package]] -name = "openssl-sys" -version = "0.9.75" +name = "overload" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pango" @@ -1041,50 +760,42 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" dependencies = [ "cfg-if", - "instant", "libc", "redox_syscall", "smallvec", - "winapi", + "windows-sys 0.42.0", ] [[package]] name = "pciid-parser" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5057bd953dfcccbaba6326a1dc2a6f0e7ca1d7b8ceca37bee610b639ef9f4a0" +checksum = "6e2ef5429455ae06f0c055262d540bf2c190ad51b72bb6cbe8488706c4dff440" dependencies = [ + "serde", "tracing", - "ureq", ] -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - [[package]] name = "pest" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0560d531d1febc25a3c9398a62a71256c0178f2e3443baedd9ad4bb8c9deb4" +checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" dependencies = [ "thiserror", "ucd-trie", @@ -1104,15 +815,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" - -[[package]] -name = "ppv-lite86" -version = "0.2.16" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "proc-macro-crate" @@ -1167,36 +872,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "redox_syscall" version = "0.2.16" @@ -1218,71 +893,19 @@ dependencies = [ ] [[package]] -name = "regex-syntax" -version = "0.6.27" +name = "regex-automata" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", + "regex-syntax", ] [[package]] -name = "ring" -version = "0.16.20" +name = "regex-syntax" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "rustc_version" @@ -1293,73 +916,18 @@ dependencies = [ "semver", ] -[[package]] -name = "rustls" -version = "0.20.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] - [[package]] name = "ryu" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" -[[package]] -name = "schannel" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" -dependencies = [ - "lazy_static", - "windows-sys", -] - [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "security-framework" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "0.11.0" @@ -1380,18 +948,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.144" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" dependencies = [ "proc-macro2", "quote", @@ -1400,9 +968,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" dependencies = [ "itoa", "ryu", @@ -1410,35 +978,25 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_yaml" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "6d232d893b10de3eb7258ff01974d6ee20663d8e833263c99409d4b13a0209da" dependencies = [ - "form_urlencoded", + "indexmap", "itoa", "ryu", "serde", + "unsafe-libyaml", ] [[package]] -name = "shared_library" -version = "0.1.9" +name = "sharded-slab" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" dependencies = [ "lazy_static", - "libc", -] - -[[package]] -name = "signal-hook" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" -dependencies = [ - "libc", - "signal-hook-registry", ] [[package]] @@ -1475,42 +1033,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "strum" version = "0.21.0" @@ -1523,7 +1045,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2", "quote", "syn", @@ -1548,7 +1070,7 @@ checksum = "480c269f870722b3b08d2f13053ce0c2ab722839f472863c3e2d61ff3a1c2fa6" dependencies = [ "anyhow", "cfg-expr", - "heck", + "heck 0.3.3", "itertools", "pkg-config", "strum", @@ -1558,52 +1080,20 @@ dependencies = [ "version-compare", ] -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" -version = "1.0.32" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.32" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -1611,60 +1101,41 @@ dependencies = [ ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "thread_local" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ - "tinyvec_macros", + "once_cell", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - [[package]] name = "tokio" -version = "1.20.1" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" dependencies = [ "autocfg", "bytes", "libc", "memchr", "mio", - "num_cpus", - "once_cell", "pin-project-lite", + "signal-hook-registry", "socket2", + "tokio-macros", "winapi", ] [[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.3" +name = "tokio-macros" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1676,17 +1147,11 @@ dependencies = [ "serde", ] -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", @@ -1696,9 +1161,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", @@ -1707,30 +1172,48 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", + "valuable", ] [[package]] -name = "try-lock" -version = "0.2.3" +name = "tracing-log" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] [[package]] -name = "ucd-trie" -version = "0.1.4" +name = "tracing-subscriber" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] [[package]] -name = "unicode-bidi" -version = "0.3.8" +name = "ucd-trie" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-ident" @@ -1738,73 +1221,23 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" -[[package]] -name = "unicode-normalization" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] -name = "unicode-width" -version = "0.1.9" +name = "unsafe-libyaml" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68" [[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "ureq" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f" -dependencies = [ - "base64", - "chunked_transfer", - "flate2", - "log", - "once_cell", - "rustls", - "url", - "webpki", - "webpki-roots", -] - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vec_map" -version = "0.8.2" +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version-compare" @@ -1820,148 +1253,47 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vk-parse" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1538fa783f47fb5a6eb495f024f426599a4fe66ff3773ff2252156c64d350a" +checksum = "4c6a0bda9bbe6b9e50e6456c80aa8fe4cca3b21e4311a1130c41e4915ec2e32a" dependencies = [ "xml-rs", ] [[package]] name = "vulkano" -version = "0.26.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0c9cc7471d065c6066b2a0daaae4232a100fdb6f0aad2a4b12652a2bd8ede0" +checksum = "eecedd057ea4cd85c0e43ba2ad30e599954bd958c50d0f7849439548cfa573ce" dependencies = [ + "ahash", "ash", + "bytemuck", + "core-graphics-types", "crossbeam-queue", - "fnv", "half", - "heck", + "heck 0.4.0", "indexmap", "lazy_static", + "libloading", + "objc", "parking_lot", "proc-macro2", "quote", "regex", "serde", "serde_json", - "shared_library", "smallvec", + "thread_local", "vk-parse", ] -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" - -[[package]] -name = "web-sys" -version = "0.3.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" -dependencies = [ - "webpki", -] - [[package]] name = "winapi" version = "0.3.9" @@ -1978,15 +1310,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1999,37 +1322,88 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -2037,13 +1411,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] -name = "winreg" -version = "0.10.1" +name = "windows_x86_64_msvc" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" [[package]] name = "xml-rs" diff --git a/Cargo.toml b/Cargo.toml index 83b3b4c2..50b30241 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["daemon", "cli", "gui"] \ No newline at end of file +members = ["lact-daemon", "lact-schema", "lact-gui"] diff --git a/cli/Cargo.toml b/cli/Cargo.toml deleted file mode 100644 index 541e0448..00000000 --- a/cli/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "cli" -version = "0.1.0" -authors = ["Ilya Zlobintsev "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -daemon = { path = "../daemon" } -structopt = "0.3" -log = "0.4" -env_logger = "0.8" -colored = "2" \ No newline at end of file diff --git a/cli/src/main.rs b/cli/src/main.rs deleted file mode 100644 index 1c1ffd76..00000000 --- a/cli/src/main.rs +++ /dev/null @@ -1,236 +0,0 @@ -use colored::*; -use daemon::daemon_connection::DaemonConnection; -use structopt::StructOpt; - -#[derive(StructOpt)] -enum ConfigOpt { - Show, - AllowOnlineUpdating, - DisallowOnlineUpdating, -} - -#[derive(StructOpt)] -enum CurveOpt { - /// Shows current fan control information - Status { - /// Specify a GPU ID as printed in `lact-cli gpus`. By default, all GPUs are printed. - gpu_id: Option, - }, -} - -#[derive(StructOpt)] -#[structopt(rename_all = "lower")] -enum Opt { - /// Realtime GPU information - Metrics { - /// Specify a GPU ID as printed in `lact-cli gpus`. By default, all GPUs are printed. - gpu_id: Option, - }, - /// Get GPU list - Gpus, - /// General information about the GPU - Info { - /// Specify a GPU ID as printed in `lact-cli gpus`. By default, all GPUs are printed. - gpu_id: Option, - }, - Config(ConfigOpt), - /// Fan curve control - Curve(CurveOpt), -} - -fn main() { - env_logger::init(); - - let opt = Opt::from_args(); - - let d = DaemonConnection::new().unwrap(); - log::trace!("connection established"); - - match opt { - Opt::Gpus => { - let gpus = d.get_gpus(); - println!("{:?}", gpus); - } - Opt::Metrics { gpu_id } => { - let mut gpu_ids: Vec = Vec::new(); - - if let Some(gpu_id) = gpu_id { - gpu_ids.push(gpu_id); - } else { - for (gpu_id, _) in d.get_gpus().unwrap() { - gpu_ids.push(gpu_id); - } - } - - for gpu_id in gpu_ids { - print_stats(&d, gpu_id); - } - } - Opt::Info { gpu_id } => { - let mut gpu_ids: Vec = Vec::new(); - - if let Some(gpu_id) = gpu_id { - gpu_ids.push(gpu_id); - } else { - for (gpu_id, _) in d.get_gpus().unwrap() { - gpu_ids.push(gpu_id); - } - } - - for gpu_id in gpu_ids { - print_info(&d, gpu_id); - } - } - Opt::Curve(curve) => match curve { - CurveOpt::Status { gpu_id } => { - let mut gpu_ids: Vec = Vec::new(); - - if let Some(gpu_id) = gpu_id { - gpu_ids.push(gpu_id); - } else { - for (gpu_id, _) in d.get_gpus().unwrap() { - gpu_ids.push(gpu_id); - } - } - - for gpu_id in gpu_ids { - print_fan_curve(&d, gpu_id); - } - } - }, - Opt::Config(config_opt) => match config_opt { - ConfigOpt::Show => print_config(&d), - ConfigOpt::AllowOnlineUpdating => enable_online_update(&d), - ConfigOpt::DisallowOnlineUpdating => disable_online_update(&d), - }, - } -} - -fn disable_online_update(d: &DaemonConnection) { - let mut config = d.get_config().unwrap(); - config.allow_online_update = Some(false); - d.set_config(config).unwrap(); -} - -fn enable_online_update(d: &DaemonConnection) { - let mut config = d.get_config().unwrap(); - config.allow_online_update = Some(true); - d.set_config(config).unwrap(); -} - -fn print_config(d: &DaemonConnection) { - let config = d.get_config().unwrap(); - - println!( - "{} {:?}", - "Online PCI DB updating:".purple(), - config.allow_online_update - ); -} - -fn print_fan_curve(d: &DaemonConnection, gpu_id: u32) { - let fan_control = d.get_fan_control(gpu_id).unwrap(); - - if fan_control.enabled { - println!("{}", "Fan curve:".yellow()); - - for (temp, fan_speed) in fan_control.curve { - println!( - "{}{}: {}{}", - temp.to_string().yellow(), - "C°".yellow(), - fan_speed.round().to_string().bold(), - "%".bold() - ); - } - } else { - println!("{}", "Automatic fan control used".yellow()); - } -} - -fn print_info(d: &DaemonConnection, gpu_id: u32) { - let gpu_info = d.get_gpu_info(gpu_id).unwrap(); - println!( - "{} {}", - "GPU Model:".blue(), - gpu_info.vendor_data.card_model.unwrap_or_default().bold() - ); - println!( - "{} {}", - "GPU Vendor:".blue(), - gpu_info.vendor_data.gpu_vendor.unwrap_or_default().bold() - ); - println!("{} {}", "Driver in use:".blue(), gpu_info.driver.bold()); - println!( - "{} {}", - "VBIOS Version:".blue(), - gpu_info.vbios_version.bold() - ); - println!( - "{} {}", - "VRAM Size:".blue(), - gpu_info.vram_size.to_string().bold() - ); - println!("{} {}", "Link Speed:".blue(), gpu_info.link_speed.bold()); -} - -fn print_stats(d: &DaemonConnection, gpu_id: u32) { - let gpu_stats = d.get_gpu_stats(gpu_id).unwrap(); - println!( - "{} {}/{}{}", - "VRAM Usage:".green(), - gpu_stats.mem_used.unwrap_or_default().to_string().bold(), - gpu_stats.mem_total.unwrap_or_default().to_string().bold(), - "MiB".bold(), - ); - println!( - "{} {}{}", - "Temperature:".green(), - gpu_stats - .temperatures - .get("edge") - .unwrap() - .current - .to_string() - .bold(), - "°C".bold(), - ); - println!( - "{} {}/{}{}", - "Fan Speed:".green(), - gpu_stats.fan_speed.unwrap_or_default().to_string().bold(), - gpu_stats - .max_fan_speed - .unwrap_or_default() - .to_string() - .bold(), - "RPM".bold(), - ); - println!( - "{} {}{}", - "GPU Clock:".green(), - gpu_stats.gpu_freq.unwrap_or_default().to_string().bold(), - "MHz".bold(), - ); - println!( - "{} {}{}", - "GPU Voltage:".green(), - (gpu_stats.voltage.unwrap_or_default() as f64 / 1000.0) - .to_string() - .bold(), - "V".bold(), - ); - println!( - "{} {}{}", - "VRAM Clock:".green(), - gpu_stats.mem_freq.unwrap_or_default().to_string().bold(), - "MHz".bold(), - ); - println!( - "{} {}/{}{}", - "Power Usage:".green(), - gpu_stats.power_avg.unwrap_or_default().to_string().bold(), - gpu_stats.power_cap.unwrap_or_default().to_string().bold(), - "W".bold(), - ); -} diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml deleted file mode 100644 index eb8c3ee9..00000000 --- a/daemon/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "daemon" -version = "0.1.0" -authors = ["Ilya Zlobintsev "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -bincode = "1.3" -serde = { version = "1.0", features = ["derive", "rc"] } -serde_json = "1.0" -vulkano = "0.26" -log = "0.4" -env_logger = "0.9" -rand = "0.8" -signal-hook = "0.3" -reqwest = { version = "0.11", features = ["blocking", "json"] } -nix = "0.23" -pciid-parser = { version = "0.6.0", features = ["online"] } diff --git a/daemon/src/config.rs b/daemon/src/config.rs deleted file mode 100644 index 73a30e6e..00000000 --- a/daemon/src/config.rs +++ /dev/null @@ -1,118 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, HashMap}; -use std::fs; -use std::io; -use std::path::PathBuf; - -use crate::gpu_controller::PowerProfile; - -#[derive(Debug)] -pub enum ConfigError { - IoError(io::Error), - ParseError(serde_json::Error), -} - -impl From for ConfigError { - fn from(error: io::Error) -> Self { - ConfigError::IoError(error) - } -} - -impl From for ConfigError { - fn from(error: serde_json::Error) -> Self { - ConfigError::ParseError(error) - } -} - -#[derive(Deserialize, Serialize, Debug, Clone, Hash, Eq)] -pub struct GpuIdentifier { - pub pci_id: String, - pub card_model: Option, - pub gpu_model: Option, - pub path: PathBuf, -} - -impl PartialEq for GpuIdentifier { - fn eq(&self, other: &Self) -> bool { - self.pci_id == other.pci_id - && self.gpu_model == other.gpu_model - && self.card_model == other.card_model - } -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct GpuConfig { - pub fan_control_enabled: bool, - pub fan_curve: BTreeMap, - pub power_cap: i64, - pub power_profile: PowerProfile, - pub gpu_max_clock: i64, - pub gpu_max_voltage: Option, - pub vram_max_clock: i64, -} - -impl GpuConfig { - pub fn new() -> Self { - let mut fan_curve: BTreeMap = BTreeMap::new(); - fan_curve.insert(20, 0f64); - fan_curve.insert(40, 0f64); - fan_curve.insert(60, 50f64); - fan_curve.insert(80, 80f64); - fan_curve.insert(100, 100f64); - - GpuConfig { - fan_curve, - fan_control_enabled: false, - power_cap: -1, - power_profile: PowerProfile::Auto, - gpu_max_clock: 0, - gpu_max_voltage: None, - vram_max_clock: 0, - } - } -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -pub struct Config { - pub gpu_configs: HashMap, - pub allow_online_update: Option, - pub config_path: PathBuf, - pub group: String, -} - -impl Config { - pub fn new(config_path: &PathBuf) -> Self { - let gpu_configs: HashMap = HashMap::new(); - - Config { - gpu_configs, - allow_online_update: None, - config_path: config_path.clone(), - group: String::from("wheel"), - } - } - - pub fn read_from_file(path: &PathBuf) -> Result { - let json = fs::read_to_string(path)?; - - Ok(serde_json::from_str::(&json)?) - } - - pub fn save(&self) -> Result<(), ConfigError> { - let json = serde_json::to_string_pretty(self)?; - log::info!("saving {}", json.to_string()); - - Ok(fs::write(&self.config_path, &json.to_string())?) - } -} - -/*#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn write_config() -> Result<(), ConfigError> { - let c = Config::new(); - c.save(PathBuf::from("/tmp/config.json")) - } -}*/ diff --git a/daemon/src/daemon_connection.rs b/daemon/src/daemon_connection.rs deleted file mode 100644 index 2955c10c..00000000 --- a/daemon/src/daemon_connection.rs +++ /dev/null @@ -1,228 +0,0 @@ -use crate::config::Config; -use crate::gpu_controller::{FanControlInfo, GpuStats}; -use crate::gpu_controller::{GpuInfo, PowerProfile}; -use crate::Daemon; -use crate::DaemonError; -use crate::{Action, DaemonResponse, SOCK_PATH}; -use std::collections::{BTreeMap, HashMap}; - -#[derive(Clone, Copy)] -pub struct DaemonConnection {} - -pub const BUFFER_SIZE: usize = 4096; - -impl DaemonConnection { - pub fn new() -> Result { - let addr = nix::sys::socket::SockAddr::Unix( - nix::sys::socket::UnixAddr::new_abstract(SOCK_PATH.as_bytes()).unwrap(), - ); - let socket = nix::sys::socket::socket( - nix::sys::socket::AddressFamily::Unix, - nix::sys::socket::SockType::Stream, - nix::sys::socket::SockFlag::empty(), - None, - ) - .expect("Creating socket failed"); - nix::sys::socket::connect(socket, &addr).expect("Socket connect failed"); - - nix::unistd::write(socket, &bincode::serialize(&Action::CheckAlive).unwrap()) - .expect("Writing check alive to socket failed"); - - nix::sys::socket::shutdown(socket, nix::sys::socket::Shutdown::Write) - .expect("Could not shut down"); - - let mut buffer = Vec::::new(); - buffer.resize(BUFFER_SIZE, 0); - loop { - match nix::unistd::read(socket, &mut buffer) { - Ok(0) => { - break; - } - Ok(n) => { - assert!(n < buffer.len()); - if n < buffer.len() { - buffer.resize(n, 0); - } - break; - } - Err(e) => { - panic!("Error reading from socket: {}", e); - } - } - } - nix::sys::socket::shutdown(socket, nix::sys::socket::Shutdown::Both) - .expect("Could not shut down"); - - nix::unistd::close(socket).expect("Failed to close"); - - let result: Result = - bincode::deserialize(&buffer).expect("failed to deserialize message"); - - match result { - Ok(_) => Ok(DaemonConnection {}), - Err(_) => Err(DaemonError::ConnectionFailed), - } - } - - fn send_action(&self, action: Action) -> Result { - let addr = nix::sys::socket::SockAddr::Unix( - nix::sys::socket::UnixAddr::new_abstract(SOCK_PATH.as_bytes()).unwrap(), - ); - let socket = nix::sys::socket::socket( - nix::sys::socket::AddressFamily::Unix, - nix::sys::socket::SockType::Stream, - nix::sys::socket::SockFlag::empty(), - None, - ) - .expect("Socket failed"); - nix::sys::socket::connect(socket, &addr).expect("connect failed"); - - let b = bincode::serialize(&action).unwrap(); - nix::unistd::write(socket, &b).expect("Writing action to socket failed"); - - nix::sys::socket::shutdown(socket, nix::sys::socket::Shutdown::Write) - .expect("Could not shut down"); - - let buffer = Daemon::read_buffer(socket); - - nix::sys::socket::shutdown(socket, nix::sys::socket::Shutdown::Both) - .expect("Failed to shut down"); - - nix::unistd::close(socket).expect("Failed to close"); - - bincode::deserialize(&buffer).expect("failed to deserialize message") - } - - pub fn get_gpu_stats(&self, gpu_id: u32) -> Result { - match self.send_action(Action::GetStats(gpu_id))? { - DaemonResponse::GpuStats(stats) => Ok(stats), - _ => unreachable!(), - } - } - - pub fn get_gpu_info(&self, gpu_id: u32) -> Result { - match self.send_action(Action::GetInfo(gpu_id))? { - DaemonResponse::GpuInfo(info) => Ok(info), - _ => unreachable!("impossible enum variant"), - } - } - - pub fn start_fan_control(&self, gpu_id: u32) -> Result<(), DaemonError> { - match self.send_action(Action::StartFanControl(gpu_id))? { - DaemonResponse::OK => Ok(()), - _ => Err(DaemonError::HWMonError), - } - } - - pub fn stop_fan_control(&self, gpu_id: u32) -> Result<(), DaemonError> { - match self.send_action(Action::StopFanControl(gpu_id))? { - DaemonResponse::OK => Ok(()), - _ => Err(DaemonError::HWMonError), - } - } - - pub fn get_fan_control(&self, gpu_id: u32) -> Result { - match self.send_action(Action::GetFanControl(gpu_id))? { - DaemonResponse::FanControlInfo(info) => Ok(info), - _ => unreachable!(), - } - } - - pub fn set_fan_curve(&self, gpu_id: u32, curve: BTreeMap) -> Result<(), DaemonError> { - match self.send_action(Action::SetFanCurve(gpu_id, curve))? { - DaemonResponse::OK => Ok(()), - _ => unreachable!(), - } - } - - pub fn set_power_cap(&self, gpu_id: u32, cap: i64) -> Result<(), DaemonError> { - match self.send_action(Action::SetPowerCap(gpu_id, cap))? { - DaemonResponse::OK => Ok(()), - _ => unreachable!(), - } - } - - pub fn set_power_profile(&self, gpu_id: u32, profile: PowerProfile) -> Result<(), DaemonError> { - match self.send_action(Action::SetPowerProfile(gpu_id, profile))? { - DaemonResponse::OK => Ok(()), - _ => unreachable!(), - } - } - - /*pub fn set_gpu_power_state(&self, gpu_id: u32, num: u32, clockspeed: i64, voltage: Option) -> Result<(), DaemonError> { - match self.send_action(Action::SetGPUPowerState(gpu_id, num, clockspeed, voltage))? { - DaemonResponse::OK => Ok(()), - _ => unreachable!(), - } - }*/ - - pub fn set_gpu_max_power_state( - &self, - gpu_id: u32, - clockspeed: i64, - voltage: Option, - ) -> Result<(), DaemonError> { - match self.send_action(Action::SetGPUMaxPowerState(gpu_id, clockspeed, voltage))? { - DaemonResponse::OK => Ok(()), - _ => unreachable!(), - } - } - - pub fn set_vram_max_clock(&self, gpu_id: u32, clockspeed: i64) -> Result<(), DaemonError> { - match self.send_action(Action::SetVRAMMaxClock(gpu_id, clockspeed))? { - DaemonResponse::OK => Ok(()), - _ => unreachable!(), - } - } - - pub fn commit_gpu_power_states(&self, gpu_id: u32) -> Result<(), DaemonError> { - match self.send_action(Action::CommitGPUPowerStates(gpu_id))? { - DaemonResponse::OK => Ok(()), - _ => unreachable!(), - } - } - - pub fn reset_gpu_power_states(&self, gpu_id: u32) -> Result<(), DaemonError> { - match self.send_action(Action::ResetGPUPowerStates(gpu_id))? { - DaemonResponse::OK => Ok(()), - _ => unreachable!(), - } - } - - pub fn get_gpus(&self) -> Result>, DaemonError> { - match self.send_action(Action::GetGpus)? { - DaemonResponse::Gpus(gpus) => Ok(gpus), - _ => unreachable!(), - } - } - - pub fn shutdown(&self) { - let addr = nix::sys::socket::SockAddr::Unix( - nix::sys::socket::UnixAddr::new_abstract(SOCK_PATH.as_bytes()).unwrap(), - ); - let socket = nix::sys::socket::socket( - nix::sys::socket::AddressFamily::Unix, - nix::sys::socket::SockType::Stream, - nix::sys::socket::SockFlag::empty(), - None, - ) - .expect("Socket failed"); - nix::sys::socket::connect(socket, &addr).expect("connect failed"); - nix::unistd::write(socket, &mut &bincode::serialize(&Action::Shutdown).unwrap()) - .expect("Writing shutdown to socket failed"); - } - - pub fn get_config(&self) -> Result { - match self.send_action(Action::GetConfig)? { - DaemonResponse::Config(config) => Ok(config), - _ => unreachable!(), - } - } - - pub fn set_config(&self, config: Config) -> Result<(), DaemonError> { - match self.send_action(Action::SetConfig(config))? { - DaemonResponse::OK => Ok(()), - _ => unreachable!(), - } - } -} diff --git a/daemon/src/gpu_controller.rs b/daemon/src/gpu_controller.rs deleted file mode 100644 index 518bdf16..00000000 --- a/daemon/src/gpu_controller.rs +++ /dev/null @@ -1,1120 +0,0 @@ -use crate::{ - config::{GpuConfig, GpuIdentifier}, - hw_mon::{HWMon, HWMonError, Temperature}, -}; -use pciid_parser::{schema::DeviceInfo, Database}; -use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, HashMap}; -use std::fs; -use std::num::ParseIntError; -use std::path::PathBuf; -use vulkano::device::physical::PhysicalDevice; -use vulkano::instance::{Instance, InstanceExtensions}; - -#[derive(Serialize, Deserialize, Debug)] -pub enum GpuControllerError { - NotSupported, - PermissionDenied, - UnknownError, - ParseError(String), -} - -impl From for GpuControllerError { - fn from(err: std::io::Error) -> GpuControllerError { - match err.kind() { - std::io::ErrorKind::PermissionDenied => GpuControllerError::PermissionDenied, - std::io::ErrorKind::NotFound => GpuControllerError::NotSupported, - _ => GpuControllerError::UnknownError, - } - } -} - -impl From for GpuControllerError { - fn from(err: ParseIntError) -> GpuControllerError { - GpuControllerError::ParseError(err.to_string()) - } -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub enum PowerProfile { - Auto, - Low, - High, -} - -impl Default for PowerProfile { - fn default() -> Self { - PowerProfile::Auto - } -} - -impl PowerProfile { - pub fn from_str(profile: &str) -> Result { - match profile { - "auto" | "Automatic" => Ok(PowerProfile::Auto), - "high" | "Highest Clocks" => Ok(PowerProfile::High), - "low" | "Lowest Clocks" => Ok(PowerProfile::Low), - _ => Err(GpuControllerError::ParseError( - "unrecognized GPU power profile".to_string(), - )), - } - } - - pub fn to_string(&self) -> String { - match self { - PowerProfile::Auto => "auto".to_string(), - PowerProfile::High => "high".to_string(), - PowerProfile::Low => "low".to_string(), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum ClocksTable { - Old(ClocksTableOld), - New(ClocksTableNew), -} - -#[derive(Serialize, Deserialize, Debug, Clone, Default)] -pub struct ClocksTableOld { - pub gpu_power_levels: BTreeMap, // - pub mem_power_levels: BTreeMap, - pub gpu_clocks_range: (i64, i64), - pub mem_clocks_range: (i64, i64), - pub voltage_range: (i64, i64), //IN MILLIVOLTS -} - -#[derive(Serialize, Deserialize, Debug, Clone, Default)] -pub struct ClocksTableNew { - pub current_gpu_clocks: (i64, i64), - pub current_max_mem_clock: i64, - // pub vddc_curve: [(i64, i64); 3], - pub gpu_clocks_range: (i64, i64), - pub mem_clocks_range: (i64, i64), - // pub voltage_range: (i64, i64), //IN MILLIVOLTS -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct GpuStats { - pub mem_used: Option, - pub mem_total: Option, - pub mem_freq: Option, - pub gpu_freq: Option, - pub temperatures: HashMap, - pub power_avg: Option, - pub power_cap: Option, - pub power_cap_max: Option, - pub fan_speed: Option, - pub max_fan_speed: Option, - pub voltage: Option, - pub gpu_usage: Option, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct FanControlInfo { - pub enabled: bool, - pub curve: BTreeMap, -} -#[derive(Serialize, Deserialize, Debug, Clone, Default)] -pub struct VulkanInfo { - pub device_name: String, - pub api_version: String, - pub features: HashMap, -} - -#[derive(Serialize, Deserialize, Debug, Clone, Default)] -pub struct GpuInfo { - pub vendor_data: VendorData, - pub model_id: String, - pub vendor_id: String, - pub driver: String, - pub vbios_version: String, - pub vram_size: u64, //in MiB - pub link_speed: String, - pub link_width: u8, - pub vulkan_info: VulkanInfo, - pub pci_slot: String, - pub power_profile: Option, - pub clocks_table: Option, - pub power_cap: Option, - pub power_cap_max: Option, -} - -#[derive(Serialize, Deserialize, Debug, Clone, Default)] -pub struct VendorData { - pub gpu_vendor: Option, - pub gpu_model: Option, - pub card_vendor: Option, - pub card_model: Option, -} - -impl From> for VendorData { - fn from(info: DeviceInfo) -> Self { - Self { - gpu_vendor: info.vendor_name.map(str::to_owned), - gpu_model: info.device_name.map(str::to_owned), - card_vendor: info.subvendor_name.map(str::to_owned), - card_model: info.subdevice_name.map(str::to_owned), - } - } -} - -#[derive(Deserialize, Serialize)] -pub struct GpuController { - pub hw_path: PathBuf, - hw_mon: Option, - gpu_info: GpuInfo, - config: GpuConfig, -} - -impl GpuController { - pub fn new(hw_path: PathBuf, config: GpuConfig, pci_db: &Option) -> Self { - let mut controller = GpuController { - hw_path: hw_path.clone(), - hw_mon: None, - config: GpuConfig::new(), - gpu_info: GpuInfo::default(), - }; - - controller.gpu_info = controller.get_info_initial(pci_db); - - controller.load_config(&config); - - controller - } - - pub fn load_config(&mut self, config: &GpuConfig) { - self.hw_mon = match fs::read_dir(self.hw_path.join("hwmon")) { - Ok(mut path) => { - let path = path.next().unwrap().unwrap().path(); - let hw_mon = HWMon::new( - &path, - config.fan_control_enabled, - config.fan_curve.clone(), - Some(config.power_cap), - ); - Some(hw_mon) - } - _ => None, - }; - - #[allow(unused_must_use)] - { - self.set_power_profile(config.power_profile.clone()); - - self.set_gpu_max_power_state(config.gpu_max_clock, config.gpu_max_voltage); - - self.set_vram_max_clockspeed(config.vram_max_clock); - - self.commit_gpu_power_states(); - } - } - - pub fn get_config(&self) -> GpuConfig { - self.config.clone() - } - - pub fn get_identifier(&self) -> GpuIdentifier { - let gpu_info = self.get_info(); - GpuIdentifier { - pci_id: gpu_info.pci_slot.clone(), - card_model: gpu_info.vendor_data.card_model.clone(), - gpu_model: gpu_info.vendor_data.gpu_model.clone(), - path: self.hw_path.clone(), - } - } - - pub fn get_info(&self) -> GpuInfo { - let mut info = self.gpu_info.clone(); - - info.power_profile = match self.get_power_profile() { - Ok(p) => Some(p), - Err(_) => None, - }; - - info.clocks_table = match self.get_clocks_table() { - Ok(t) => Some(t), - Err(_) => None, - }; - - if let Some(hw_mon) = &self.hw_mon { - info.power_cap = hw_mon.get_power_cap(); - info.power_cap_max = hw_mon.get_power_cap_max(); - } - - info - } - - fn get_info_initial(&self, pci_db: &Option) -> GpuInfo { - let uevent = - fs::read_to_string(self.hw_path.join("uevent")).expect("Failed to read uevent"); - - let mut driver = String::new(); - let mut vendor_id = String::new(); - let mut model_id = String::new(); - let mut card_vendor_id = String::new(); - let mut card_model_id = String::new(); - let mut pci_slot = String::new(); - - for line in uevent.split('\n') { - let split = line.split('=').collect::>(); - match split.get(0).unwrap() { - &"DRIVER" => driver = split.get(1).unwrap().to_string(), - &"PCI_ID" => { - let ids = split - .last() - .expect("failed to get split") - .split(':') - .collect::>(); - vendor_id = ids.get(0).unwrap().to_string(); - model_id = ids.get(1).unwrap().to_string(); - } - &"PCI_SUBSYS_ID" => { - let ids = split - .last() - .expect("failed to get split") - .split(':') - .collect::>(); - card_vendor_id = ids.get(0).unwrap().to_string(); - card_model_id = ids.get(1).unwrap().to_string(); - } - &"PCI_SLOT_NAME" => pci_slot = split.get(1).unwrap().to_string(), - _ => (), - } - } - - let vbios_version = match fs::read_to_string(self.hw_path.join("vbios_version")) { - Ok(v) => v, - Err(_) => "".to_string(), - } - .trim() - .to_string(); - - let vram_size = match fs::read_to_string(self.hw_path.join("mem_info_vram_total")) { - Ok(a) => a.trim().parse::().unwrap() / 1024 / 1024, - Err(_) => 0, - }; - - let link_speed = match fs::read_to_string(self.hw_path.join("current_link_speed")) { - Ok(a) => a.trim().to_string(), - Err(_) => "".to_string(), - }; - - let link_width = match fs::read_to_string(self.hw_path.join("current_link_width")) { - Ok(a) => a.trim().parse::().unwrap(), - Err(_) => 0, - }; - - let vulkan_info = GpuController::get_vulkan_info(&model_id); - - let vendor_data = match pci_db { - Some(db) => db - .get_device_info(&vendor_id, &model_id, &card_vendor_id, &card_model_id) - .into(), - None => match Database::read() { - Ok(db) => db - .get_device_info(&vendor_id, &model_id, &card_vendor_id, &card_model_id) - .into(), - Err(err) => { - println!( - "{:?} pci.ids not found! Make sure you have 'hwdata' installed", - err - ); - VendorData::default() - } - }, - }; - - log::info!("Vendor data: {:?}", vendor_data); - - GpuInfo { - vendor_data, - model_id, - vendor_id, - driver, - vbios_version, - vram_size, - link_speed, - link_width, - vulkan_info, - pci_slot, - power_profile: None, - clocks_table: None, - power_cap: None, - power_cap_max: None, - } - } - - pub fn get_stats(&self) -> Result { - let mem_total = match fs::read_to_string(self.hw_path.join("mem_info_vram_total")) { - Ok(a) => Some(a.trim().parse::().unwrap() / 1024 / 1024), - Err(_) => None, - }; - - let mem_used = match fs::read_to_string(self.hw_path.join("mem_info_vram_used")) { - Ok(a) => Some(a.trim().parse::().unwrap() / 1024 / 1024), - Err(_) => None, - }; - - let gpu_usage = match fs::read_to_string(self.hw_path.join("gpu_busy_percent")) { - Ok(a) => Some(a.trim().parse::().unwrap()), - Err(_) => None, - }; - - let ( - mem_freq, - gpu_freq, - temperatures, - power_avg, - power_cap, - power_cap_max, - fan_speed, - max_fan_speed, - voltage, - ) = match &self.hw_mon { - Some(hw_mon) => ( - hw_mon.get_mem_freq(), - hw_mon.get_gpu_freq(), - hw_mon.get_temps(), - hw_mon.get_power_avg(), - hw_mon.get_power_cap(), - hw_mon.get_power_cap_max(), - hw_mon.get_fan_speed(), - hw_mon.get_fan_max_speed(), - hw_mon.get_voltage(), - ), - None => return Err(HWMonError::NoHWMon), - }; - - Ok(GpuStats { - mem_total, - mem_used, - mem_freq, - gpu_freq, - temperatures, - power_avg, - power_cap, - power_cap_max, - fan_speed, - max_fan_speed, - voltage, - gpu_usage, - }) - } - - pub fn start_fan_control(&mut self) -> Result<(), HWMonError> { - match &self.hw_mon { - Some(hw_mon) => match hw_mon.start_fan_control() { - Ok(_) => { - self.config.fan_control_enabled = true; - Ok(()) - } - Err(e) => Err(e), - }, - None => Err(HWMonError::NoHWMon), - } - } - - pub fn stop_fan_control(&mut self) -> Result<(), HWMonError> { - match &self.hw_mon { - Some(hw_mon) => match hw_mon.stop_fan_control() { - Ok(_) => { - self.config.fan_control_enabled = false; - Ok(()) - } - Err(e) => Err(e), - }, - None => Err(HWMonError::NoHWMon), - } - } - - pub fn get_fan_control(&self) -> Result { - match &self.hw_mon { - Some(hw_mon) => match hw_mon.get_fan_speed() { - Some(_) => { - let control = hw_mon.get_fan_control(); - Ok(FanControlInfo { - enabled: control.0, - curve: control.1, - }) - } - None => Err(HWMonError::Unsupported), - }, - None => Err(HWMonError::NoHWMon), - } - } - - pub fn set_fan_curve(&mut self, curve: BTreeMap) -> Result<(), HWMonError> { - match &self.hw_mon { - Some(hw_mon) => { - hw_mon.set_fan_curve(curve.clone()); - self.config.fan_curve = curve; - Ok(()) - } - None => Err(HWMonError::NoHWMon), - } - } - - pub fn set_power_cap(&mut self, cap: i64) -> Result<(), HWMonError> { - match &mut self.hw_mon { - Some(hw_mon) => { - hw_mon.set_power_cap(cap).unwrap(); - self.config.power_cap = cap; - Ok(()) - } - None => Err(HWMonError::NoHWMon), - } - } - - pub fn get_power_cap(&self) -> Result<(i64, i64), HWMonError> { - match &self.hw_mon { - Some(hw_mon) => { - let min = hw_mon - .get_power_cap() - .ok_or_else(|| HWMonError::Unsupported)?; - let max = hw_mon - .get_power_cap_max() - .ok_or_else(|| HWMonError::Unsupported)?; - - Ok((min, max)) - } - None => Err(HWMonError::NoHWMon), - } - } - - fn get_power_profile(&self) -> Result { - match fs::read_to_string(self.hw_path.join("power_dpm_force_performance_level")) { - Ok(s) => Ok(PowerProfile::from_str(&s.trim()).unwrap()), - Err(_) => Err(GpuControllerError::NotSupported), - } - } - - pub fn set_power_profile(&mut self, profile: PowerProfile) -> Result<(), GpuControllerError> { - match fs::write( - self.hw_path.join("power_dpm_force_performance_level"), - profile.to_string(), - ) { - Ok(_) => { - self.config.power_profile = profile; - Ok(()) - } - Err(_) => Err(GpuControllerError::NotSupported), - } - } - - fn get_clocks_table(&self) -> Result { - match fs::read_to_string(self.hw_path.join("pp_od_clk_voltage")) { - Ok(table) => Self::parse_clocks_table(&table), - Err(_) => Err(GpuControllerError::NotSupported), - } - } - - fn parse_clocks_table(table: &str) -> Result { - if table.contains("CURVE") { - Ok(ClocksTable::New(Self::parse_clocks_table_new(table)?)) - } else { - Ok(ClocksTable::Old(Self::parse_clocks_table_old(table)?)) - } - } - - fn parse_clocks_table_old(table: &str) -> Result { - let mut clocks_table = ClocksTableOld::default(); - - let mut lines_iter = table.trim().split("\n").into_iter(); - - log::trace!("Reading clocks table"); - - while let Some(line) = lines_iter.next() { - let line = line.trim(); - log::trace!("Parsing line {}", line); - - match line { - "OD_SCLK:" | "OD_MCLK:" => { - let is_vram = match line { - "OD_SCLK:" => false, - "OD_MCLK:" => true, - _ => unreachable!(), - }; - - log::trace!("Parsing clock levels"); - - // If `next()` is used on the main iterator directly, it will consume the `OD_MCLK:` aswell, - // which means the outer loop won't recognize that the next lines are of a different clock type. - // Thus, it is better to count how many lines were of the clock levels and then substract that amount from the main iterator. - let mut i = 0; - let mut lines = lines_iter.clone(); - - while let Some(line) = lines.next() { - let line = line.trim(); - log::trace!("Parsing power level line {}", line); - - // Probably shouldn't unwrap, will fail on empty lines in clocks table - if let Some(_) = line.chars().next().unwrap().to_digit(10) { - let (num, clock, voltage) = - GpuController::parse_clock_voltage_line(line)?; - - log::trace!("Power level {}: {}MHz {}mV", num, clock, voltage); - - if is_vram { - clocks_table.mem_power_levels.insert(num, (clock, voltage)); - } else { - clocks_table.gpu_power_levels.insert(num, (clock, voltage)); - } - - i += 1; - } else { - // Probably a better way to do this - for _ in 0..i { - lines_iter.next().unwrap(); - } - log::trace!("Finished reading clock levels"); - break; - } - } - } - "OD_RANGE:" => { - log::trace!("Parsing clock and voltage ranges"); - - while let Some(line) = lines_iter.next() { - let mut split = line.split_whitespace(); - - let name = split.next().ok_or_else(|| { - GpuControllerError::ParseError("failed to get range name".to_string()) - })?; - let min = split.next().ok_or_else(|| { - GpuControllerError::ParseError( - "failed to get range minimal value".to_string(), - ) - })?; - let max = split.next().ok_or_else(|| { - GpuControllerError::ParseError( - "failed to get range maximum value".to_string(), - ) - })?; - - match name { - "SCLK:" => { - let min_clock: i64 = min.replace("MHz", "").parse()?; - let max_clock: i64 = max.replace("MHz", "").parse()?; - - clocks_table.gpu_clocks_range = (min_clock, max_clock); - } - "MCLK:" => { - let min_clock: i64 = min.replace("MHz", "").parse()?; - let max_clock: i64 = max.replace("MHz", "").parse()?; - - clocks_table.mem_clocks_range = (min_clock, max_clock); - } - "VDDC:" => { - let min_voltage: i64 = min.replace("mV", "").parse()?; - let max_voltage: i64 = max.replace("mV", "").parse()?; - - clocks_table.voltage_range = (min_voltage, max_voltage); - } - _ => { - return Err(GpuControllerError::ParseError( - "unrecognized voltage range type".to_string(), - )) - } - } - } - } - _ => { - return Err(GpuControllerError::ParseError( - "unrecognized line type".to_string(), - )) - } - } - } - - log::trace!("Successfully parsed the clocks table"); - Ok(clocks_table) - } - - fn parse_clocks_table_new(table: &str) -> Result { - log::trace!("Detected clocks table format for Vega20 or newer"); - - let mut clocks_table = ClocksTableNew::default(); - - let mut lines_iter = table.trim().split("\n").into_iter(); - - log::trace!("Reading clocks table"); - - while let Some(line) = &lines_iter.next() { - let line = line.trim(); - log::trace!("Parsing line {}", line); - - match line { - "OD_SCLK:" => { - let min_clock_line = lines_iter - .next() - .ok_or_else(|| { - GpuControllerError::ParseError( - "unexpeceted clocks file end".to_string(), - ) - })? - .trim() - .to_lowercase(); - - let min_clock: i64 = min_clock_line - .strip_prefix("0:") - .ok_or_else(|| { - GpuControllerError::ParseError(format!( - "invalid clock line prefix in {}", - min_clock_line - )) - })? - .strip_suffix("mhz") - .ok_or_else(|| { - GpuControllerError::ParseError(format!( - "invalid clock line suffix in {}", - min_clock_line - )) - })? - .trim() - .parse()?; - - let max_clock_line = lines_iter - .next() - .ok_or_else(|| { - GpuControllerError::ParseError( - "unexpeceted clocks file end".to_string(), - ) - })? - .trim() - .to_lowercase(); - - let max_clock: i64 = max_clock_line - .strip_prefix("1:") - .ok_or_else(|| { - GpuControllerError::ParseError(format!( - "invalid clock line prefix in {}", - min_clock_line - )) - })? - .strip_suffix("mhz") - .ok_or_else(|| { - GpuControllerError::ParseError(format!( - "invalid clock line suffix in {}", - min_clock_line - )) - })? - .trim() - .parse()?; - - clocks_table.current_gpu_clocks = (min_clock, max_clock); - } - "OD_MCLK:" => { - let max_clock_line = lines_iter - .next() - .ok_or_else(|| { - GpuControllerError::ParseError("unexpected clocks file end".to_string()) - })? - .trim() - .to_lowercase(); - - let max_clock = max_clock_line - .strip_prefix("1:") - .ok_or_else(|| { - GpuControllerError::ParseError(format!( - "invalid clock line prefix in {}", - max_clock_line - )) - })? - .strip_suffix("mhz") - .ok_or_else(|| { - GpuControllerError::ParseError(format!( - "invalid clock line suffix in {}", - max_clock_line - )) - })? - .trim() - .parse()?; - - clocks_table.current_max_mem_clock = max_clock; - } - "OD_RANGE:" => { - while let Some(line) = &lines_iter.next() { - let line = line.trim(); - log::trace!("Parsing OD_RANGE line {}", &line); - - match &line[..5] { - "SCLK:" => { - let mut split = line.split_whitespace(); - - // Skips the 'SCLK' - split.next().unwrap(); - - let min_clock = split - .next() - .unwrap() - .strip_suffix("Mhz") - .ok_or_else(|| { - GpuControllerError::ParseError("missing suffix".to_string()) - })? - .parse()?; - - let max_clock = split - .next() - .unwrap() - .strip_suffix("Mhz") - .ok_or_else(|| { - GpuControllerError::ParseError("missing suffix".to_string()) - })? - .parse()?; - - clocks_table.gpu_clocks_range = (min_clock, max_clock); - } - "MCLK:" => { - let mut split = line.split_whitespace(); - - // Skips the 'SCLK' - split.next().unwrap(); - - let min_clock = split - .next() - .unwrap() - .strip_suffix("Mhz") - .ok_or_else(|| { - GpuControllerError::ParseError("missing suffix".to_string()) - })? - .parse()?; - - let max_clock = split - .next() - .unwrap() - .strip_suffix("Mhz") - .ok_or_else(|| { - GpuControllerError::ParseError("missing suffix".to_string()) - })? - .parse()?; - - clocks_table.mem_clocks_range = (min_clock, max_clock); - } - _ => { - log::trace!("OD_RANGE ended"); - break; - } - } - } - } - _ => { - log::trace!("Skipping line"); - continue; - } - } - } - - Ok(clocks_table) - } - - /*pub fn set_gpu_power_state( - &mut self, - num: u32, - clockspeed: i64, - voltage: Option, - ) -> Result<(), GpuControllerError> { - let mut line = format!("s {} {}", num, clockspeed); - - if let Some(voltage) = voltage { - line.push_str(&format!(" {}", voltage)); - } - line.push_str("\n"); - - log::info!("Setting gpu power state {}", line); - log::info!("Writing {} to pp_od_clk_voltage", line); - - fs::write(self.hw_path.join("pp_od_clk_voltage"), line)?; - - self.config - .gpu_power_states - .insert(num, (clockspeed, voltage.unwrap())); - - Ok(()) - }*/ - - pub fn set_gpu_max_power_state( - &mut self, - clockspeed: i64, - voltage: Option, - ) -> Result<(), GpuControllerError> { - match self.get_clocks_table()? { - ClocksTable::Old(clocks_table) => { - let profile = { clocks_table.gpu_power_levels.iter().next_back().unwrap().0 }; - - let mut line = format!("s {} {}", profile, clockspeed); - - if let Some(voltage) = voltage { - line.push_str(&format!(" {}", voltage)); - } - line.push_str("\n"); - - log::info!("Writing {} to pp_od_clk_voltage", line); - - fs::write(self.hw_path.join("pp_od_clk_voltage"), line)?; - - self.config.gpu_max_clock = clockspeed; - self.config.gpu_max_voltage = voltage; - } - ClocksTable::New(_) => { - let s_line = format!("s 1 {}\n", clockspeed); - - fs::write(self.hw_path.join("pp_od_clk_voltage"), s_line)?; - - if let Some(voltage) = voltage { - let vc_line = format!("vc 2 {} {}\n", clockspeed, voltage); - - fs::write(self.hw_path.join("pp_od_clk_voltage"), vc_line)?; - } - } - } - - Ok(()) - } - - pub fn set_vram_max_clockspeed(&mut self, clockspeed: i64) -> Result<(), GpuControllerError> { - match self.get_clocks_table()? { - ClocksTable::Old(clocks_table) => { - let (profile, voltage) = { - let power_level = clocks_table.mem_power_levels.iter().next_back().unwrap(); - log::info!("Using mem power level {:?}", power_level); - (power_level.0, power_level.1 .1) - }; - - let line = format!("m {} {} {}\n", profile, clockspeed, voltage); - - log::info!("Writing {} to pp_od_clk_voltage", line); - - fs::write(self.hw_path.join("pp_od_clk_voltage"), line)?; - - self.config.vram_max_clock = clockspeed; - } - ClocksTable::New(_) => { - let s_line = format!("m 1 {}\n", clockspeed); - - fs::write(self.hw_path.join("pp_od_clk_voltage"), s_line)?; - } - } - - Ok(()) - } - - pub fn commit_gpu_power_states(&mut self) -> Result<(), GpuControllerError> { - fs::write(self.hw_path.join("pp_od_clk_voltage"), b"c\n")?; - Ok(()) - } - - pub fn reset_gpu_power_states(&mut self) -> Result<(), GpuControllerError> { - fs::write(self.hw_path.join("pp_od_clk_voltage"), b"r\n")?; - Ok(()) - } - - fn get_vulkan_info(pci_id: &str) -> VulkanInfo { - let mut device_name = String::from("Not supported"); - let mut api_version = String::new(); - let mut features = HashMap::new(); - - let pci_id = u32::from_str_radix(pci_id, 16).expect("Invalid device ID"); - - match Instance::new( - None, - vulkano::Version::V1_5, - &InstanceExtensions::none(), - None, - ) { - Ok(instance) => { - for physical in PhysicalDevice::enumerate(&instance) { - let properties = physical.properties(); - - if properties.device_id == pci_id { - api_version = physical.api_version().to_string(); - device_name = properties.device_name.clone(); - - let features_string = format!("{:?}", physical.supported_features()); - let features_string = features_string - .replace("Features", "") - .replace("{", "") - .replace("}", ""); - - for feature in features_string.split(',') { - // let (name, supported) = feature.split_once(':').unwrap(); Use this once it's in stable - let mut split = feature.split(':'); - let name = split.next().unwrap().trim(); - let supported = split.next().unwrap().trim(); - - let supported: bool = supported.parse().unwrap(); - - features.insert(name.to_string(), supported); - } - - break; - } - } - } - Err(_) => (), - } - - VulkanInfo { - device_name, - api_version, - features, - } - } - - fn parse_clock_voltage_line(line: &str) -> Result<(u32, i64, i64), GpuControllerError> { - log::trace!("Parsing line {}", line); - - let line = line.to_uppercase(); - let line_parts: Vec<&str> = line.split_whitespace().collect(); - - let num: u32 = line_parts - .get(0) - .ok_or_else(|| { - GpuControllerError::ParseError("failed to read the power level number".to_string()) - })? - .chars() - .nth(0) - .unwrap() - .to_digit(10) - .unwrap(); - let clock: i64 = line_parts - .get(1) - .ok_or_else(|| { - GpuControllerError::ParseError("failed to read the clockspeed".to_string()) - })? - .strip_suffix("MHZ") - .ok_or_else(|| GpuControllerError::ParseError("failed to strip \"MHZ\"".to_string()))? - .parse()?; - let voltage: i64 = line_parts - .get(2) - .ok_or_else(|| { - GpuControllerError::ParseError("failed to read the voltage".to_string()) - })? - .strip_suffix("MV") - .ok_or_else(|| GpuControllerError::ParseError("failed to strip \"mV\"".to_string()))? - .parse()?; - - Ok((num, clock, voltage)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn init() { - let _ = env_logger::builder().is_test(true).try_init(); - } - - // pp_od_clk_voltage taken from an RX 580 - #[test] - fn parse_clocks_table_polaris() { - init(); - - let pp_od_clk_voltage = r#" - OD_SCLK: - 0: 300MHz 750mV - 1: 600MHz 769mV - 2: 900MHz 912mV - 3: 1145MHz 1125mV - 4: 1215MHz 1150mV - 5: 1257MHz 1150mV - 6: 1300MHz 1150mV - 7: 1366MHz 1150mV - OD_MCLK: - 0: 300MHz 750mV - 1: 1000MHz 825mV - 2: 1750MHz 975mV - OD_RANGE: - SCLK: 300MHz 2000MHz - MCLK: 300MHz 2250MHz - VDDC: 750mV 1200mV"#; - - match GpuController::parse_clocks_table(pp_od_clk_voltage).unwrap() { - ClocksTable::Old(clocks_table) => { - log::trace!("{:?}", clocks_table); - - assert_eq!(clocks_table.gpu_clocks_range, (300, 2000)); - assert_eq!(clocks_table.mem_clocks_range, (300, 2250)); - assert_eq!(clocks_table.voltage_range, (750, 1200)); - - assert_eq!( - clocks_table.gpu_power_levels.get(&6).unwrap(), - &(1300, 1150) - ); - assert_eq!(clocks_table.mem_power_levels.get(&1).unwrap(), &(1000, 825)); - } - _ => panic!("Invalid clocks format detected"), - } - } - - // pp_od_clk_voltage taken from a Vega 56 - #[test] - fn parse_clocks_table_vega() { - init(); - - let pp_od_clk_voltage = r#" - OD_SCLK: - 0: 852Mhz 800mV - 1: 991Mhz 900mV - 2: 1138Mhz 950mV - 3: 1269Mhz 1000mV - 4: 1312Mhz 1050mV - 5: 1474Mhz 1100mV - 6: 1538Mhz 1150mV - 7: 1590Mhz 1157mV - OD_MCLK: - 0: 167Mhz 800mV - 1: 500Mhz 800mV - 2: 700Mhz 900mV - 3: 900Mhz 950mV - OD_RANGE: - SCLK: 852MHz 2400MHz - MCLK: 167MHz 1500MHz - VDDC: 800mV 1200mV"#; - - let clocks_table = GpuController::parse_clocks_table(pp_od_clk_voltage).unwrap(); - - log::trace!("{:?}", clocks_table); - } - - // pp_od_clk_voltage taken from an RX 5700 XT - #[test] - fn parse_clocks_table_navi() { - init(); - - let pp_od_clk_voltage = r#" - OD_SCLK: - 0: 800Mhz - 1: 2100Mhz - OD_MCLK: - 1: 875MHz - OD_VDDC_CURVE: - 0: 800MHz 711mV - 1: 1450MHz 801mV - 2: 2100MHz 1191mV - OD_RANGE: - SCLK: 800Mhz 2150Mhz - MCLK: 625Mhz 950Mhz - VDDC_CURVE_SCLK[0]: 800Mhz 2150Mhz - VDDC_CURVE_VOLT[0]: 750mV 1200mV - VDDC_CURVE_SCLK[1]: 800Mhz 2150Mhz - VDDC_CURVE_VOLT[1]: 750mV 1200mV - VDDC_CURVE_SCLK[2]: 800Mhz 2150Mhz - VDDC_CURVE_VOLT[2]: 750mV 1200mV - "#; - - match GpuController::parse_clocks_table(pp_od_clk_voltage).unwrap() { - ClocksTable::New(clocks_table) => { - log::trace!("{:?}", clocks_table); - - assert_eq!(clocks_table.gpu_clocks_range, (800, 2150)); - assert_eq!(clocks_table.mem_clocks_range, (625, 950)); - - assert_eq!(clocks_table.current_gpu_clocks, (800, 2100)); - assert_eq!(clocks_table.current_max_mem_clock, 875); - } - _ => panic!("Invalid clocks format detected"), - } - } -} diff --git a/daemon/src/hw_mon.rs b/daemon/src/hw_mon.rs deleted file mode 100644 index 2729a615..00000000 --- a/daemon/src/hw_mon.rs +++ /dev/null @@ -1,306 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, HashMap}; -use std::path::PathBuf; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, RwLock}; -use std::time::Duration; -use std::{fs, thread}; - -#[derive(Serialize, Deserialize, Debug, Clone, Copy)] -pub struct Temperature { - pub current: i64, - pub crit: i64, - pub crit_hyst: i64, -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum HWMonError { - PermissionDenied, - InvalidValue, - Unsupported, - NoHWMon, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct HWMon { - hwmon_path: PathBuf, - fan_control: Arc, - fan_curve: Arc>>, -} - -impl HWMon { - pub fn new( - hwmon_path: &PathBuf, - fan_control_enabled: bool, - fan_curve: BTreeMap, - power_cap: Option, - ) -> HWMon { - let mut mon = HWMon { - hwmon_path: hwmon_path.clone(), - fan_control: Arc::new(AtomicBool::new(false)), - fan_curve: Arc::new(RwLock::new(fan_curve)), - }; - - if fan_control_enabled { - mon.start_fan_control().unwrap(); - } - if let Some(cap) = power_cap { - #[allow(unused_must_use)] - { - mon.set_power_cap(cap); - } - } - - mon - } - - pub fn get_fan_max_speed(&self) -> Option { - match fs::read_to_string(self.hwmon_path.join("fan1_max")) { - Ok(speed) => Some(speed.trim().parse().unwrap()), - Err(_) => None, - } - } - - pub fn get_fan_speed(&self) -> Option { - /*if self.fan_control.load(Ordering::SeqCst) { - let pwm1 = fs::read_to_string(self.hwmon_path.join("pwm1")) - .expect("Couldn't read pwm1") - .trim() - .parse::() - .unwrap(); - - self.fan_max_speed / 255 * pwm1 - } - else { - fs::read_to_string(self.hwmon_path.join("fan1_input")) - .expect("Couldn't read fan speed") - .trim() - .parse::() - .unwrap() - }*/ - match fs::read_to_string(self.hwmon_path.join("fan1_input")) { - Ok(a) => Some(a.trim().parse::().unwrap()), - _ => None, - } - } - - pub fn get_mem_freq(&self) -> Option { - let filename = self.hwmon_path.join("freq2_input"); - - match fs::read_to_string(filename) { - Ok(freq) => Some(freq.trim().parse::().unwrap() / 1000 / 1000), - Err(_) => None, - } - } - - pub fn get_gpu_freq(&self) -> Option { - let filename = self.hwmon_path.join("freq1_input"); - - match fs::read_to_string(filename) { - Ok(freq) => Some(freq.trim().parse::().unwrap() / 1000 / 1000), - Err(_) => None, - } - } - - pub fn get_temps(&self) -> HashMap { - let mut temps = HashMap::new(); - - for i in 1..3 { - let label_filename = self.hwmon_path.join(format!("temp{}_label", i)); - - match fs::read_to_string(label_filename) { - Ok(label) => { - // If there's a label identifying the sensor, there should always be input and crit files too. But just in case using .unwrap_or_default() - let current = { - let filename = self.hwmon_path.join(format!("temp{}_input", i)); - fs::read_to_string(filename) - .unwrap_or_default() - .trim() - .parse::() - .unwrap_or_default() - / 1000 - }; - - let crit = { - let filename = self.hwmon_path.join(format!("temp{}_crit", i)); - fs::read_to_string(filename) - .unwrap_or_default() - .trim() - .parse::() - .unwrap_or_default() - / 1000 - }; - - let crit_hyst = { - let filename = self.hwmon_path.join(format!("temp{}_crit_hyst", i)); - fs::read_to_string(filename) - .unwrap_or_default() - .trim() - .parse::() - .unwrap_or_default() - / 1000 - }; - - temps.insert( - label.trim().to_string(), - Temperature { - current, - crit, - crit_hyst, - }, - ); - } - Err(_) => break, - } - } - - temps - } - - pub fn get_voltage(&self) -> Option { - let filename = self.hwmon_path.join("in0_input"); - - match fs::read_to_string(filename) { - Ok(voltage) => Some(voltage.trim().parse::().unwrap()), - Err(_) => None, - } - } - - pub fn get_power_cap_max(&self) -> Option { - let filename = self.hwmon_path.join("power1_cap_max"); - - match fs::read_to_string(filename) { - Ok(power_cap) => Some(power_cap.trim().parse::().unwrap() / 1000000), - _ => None, - } - } - - pub fn get_power_cap(&self) -> Option { - let filename = self.hwmon_path.join("power1_cap"); - - match fs::read_to_string(filename) { - Ok(a) => Some(a.trim().parse::().unwrap() / 1000000), - _ => None, - } - } - - pub fn set_power_cap(&mut self, cap: i64) -> Result<(), HWMonError> { - if cap - > self - .get_power_cap_max() - .ok_or_else(|| HWMonError::Unsupported)? - { - return Err(HWMonError::InvalidValue); - } - - let cap = cap * 1000000; - log::trace!("setting power cap to {}", cap); - - match fs::write(self.hwmon_path.join("power1_cap"), cap.to_string()) { - Ok(_) => Ok(()), - Err(_) => Err(HWMonError::PermissionDenied), - } - } - - pub fn get_power_avg(&self) -> Option { - let filename = self.hwmon_path.join("power1_average"); - - match fs::read_to_string(filename) { - Ok(a) => Some(a.trim().parse::().unwrap() / 1000000), - Err(_) => None, - } - } - - pub fn set_fan_curve(&self, curve: BTreeMap) { - log::trace!("trying to set curve"); - let mut current = self.fan_curve.write().unwrap(); - current.clear(); - - for (k, v) in curve.iter() { - current.insert(k.clone(), v.clone()); - } - log::trace!("set curve to {:?}", current); - } - - pub fn start_fan_control(&self) -> Result<(), HWMonError> { - if self.fan_control.load(Ordering::SeqCst) { - return Ok(()); - } - self.fan_control.store(true, Ordering::SeqCst); - - match fs::write(self.hwmon_path.join("pwm1_enable"), "1") { - Ok(_) => { - let s = self.clone(); - - thread::spawn(move || { - while s.fan_control.load(Ordering::SeqCst) { - let temps = s.get_temps(); - log::trace!("Temps: {:?}", temps); - - // Use junction temp when available, otherwise fall back to edge - let temps = match temps.get("junction") { - Some(temp) => temp, - None => temps.get("edge").unwrap(), - }; - - if temps.current >= temps.crit || temps.current <= temps.crit_hyst { - println!("CRITICAL TEMPERATURE DETECTED! FORCING MAX FAN SPEED"); - fs::write(s.hwmon_path.join("pwm1"), 255.to_string()) - .expect("Failed to set gpu temp in critical scenario (Warning: GPU Overheating!)"); - } - - log::trace!("Current gpu temp: {}", temps.current); - - let curve = s.fan_curve.read().unwrap(); - - for (t_low, s_low) in curve.iter() { - match curve.range(t_low..).nth(1) { - Some((t_high, s_high)) => { - if (t_low..t_high).contains(&&temps.current) { - let speed_ratio = (temps.current - t_low) as f64 - / (t_high - t_low) as f64; //The ratio of which speed to choose within the range of current lower and upper speeds - let speed_percent = - s_low + ((s_high - s_low) * speed_ratio); - let pwm = (255f64 * (speed_percent / 100f64)) as i64; - log::trace!("pwm: {}", pwm); - - fs::write(s.hwmon_path.join("pwm1"), pwm.to_string()) - .expect("Failed to write to pwm1"); - - log::trace!("In the range of {}..{}c {}..{}%, setting speed {}% ratio {}", t_low, t_high, s_low, s_high, speed_percent, speed_ratio); - break; - } - } - None => continue, - } - } - drop(curve); //needed to release rwlock so that the curve can be changed - - thread::sleep(Duration::from_millis(1000)); - } - }); - Ok(()) - } - Err(_) => Err(HWMonError::PermissionDenied), - } - } - - pub fn stop_fan_control(&self) -> Result<(), HWMonError> { - match fs::write(self.hwmon_path.join("pwm1_enable"), "2") { - Ok(_) => { - self.fan_control.store(false, Ordering::SeqCst); - log::trace!("Stopping fan control"); - Ok(()) - } - Err(_) => Err(HWMonError::PermissionDenied), - } - } - - pub fn get_fan_control(&self) -> (bool, BTreeMap) { - log::trace!("Fan control: {}", self.fan_control.load(Ordering::SeqCst)); - ( - self.fan_control.load(Ordering::SeqCst), - self.fan_curve.read().unwrap().clone(), - ) - } -} diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs deleted file mode 100644 index 13ed0d60..00000000 --- a/daemon/src/lib.rs +++ /dev/null @@ -1,498 +0,0 @@ -pub mod config; -pub mod daemon_connection; -pub mod gpu_controller; -pub mod hw_mon; - -use config::{Config, GpuConfig}; -use gpu_controller::PowerProfile; -use pciid_parser::Database; -use rand::prelude::*; -use serde::{Deserialize, Serialize}; -use std::path::PathBuf; -use std::{ - collections::{BTreeMap, HashMap}, - fs, -}; - -use crate::gpu_controller::GpuController; - -// Abstract socket allows anyone to connect without worrying about permissions -// https://unix.stackexchange.com/questions/579612/unix-domain-sockets-for-non-root-user -pub const SOCK_PATH: &str = "amdgpu-configurator.sock"; -pub const BUFFER_SIZE: usize = 16384; - -pub struct Daemon { - gpu_controllers: HashMap, - listener: std::os::unix::io::RawFd, - config: Config, -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum Action { - CheckAlive, - GetConfig, - SetConfig(Config), - GetGpus, - GetInfo(u32), - GetStats(u32), - StartFanControl(u32), - StopFanControl(u32), - GetFanControl(u32), - SetFanCurve(u32, BTreeMap), - SetPowerCap(u32, i64), - SetPowerProfile(u32, PowerProfile), - // SetGPUPowerState(u32, u32, i64, Option), - SetGPUMaxPowerState(u32, i64, Option), - SetVRAMMaxClock(u32, i64), - CommitGPUPowerStates(u32), - ResetGPUPowerStates(u32), - Shutdown, -} - -impl Daemon { - pub fn new(unprivileged: bool) -> Daemon { - let addr = nix::sys::socket::SockAddr::Unix( - nix::sys::socket::UnixAddr::new_abstract(SOCK_PATH.as_bytes()).unwrap(), - ); - let listener = nix::sys::socket::socket( - nix::sys::socket::AddressFamily::Unix, - nix::sys::socket::SockType::Stream, - nix::sys::socket::SockFlag::empty(), - None, - ) - .expect("Socket failed"); - nix::sys::socket::bind(listener, &addr).expect("Bind failed"); - nix::sys::socket::listen(listener, 128).expect("Listen failed"); - - let config_path = PathBuf::from("/etc/lact.json"); - let mut config = if unprivileged { - Config::new(&config_path) - } else { - match Config::read_from_file(&config_path) { - Ok(c) => { - log::info!("Loaded config from {}", c.config_path.to_string_lossy()); - c - } - Err(_) => { - log::info!("Config not found, creating"); - let c = Config::new(&config_path); - //c.save().unwrap(); - c - } - } - }; - - log::info!("Using config {:?}", config); - - let gpu_controllers = Self::load_gpu_controllers(&mut config); - - if !unprivileged { - config.save().unwrap(); - } - - Daemon { - listener, - gpu_controllers, - config, - } - } - - fn load_gpu_controllers(config: &mut Config) -> HashMap { - let pci_db = match config.allow_online_update { - Some(true) => match Database::get_online() { - Ok(db) => Some(db), - Err(e) => { - log::info!("Error updating PCI db: {:?}", e); - None - } - }, - Some(false) | None => None, - }; - - let mut gpu_controllers: HashMap = HashMap::new(); - - 'entries: for entry in - fs::read_dir("/sys/class/drm").expect("Could not open /sys/class/drm") - { - let entry = entry.unwrap(); - if entry.file_name().len() == 5 { - if entry.file_name().to_str().unwrap().split_at(4).0 == "card" { - log::info!("Initializing {:?}", entry.path()); - - let mut controller = - GpuController::new(entry.path().join("device"), GpuConfig::new(), &pci_db); - - let current_identifier = controller.get_identifier(); - - log::info!( - "Searching the config for GPU with identifier {:?}", - current_identifier - ); - - log::info!("{}", &config.gpu_configs.len()); - for (id, (gpu_identifier, gpu_config)) in &config.gpu_configs { - log::info!("Comparing with {:?}", gpu_identifier); - if current_identifier == *gpu_identifier { - controller.load_config(&gpu_config); - gpu_controllers.insert(id.clone(), controller); - log::info!("already known"); - continue 'entries; - } - - /*if gpu_info.pci_slot == gpu_identifier.pci_id - && gpu_info.vendor_data.card_model == gpu_identifier.card_model - && gpu_info.vendor_data.gpu_model == gpu_identifier.gpu_model - { - controller.load_config(&gpu_config); - gpu_controllers.insert(id.clone(), controller); - log::info!("already known"); - continue 'entries; - }*/ - } - - log::info!("initializing for the first time"); - - let id: u32 = random(); - - config - .gpu_configs - .insert(id, (controller.get_identifier(), controller.get_config())); - gpu_controllers.insert(id, controller); - } - } - } - - gpu_controllers - } - - pub fn listen(mut self) { - loop { - let stream = nix::sys::socket::accept(self.listener).expect("Accept failed"); - if stream < 0 { - log::error!("Error from accept"); - break; - } else { - Daemon::handle_connection(&mut self, stream); - } - } - } - - pub fn read_buffer(stream: i32) -> Vec { - log::trace!("Reading buffer"); - let mut buffer = Vec::::new(); - buffer.resize(BUFFER_SIZE, 0); - loop { - match nix::unistd::read(stream, &mut buffer) { - Ok(0) => { - break; - } - Ok(n) => { - assert!(n < buffer.len()); - if n < buffer.len() { - buffer.resize(n, 0); - } - break; - } - Err(e) => { - panic!("Error reading from socket: {}", e); - } - } - } - - buffer - } - - fn handle_connection(&mut self, stream: i32) { - let buffer = Self::read_buffer(stream); - - //log::trace!("finished reading, buffer size {}", buffer.len()); - log::trace!("Attempting to deserialize {:?}", &buffer); - //log::trace!("{:?}", action); - - match bincode::deserialize::(&buffer) { - Ok(action) => { - log::trace!("Executing action {:?}", action); - let response: Result = match action { - Action::CheckAlive => Ok(DaemonResponse::OK), - Action::GetGpus => { - let mut gpus: HashMap> = HashMap::new(); - for (id, controller) in &self.gpu_controllers { - gpus.insert(*id, controller.get_info().vendor_data.gpu_model.clone()); - } - Ok(DaemonResponse::Gpus(gpus)) - } - Action::GetStats(i) => match self.gpu_controllers.get(&i) { - Some(controller) => match controller.get_stats() { - Ok(stats) => Ok(DaemonResponse::GpuStats(stats)), - Err(_) => Err(DaemonError::HWMonError), - }, - None => Err(DaemonError::InvalidID), - }, - Action::GetInfo(i) => match self.gpu_controllers.get(&i) { - Some(controller) => { - Ok(DaemonResponse::GpuInfo(controller.get_info().clone())) - } - None => Err(DaemonError::InvalidID), - }, - Action::StartFanControl(i) => match self.gpu_controllers.get_mut(&i) { - Some(controller) => match controller.start_fan_control() { - Ok(_) => { - self.config.gpu_configs.insert( - i, - (controller.get_identifier(), controller.get_config()), - ); - self.config.save().unwrap(); - Ok(DaemonResponse::OK) - } - Err(_) => Err(DaemonError::HWMonError), - }, - None => Err(DaemonError::InvalidID), - }, - Action::StopFanControl(i) => match self.gpu_controllers.get_mut(&i) { - Some(controller) => match controller.stop_fan_control() { - Ok(_) => { - self.config.gpu_configs.insert( - i, - (controller.get_identifier(), controller.get_config()), - ); - self.config.save().unwrap(); - Ok(DaemonResponse::OK) - } - Err(_) => Err(DaemonError::HWMonError), - }, - None => Err(DaemonError::InvalidID), - }, - Action::GetFanControl(i) => match self.gpu_controllers.get(&i) { - Some(controller) => match controller.get_fan_control() { - Ok(info) => Ok(DaemonResponse::FanControlInfo(info)), - Err(_) => Err(DaemonError::HWMonError), - }, - None => Err(DaemonError::InvalidID), - }, - Action::SetFanCurve(i, curve) => match self.gpu_controllers.get_mut(&i) { - Some(controller) => match controller.set_fan_curve(curve) { - Ok(_) => { - self.config.gpu_configs.insert( - i, - (controller.get_identifier(), controller.get_config()), - ); - self.config.save().unwrap(); - Ok(DaemonResponse::OK) - } - Err(_) => Err(DaemonError::HWMonError), - }, - None => Err(DaemonError::InvalidID), - }, - Action::SetPowerCap(i, cap) => match self.gpu_controllers.get_mut(&i) { - Some(controller) => match controller.set_power_cap(cap) { - Ok(_) => { - self.config.gpu_configs.insert( - i, - (controller.get_identifier(), controller.get_config()), - ); - self.config.save().unwrap(); - Ok(DaemonResponse::OK) - } - Err(_) => Err(DaemonError::HWMonError), - }, - None => Err(DaemonError::InvalidID), - }, - Action::SetPowerProfile(i, profile) => match self.gpu_controllers.get_mut(&i) { - Some(controller) => match controller.set_power_profile(profile) { - Ok(_) => { - self.config.gpu_configs.insert( - i, - (controller.get_identifier(), controller.get_config()), - ); - self.config.save().unwrap(); - Ok(DaemonResponse::OK) - } - Err(_) => Err(DaemonError::ControllerError), - }, - None => Err(DaemonError::InvalidID), - }, - /*Action::SetGPUPowerState(i, num, clockspeed, voltage) => { - match self.gpu_controllers.get_mut(&i) { - Some(controller) => { - match controller.set_gpu_power_state(num, clockspeed, voltage) { - Ok(_) => { - self.config.gpu_configs.insert( - i, - (controller.get_identifier(), controller.get_config()), - ); - self.config.save().unwrap(); - Ok(DaemonResponse::OK) - } - Err(_) => Err(DaemonError::ControllerError), - } - } - None => Err(DaemonError::InvalidID), - } - }*/ - Action::SetGPUMaxPowerState(i, clockspeed, voltage) => { - match self.gpu_controllers.get_mut(&i) { - Some(controller) => { - match controller.set_gpu_max_power_state(clockspeed, voltage) { - Ok(()) => { - self.config.gpu_configs.insert( - i, - (controller.get_identifier(), controller.get_config()), - ); - self.config.save().unwrap(); - Ok(DaemonResponse::OK) - } - Err(_) => Err(DaemonError::ControllerError), - } - } - None => Err(DaemonError::InvalidID), - } - } - Action::SetVRAMMaxClock(i, clockspeed) => { - match self.gpu_controllers.get_mut(&i) { - Some(controller) => { - match controller.set_vram_max_clockspeed(clockspeed) { - Ok(()) => { - self.config.gpu_configs.insert( - i, - (controller.get_identifier(), controller.get_config()), - ); - self.config.save().unwrap(); - Ok(DaemonResponse::OK) - } - Err(_) => Err(DaemonError::ControllerError), - } - } - None => Err(DaemonError::InvalidID), - } - } - Action::CommitGPUPowerStates(i) => match self.gpu_controllers.get_mut(&i) { - Some(controller) => match controller.commit_gpu_power_states() { - Ok(_) => { - self.config.gpu_configs.insert( - i, - (controller.get_identifier(), controller.get_config()), - ); - self.config.save().unwrap(); - Ok(DaemonResponse::OK) - } - Err(_) => Err(DaemonError::ControllerError), - }, - None => Err(DaemonError::InvalidID), - }, - Action::ResetGPUPowerStates(i) => match self.gpu_controllers.get_mut(&i) { - Some(controller) => match controller.reset_gpu_power_states() { - Ok(_) => { - self.config.gpu_configs.insert( - i, - (controller.get_identifier(), controller.get_config()), - ); - self.config.save().unwrap(); - Ok(DaemonResponse::OK) - } - Err(_) => Err(DaemonError::ControllerError), - }, - None => Err(DaemonError::InvalidID), - }, - Action::Shutdown => { - for (id, controller) in &mut self.gpu_controllers { - #[allow(unused_must_use)] - { - controller.reset_gpu_power_states(); - controller.commit_gpu_power_states(); - controller.set_power_profile(PowerProfile::Auto); - - if self - .config - .gpu_configs - .get(id) - .unwrap() - .1 - .fan_control_enabled - { - controller.stop_fan_control(); - } - } - } - std::process::exit(0); - } - Action::SetConfig(config) => { - self.config = config; - self.gpu_controllers.clear(); - self.gpu_controllers = Self::load_gpu_controllers(&mut self.config); - self.config.save().expect("Failed to save config"); - Ok(DaemonResponse::OK) - } - Action::GetConfig => Ok(DaemonResponse::Config(self.config.clone())), - }; - - let buffer = bincode::serialize(&response).unwrap(); - - log::trace!("Responding, buffer length {}", buffer.len()); - nix::unistd::write(stream, &buffer).expect("Writing response to socket failed"); - - nix::sys::socket::shutdown(stream, nix::sys::socket::Shutdown::Both) - .expect("Failed to shut down"); - nix::unistd::close(stream).expect("Failed to close"); - - log::trace!("Finished responding"); - } - Err(_) => { - println!("Failed deserializing action"); - } - } - } -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum DaemonResponse { - OK, - GpuInfo(gpu_controller::GpuInfo), - GpuStats(gpu_controller::GpuStats), - Gpus(HashMap>), - PowerCap((i64, i64)), - FanControlInfo(gpu_controller::FanControlInfo), - Config(Config), -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum DaemonError { - ConnectionFailed, - InvalidID, - HWMonError, - ControllerError, -} - -#[cfg(test)] -mod tests { - use crate::gpu_controller::VendorData; - - use super::*; - - fn init() { - let _ = env_logger::builder().is_test(true).try_init(); - } - - #[test] - fn recognize_polaris() { - init(); - - let db = Database::get_online().unwrap(); - - let vendor_data: VendorData = db.get_device_info("1002", "67df", "1da2", "e387").into(); - - assert_eq!( - vendor_data.gpu_vendor, - Some("Advanced Micro Devices, Inc. [AMD/ATI]".to_string()) - ); - - assert_eq!( - vendor_data.gpu_model, - Some("Ellesmere [Radeon RX 470/480/570/570X/580/580X/590]".to_string()) - ); - - assert_eq!( - vendor_data.card_model, - Some("Radeon RX 580 Pulse 4GB".to_string()) - ); - } -} diff --git a/daemon/src/main.rs b/daemon/src/main.rs deleted file mode 100644 index ed17e1fe..00000000 --- a/daemon/src/main.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::thread; - -use daemon::{daemon_connection::DaemonConnection, Daemon}; -use signal_hook::consts::{SIGINT, SIGTERM}; -use signal_hook::iterator::Signals; - -fn main() { - env_logger::init(); - let d = Daemon::new(false); - let mut signals = Signals::new(&[SIGTERM, SIGINT]).unwrap(); - - thread::spawn(move || { - for _ in signals.forever() { - log::info!("Shutting down"); - let d = DaemonConnection::new().unwrap(); - d.shutdown(); - } - }); - - d.listen(); -} diff --git a/deb/DEBIAN/conffiles b/deb/DEBIAN/conffiles deleted file mode 100644 index e69de29b..00000000 diff --git a/deb/DEBIAN/control b/deb/DEBIAN/control deleted file mode 100644 index 21ea5bc0..00000000 --- a/deb/DEBIAN/control +++ /dev/null @@ -1,9 +0,0 @@ -Package: lact -Version: 0.1 -Architecture: all -Essential: no -Section: admin -Priority: optional -Depends: libgtk-3-0 -Maintainer: Ilya Zlobintsev -Description: AMDGPU Control Utility diff --git a/deb/usr/lib/systemd/system/lactd.service b/deb/usr/lib/systemd/system/lactd.service deleted file mode 100755 index ec16ce1d..00000000 --- a/deb/usr/lib/systemd/system/lactd.service +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=AMDGPU Control Daemon -After=multi-user.target - -[Service] -ExecStart=/usr/bin/lact-daemon - -[Install] -WantedBy=graphical.target diff --git a/deb/usr/share/applications/lact.desktop b/deb/usr/share/applications/lact.desktop deleted file mode 100644 index 532759fa..00000000 --- a/deb/usr/share/applications/lact.desktop +++ /dev/null @@ -1,5 +0,0 @@ -[Desktop Entry] -Type=Application -Name=LACT -Description=AMDGPU Control Application -Exec=lact-gui \ No newline at end of file diff --git a/gui/Cargo.toml b/gui/Cargo.toml deleted file mode 100644 index 5116b30d..00000000 --- a/gui/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "gui" -version = "0.1.0" -authors = ["Ilya Zlobintsev "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -daemon = { path = "../daemon" } - -gtk = { version = "0.14", features = ["v3_22"] } -pango = "0.14" -glib = "0.14" - -log = "0.4" -env_logger = "0.9" \ No newline at end of file diff --git a/gui/src/main.rs b/gui/src/main.rs deleted file mode 100644 index 359092a6..00000000 --- a/gui/src/main.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::thread; - -use app::App; -use daemon::{daemon_connection::DaemonConnection, Daemon}; -use gtk::prelude::*; -use gtk::*; - -mod app; - -fn main() { - env_logger::init(); - if gtk::init().is_err() { - panic!("Cannot initialize GTK"); - } - - let connection = connect_daemon(); - - ask_for_online_update(&connection); - - let app = App::new(connection); - - app.run().unwrap(); -} - -fn ask_for_online_update(connection: &DaemonConnection) { - let mut config = connection.get_config().unwrap(); - - if let None = config.allow_online_update { - log::trace!("Online access permission not configured! Showing the dialog"); - - let diag = MessageDialog::new( - None::<&Window>, - DialogFlags::empty(), - MessageType::Warning, - ButtonsType::YesNo, - "Do you wish to use the online database for GPU identification?", - ); - match diag.run() { - ResponseType::Yes => config.allow_online_update = Some(true), - ResponseType::No => config.allow_online_update = Some(false), - _ => unreachable!(), - } - diag.hide(); - - connection.set_config(config).unwrap(); - } -} - -fn connect_daemon() -> DaemonConnection { - match DaemonConnection::new() { - Ok(connection) => { - println!("Connection to daemon established"); - connection - } - Err(e) => { - println!("Error {:?} connecting to daemon", e); - println!("Starting unprivileged daemon instance"); - - thread::spawn(move || { - let daemon = Daemon::new(true); - daemon.listen(); - }); - - let dialog = MessageDialog::new( - None::<>k::Window>, - DialogFlags::empty(), - gtk::MessageType::Warning, - gtk::ButtonsType::Ok, - "Unable to connect to daemon. Running in unprivileged mode.", - ); - - dialog.run(); - dialog.close(); - - DaemonConnection::new().unwrap() - } - } -} diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml new file mode 100644 index 00000000..1bdce5af --- /dev/null +++ b/lact-daemon/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "lact-daemon" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs", branch = "blocking", features = [ + "serde", +] } +anyhow = "1.0.66" +bincode = "1.3.3" +nix = "0.25.0" +pciid-parser = { version = "0.6.2", features = ["serde"] } +serde = { version = "1.0.147", features = ["derive"] } +serde_json = "1.0.87" +serde_yaml = "0.9.14" +tokio = { version = "1.21.2", features = [ + "rt", + "macros", + "net", + "io-util", + "time", + "signal", + "sync", +] } +tracing = "0.1.37" +tracing-subscriber = "0.3.16" +vulkano = "0.32.1" +lact-schema = { path = "../lact-schema" } diff --git a/lact-daemon/src/config.rs b/lact-daemon/src/config.rs new file mode 100644 index 00000000..15894bb3 --- /dev/null +++ b/lact-daemon/src/config.rs @@ -0,0 +1,115 @@ +use crate::server::gpu_controller::fan_control::FanCurve; +use anyhow::Context; +use nix::unistd::getuid; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, env, fs, path::PathBuf}; +use tracing::debug; + +const FILE_NAME: &str = "config.yaml"; + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)] +pub struct Config { + pub daemon: DaemonConfig, + pub gpus: HashMap, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct DaemonConfig { + pub log_level: String, +} + +impl Default for DaemonConfig { + fn default() -> Self { + Self { + log_level: "info".to_owned(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)] +pub struct GpuConfig { + pub fan_control_enabled: bool, + pub fan_control_settings: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct FanControlSettings { + pub temperature_key: String, + pub interval_ms: u64, + pub curve: FanCurve, +} + +impl Config { + pub fn load() -> anyhow::Result> { + let path = get_path(); + if path.exists() { + let raw_config = fs::read_to_string(path).context("Could not open config file")?; + let config = + serde_yaml::from_str(&raw_config).context("Could not deserialize config")?; + Ok(Some(config)) + } else { + let parent = path.parent().unwrap(); + fs::create_dir_all(parent)?; + Ok(None) + } + } + + pub fn save(&self) -> anyhow::Result<()> { + let path = get_path(); + debug!("Saving config to {path:?}"); + let raw_config = serde_yaml::to_string(self)?; + fs::write(path, raw_config).context("Could not write config") + } + + pub fn load_or_create() -> anyhow::Result { + match Config::load()? { + Some(config) => Ok(config), + None => { + let config = Config::default(); + config.save()?; + Ok(config) + } + } + } +} + +fn get_path() -> PathBuf { + let uid = getuid(); + if uid.is_root() { + PathBuf::from("/etc/lact").join(FILE_NAME) + } else { + let config_dir = PathBuf::from(env::var("XDG_CONFIG_HOME").unwrap_or_else(|_| { + let home = env::var("HOME").expect("$HOME variable is not set"); + format!("{home}/.config") + })); + config_dir.join("lact").join(FILE_NAME) + } +} + +#[cfg(test)] +mod tests { + use super::{Config, DaemonConfig, FanControlSettings, GpuConfig}; + use crate::server::gpu_controller::fan_control::FanCurve; + + #[test] + fn serde_de_full() { + let config = Config { + daemon: DaemonConfig::default(), + gpus: [( + "my-gpu-id".to_owned(), + GpuConfig { + fan_control_enabled: true, + fan_control_settings: Some(FanControlSettings { + curve: FanCurve::default(), + temperature_key: "edge".to_owned(), + interval_ms: 500, + }), + }, + )] + .into(), + }; + let data = serde_yaml::to_string(&config).unwrap(); + let deserialized_config: Config = serde_yaml::from_str(&data).unwrap(); + assert_eq!(config, deserialized_config); + } +} diff --git a/lact-daemon/src/fork.rs b/lact-daemon/src/fork.rs new file mode 100644 index 00000000..b549b114 --- /dev/null +++ b/lact-daemon/src/fork.rs @@ -0,0 +1,103 @@ +use anyhow::{anyhow, Context}; +use nix::{ + sys::wait::waitpid, + unistd::{fork, ForkResult}, +}; +use serde::{de::DeserializeOwned, Serialize}; +use std::{ + fmt::Debug, + io::{BufReader, Read, Write}, + mem::size_of, + os::unix::net::UnixStream, +}; +use tracing::trace; + +pub unsafe fn run_forked(f: F) -> anyhow::Result +where + T: Serialize + DeserializeOwned + Debug, + F: FnOnce() -> Result, +{ + let (rx, mut tx) = UnixStream::pair()?; + let mut rx = BufReader::new(rx); + + match fork()? { + ForkResult::Parent { child } => { + trace!("Waiting for message from child"); + + let mut size_buf = [0u8; size_of::()]; + rx.read_exact(&mut size_buf)?; + let size = usize::from_ne_bytes(size_buf); + + let mut data_buf = vec![0u8; size]; + rx.read_exact(&mut data_buf)?; + + trace!("Received {} data bytes from child", data_buf.len()); + + waitpid(child, None)?; + + let data: Result = bincode::deserialize(&data_buf) + .context("Could not deserialize response from child")?; + + data.map_err(|err| anyhow!("{err}")) + } + ForkResult::Child => { + let response = f(); + trace!("Sending response to parent: {response:?}"); + + let send_result = (|| { + let data = bincode::serialize(&response)?; + tx.write_all(&data.len().to_ne_bytes())?; + tx.write_all(&data)?; + Ok::<_, anyhow::Error>(()) + })(); + + let exit_code = match send_result { + Ok(()) => 0, + Err(_) => 1, + }; + trace!("Exiting child with code {exit_code}"); + std::process::exit(exit_code); + } + } +} + +#[cfg(test)] +mod tests { + use super::run_forked; + + #[test] + fn basic() { + let response = unsafe { run_forked(|| Ok(String::from("hello"))).unwrap() }; + assert_eq!(response, "hello"); + } + + #[test] + fn error() { + let response = + unsafe { run_forked::<(), _>(|| Err("something went wrong".to_owned())) }.unwrap_err(); + assert_eq!(response.to_string(), "something went wrong"); + } + + #[test] + fn vec() { + let response = unsafe { + run_forked(|| { + let mut data = Vec::new(); + data.push("hello".to_owned()); + data.push("world".to_owned()); + data.push("123".to_owned()); + Ok(data) + }) + .unwrap() + }; + assert_eq!(response, vec!["hello", "world", "123"]) + } + + #[test] + fn pci_db() { + let db = unsafe { + run_forked(|| pciid_parser::Database::read().map_err(|err| err.to_string())).unwrap() + }; + assert_ne!(db.classes.len(), 0); + } +} diff --git a/lact-daemon/src/main.rs b/lact-daemon/src/main.rs new file mode 100644 index 00000000..d0ce23cf --- /dev/null +++ b/lact-daemon/src/main.rs @@ -0,0 +1,37 @@ +mod config; +mod fork; +mod server; +mod socket; + +use anyhow::Context; +use config::Config; +use server::Server; +use std::str::FromStr; +use tokio::signal::ctrl_c; +use tracing::{warn, Level}; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> anyhow::Result<()> { + let config = Config::load_or_create()?; + + let max_level = Level::from_str(&config.daemon.log_level).context("Invalid log level")?; + tracing_subscriber::fmt().with_max_level(max_level).init(); + + let server = Server::new(config).await?; + let handler = server.handler.clone(); + + tokio::spawn(async move { + ctrl_c().await.expect("Could not listen to shutdown signal"); + + for controller in handler.gpu_controllers.values() { + if let Err(err) = controller.stop_fan_control().await { + warn!("Could not stop fan control on shutdown: {err}"); + } + } + + socket::cleanup(); + std::process::exit(0); + }); + + Ok(server.run().await) +} diff --git a/lact-daemon/src/server/gpu_controller/fan_control.rs b/lact-daemon/src/server/gpu_controller/fan_control.rs new file mode 100644 index 00000000..75a8071c --- /dev/null +++ b/lact-daemon/src/server/gpu_controller/fan_control.rs @@ -0,0 +1,134 @@ +use amdgpu_sysfs::hw_mon::Temperature; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use tracing::error; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct FanCurve(BTreeMap); + +impl FanCurve { + pub fn rpm_at_temp(&self, temp: Temperature, min_rpm: u32, max_rpm: u32) -> u32 { + let current = temp.current.expect("No current temp"); + + // This scenario is most likely unreachable as the kernel shuts down the GPU when it reaches critical temperature + if temp.crit.filter(|crit| current > *crit).is_some() + || temp.crit_hyst.filter(|hyst| current < *hyst).is_some() + { + error!("GPU temperature is beyond critical values! {current}°C"); + return max_rpm; + } + + let current = current as i32; + let maybe_lower = self.0.range(..current).next_back(); + let maybe_higher = self.0.range(current..).next(); + + let percentage = match (maybe_lower, maybe_higher) { + (Some((lower_temp, lower_speed)), Some((higher_temp, higher_speed))) => { + let speed_ratio = (current - lower_temp) as f32 / (higher_temp - lower_temp) as f32; + lower_speed + (higher_speed - lower_speed) * speed_ratio + } + (Some((_, lower_speed)), None) => *lower_speed, + (None, Some((_, higher_speed))) => *higher_speed, + (None, None) => panic!("Could not find fan speed on the curve! This is a bug."), + }; + + ((max_rpm - min_rpm) as f32 * percentage) as u32 + } +} + +impl Default for FanCurve { + fn default() -> Self { + Self( + [ + (30, 0.0), + (40, 0.2), + (50, 0.35), + (60, 0.5), + (70, 0.75), + (80, 1.0), + ] + .into(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::FanCurve; + use amdgpu_sysfs::hw_mon::Temperature; + + fn simple_rpm(temp: f32, min_rpm: u32, max_rpm: u32) -> u32 { + let curve = FanCurve([(0, 0.0), (100, 1.0)].into()); + let temp = Temperature { + current: Some(temp), + crit: Some(150.0), + crit_hyst: Some(-100.0), + }; + curve.rpm_at_temp(temp, min_rpm, max_rpm) + } + + #[test] + fn simple_curve_middle() { + let rpm = simple_rpm(45.0, 0, 200); + assert_eq!(rpm, 90); + } + + #[test] + fn simple_curve_start() { + let rpm = simple_rpm(0.0, 0, 200); + assert_eq!(rpm, 0); + } + + #[test] + fn simple_curve_end() { + let rpm = simple_rpm(100.0, 0, 200); + assert_eq!(rpm, 200); + } + + #[test] + fn simple_curve_before() { + let rpm = simple_rpm(-5.0, 0, 200); + assert_eq!(rpm, 0); + } + + #[test] + fn simple_curve_after() { + let rpm = simple_rpm(105.0, 0, 200); + assert_eq!(rpm, 200); + } + + #[test] + fn curve_crit() { + let curve = FanCurve([(20, 0.0), (80, 100.0)].into()); + let temp = Temperature { + current: Some(100.0), + crit: Some(90.0), + crit_hyst: Some(0.0), + }; + let rpm = curve.rpm_at_temp(temp, 0, 200); + assert_eq!(rpm, 200); + } + + #[test] + fn default_curve() { + let curve = FanCurve::default(); + let rpm_at_temp = |current: f32| { + let temp = Temperature { + current: Some(current), + crit: Some(90.0), + crit_hyst: Some(0.0), + }; + curve.rpm_at_temp(temp, 0, 1000) + }; + assert_eq!(rpm_at_temp(20.0), 0); + assert_eq!(rpm_at_temp(30.0), 0); + assert_eq!(rpm_at_temp(33.0), 60); + assert_eq!(rpm_at_temp(60.0), 500); + assert_eq!(rpm_at_temp(65.0), 625); + assert_eq!(rpm_at_temp(70.0), 750); + assert_eq!(rpm_at_temp(79.0), 975); + assert_eq!(rpm_at_temp(85.0), 1000); + assert_eq!(rpm_at_temp(100.0), 1000); + assert_eq!(rpm_at_temp(-5.0), 1000); + } +} diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs new file mode 100644 index 00000000..ad2ed492 --- /dev/null +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -0,0 +1,236 @@ +pub mod fan_control; + +use self::fan_control::FanCurve; +use super::vulkan::get_vulkan_info; +use crate::fork::run_forked; +use amdgpu_sysfs::{ + gpu_handle::GpuHandle, + hw_mon::{FanControlMethod, HwMon}, +}; +use anyhow::{anyhow, Context}; +use lact_schema::{DeviceInfo, DeviceStats, GpuPciInfo, LinkInfo, PciInfo}; +use pciid_parser::Database; +use std::{ + borrow::Cow, + path::PathBuf, + sync::{Arc, Mutex}, + time::Duration, +}; +use tokio::{select, sync::Notify, task::JoinHandle, time::sleep}; +use tracing::{debug, error, info, warn}; + +type FanControlHandle = (Arc, JoinHandle<()>); + +pub struct GpuController { + pub handle: GpuHandle, + pub pci_info: Option, + pub fan_control_handle: Arc>>, +} + +impl GpuController { + pub fn new_from_path(sysfs_path: PathBuf) -> anyhow::Result { + let handle = GpuHandle::new_from_path(sysfs_path) + .map_err(|error| anyhow!("failed to initialize gpu handle: {error}"))?; + + let mut device_pci_info = None; + let mut subsystem_pci_info = None; + + if let Some((vendor_id, model_id)) = handle.get_pci_id() { + device_pci_info = Some(PciInfo { + vendor_id: vendor_id.to_owned(), + vendor: None, + model_id: model_id.to_owned(), + model: None, + }); + + if let Some((subsys_vendor_id, subsys_model_id)) = handle.get_pci_subsys_id() { + let (new_device_info, new_subsystem_info) = unsafe { + run_forked(|| { + let pci_db = Database::read().map_err(|err| err.to_string())?; + let pci_device_info = pci_db.get_device_info( + vendor_id, + model_id, + subsys_vendor_id, + subsys_model_id, + ); + + let device_pci_info = PciInfo { + vendor_id: vendor_id.to_owned(), + vendor: pci_device_info.vendor_name.map(str::to_owned), + model_id: model_id.to_owned(), + model: pci_device_info.device_name.map(str::to_owned), + }; + let subsystem_pci_info = PciInfo { + vendor_id: subsys_vendor_id.to_owned(), + vendor: pci_device_info.subvendor_name.map(str::to_owned), + model_id: subsys_model_id.to_owned(), + model: pci_device_info.subdevice_name.map(str::to_owned), + }; + Ok((device_pci_info, subsystem_pci_info)) + })? + }; + device_pci_info = Some(new_device_info); + subsystem_pci_info = Some(new_subsystem_info); + } + } + + let pci_info = device_pci_info.and_then(|device_pci_info| { + Some(GpuPciInfo { + device_pci_info, + subsystem_pci_info: subsystem_pci_info?, + }) + }); + + Ok(Self { + handle, + pci_info, + fan_control_handle: Arc::new(Mutex::new(None)), + }) + } + + pub fn get_info(&self) -> DeviceInfo { + let vulkan_info = self.pci_info.as_ref().and_then(|pci_info| { + match get_vulkan_info( + &pci_info.device_pci_info.vendor_id, + &pci_info.device_pci_info.model_id, + ) { + Ok(info) => Some(info), + Err(err) => { + warn!("Could not load vulkan info: {err}"); + None + } + } + }); + let pci_info = self.pci_info.as_ref().map(Cow::Borrowed); + let driver = self.handle.get_driver(); + let vbios_version = self.handle.get_vbios_version(); + let link_info = self.get_link_info(); + + DeviceInfo { + pci_info, + vulkan_info, + driver, + vbios_version, + link_info, + } + } + + fn get_link_info(&self) -> LinkInfo { + LinkInfo { + current_width: self.handle.get_current_link_width(), + current_speed: self.handle.get_current_link_speed(), + max_width: self.handle.get_max_link_width(), + max_speed: self.handle.get_max_link_speed(), + } + } + + pub fn get_stats(&self) -> anyhow::Result { + Ok(DeviceStats { + fan_speed_current: self.hw_mon_and_then(HwMon::get_fan_current), + fan_speed_max: self.hw_mon_and_then(HwMon::get_fan_max), + fan_speed_min: self.hw_mon_and_then(HwMon::get_fan_min), + fan_control_enabled: self + .fan_control_handle + .lock() + .map_err(|err| anyhow!("Could not lock fan control mutex: {err}"))? + .is_some(), + temps: self.hw_mon_map(HwMon::get_temps).unwrap_or_default(), + total_vram: self.handle.get_total_vram(), + used_vram: self.handle.get_used_vram(), + busy_percent: self.handle.get_busy_percent(), + performance_level: self.handle.get_power_force_performance_level(), + }) + } + + fn hw_mon_and_then(&self, f: fn(&HwMon) -> Option) -> Option { + self.handle.hw_monitors.first().and_then(f) + } + + fn hw_mon_map(&self, f: fn(&HwMon) -> U) -> Option { + self.handle.hw_monitors.first().map(f) + } + + pub fn start_fan_control( + &self, + curve: FanCurve, + temp_key: String, + interval: Duration, + ) -> anyhow::Result<()> { + let hw_mon = self + .handle + .hw_monitors + .first() + .cloned() + .context("This GPU has no monitor")?; + hw_mon + .set_fan_control_method(FanControlMethod::Manual) + .context("Could not set fan control method")?; + + let max_rpm = hw_mon.get_fan_max().context("Could not get min RPM")?; + let min_rpm = hw_mon.get_fan_min().context("Could not get max RPM")?; + + let mut notify_guard = self + .fan_control_handle + .lock() + .map_err(|err| anyhow!("Lock error: {err}"))?; + + if notify_guard.is_some() { + return Ok(()); + } + + let notify = Arc::new(Notify::new()); + let task_notify = notify.clone(); + + let notify_handle = self.fan_control_handle.clone(); + let handle = tokio::spawn(async move { + loop { + debug!("Fan control tick"); + let mut temps = hw_mon.get_temps(); + let temp = temps + .remove(&temp_key) + .expect("Could not get temperature by given key"); + let target_rpm = curve.rpm_at_temp(temp, min_rpm, max_rpm); + + if let Err(err) = hw_mon.set_fan_target(target_rpm) { + error!("Could not set fan speed: {err}, disabling fan control"); + break; + } + + select! { + _ = sleep(interval) => (), + _ = task_notify.notified() => break, + } + } + info!("Shutting down fan control"); + if let Err(err) = hw_mon.set_fan_control_method(FanControlMethod::Auto) { + error!("Could not set fan control back to automatic: {err}"); + } + notify_handle + .lock() + .expect("Fan control mutex error") + .take(); + }); + + *notify_guard = Some((notify, handle)); + + info!( + "Started fan control with interval {}ms", + interval.as_millis() + ); + + Ok(()) + } + + pub async fn stop_fan_control(&self) -> anyhow::Result<()> { + let maybe_notify = self + .fan_control_handle + .lock() + .map_err(|err| anyhow!("Lock error: {err}"))? + .take(); + if let Some((notify, handle)) = maybe_notify { + notify.notify_one(); + handle.await?; + } + Ok(()) + } +} diff --git a/lact-daemon/src/server/handler.rs b/lact-daemon/src/server/handler.rs new file mode 100644 index 00000000..21bb9ee9 --- /dev/null +++ b/lact-daemon/src/server/handler.rs @@ -0,0 +1,153 @@ +use super::gpu_controller::{fan_control::FanCurve, GpuController}; +use crate::config::{Config, FanControlSettings}; +use amdgpu_sysfs::sysfs::SysFS; +use anyhow::{anyhow, Context}; +use lact_schema::{DeviceInfo, DeviceListEntry, DeviceStats}; +use std::{ + collections::HashMap, + path::PathBuf, + sync::{Arc, RwLock}, + time::Duration, +}; +use tracing::{debug, info, trace, warn}; + +#[derive(Clone)] +pub struct Handler { + pub config: Arc>, + pub gpu_controllers: Arc>, +} + +impl<'a> Handler { + pub async fn new(config: Config) -> anyhow::Result { + let mut controllers = HashMap::new(); + + let base_path = PathBuf::from("/sys/class/drm"); + + for entry in base_path + .read_dir() + .map_err(|error| anyhow!("Failed to read sysfs: {error}"))? + { + let entry = entry?; + + let name = entry + .file_name() + .into_string() + .map_err(|_| anyhow!("non-utf path"))?; + if name.starts_with("card") && !name.contains('-') { + trace!("trying gpu controller at {:?}", entry.path()); + let device_path = entry.path().join("device"); + match GpuController::new_from_path(device_path) { + Ok(controller) => { + let handle = &controller.handle; + let pci_id = handle.get_pci_id().context("Device has no vendor id")?; + let pci_subsys_id = handle + .get_pci_subsys_id() + .context("Device has no subsys id")?; + let pci_slot_name = handle + .get_pci_slot_name() + .context("Device has no pci slot")?; + + let id = format!( + "{}:{}-{}:{}-{}", + pci_id.0, pci_id.1, pci_subsys_id.0, pci_subsys_id.1, pci_slot_name + ); + + debug!( + "initialized GPU controller {} for path {:?}", + id, + handle.get_path() + ); + + controllers.insert(id, controller); + } + Err(error) => { + warn!( + "failed to initialize controller at {:?}, {error}", + entry.path() + ); + } + } + } + } + + for (id, gpu_config) in &config.gpus { + if let Some(controller) = controllers.get(id) { + if gpu_config.fan_control_enabled { + let settings = gpu_config.fan_control_settings.as_ref().context( + "Fan control is enabled but no settings are defined (invalid config?)", + )?; + let interval = Duration::from_millis(settings.interval_ms); + controller.start_fan_control( + settings.curve.clone(), + settings.temperature_key.clone(), + interval, + )?; + } + } else { + info!("Could not find GPU with id {id} defined in configuration"); + } + } + + Ok(Self { + gpu_controllers: Arc::new(controllers), + config: Arc::new(RwLock::new(config)), + }) + } + + fn controller_by_id(&self, id: &str) -> anyhow::Result<&GpuController> { + Ok(self + .gpu_controllers + .get(id) + .as_ref() + .context("No controller with such id")?) + } + + pub fn list_devices(&'a self) -> Vec> { + self.gpu_controllers + .iter() + .map(|(id, controller)| { + let name = controller + .pci_info + .as_ref() + .and_then(|pci_info| pci_info.device_pci_info.model.as_deref()); + DeviceListEntry { id, name } + }) + .collect() + } + + pub fn get_device_info(&'a self, id: &str) -> anyhow::Result> { + Ok(self.controller_by_id(id)?.get_info()) + } + + pub fn get_gpu_stats(&'a self, id: &str) -> anyhow::Result { + self.controller_by_id(id)?.get_stats() + } + + pub async fn set_fan_control(&'a self, id: &str, enabled: bool) -> anyhow::Result<()> { + if enabled { + let mut config_guard = self.config.write().map_err(|err| anyhow!("{err}"))?; + let gpu_config = config_guard.gpus.entry(id.to_owned()).or_default(); + let settings = + gpu_config + .fan_control_settings + .get_or_insert_with(|| FanControlSettings { + curve: FanCurve::default(), + temperature_key: "edge".to_owned(), + interval_ms: 500, + }); + let interval = Duration::from_millis(settings.interval_ms); + + self.controller_by_id(id)?.start_fan_control( + settings.curve.clone(), + settings.temperature_key.clone(), + interval, + )?; + gpu_config.fan_control_enabled = true; + config_guard.save().context("Could not save config")?; + + Ok(()) + } else { + self.controller_by_id(id)?.stop_fan_control().await + } + } +} diff --git a/lact-daemon/src/server/mod.rs b/lact-daemon/src/server/mod.rs new file mode 100644 index 00000000..14539267 --- /dev/null +++ b/lact-daemon/src/server/mod.rs @@ -0,0 +1,94 @@ +pub mod gpu_controller; +mod handler; +// mod pci; +mod vulkan; + +use self::handler::Handler; +use crate::{config::Config, socket}; +use lact_schema::{ + request::Request, + response::{Pong, Response}, +}; +use serde::Serialize; +use tokio::{ + io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, + net::{UnixListener, UnixStream}, +}; +use tracing::{debug, error, instrument}; + +pub struct Server { + pub handler: Handler, + listener: UnixListener, +} + +impl Server { + pub async fn new(config: Config) -> anyhow::Result { + let handler = Handler::new(config).await?; + let listener = socket::listen().await?; + + Ok(Self { listener, handler }) + } + + pub async fn run(self) { + loop { + match self.listener.accept().await { + Ok((stream, _)) => { + let handler = self.handler.clone(); + tokio::spawn(async move { + if let Err(error) = handle_stream(stream, handler).await { + error!("{error}") + } + }); + } + Err(error) => { + error!("failed to handle connection: {error}"); + } + } + } + } +} + +#[instrument(level = "debug", skip(stream, handler))] +async fn handle_stream(stream: UnixStream, handler: Handler) -> anyhow::Result<()> { + let mut stream = BufReader::new(stream); + + let mut buf = String::new(); + while stream.read_line(&mut buf).await? != 0 { + debug!("Handling request: {}", buf.trim_end()); + + let maybe_request = serde_json::from_str(&buf); + let response = match maybe_request { + Ok(request) => match handle_request(request, &handler).await { + Ok(response) => response, + Err(error) => serde_json::to_vec(&Response::<()>::Error(format!("{error:#}")))?, + }, + Err(error) => serde_json::to_vec(&Response::<()>::Error(format!( + "Failed to deserialize request: {error}" + )))?, + }; + + stream.write_all(&response).await?; + stream.write_all(b"\n").await?; + + buf.clear(); + } + + Ok(()) +} + +#[instrument(level = "debug", skip(handler))] +async fn handle_request<'a>(request: Request<'a>, handler: &'a Handler) -> anyhow::Result> { + match request { + Request::Ping => ok_response(Pong), + Request::ListDevices => ok_response(handler.list_devices()), + Request::DeviceInfo { id } => ok_response(handler.get_device_info(id)?), + Request::DeviceStats { id } => ok_response(handler.get_gpu_stats(id)?), + Request::SetFanControl { id, enabled } => { + ok_response(handler.set_fan_control(id, enabled).await?) + } + } +} + +fn ok_response(data: T) -> anyhow::Result> { + Ok(serde_json::to_vec(&Response::Ok(data))?) +} diff --git a/lact-daemon/src/server/vulkan.rs b/lact-daemon/src/server/vulkan.rs new file mode 100644 index 00000000..28288d5e --- /dev/null +++ b/lact-daemon/src/server/vulkan.rs @@ -0,0 +1,78 @@ +use crate::fork::run_forked; +use lact_schema::VulkanInfo; +use vulkano::{ + instance::{Instance, InstanceCreateInfo}, + VulkanLibrary, +}; + +pub fn get_vulkan_info(vendor_id: &str, device_id: &str) -> anyhow::Result { + let vendor_id = u32::from_str_radix(vendor_id, 16)?; + let device_id = u32::from_str_radix(device_id, 16)?; + + unsafe { + run_forked(|| { + let library = VulkanLibrary::new().map_err(|err| err.to_string())?; + let instance = Instance::new(library, InstanceCreateInfo::default()) + .map_err(|err| err.to_string())?; + let devices = instance + .enumerate_physical_devices() + .map_err(|err| err.to_string())?; + + for device in devices { + let properties = device.properties(); + // Not sure how this works with systems that have multiple identical GPUs + if properties.vendor_id == vendor_id && properties.device_id == device_id { + let info = VulkanInfo { + device_name: properties.device_name.clone(), + api_version: device.api_version().to_string(), + driver_name: properties.driver_name.clone(), + supported_features: vulkano_struct_to_vec(device.supported_features()), + supported_extensions: vulkano_struct_to_vec(device.supported_extensions()), + }; + return Ok(info); + } + } + + Err("Could not find a vulkan device with matching pci ids".to_owned()) + }) + } +} + +fn vulkano_struct_to_vec(data: D) -> Vec { + let output = format!("{data:?}"); + let trimmed_output = output.trim_start_matches('[').trim_end_matches(']'); + + trimmed_output + .split(',') + .map(|s| s.trim().to_owned()) + .collect() +} + +#[cfg(test)] +mod tests { + use super::vulkano_struct_to_vec; + use vulkano::device::{DeviceExtensions, Features}; + + #[test] + fn features_to_vec() { + let features = Features { + geometry_shader: true, + tessellation_shader: true, + ..Features::empty() + }; + let vec = vulkano_struct_to_vec(features); + assert_eq!(vec, vec!["geometryShader", "tessellationShader"]); + } + + #[test] + fn extensions_to_vec() { + let extensions = DeviceExtensions { + khr_external_fence: true, + khr_video_queue: true, + ..DeviceExtensions::empty() + }; + + let vec = vulkano_struct_to_vec(extensions); + assert_eq!(vec, vec!["VK_KHR_external_fence", "VK_KHR_video_queue"]); + } +} diff --git a/lact-daemon/src/socket.rs b/lact-daemon/src/socket.rs new file mode 100644 index 00000000..f4b8b909 --- /dev/null +++ b/lact-daemon/src/socket.rs @@ -0,0 +1,64 @@ +use nix::{ + sys::stat::{umask, Mode}, + unistd::{chown, getuid, Gid, Group}, +}; +use std::{fs, path::PathBuf, str::FromStr}; +use tokio::net::UnixListener; +use tracing::{debug, info}; + +const ADMIN_GROUPS: &[&str] = &["wheel", "sudo"]; + +pub fn get_socket_path() -> PathBuf { + let uid = getuid(); + if uid.is_root() { + PathBuf::from_str("/var/run/lactd.sock").unwrap() + } else { + PathBuf::from_str(&format!("/var/run/user/{}/lactd.sock", uid)).unwrap() + } +} + +pub fn cleanup() { + let socket_path = get_socket_path(); + + if socket_path.exists() { + fs::remove_file(socket_path).expect("failed to remove socket") + } + debug!("removed socket"); +} + +pub async fn listen() -> anyhow::Result { + let socket_path = get_socket_path(); + + if socket_path.exists() { + fs::remove_file(&socket_path)?; + } + + let socket_mask = Mode::S_IXUSR | Mode::S_IXGRP | Mode::S_IRWXO; + umask(socket_mask); + + let listener = UnixListener::bind(&socket_path)?; + + chown(&socket_path, None, Some(socket_gid()))?; + + info!("listening on {socket_path:?}"); + Ok(listener) +} + +fn socket_gid() -> Gid { + if getuid().is_root() { + // Check if the group exists + for group_name in ADMIN_GROUPS { + if let Ok(Some(group)) = Group::from_name(group_name) { + return group.gid; + } + } + + if let Ok(Some(group)) = Group::from_gid(Gid::from_raw(1000)) { + group.gid + } else { + Gid::current() + } + } else { + Gid::current() + } +} diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml new file mode 100644 index 00000000..b0e7f34b --- /dev/null +++ b/lact-gui/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "lact-gui" +version = "0.1.0" +authors = ["Ilya Zlobintsev "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lact-schema = { path = "../lact-schema" } +gtk = { version = "0.14", features = ["v3_22"] } +pango = "0.14" +glib = "0.14" +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } +nix = "0.25.0" +anyhow = "1.0.66" +serde_json = "1.0.87" +serde = "1.0.147" +amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs", branch = "blocking" } diff --git a/gui/src/app.rs b/lact-gui/src/app.rs similarity index 65% rename from gui/src/app.rs rename to lact-gui/src/app.rs index ddcb2ce6..c295b01a 100644 --- a/gui/src/app.rs +++ b/lact-gui/src/app.rs @@ -2,25 +2,19 @@ mod apply_revealer; mod header; mod root_stack; -extern crate gtk; - -use std::sync::Arc; +use std::fs; +use std::sync::{Arc, RwLock}; use std::thread; use std::time::Duration; -use std::{ - fs, - sync::atomic::{AtomicU32, Ordering}, -}; +use crate::client::DaemonClient; use apply_revealer::ApplyRevealer; -use daemon::daemon_connection::DaemonConnection; -use daemon::gpu_controller::GpuStats; -use daemon::DaemonError; use gtk::prelude::*; use gtk::*; - use header::Header; +use lact_schema::DeviceStats; use root_stack::RootStack; +use tracing::{debug, error, info, trace}; #[derive(Clone)] pub struct App { @@ -28,11 +22,11 @@ pub struct App { pub header: Header, root_stack: RootStack, apply_revealer: ApplyRevealer, - daemon_connection: DaemonConnection, + daemon_client: DaemonClient, } impl App { - pub fn new(daemon_connection: DaemonConnection) -> Self { + pub fn new(daemon_client: DaemonClient) -> Self { let window = Window::new(WindowType::Toplevel); let header = Header::new(); @@ -66,29 +60,28 @@ impl App { header, root_stack, apply_revealer, - daemon_connection, + daemon_client, } } - pub fn run(&self) -> Result<(), DaemonError> { + pub fn run(&self) -> anyhow::Result<()> { self.window.show_all(); - let current_gpu_id = Arc::new(AtomicU32::new(0)); + let current_gpu_id = Arc::new(RwLock::new(String::new())); { let current_gpu_id = current_gpu_id.clone(); let app = self.clone(); self.header.connect_gpu_selection_changed(move |gpu_id| { - log::info!("GPU Selection changed"); - app.set_info(gpu_id); - current_gpu_id.store(gpu_id, Ordering::SeqCst); + info!("GPU Selection changed"); + app.set_info(&gpu_id); + *current_gpu_id.write().unwrap() = gpu_id; }); } - let gpus = self.daemon_connection.get_gpus()?; - - self.header.set_gpus(gpus); + let devices = self.daemon_client.list_devices()?; + self.header.set_devices(&devices); // Show apply button on setting changes { @@ -97,14 +90,14 @@ impl App { self.root_stack .thermals_page .connect_settings_changed(move || { - log::info!("Settings changed, showing apply button"); + debug!("Settings changed, showing apply button"); apply_revealer.show(); }); let apply_revealer = self.apply_revealer.clone(); self.root_stack.oc_page.connect_settings_changed(move || { - log::info!("Settings changed, showing apply button"); + debug!("Settings changed, showing apply button"); apply_revealer.show(); }); } @@ -113,19 +106,20 @@ impl App { let app = self.clone(); let current_gpu_id = current_gpu_id.clone(); - self.root_stack.oc_page.connect_clocks_reset(move || { - log::info!("Resetting clocks, but not applying"); + // TODO + /*self.root_stack.oc_page.connect_clocks_reset(move || { + info!("Resetting clocks, but not applying"); let gpu_id = current_gpu_id.load(Ordering::SeqCst); - app.daemon_connection + app.daemon_client .reset_gpu_power_states(gpu_id) .expect("Failed to reset clocks"); app.set_info(gpu_id); app.apply_revealer.show(); - }) + })*/ } // Apply settings @@ -134,30 +128,25 @@ impl App { let app = self.clone(); self.apply_revealer.connect_apply_button_clicked(move || { - log::info!("Applying settings"); + info!("Applying settings"); - let gpu_id = current_gpu_id.load(Ordering::SeqCst); + let gpu_id = current_gpu_id.read().unwrap(); { let thermals_settings = app.root_stack.thermals_page.get_thermals_settings(); - if thermals_settings.automatic_fan_control_enabled { - app.daemon_connection - .stop_fan_control(gpu_id) - .unwrap_or(println!("Failed to stop fan control")); - } else { - app.daemon_connection - .start_fan_control(gpu_id) - .unwrap_or(println!("Failed to start fan control")); - } + app.daemon_client + .set_fan_control(&gpu_id, thermals_settings.automatic_fan_control_enabled) + .expect("Could not set fan control"); - app.daemon_connection - .set_fan_curve(gpu_id, thermals_settings.curve) - .unwrap_or(println!("Failed to set fan curve")); + // TODO + /*app.daemon_client + .set_fan_curve(gpu_id, thermals_settings.curve) + .unwrap_or(println!("Failed to set fan curve"));*/ } - if let Some(clocks_settings) = app.root_stack.oc_page.get_clocks() { - app.daemon_connection + /*if let Some(clocks_settings) = app.root_stack.oc_page.get_clocks() { + app.daemon_client .set_gpu_max_power_state( gpu_id, clocks_settings.gpu_clock, @@ -165,28 +154,28 @@ impl App { ) .expect("Failed to set GPU clockspeed/voltage"); - app.daemon_connection + app.daemon_client .set_vram_max_clock(gpu_id, clocks_settings.vram_clock) .expect("Failed to set VRAM Clock"); - app.daemon_connection + app.daemon_client .commit_gpu_power_states(gpu_id) .expect("Failed to commit power states"); } if let Some(profile) = app.root_stack.oc_page.get_power_profile() { - app.daemon_connection + app.daemon_client .set_power_profile(gpu_id, profile) .expect("Failed to set power profile"); } if let Some(cap) = app.root_stack.oc_page.get_power_cap() { - app.daemon_connection + app.daemon_client .set_power_cap(gpu_id, cap) .expect("Failed to set power cap"); - } + }*/ - app.set_info(gpu_id); + app.set_info(&gpu_id); }); } @@ -195,28 +184,29 @@ impl App { Ok(gtk::main()) } - fn set_info(&self, gpu_id: u32) { - let gpu_info = self.daemon_connection.get_gpu_info(gpu_id).unwrap(); - log::trace!("Setting info {:?}", &gpu_info); + fn set_info(&self, gpu_id: &str) { + let info = self.daemon_client.get_device_info(gpu_id).unwrap(); + trace!("Setting info {info:?}"); - self.root_stack.info_page.set_info(&gpu_info); + self.root_stack.info_page.set_info(&info); - log::trace!("Setting clocks"); - self.root_stack.oc_page.set_info(&gpu_info); + trace!("Setting clocks"); + self.root_stack.oc_page.set_info(&info); - log::trace!("Setting power profile {:?}", gpu_info.power_profile); + // TODO: this should be stats + /*trace!("Setting performance level {:?}", info.power_profile); self.root_stack .oc_page .set_power_profile(&gpu_info.power_profile); log::trace!("Setting fan control info"); - match self.daemon_connection.get_fan_control(gpu_id) { + match self.daemon_client.get_fan_control(gpu_id) { Ok(fan_control_info) => self .root_stack .thermals_page .set_ventilation_info(fan_control_info), Err(_) => self.root_stack.thermals_page.hide_fan_controls(), - } + }*/ { // It's overkill to both show and hide the frame, but it needs to be done in set_info because show_all overrides the default hidden state of the frame. @@ -226,7 +216,7 @@ impl App { let ppfeaturemask = ppfeaturemask.trim().strip_prefix("0x").unwrap(); - log::trace!("ppfeaturemask {}", ppfeaturemask); + trace!("ppfeaturemask {}", ppfeaturemask); let ppfeaturemask: u64 = u64::from_str_radix(ppfeaturemask, 16).expect("Invalid ppfeaturemask"); @@ -238,7 +228,7 @@ impl App { } } Err(_) => { - log::info!("Failed to read feature mask! This is expected if your system doesn't have an AMD GPU."); + info!("Failed to read feature mask! This is expected if your system doesn't have an AMD GPU."); self.root_stack.oc_page.warning_frame.hide(); } } @@ -247,7 +237,7 @@ impl App { self.apply_revealer.hide(); } - fn start_stats_update_loop(&self, current_gpu_id: Arc) { + fn start_stats_update_loop(&self, current_gpu_id: Arc>) { let context = glib::MainContext::default(); let _guard = context.acquire(); @@ -255,15 +245,18 @@ impl App { // The loop that gets stats let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); { - let daemon_connection = self.daemon_connection.clone(); + let daemon_connection = self.daemon_client.clone(); thread::spawn(move || loop { - let gpu_id = current_gpu_id.load(Ordering::SeqCst); - - if let Ok(stats) = daemon_connection.get_gpu_stats(gpu_id) { - sender.send(GuiUpdateMsg::GpuStats(stats)).unwrap(); + let gpu_id = current_gpu_id.read().unwrap(); + match daemon_connection.get_device_stats(&gpu_id) { + Ok(stats) => { + sender.send(GuiUpdateMsg::GpuStats(stats)).unwrap(); + } + Err(err) => { + error!("Could not fetch stats: {err}"); + } } - thread::sleep(Duration::from_millis(500)); }); } @@ -276,8 +269,8 @@ impl App { receiver.attach(None, move |msg| { match msg { GuiUpdateMsg::GpuStats(stats) => { - log::trace!("New stats received, updating"); - thermals_page.set_thermals_info(&stats); + trace!("New stats received, updating {stats:?}"); + thermals_page.set_stats(&stats); oc_page.set_stats(&stats); } /*GuiUpdateMsg::FanControlInfo(fan_control_info) => { thermals_page.set_ventilation_info(fan_control_info) @@ -292,5 +285,5 @@ impl App { enum GuiUpdateMsg { // FanControlInfo(FanControlInfo), - GpuStats(GpuStats), + GpuStats(DeviceStats), } diff --git a/gui/src/app/apply_revealer.rs b/lact-gui/src/app/apply_revealer.rs similarity index 100% rename from gui/src/app/apply_revealer.rs rename to lact-gui/src/app/apply_revealer.rs diff --git a/gui/src/app/header.rs b/lact-gui/src/app/header.rs similarity index 79% rename from gui/src/app/header.rs rename to lact-gui/src/app/header.rs index 7a4add61..5514029e 100644 --- a/gui/src/app/header.rs +++ b/lact-gui/src/app/header.rs @@ -1,7 +1,7 @@ use gtk::prelude::*; use gtk::*; +use lact_schema::DeviceListEntry; use pango::EllipsizeMode; -use std::collections::HashMap; #[derive(Clone)] pub struct Header { @@ -35,10 +35,10 @@ impl Header { self.switcher.set_stack(Some(stack)); } - pub fn set_gpus(&self, gpus: HashMap>) { - for (id, name) in &gpus { + pub fn set_devices(&self, gpus: &[DeviceListEntry<'_>]) { + for entry in gpus { self.gpu_selector - .append(Some(&id.to_string()), &name.clone().unwrap_or_default()); + .append(Some(entry.id), entry.name.unwrap_or_default()); } //limits the length of gpu names in combobox @@ -50,10 +50,10 @@ impl Header { self.gpu_selector.set_active(Some(0)); } - pub fn connect_gpu_selection_changed(&self, f: F) { + pub fn connect_gpu_selection_changed(&self, f: F) { self.gpu_selector.connect_changed(move |gpu_selector| { let selected_id = gpu_selector.active_id().unwrap(); - f(selected_id.parse().unwrap()); + f(selected_id.to_string()); }); } } diff --git a/gui/src/app/root_stack.rs b/lact-gui/src/app/root_stack.rs similarity index 100% rename from gui/src/app/root_stack.rs rename to lact-gui/src/app/root_stack.rs diff --git a/gui/src/app/root_stack/info_page.rs b/lact-gui/src/app/root_stack/info_page.rs similarity index 70% rename from gui/src/app/root_stack/info_page.rs rename to lact-gui/src/app/root_stack/info_page.rs index 62c98d41..0c0b597e 100644 --- a/gui/src/app/root_stack/info_page.rs +++ b/lact-gui/src/app/root_stack/info_page.rs @@ -1,8 +1,8 @@ mod vulkan_info; -use daemon::gpu_controller::GpuInfo; use gtk::prelude::*; use gtk::*; +use lact_schema::DeviceInfo; use vulkan_info::VulkanInfoFrame; #[derive(Clone)] @@ -148,28 +148,56 @@ impl InformationPage { } } - pub fn set_info(&self, gpu_info: &GpuInfo) { - self.gpu_name_label.set_markup(&format!( - "{}", - match &gpu_info.vendor_data.card_model { - Some(card_model) => card_model.clone(), - None => gpu_info.vendor_data.gpu_model.clone().unwrap_or_default(), - } - )); - self.gpu_manufacturer_label.set_markup(&format!( - "{}", - gpu_info.vendor_data.card_vendor.clone().unwrap_or_default() - )); + pub fn set_info(&self, gpu_info: &DeviceInfo) { + let gpu_name = gpu_info + .pci_info + .and_then(|pci_info| { + pci_info + .subsystem_pci_info + .model + .or_else(|| pci_info.device_pci_info.model) + }) + .unwrap_or_default(); + self.gpu_name_label + .set_markup(&format!("{gpu_name}",)); + + let gpu_manufacturer = gpu_info + .pci_info + .and_then(|pci_info| { + pci_info + .subsystem_pci_info + .vendor + .or_else(|| pci_info.device_pci_info.model) + }) + .unwrap_or_default(); + self.gpu_manufacturer_label + .set_markup(&format!("{gpu_manufacturer}",)); + + let vbios_version = gpu_info.vbios_version.as_deref().unwrap_or(""); self.vbios_version_label - .set_markup(&format!("{}", gpu_info.vbios_version)); + .set_markup(&format!("{vbios_version}",)); + self.driver_label .set_markup(&format!("{}", gpu_info.driver)); + + let vram_size = gpu_info + .vram_size + .map_or_else(|| "".to_owned(), |size| size.to_string()); self.vram_size_label - .set_markup(&format!("{}", gpu_info.vram_size)); - self.link_speed_label.set_markup(&format!( - "{} x{}", - gpu_info.link_speed, gpu_info.link_width - )); + .set_markup(&format!("{vram_size}")); + + let link_speed = gpu_info + .link_info + .current_speed + .as_deref() + .unwrap_or(""); + let link_width = gpu_info + .link_info + .current_width + .as_deref() + .unwrap_or(""); + self.link_speed_label + .set_markup(&format!("{link_speed} x{link_width}",)); self.vulkan_info_frame.set_info(&gpu_info.vulkan_info); diff --git a/gui/src/app/root_stack/info_page/vulkan_info.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs similarity index 93% rename from gui/src/app/root_stack/info_page/vulkan_info.rs rename to lact-gui/src/app/root_stack/info_page/vulkan_info.rs index 5f6f4cc1..4ec077e3 100644 --- a/gui/src/app/root_stack/info_page/vulkan_info.rs +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs @@ -1,6 +1,7 @@ -use daemon::gpu_controller::VulkanInfo; use gtk::prelude::*; use gtk::*; +use lact_schema::VulkanInfo; +use tracing::trace; #[derive(Clone)] pub struct VulkanInfoFrame { @@ -96,14 +97,14 @@ impl VulkanInfoFrame { } pub fn set_info(&self, vulkan_info: &VulkanInfo) { - log::trace!("Setting vulkan info: {:?}", vulkan_info); + trace!("Setting vulkan info: {:?}", vulkan_info); self.device_name_label .set_markup(&format!("{}", vulkan_info.device_name)); self.version_label .set_markup(&format!("{}", vulkan_info.api_version)); - for (feature, supported) in vulkan_info.features.iter() { + /*for (feature, supported) in vulkan_info.features.iter() { let vbox = Box::new(Orientation::Horizontal, 5); let feature_name_label = Label::new(Some(feature)); @@ -118,6 +119,7 @@ impl VulkanInfoFrame { vbox.pack_start(&feature_supported_checkbutton, false, false, 0); self.features_box.pack_end(&vbox, false, false, 0); - } + }*/ + // TODO } } diff --git a/gui/src/app/root_stack/oc_page.rs b/lact-gui/src/app/root_stack/oc_page.rs similarity index 74% rename from gui/src/app/root_stack/oc_page.rs rename to lact-gui/src/app/root_stack/oc_page.rs index 1ce6be94..1adcea52 100644 --- a/gui/src/app/root_stack/oc_page.rs +++ b/lact-gui/src/app/root_stack/oc_page.rs @@ -1,17 +1,17 @@ mod clocks_frame; +mod performance_level_frame; mod power_cap_frame; -mod power_profile_frame; mod stats_grid; mod warning_frame; +use amdgpu_sysfs::gpu_handle::PerformanceLevel; +use clocks_frame::ClocksFrame; use clocks_frame::ClocksSettings; -use daemon::gpu_controller::{GpuInfo, GpuStats, PowerProfile}; use gtk::prelude::*; use gtk::*; - -use clocks_frame::ClocksFrame; +use lact_schema::{DeviceInfo, DeviceStats}; +use performance_level_frame::PowerProfileFrame; use power_cap_frame::PowerCapFrame; -use power_profile_frame::PowerProfileFrame; use stats_grid::StatsGrid; use warning_frame::WarningFrame; @@ -19,7 +19,7 @@ use warning_frame::WarningFrame; pub struct OcPage { pub container: Box, stats_grid: StatsGrid, - power_profile_frame: PowerProfileFrame, + performance_level_frame: PowerProfileFrame, power_cap_frame: PowerCapFrame, clocks_frame: ClocksFrame, pub warning_frame: WarningFrame, @@ -52,14 +52,14 @@ impl OcPage { Self { container, stats_grid, - power_profile_frame, + performance_level_frame: power_profile_frame, clocks_frame, warning_frame, power_cap_frame, } } - pub fn set_stats(&self, stats: &GpuStats) { + pub fn set_stats(&self, stats: &DeviceStats) { self.stats_grid.set_stats(stats); } @@ -72,7 +72,7 @@ impl OcPage { pub fn connect_settings_changed(&self, f: F) { { let f = f.clone(); - self.power_profile_frame + self.performance_level_frame .connect_power_profile_changed(move || { f(); }); @@ -90,34 +90,35 @@ impl OcPage { } } - pub fn set_power_profile(&self, profile: &Option) { + pub fn set_performance_level(&self, profile: Option) { match profile { Some(profile) => { - self.power_profile_frame.show(); - self.power_profile_frame.set_active_profile(profile); + self.performance_level_frame.show(); + self.performance_level_frame.set_active_profile(profile); } - None => self.power_profile_frame.hide(), + None => self.performance_level_frame.hide(), } } pub fn get_power_profile(&self) -> Option { - match self.power_profile_frame.get_visibility() { - true => Some(self.power_profile_frame.get_selected_power_profile()), + match self.performance_level_frame.get_visibility() { + true => Some(self.performance_level_frame.get_selected_power_profile()), false => None, } } - pub fn set_info(&self, info: &GpuInfo) { - match &info.clocks_table { + pub fn set_info(&self, info: &DeviceInfo) { + // TODO + /*match &info.clocks_table { Some(clocks_table) => { self.clocks_frame.show(); self.clocks_frame.set_clocks(clocks_table); } None => self.clocks_frame.hide(), - } + }*/ - self.power_cap_frame - .set_data(info.power_cap, info.power_cap_max); + /*self.power_cap_frame + .set_data(info.power_cap, info.power_cap_max);*/ } pub fn get_clocks(&self) -> Option { diff --git a/gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs similarity index 100% rename from gui/src/app/root_stack/oc_page/clocks_frame.rs rename to lact-gui/src/app/root_stack/oc_page/clocks_frame.rs diff --git a/gui/src/app/root_stack/oc_page/power_profile_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs similarity index 100% rename from gui/src/app/root_stack/oc_page/power_profile_frame.rs rename to lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs diff --git a/gui/src/app/root_stack/oc_page/power_cap_frame.rs b/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs similarity index 100% rename from gui/src/app/root_stack/oc_page/power_cap_frame.rs rename to lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs diff --git a/gui/src/app/root_stack/oc_page/stats_grid.rs b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs similarity index 98% rename from gui/src/app/root_stack/oc_page/stats_grid.rs rename to lact-gui/src/app/root_stack/oc_page/stats_grid.rs index 8bb6d538..8f26f282 100644 --- a/gui/src/app/root_stack/oc_page/stats_grid.rs +++ b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs @@ -1,6 +1,6 @@ -use daemon::gpu_controller::GpuStats; use gtk::prelude::*; use gtk::*; +use lact_schema::DeviceStats; #[derive(Clone)] pub struct StatsGrid { @@ -143,7 +143,7 @@ impl StatsGrid { } } - pub fn set_stats(&self, stats: &GpuStats) { + pub fn set_stats(&self, stats: &DeviceStats) { self.vram_usage_bar.set_value( stats.mem_used.unwrap_or_else(|| 0) as f64 / stats.mem_total.unwrap_or_else(|| 0) as f64, diff --git a/gui/src/app/root_stack/oc_page/warning_frame.rs b/lact-gui/src/app/root_stack/oc_page/warning_frame.rs similarity index 100% rename from gui/src/app/root_stack/oc_page/warning_frame.rs rename to lact-gui/src/app/root_stack/oc_page/warning_frame.rs diff --git a/gui/src/app/root_stack/software_page.rs b/lact-gui/src/app/root_stack/software_page.rs similarity index 100% rename from gui/src/app/root_stack/software_page.rs rename to lact-gui/src/app/root_stack/software_page.rs diff --git a/gui/src/app/root_stack/thermals_page.rs b/lact-gui/src/app/root_stack/thermals_page.rs similarity index 79% rename from gui/src/app/root_stack/thermals_page.rs rename to lact-gui/src/app/root_stack/thermals_page.rs index d6a69f95..7956798f 100644 --- a/gui/src/app/root_stack/thermals_page.rs +++ b/lact-gui/src/app/root_stack/thermals_page.rs @@ -1,9 +1,10 @@ mod fan_curve_frame; -use daemon::gpu_controller::{FanControlInfo, GpuStats}; use gtk::prelude::*; use gtk::*; +use lact_schema::DeviceStats; use std::collections::BTreeMap; +use tracing::trace; use fan_curve_frame::FanCurveFrame; @@ -100,7 +101,7 @@ impl ThermalsPage { { let fan_curve_frame = fan_curve_frame.clone(); fan_control_enabled_switch.connect_changed_active(move |switch| { - log::trace!("Fan control switch toggled"); + trace!("Fan control switch toggled"); if switch.state() { { glib::idle_add(|| { @@ -128,48 +129,46 @@ impl ThermalsPage { } } - pub fn set_thermals_info(&self, stats: &GpuStats) { - self.temp_label.set_markup(&format!("{}", { - let mut temperatures = Vec::new(); - - for (label, temp) in stats.temperatures.iter() { - temperatures.push(format!("{}: {}°C", label, temp.current)); - } - - temperatures.sort(); + pub fn set_stats(&self, stats: &DeviceStats) { + let mut temperatures: Vec = stats + .temps + .iter() + .filter_map(|(label, temp)| temp.current.map(|current| format!("{label}: {current}°C"))) + .collect(); + temperatures.sort(); + let temperatures_text = if temperatures.is_empty() { + String::from("No sensors found") + } else { + temperatures.join("\n") + }; - if !temperatures.is_empty() { - temperatures.join("\n") - } else { - String::from("No sensors found") - } - })); + self.temp_label + .set_markup(&format!("{temperatures_text}",)); - match stats.fan_speed { - Some(fan_speed) => self.fan_speed_label.set_markup(&format!( + match stats.fan_speed_current { + Some(fan_speed_current) => self.fan_speed_label.set_markup(&format!( "{} RPM ({}%)", - fan_speed, - (fan_speed as f64 / stats.max_fan_speed.unwrap() as f64 * 100.0).round() + fan_speed_current, + (fan_speed_current as f64 + / stats.fan_speed_max.unwrap_or(fan_speed_current) as f64 + * 100.0) + .round() )), None => self.fan_speed_label.set_text("No fan detected"), } - } - - pub fn set_ventilation_info(&self, fan_control_info: FanControlInfo) { - log::info!("Setting fan control info {:?}", fan_control_info); self.fan_control_enabled_switch.set_visible(true); - self.fan_control_enabled_switch - .set_active(!fan_control_info.enabled); + .set_active(!stats.fan_control_enabled); - if !fan_control_info.enabled { - self.fan_curve_frame.hide(); - } else { + if stats.fan_control_enabled { self.fan_curve_frame.show(); + } else { + self.fan_curve_frame.hide(); } - self.fan_curve_frame.set_curve(&fan_control_info.curve); + // TODO + // self.fan_curve_frame.set_curve(&fan_control_info.curve); } pub fn connect_settings_changed(&self, f: F) { diff --git a/gui/src/app/root_stack/thermals_page/fan_curve_frame.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs similarity index 100% rename from gui/src/app/root_stack/thermals_page/fan_curve_frame.rs rename to lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs diff --git a/lact-gui/src/client.rs b/lact-gui/src/client.rs new file mode 100644 index 00000000..72b34a0c --- /dev/null +++ b/lact-gui/src/client.rs @@ -0,0 +1,84 @@ +use anyhow::{anyhow, Context}; +use lact_schema::{request::Request, response::Response, DeviceInfo, DeviceListEntry, DeviceStats}; +use nix::unistd::getuid; +use serde::de::DeserializeOwned; +use std::{ + io::{BufRead, BufReader, Write}, + os::unix::net::UnixStream, + path::PathBuf, + sync::{Arc, Mutex}, +}; + +#[derive(Clone)] +pub struct DaemonClient { + stream: Arc, UnixStream)>>, +} + +impl DaemonClient { + pub fn connect() -> anyhow::Result { + let path = + get_socket_path().context("Could not connect to daemon: socket file not found")?; + let stream = UnixStream::connect(path).context("Could not connect to daemon")?; + let reader = BufReader::new(stream.try_clone()?); + + Ok(Self { + stream: Arc::new(Mutex::new((reader, stream))), + }) + } + + fn make_request(&self, request: Request) -> anyhow::Result { + let (reader, writer) = *self.stream.lock().map_err(|err| anyhow!("{err}"))?; + + if !reader.buffer().is_empty() { + return Err(anyhow!("Another request was not processed properly")); + } + + let request_payload = serde_json::to_string(&request)?; + writer.write_all(request_payload.as_bytes())?; + writer.write_all(b"\n")?; + + let mut response_payload = String::new(); + reader.read_line(&mut response_payload); + + let response: Response = serde_json::from_str(&response_payload) + .context("Could not deserialize response from daemon")?; + + match response { + Response::Ok(data) => Ok(data), + Response::Error(error) => Err(anyhow!("Error from daemon: {error}")), + } + } + + pub fn list_devices<'a>(&self) -> anyhow::Result>> { + self.make_request(Request::ListDevices) + } + + pub fn set_fan_control(&self, id: &str, enabled: bool) -> anyhow::Result<()> { + self.make_request(Request::SetFanControl { id, enabled }) + } + + pub fn get_device_info(&self, id: &str) -> anyhow::Result { + self.make_request(Request::DeviceInfo { id }) + } + + pub fn get_device_stats(&self, id: &str) -> anyhow::Result { + self.make_request(Request::DeviceStats { id }) + } +} + +fn get_socket_path() -> Option { + let root_path = PathBuf::from("/var/run/lactd.sock"); + + if root_path.exists() { + return Some(root_path); + } + + let uid = getuid(); + let user_path = PathBuf::from(format!("/var/run/user/{}/lactd.sock", uid)); + + if user_path.exists() { + Some(user_path) + } else { + None + } +} diff --git a/lact-gui/src/main.rs b/lact-gui/src/main.rs new file mode 100644 index 00000000..37a77bfd --- /dev/null +++ b/lact-gui/src/main.rs @@ -0,0 +1,27 @@ +mod app; +mod client; + +use anyhow::{anyhow, Context}; +use app::App; +use client::DaemonClient; +use tracing::metadata::LevelFilter; +use tracing_subscriber::EnvFilter; + +fn main() -> anyhow::Result<()> { + let env_filter = EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .from_env_lossy(); + tracing_subscriber::fmt().with_env_filter(env_filter).init(); + + if let Err(err) = gtk::init() { + return Err(anyhow!("Cannot initialize GTK: {err}")); + } + + let connection = DaemonClient::connect().context("Could not connect to daemon")?; + + let app = App::new(connection); + + app.run()?; + + Ok(()) +} diff --git a/lact-schema/Cargo.toml b/lact-schema/Cargo.toml new file mode 100644 index 00000000..b7b97b1c --- /dev/null +++ b/lact-schema/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "lact-schema" +version = "0.1.0" +edition = "2021" + +[dependencies] +amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs", branch = "blocking", features = [ + "serde", +] } +serde = { version = "1.0.147", features = ["derive"] } + +[dev-dependencies] +serde_json = "1.0.87" diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs new file mode 100644 index 00000000..162c58a3 --- /dev/null +++ b/lact-schema/src/lib.rs @@ -0,0 +1,68 @@ +pub mod request; +pub mod response; +#[cfg(test)] +mod tests; + +use amdgpu_sysfs::{gpu_handle::PerformanceLevel, hw_mon::Temperature}; +use serde::{Deserialize, Serialize}; +use std::{borrow::Cow, collections::HashMap}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DeviceListEntry<'a> { + pub id: &'a str, + pub name: Option<&'a str>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GpuPciInfo { + pub device_pci_info: PciInfo, + pub subsystem_pci_info: PciInfo, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DeviceInfo<'a> { + #[serde(borrow)] + pub pci_info: Option>, + pub vulkan_info: Option, + pub driver: &'a str, + pub vbios_version: Option, + pub link_info: LinkInfo, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct LinkInfo { + pub current_width: Option, + pub current_speed: Option, + pub max_width: Option, + pub max_speed: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VulkanInfo { + pub device_name: String, + pub api_version: String, + pub driver_name: Option, + pub supported_features: Vec, + pub supported_extensions: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct PciInfo { + pub vendor_id: String, + pub vendor: Option, + pub model_id: String, + pub model: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DeviceStats { + pub fan_speed_current: Option, + pub fan_speed_max: Option, + pub fan_speed_min: Option, + pub fan_control_enabled: bool, + pub temps: HashMap, + pub total_vram: Option, + pub used_vram: Option, + pub busy_percent: Option, + pub performance_level: Option, +} diff --git a/lact-schema/src/request.rs b/lact-schema/src/request.rs new file mode 100644 index 00000000..8ed56616 --- /dev/null +++ b/lact-schema/src/request.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +#[serde(tag = "command", content = "args", rename_all = "snake_case")] +pub enum Request<'a> { + Ping, + ListDevices, + DeviceInfo { id: &'a str }, + DeviceStats { id: &'a str }, + SetFanControl { id: &'a str, enabled: bool }, +} diff --git a/lact-schema/src/response.rs b/lact-schema/src/response.rs new file mode 100644 index 00000000..b83e4dad --- /dev/null +++ b/lact-schema/src/response.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "status", content = "data", rename_all = "snake_case")] +pub enum Response { + Ok(T), + Error(String), +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Pong; diff --git a/lact-schema/src/tests.rs b/lact-schema/src/tests.rs new file mode 100644 index 00000000..77221a2d --- /dev/null +++ b/lact-schema/src/tests.rs @@ -0,0 +1,48 @@ +use crate::{ + request::Request, + response::{Pong, Response}, +}; +use serde_json::json; + +#[test] +fn ping_requset() { + let value = r#"{ + "command": "ping" + }"#; + let request: Request = serde_json::from_str(value).unwrap(); + + assert_eq!(request, Request::Ping); +} + +#[test] +fn pong_response() { + let expected_response = json!({ + "status": "ok", + "data": null + }); + let response = Response::Ok(Pong); + + assert_eq!(serde_json::to_value(&response).unwrap(), expected_response); +} + +#[test] +fn controllers_response() { + let expected_response = json!({ + "status": "ok", + "data": ["1002:67DF-1DA2:E387-0000:0f:00.0"] + }); + let response = Response::Ok(vec!["1002:67DF-1DA2:E387-0000:0f:00.0"]); + assert_eq!(serde_json::to_value(response).unwrap(), expected_response); +} + +#[test] +fn error_response() { + let expected_response = json!({ + "status": "error", + "data": "my super error" + }); + + let response = Response::<()>::Error("my super error".to_owned()); + + assert_eq!(serde_json::to_value(&response).unwrap(), expected_response); +} From 37cdb8160ae46a0253b1425dd0271b635b03dae1 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Fri, 18 Nov 2022 19:35:31 +0200 Subject: [PATCH 002/113] feat: somewhat working ui --- Cargo.lock | 2 +- lact-gui/src/app.rs | 36 ++++++++----- lact-gui/src/app/root_stack.rs | 12 ++--- lact-gui/src/app/root_stack/info_page.rs | 29 +++++++---- lact-gui/src/app/root_stack/oc_page.rs | 19 +++---- .../app/root_stack/oc_page/clocks_frame.rs | 1 - .../oc_page/performance_level_frame.rs | 21 ++++---- .../thermals_page/fan_curve_frame.rs | 8 +-- lact-gui/src/client.rs | 51 +++++++++++++------ 9 files changed, 108 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97853084..a940e313 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ dependencies = [ [[package]] name = "amdgpu-sysfs" version = "0.5.0" -source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=blocking#55ddcc95cddde27e5c5147d9b5ee4831b5d7aaf8" +source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=blocking#0308d4c475f5091f7e8bc4b7b5d8c101f8dd40d7" dependencies = [ "serde", ] diff --git a/lact-gui/src/app.rs b/lact-gui/src/app.rs index c295b01a..8701e73d 100644 --- a/lact-gui/src/app.rs +++ b/lact-gui/src/app.rs @@ -80,7 +80,8 @@ impl App { }); } - let devices = self.daemon_client.list_devices()?; + let devices_buf = self.daemon_client.list_devices()?; + let devices = devices_buf.inner()?; self.header.set_devices(&devices); // Show apply button on setting changes @@ -96,10 +97,10 @@ impl App { let apply_revealer = self.apply_revealer.clone(); - self.root_stack.oc_page.connect_settings_changed(move || { - debug!("Settings changed, showing apply button"); - apply_revealer.show(); - }); + // self.root_stack.oc_page.connect_settings_changed(move || { + // debug!("Settings changed, showing apply button"); + // apply_revealer.show(); + // }); } { @@ -185,13 +186,17 @@ impl App { } fn set_info(&self, gpu_id: &str) { - let info = self.daemon_client.get_device_info(gpu_id).unwrap(); + let info_buf = self + .daemon_client + .get_device_info(gpu_id) + .expect("Could not fetch info"); + let info = info_buf.inner().unwrap(); trace!("Setting info {info:?}"); self.root_stack.info_page.set_info(&info); - trace!("Setting clocks"); - self.root_stack.oc_page.set_info(&info); + // trace!("Setting clocks"); + // self.root_stack.oc_page.set_info(&info); // TODO: this should be stats /*trace!("Setting performance level {:?}", info.power_profile); @@ -221,15 +226,15 @@ impl App { let ppfeaturemask: u64 = u64::from_str_radix(ppfeaturemask, 16).expect("Invalid ppfeaturemask"); - if (ppfeaturemask & PP_OVERDRIVE_MASK as u64) > 0 { + /*if (ppfeaturemask & PP_OVERDRIVE_MASK as u64) > 0 { self.root_stack.oc_page.warning_frame.hide(); } else { self.root_stack.oc_page.warning_frame.show(); - } + }*/ } Err(_) => { info!("Failed to read feature mask! This is expected if your system doesn't have an AMD GPU."); - self.root_stack.oc_page.warning_frame.hide(); + // self.root_stack.oc_page.warning_frame.hide(); } } } @@ -249,7 +254,10 @@ impl App { thread::spawn(move || loop { let gpu_id = current_gpu_id.read().unwrap(); - match daemon_connection.get_device_stats(&gpu_id) { + match daemon_connection + .get_device_stats(&gpu_id) + .and_then(|stats| stats.inner()) + { Ok(stats) => { sender.send(GuiUpdateMsg::GpuStats(stats)).unwrap(); } @@ -264,14 +272,14 @@ impl App { // Receiving stats into the gui event loop { let thermals_page = self.root_stack.thermals_page.clone(); - let oc_page = self.root_stack.oc_page.clone(); + // let oc_page = self.root_stack.oc_page.clone(); receiver.attach(None, move |msg| { match msg { GuiUpdateMsg::GpuStats(stats) => { trace!("New stats received, updating {stats:?}"); thermals_page.set_stats(&stats); - oc_page.set_stats(&stats); + // oc_page.set_stats(&stats); } /*GuiUpdateMsg::FanControlInfo(fan_control_info) => { thermals_page.set_ventilation_info(fan_control_info) }*/ diff --git a/lact-gui/src/app/root_stack.rs b/lact-gui/src/app/root_stack.rs index 60bd65f4..0b232c29 100644 --- a/lact-gui/src/app/root_stack.rs +++ b/lact-gui/src/app/root_stack.rs @@ -1,5 +1,5 @@ mod info_page; -mod oc_page; +// mod oc_page; mod software_page; mod thermals_page; @@ -7,7 +7,7 @@ use gtk::prelude::*; use gtk::*; use info_page::InformationPage; -use oc_page::OcPage; +// use oc_page::OcPage; use software_page::SoftwarePage; use thermals_page::ThermalsPage; @@ -17,7 +17,7 @@ pub struct RootStack { pub info_page: InformationPage, pub thermals_page: ThermalsPage, pub software_page: SoftwarePage, - pub oc_page: OcPage, + // pub oc_page: OcPage, } impl RootStack { @@ -28,9 +28,9 @@ impl RootStack { container.add_titled(&info_page.container, "info_page", "Information"); - let oc_page = OcPage::new(); + // let oc_page = OcPage::new(); - container.add_titled(&oc_page.container, "oc_page", "OC"); + // container.add_titled(&oc_page.container, "oc_page", "OC"); let thermals_page = ThermalsPage::new(); @@ -44,7 +44,7 @@ impl RootStack { container, info_page, thermals_page, - oc_page, + // oc_page, software_page, } } diff --git a/lact-gui/src/app/root_stack/info_page.rs b/lact-gui/src/app/root_stack/info_page.rs index 0c0b597e..86819ce5 100644 --- a/lact-gui/src/app/root_stack/info_page.rs +++ b/lact-gui/src/app/root_stack/info_page.rs @@ -151,11 +151,13 @@ impl InformationPage { pub fn set_info(&self, gpu_info: &DeviceInfo) { let gpu_name = gpu_info .pci_info + .as_ref() .and_then(|pci_info| { pci_info .subsystem_pci_info .model - .or_else(|| pci_info.device_pci_info.model) + .as_deref() + .or_else(|| pci_info.device_pci_info.model.as_deref()) }) .unwrap_or_default(); self.gpu_name_label @@ -163,43 +165,48 @@ impl InformationPage { let gpu_manufacturer = gpu_info .pci_info + .as_ref() .and_then(|pci_info| { pci_info .subsystem_pci_info .vendor - .or_else(|| pci_info.device_pci_info.model) + .as_deref() + .or_else(|| pci_info.device_pci_info.model.as_deref()) }) .unwrap_or_default(); self.gpu_manufacturer_label .set_markup(&format!("{gpu_manufacturer}",)); - let vbios_version = gpu_info.vbios_version.as_deref().unwrap_or(""); + let vbios_version = gpu_info.vbios_version.as_deref().unwrap_or("unknown"); self.vbios_version_label .set_markup(&format!("{vbios_version}",)); self.driver_label .set_markup(&format!("{}", gpu_info.driver)); - let vram_size = gpu_info - .vram_size - .map_or_else(|| "".to_owned(), |size| size.to_string()); - self.vram_size_label - .set_markup(&format!("{vram_size}")); + // TODO + // let vram_size = gpu_info + // .vram_size + // .map_or_else(|| "unknown".to_owned(), |size| size.to_string()); + // self.vram_size_label + // .set_markup(&format!("{vram_size}")); let link_speed = gpu_info .link_info .current_speed .as_deref() - .unwrap_or(""); + .unwrap_or("unknown"); let link_width = gpu_info .link_info .current_width .as_deref() - .unwrap_or(""); + .unwrap_or("unknown"); self.link_speed_label .set_markup(&format!("{link_speed} x{link_width}",)); - self.vulkan_info_frame.set_info(&gpu_info.vulkan_info); + if let Some(vulkan_info) = &gpu_info.vulkan_info { + self.vulkan_info_frame.set_info(vulkan_info); + } self.container.show_all(); } diff --git a/lact-gui/src/app/root_stack/oc_page.rs b/lact-gui/src/app/root_stack/oc_page.rs index 1adcea52..c792cb39 100644 --- a/lact-gui/src/app/root_stack/oc_page.rs +++ b/lact-gui/src/app/root_stack/oc_page.rs @@ -1,12 +1,10 @@ -mod clocks_frame; +// mod clocks_frame; mod performance_level_frame; mod power_cap_frame; mod stats_grid; mod warning_frame; use amdgpu_sysfs::gpu_handle::PerformanceLevel; -use clocks_frame::ClocksFrame; -use clocks_frame::ClocksSettings; use gtk::prelude::*; use gtk::*; use lact_schema::{DeviceInfo, DeviceStats}; @@ -21,7 +19,7 @@ pub struct OcPage { stats_grid: StatsGrid, performance_level_frame: PowerProfileFrame, power_cap_frame: PowerCapFrame, - clocks_frame: ClocksFrame, + // clocks_frame: ClocksFrame, pub warning_frame: WarningFrame, } @@ -45,15 +43,15 @@ impl OcPage { container.pack_start(&power_profile_frame.container, false, true, 0); - let clocks_frame = ClocksFrame::new(); + // let clocks_frame = ClocksFrame::new(); - container.pack_start(&clocks_frame.container, false, true, 0); + // container.pack_start(&clocks_frame.container, false, true, 0); Self { container, stats_grid, performance_level_frame: power_profile_frame, - clocks_frame, + // clocks_frame, warning_frame, power_cap_frame, } @@ -100,9 +98,12 @@ impl OcPage { } } - pub fn get_power_profile(&self) -> Option { + pub fn get_performance_level(&self) -> Option { match self.performance_level_frame.get_visibility() { - true => Some(self.performance_level_frame.get_selected_power_profile()), + true => Some( + self.performance_level_frame + .get_selected_performance_level(), + ), false => None, } } diff --git a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs index 6605b128..8c249c89 100644 --- a/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs +++ b/lact-gui/src/app/root_stack/oc_page/clocks_frame.rs @@ -1,4 +1,3 @@ -use daemon::gpu_controller::ClocksTable; use gtk::prelude::*; use gtk::*; diff --git a/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs index 43a054f3..f7445bab 100644 --- a/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs +++ b/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs @@ -1,4 +1,4 @@ -use daemon::gpu_controller::PowerProfile; +use amdgpu_sysfs::gpu_handle::PerformanceLevel; use gtk::prelude::*; use gtk::*; @@ -57,11 +57,12 @@ impl PowerProfileFrame { } } - pub fn set_active_profile(&self, profile: &PowerProfile) { - match profile { - PowerProfile::Auto => self.combo_box.set_active_id(Some("0")), - PowerProfile::High => self.combo_box.set_active_id(Some("1")), - PowerProfile::Low => self.combo_box.set_active_id(Some("2")), + pub fn set_active_profile(&self, level: PerformanceLevel) { + match level { + PerformanceLevel::Auto => self.combo_box.set_active_id(Some("0")), + PerformanceLevel::High => self.combo_box.set_active_id(Some("1")), + PerformanceLevel::Low => self.combo_box.set_active_id(Some("2")), + PerformanceLevel::Manual => todo!(), }; } @@ -71,11 +72,11 @@ impl PowerProfileFrame { }); } - pub fn get_selected_power_profile(&self) -> PowerProfile { + pub fn get_selected_performance_level(&self) -> PerformanceLevel { match self.combo_box.active().unwrap() { - 0 => PowerProfile::Auto, - 1 => PowerProfile::High, - 2 => PowerProfile::Low, + 0 => PerformanceLevel::Auto, + 1 => PerformanceLevel::High, + 2 => PerformanceLevel::Low, _ => unreachable!(), } } diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs index 60f0fdab..1395ef8e 100644 --- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs +++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs @@ -1,7 +1,7 @@ -use std::collections::BTreeMap; - use gtk::prelude::*; use gtk::*; +use std::collections::BTreeMap; +use tracing::debug; #[derive(Clone)] pub struct FanCurveFrame { @@ -189,12 +189,12 @@ impl FanCurveFrame { } pub fn show(&self) { - log::info!("Manual fan control enaged, showing fan curve"); + debug!("Manual fan control enaged, showing fan curve"); self.container.set_visible(true); } pub fn hide(&self) { - log::info!("Manual fan control disenaged, hiding fan curve"); + debug!("Manual fan control disenaged, hiding fan curve"); self.container.set_visible(false); } diff --git a/lact-gui/src/client.rs b/lact-gui/src/client.rs index 72b34a0c..071148ba 100644 --- a/lact-gui/src/client.rs +++ b/lact-gui/src/client.rs @@ -1,9 +1,11 @@ use anyhow::{anyhow, Context}; use lact_schema::{request::Request, response::Response, DeviceInfo, DeviceListEntry, DeviceStats}; use nix::unistd::getuid; -use serde::de::DeserializeOwned; +use serde::{de::DeserializeOwned, Deserialize}; use std::{ io::{BufRead, BufReader, Write}, + marker::PhantomData, + ops::DerefMut, os::unix::net::UnixStream, path::PathBuf, sync::{Arc, Mutex}, @@ -26,8 +28,12 @@ impl DaemonClient { }) } - fn make_request(&self, request: Request) -> anyhow::Result { - let (reader, writer) = *self.stream.lock().map_err(|err| anyhow!("{err}"))?; + fn make_request<'a, T: Deserialize<'a>>( + &self, + request: Request, + ) -> anyhow::Result> { + let mut stream_guard = self.stream.lock().map_err(|err| anyhow!("{err}"))?; + let (reader, writer) = stream_guard.deref_mut(); if !reader.buffer().is_empty() { return Err(anyhow!("Another request was not processed properly")); @@ -38,30 +44,29 @@ impl DaemonClient { writer.write_all(b"\n")?; let mut response_payload = String::new(); - reader.read_line(&mut response_payload); + reader.read_line(&mut response_payload)?; - let response: Response = serde_json::from_str(&response_payload) - .context("Could not deserialize response from daemon")?; - - match response { - Response::Ok(data) => Ok(data), - Response::Error(error) => Err(anyhow!("Error from daemon: {error}")), - } + Ok(ResponseBuffer { + buf: response_payload, + _phantom: PhantomData, + }) } - pub fn list_devices<'a>(&self) -> anyhow::Result>> { + pub fn list_devices<'a>(&self) -> anyhow::Result>>> { self.make_request(Request::ListDevices) } pub fn set_fan_control(&self, id: &str, enabled: bool) -> anyhow::Result<()> { - self.make_request(Request::SetFanControl { id, enabled }) + self.make_request::<()>(Request::SetFanControl { id, enabled })? + .inner()?; + Ok(()) } - pub fn get_device_info(&self, id: &str) -> anyhow::Result { + pub fn get_device_info(&self, id: &str) -> anyhow::Result> { self.make_request(Request::DeviceInfo { id }) } - pub fn get_device_stats(&self, id: &str) -> anyhow::Result { + pub fn get_device_stats(&self, id: &str) -> anyhow::Result> { self.make_request(Request::DeviceStats { id }) } } @@ -82,3 +87,19 @@ fn get_socket_path() -> Option { None } } + +pub struct ResponseBuffer { + buf: String, + _phantom: PhantomData, +} + +impl<'a, T: Deserialize<'a>> ResponseBuffer { + pub fn inner(&'a self) -> anyhow::Result { + let response: Response = serde_json::from_str(&self.buf) + .context("Could not deserialize response from daemon")?; + match response { + Response::Ok(data) => Ok(data), + Response::Error(err) => Err(anyhow!("Got error from daemon: {err}")), + } + } +} From 2f626c5b4c997fd686a59fe9faa44e7de6c4b55a Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Fri, 18 Nov 2022 21:16:35 +0200 Subject: [PATCH 003/113] feat: proper feature and extension list --- Cargo.lock | 5 ++- lact-daemon/Cargo.toml | 2 +- lact-daemon/src/server/vulkan.rs | 55 ++++++++------------------------ lact-schema/src/lib.rs | 4 +-- 4 files changed, 18 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a940e313..73e57f8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1262,9 +1262,8 @@ dependencies = [ [[package]] name = "vulkano" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eecedd057ea4cd85c0e43ba2ad30e599954bd958c50d0f7849439548cfa573ce" +version = "0.32.0" +source = "git+https://github.com/ilya-zlobintsev/vulkano?branch=arr_features#0c1f4f3bfcc94748ffe25512665f796e4bb6302f" dependencies = [ "ahash", "ash", diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index 1bdce5af..a8229243 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -27,5 +27,5 @@ tokio = { version = "1.21.2", features = [ ] } tracing = "0.1.37" tracing-subscriber = "0.3.16" -vulkano = "0.32.1" +vulkano = { git = "https://github.com/ilya-zlobintsev/vulkano", branch = "arr_features" } lact-schema = { path = "../lact-schema" } diff --git a/lact-daemon/src/server/vulkan.rs b/lact-daemon/src/server/vulkan.rs index 28288d5e..0d64da7b 100644 --- a/lact-daemon/src/server/vulkan.rs +++ b/lact-daemon/src/server/vulkan.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use crate::fork::run_forked; use lact_schema::VulkanInfo; use vulkano::{ @@ -5,7 +7,7 @@ use vulkano::{ VulkanLibrary, }; -pub fn get_vulkan_info(vendor_id: &str, device_id: &str) -> anyhow::Result { +pub fn get_vulkan_info<'a>(vendor_id: &'a str, device_id: &'a str) -> anyhow::Result { let vendor_id = u32::from_str_radix(vendor_id, 16)?; let device_id = u32::from_str_radix(device_id, 16)?; @@ -26,8 +28,16 @@ pub fn get_vulkan_info(vendor_id: &str, device_id: &str) -> anyhow::Result anyhow::Result(data: D) -> Vec { - let output = format!("{data:?}"); - let trimmed_output = output.trim_start_matches('[').trim_end_matches(']'); - - trimmed_output - .split(',') - .map(|s| s.trim().to_owned()) - .collect() -} - -#[cfg(test)] -mod tests { - use super::vulkano_struct_to_vec; - use vulkano::device::{DeviceExtensions, Features}; - - #[test] - fn features_to_vec() { - let features = Features { - geometry_shader: true, - tessellation_shader: true, - ..Features::empty() - }; - let vec = vulkano_struct_to_vec(features); - assert_eq!(vec, vec!["geometryShader", "tessellationShader"]); - } - - #[test] - fn extensions_to_vec() { - let extensions = DeviceExtensions { - khr_external_fence: true, - khr_video_queue: true, - ..DeviceExtensions::empty() - }; - - let vec = vulkano_struct_to_vec(extensions); - assert_eq!(vec, vec!["VK_KHR_external_fence", "VK_KHR_video_queue"]); - } -} diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index 162c58a3..21fc78c7 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -42,8 +42,8 @@ pub struct VulkanInfo { pub device_name: String, pub api_version: String, pub driver_name: Option, - pub supported_features: Vec, - pub supported_extensions: Vec, + pub supported_features: HashMap, bool>, + pub supported_extensions: HashMap, bool>, } #[derive(Serialize, Deserialize, Debug, Clone)] From ed18f0b79db020ce88fbb3d98f965572cb5fc1bb Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Fri, 18 Nov 2022 21:22:59 +0200 Subject: [PATCH 004/113] feat: vulkan features in gui --- lact-gui/src/app/root_stack/info_page/vulkan_info.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs index 4ec077e3..9dcfe178 100644 --- a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs @@ -1,6 +1,7 @@ use gtk::prelude::*; use gtk::*; use lact_schema::VulkanInfo; +use std::collections::BTreeMap; use tracing::trace; #[derive(Clone)] @@ -104,7 +105,8 @@ impl VulkanInfoFrame { self.version_label .set_markup(&format!("{}", vulkan_info.api_version)); - /*for (feature, supported) in vulkan_info.features.iter() { + let features: BTreeMap<_, _> = vulkan_info.supported_features.iter().collect(); + for (feature, supported) in features.into_iter() { let vbox = Box::new(Orientation::Horizontal, 5); let feature_name_label = Label::new(Some(feature)); @@ -119,7 +121,6 @@ impl VulkanInfoFrame { vbox.pack_start(&feature_supported_checkbutton, false, false, 0); self.features_box.pack_end(&vbox, false, false, 0); - }*/ - // TODO + } } } From d76c0fe48b7377d1410ad105ad97e28c7b8811ad Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Fri, 18 Nov 2022 21:32:10 +0200 Subject: [PATCH 005/113] feat: update dependencies --- Cargo.lock | 310 +++++++----------- Cargo.toml | 1 + lact-daemon/Cargo.toml | 22 +- lact-daemon/src/server/gpu_controller/mod.rs | 23 +- lact-daemon/src/server/vulkan.rs | 8 +- lact-gui/Cargo.toml | 23 +- lact-gui/src/app/header.rs | 4 +- .../app/root_stack/info_page/vulkan_info.rs | 2 +- lact-schema/Cargo.toml | 7 +- lact-schema/src/lib.rs | 8 +- 10 files changed, 167 insertions(+), 241 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 73e57f8f..4e5d8b1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,9 +16,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] @@ -26,7 +26,7 @@ dependencies = [ [[package]] name = "amdgpu-sysfs" version = "0.5.0" -source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=blocking#0308d4c475f5091f7e8bc4b7b5d8c101f8dd40d7" +source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=error#cbd84338e02c98a429be0cb8da66e05ee34c2ece" dependencies = [ "serde", ] @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "atk" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a83b21d2aa75e464db56225e1bda2dd5993311ba1095acaa8fa03d1ae67026ba" +checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf" dependencies = [ "atk-sys", "bitflags", @@ -60,9 +60,9 @@ dependencies = [ [[package]] name = "atk-sys" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "badcf670157c84bb8b1cf6b5f70b650fed78da2033c9eed84c4e49b11cbe83ea" +checksum = "11ad703eb64dc058024f0e57ccfa069e15a413b98dbd50a1a950e743b7f11148" dependencies = [ "glib-sys", "gobject-sys", @@ -119,22 +119,23 @@ checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "cairo-rs" -version = "0.14.9" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b5725979db0c586d98abad2193cdb612dd40ef95cd26bd99851bf93b3cb482" +checksum = "247e1183fa769ac22121f92276dae52f89acaf297f24b1320019f439b6e3b46f" dependencies = [ "bitflags", "cairo-sys-rs", "glib", "libc", + "once_cell", "thiserror", ] [[package]] name = "cairo-sys-rs" -version = "0.14.9" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b448b876970834fda82ba3aeaccadbd760206b75388fc5c1b02f1e343b697570" +checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" dependencies = [ "glib-sys", "libc", @@ -143,9 +144,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.8.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b412e83326147c2bb881f8b40edfbf9905b9b8abaebd0e47ca190ba62fda8f0e" +checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" dependencies = [ "smallvec", ] @@ -196,12 +197,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -210,12 +210,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - [[package]] name = "field-offset" version = "0.3.4" @@ -273,6 +267,17 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-task" version = "0.3.25" @@ -286,6 +291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", @@ -294,9 +300,9 @@ dependencies = [ [[package]] name = "gdk" -version = "0.14.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d749dcfc00d8de0d7c3a289e04a04293eb5ba3d8a4e64d64911d481fa9933b" +checksum = "8f0a4a7aa015962d02634258715164977eb151b48fd250dcac48fab8d312a5aa" dependencies = [ "bitflags", "cairo-rs", @@ -310,10 +316,11 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.14.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534192cb8f01daeb8fab2c8d4baa8f9aae5b7a39130525779f5c2608e235b10f" +checksum = "0ba3e42776d1466938add08211734738d5c76e863a25b7a8064c4433a74a1a26" dependencies = [ + "bitflags", "gdk-pixbuf-sys", "gio", "glib", @@ -322,9 +329,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf-sys" -version = "0.14.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f097c0704201fbc8f69c1762dc58c6947c8bb188b8ed0bc7e65259f1894fe590" +checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016" dependencies = [ "gio-sys", "glib-sys", @@ -335,9 +342,9 @@ dependencies = [ [[package]] name = "gdk-sys" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e091b3d3d6696949ac3b3fb3c62090e5bfd7bd6850bef5c3c5ea701de1b1f1e" +checksum = "d76354f97a913e55b984759a997b693aa7dc71068c9e98bcce51aa167a0a5c5a" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -352,9 +359,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", @@ -363,26 +370,29 @@ dependencies = [ [[package]] name = "gio" -version = "0.14.8" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711c3632b3ebd095578a9c091418d10fed492da9443f58ebc8f45efbeb215cb0" +checksum = "1d4a17d999e6e4e05d87c2bb05b7140d47769bc53211711a33e2f91536458714" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-io", + "futures-util", "gio-sys", "glib", "libc", "once_cell", + "pin-project-lite", + "smallvec", "thiserror", ] [[package]] name = "gio-sys" -version = "0.14.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0a41df66e57fcc287c4bcf74fc26b884f31901ea9792ec75607289b456f48fa" +checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" dependencies = [ "glib-sys", "gobject-sys", @@ -393,31 +403,34 @@ dependencies = [ [[package]] name = "glib" -version = "0.14.8" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c515f1e62bf151ef6635f528d05b02c11506de986e43b34a5c920ef0b3796a4" +checksum = "50feee2f1e73be50e6634c901bfced69a0937c5e4e4673067ade85e093fa9bd7" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-executor", "futures-task", + "futures-util", + "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", "once_cell", "smallvec", + "thiserror", ] [[package]] name = "glib-macros" -version = "0.14.1" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aad66361f66796bfc73f530c51ef123970eb895ffba991a234fcf7bea89e518" +checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf" dependencies = [ "anyhow", - "heck 0.3.3", + "heck", "proc-macro-crate", "proc-macro-error", "proc-macro2", @@ -427,9 +440,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.14.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1d60554a212445e2a858e42a0e48cece1bd57b311a19a9468f70376cf554ae" +checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" dependencies = [ "libc", "system-deps", @@ -437,9 +450,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.14.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa92cae29759dae34ab5921d73fff5ad54b3d794ab842c117e36cafc7994c3f5" +checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" dependencies = [ "glib-sys", "libc", @@ -448,9 +461,9 @@ dependencies = [ [[package]] name = "gtk" -version = "0.14.3" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb51122dd3317e9327ec1e4faa151d1fa0d95664cd8fb8dcfacf4d4d29ac70c" +checksum = "ca4ab4a5a19d45748f405d2e00201e86d351e80cc13b9d43dfb9be35033a8bd6" dependencies = [ "atk", "bitflags", @@ -471,9 +484,9 @@ dependencies = [ [[package]] name = "gtk-sys" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c14c8d3da0545785a7c5a120345b3abb534010fb8ae0f2ef3f47c027fba303e" +checksum = "89b5f8946685d5fe44497007786600c2f368ff6b1e61a16251c89f72a97520a3" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -489,12 +502,11 @@ dependencies = [ [[package]] name = "gtk3-macros" -version = "0.14.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21de1da96dc117443fb03c2e270b2d34b7de98d0a79a19bbb689476173745b79" +checksum = "8cfd6557b1018b773e43c8de9d0d13581d6b36190d0501916cbec4731db5ccff" dependencies = [ "anyhow", - "heck 0.3.3", "proc-macro-crate", "proc-macro-error", "proc-macro2", @@ -517,15 +529,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "heck" version = "0.4.0" @@ -534,28 +537,20 @@ checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", + "serde", ] [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "lact-daemon" @@ -580,7 +575,6 @@ dependencies = [ name = "lact-gui" version = "0.1.0" dependencies = [ - "amdgpu-sysfs", "anyhow", "glib", "gtk", @@ -598,6 +592,7 @@ name = "lact-schema" version = "0.1.0" dependencies = [ "amdgpu-sysfs", + "indexmap", "serde", "serde_json", ] @@ -610,15 +605,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.132" +version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "libloading" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", "winapi", @@ -626,9 +621,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -678,14 +673,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", "wasi", - "windows-sys 0.36.1", + "windows-sys", ] [[package]] @@ -723,9 +718,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "overload" @@ -735,11 +730,12 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pango" -version = "0.14.8" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "546fd59801e5ca735af82839007edd226fe7d3bb06433ec48072be4439c28581" +checksum = "f6a83cd4015382dbb0f4fcf3ab7b277d4885711a62b2f2c1e6582a120094edad" dependencies = [ "bitflags", + "gio", "glib", "libc", "once_cell", @@ -748,9 +744,9 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.14.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2367099ca5e761546ba1d501955079f097caa186bb53ce0f718dca99ac1942fe" +checksum = "9e134909a9a293e04d2cc31928aa95679c5e4df954d0b85483159bd20d8f047f" dependencies = [ "glib-sys", "gobject-sys", @@ -778,7 +774,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -856,9 +852,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] @@ -883,9 +879,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", @@ -903,9 +899,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "rustc_version" @@ -968,9 +964,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +checksum = "8e8b3801309262e8184d9687fb697586833e939767aea0dda89f5a8e650e8bd7" dependencies = [ "itoa", "ryu", @@ -1019,43 +1015,25 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c98bba371b9b22a71a9414e420f92ddeb2369239af08200816169d5e2dd7aa" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", ] -[[package]] -name = "strum" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" - -[[package]] -name = "strum_macros" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "syn" -version = "1.0.99" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", @@ -1064,18 +1042,13 @@ dependencies = [ [[package]] name = "system-deps" -version = "3.2.0" +version = "6.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "480c269f870722b3b08d2f13053ce0c2ab722839f472863c3e2d61ff3a1c2fa6" +checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" dependencies = [ - "anyhow", "cfg-expr", - "heck 0.3.3", - "itertools", + "heck", "pkg-config", - "strum", - "strum_macros", - "thiserror", "toml", "version-compare", ] @@ -1111,9 +1084,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.21.2" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" dependencies = [ "autocfg", "bytes", @@ -1217,15 +1190,9 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-ident" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" - -[[package]] -name = "unicode-segmentation" -version = "1.10.0" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unsafe-libyaml" @@ -1241,9 +1208,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version-compare" -version = "0.0.11" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" [[package]] name = "version_check" @@ -1263,7 +1230,7 @@ dependencies = [ [[package]] name = "vulkano" version = "0.32.0" -source = "git+https://github.com/ilya-zlobintsev/vulkano?branch=arr_features#0c1f4f3bfcc94748ffe25512665f796e4bb6302f" +source = "git+https://github.com/vulkano-rs/vulkano#89e1c56db4c94752e9444483ebd1c12bdb135128" dependencies = [ "ahash", "ash", @@ -1271,7 +1238,7 @@ dependencies = [ "core-graphics-types", "crossbeam-queue", "half", - "heck 0.4.0", + "heck", "indexmap", "lazy_static", "libloading", @@ -1315,19 +1282,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -1335,12 +1289,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc", ] [[package]] @@ -1349,48 +1303,24 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - [[package]] name = "windows_aarch64_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - [[package]] name = "windows_i686_gnu" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - [[package]] name = "windows_i686_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - [[package]] name = "windows_x86_64_gnu" version = "0.42.0" @@ -1403,12 +1333,6 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - [[package]] name = "windows_x86_64_msvc" version = "0.42.0" diff --git a/Cargo.toml b/Cargo.toml index 50b30241..960cc6bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,3 @@ [workspace] +resolver = "2" members = ["lact-daemon", "lact-schema", "lact-gui"] diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index a8229243..5a81eed7 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -6,16 +6,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs", branch = "blocking", features = [ +amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs", branch = "error", features = [ "serde", ] } -anyhow = "1.0.66" -bincode = "1.3.3" -nix = "0.25.0" -pciid-parser = { version = "0.6.2", features = ["serde"] } -serde = { version = "1.0.147", features = ["derive"] } -serde_json = "1.0.87" -serde_yaml = "0.9.14" +anyhow = "1.0" +bincode = "1.3" +nix = "0.25" +pciid-parser = { version = "0.6", features = ["serde"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +serde_yaml = "0.9" tokio = { version = "1.21.2", features = [ "rt", "macros", @@ -25,7 +25,7 @@ tokio = { version = "1.21.2", features = [ "signal", "sync", ] } -tracing = "0.1.37" -tracing-subscriber = "0.3.16" -vulkano = { git = "https://github.com/ilya-zlobintsev/vulkano", branch = "arr_features" } +tracing = "0.1" +tracing-subscriber = "0.3" +vulkano = { git = "https://github.com/vulkano-rs/vulkano" } lact-schema = { path = "../lact-schema" } diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index ad2ed492..4ed3813d 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -4,6 +4,7 @@ use self::fan_control::FanCurve; use super::vulkan::get_vulkan_info; use crate::fork::run_forked; use amdgpu_sysfs::{ + error::Error, gpu_handle::GpuHandle, hw_mon::{FanControlMethod, HwMon}, }; @@ -103,7 +104,7 @@ impl GpuController { }); let pci_info = self.pci_info.as_ref().map(Cow::Borrowed); let driver = self.handle.get_driver(); - let vbios_version = self.handle.get_vbios_version(); + let vbios_version = self.handle.get_vbios_version().ok(); let link_info = self.get_link_info(); DeviceInfo { @@ -117,10 +118,10 @@ impl GpuController { fn get_link_info(&self) -> LinkInfo { LinkInfo { - current_width: self.handle.get_current_link_width(), - current_speed: self.handle.get_current_link_speed(), - max_width: self.handle.get_max_link_width(), - max_speed: self.handle.get_max_link_speed(), + current_width: self.handle.get_current_link_width().ok(), + current_speed: self.handle.get_current_link_speed().ok(), + max_width: self.handle.get_max_link_width().ok(), + max_speed: self.handle.get_max_link_speed().ok(), } } @@ -135,15 +136,15 @@ impl GpuController { .map_err(|err| anyhow!("Could not lock fan control mutex: {err}"))? .is_some(), temps: self.hw_mon_map(HwMon::get_temps).unwrap_or_default(), - total_vram: self.handle.get_total_vram(), - used_vram: self.handle.get_used_vram(), - busy_percent: self.handle.get_busy_percent(), - performance_level: self.handle.get_power_force_performance_level(), + total_vram: self.handle.get_total_vram().ok(), + used_vram: self.handle.get_used_vram().ok(), + busy_percent: self.handle.get_busy_percent().ok(), + performance_level: self.handle.get_power_force_performance_level().ok(), }) } - fn hw_mon_and_then(&self, f: fn(&HwMon) -> Option) -> Option { - self.handle.hw_monitors.first().and_then(f) + fn hw_mon_and_then(&self, f: fn(&HwMon) -> Result) -> Option { + self.handle.hw_monitors.first().and_then(|mon| f(mon).ok()) } fn hw_mon_map(&self, f: fn(&HwMon) -> U) -> Option { diff --git a/lact-daemon/src/server/vulkan.rs b/lact-daemon/src/server/vulkan.rs index 0d64da7b..73e3c773 100644 --- a/lact-daemon/src/server/vulkan.rs +++ b/lact-daemon/src/server/vulkan.rs @@ -30,14 +30,14 @@ pub fn get_vulkan_info<'a>(vendor_id: &'a str, device_id: &'a str) -> anyhow::Re driver_name: properties.driver_name.clone(), supported_features: device .supported_features() - .as_arr() + .into_iter() .map(|(name, enabled)| (Cow::Borrowed(name), enabled)) - .into(), + .collect(), supported_extensions: device .supported_extensions() - .as_arr() + .into_iter() .map(|(name, enabled)| (Cow::Borrowed(name), enabled)) - .into(), + .collect(), }; return Ok(info); } diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml index b0e7f34b..9db1e463 100644 --- a/lact-gui/Cargo.toml +++ b/lact-gui/Cargo.toml @@ -2,19 +2,16 @@ name = "lact-gui" version = "0.1.0" authors = ["Ilya Zlobintsev "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +edition = "2021" [dependencies] lact-schema = { path = "../lact-schema" } -gtk = { version = "0.14", features = ["v3_22"] } -pango = "0.14" -glib = "0.14" -tracing = "0.1.37" -tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } -nix = "0.25.0" -anyhow = "1.0.66" -serde_json = "1.0.87" -serde = "1.0.147" -amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs", branch = "blocking" } +gtk = "0.16" +pango = "0.16" +glib = "0.16" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +nix = "0.25" +anyhow = "1.0" +serde_json = "1.0" +serde = "1.0" diff --git a/lact-gui/src/app/header.rs b/lact-gui/src/app/header.rs index 5514029e..2b89005f 100644 --- a/lact-gui/src/app/header.rs +++ b/lact-gui/src/app/header.rs @@ -43,8 +43,8 @@ impl Header { //limits the length of gpu names in combobox for cell in self.gpu_selector.cells() { - cell.set_property("width-chars", &10).unwrap(); - cell.set_property("ellipsize", &EllipsizeMode::End).unwrap(); + cell.set_property("width-chars", &10); + cell.set_property("ellipsize", &EllipsizeMode::End); } self.gpu_selector.set_active(Some(0)); diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs index 9dcfe178..b84207d3 100644 --- a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs @@ -75,7 +75,7 @@ impl VulkanInfoFrame { grid.attach(&features_expander, 0, 2, 5, 1); - let features_scrolled_window = ScrolledWindow::new(NONE_ADJUSTMENT, NONE_ADJUSTMENT); + let features_scrolled_window = ScrolledWindow::builder().build(); features_scrolled_window.set_vexpand(true); diff --git a/lact-schema/Cargo.toml b/lact-schema/Cargo.toml index b7b97b1c..117fb2cb 100644 --- a/lact-schema/Cargo.toml +++ b/lact-schema/Cargo.toml @@ -4,10 +4,11 @@ version = "0.1.0" edition = "2021" [dependencies] -amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs", branch = "blocking", features = [ +amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs", branch = "error", features = [ "serde", ] } -serde = { version = "1.0.147", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } +indexmap = { version = "1.9", features = ["serde"] } [dev-dependencies] -serde_json = "1.0.87" +serde_json = "1.0" diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index 21fc78c7..0b532f0f 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -3,7 +3,9 @@ pub mod response; #[cfg(test)] mod tests; -use amdgpu_sysfs::{gpu_handle::PerformanceLevel, hw_mon::Temperature}; +pub use amdgpu_sysfs::{gpu_handle::PerformanceLevel, hw_mon::Temperature}; + +use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, collections::HashMap}; @@ -42,8 +44,8 @@ pub struct VulkanInfo { pub device_name: String, pub api_version: String, pub driver_name: Option, - pub supported_features: HashMap, bool>, - pub supported_extensions: HashMap, bool>, + pub supported_features: IndexMap, bool>, + pub supported_extensions: IndexMap, bool>, } #[derive(Serialize, Deserialize, Debug, Clone)] From 3a0f7b0aefc5e8bc11cb18177c7f9cf09f87653e Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sat, 19 Nov 2022 15:21:07 +0200 Subject: [PATCH 006/113] gui modules restructure --- lact-gui/src/{app.rs => app/mod.rs} | 0 lact-gui/src/app/root_stack/{info_page.rs => info_page/mod.rs} | 0 lact-gui/src/app/{root_stack.rs => root_stack/mod.rs} | 0 lact-gui/src/app/root_stack/{oc_page.rs => oc_page/mod.rs} | 0 .../src/app/root_stack/{thermals_page.rs => thermals_page/mod.rs} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename lact-gui/src/{app.rs => app/mod.rs} (100%) rename lact-gui/src/app/root_stack/{info_page.rs => info_page/mod.rs} (100%) rename lact-gui/src/app/{root_stack.rs => root_stack/mod.rs} (100%) rename lact-gui/src/app/root_stack/{oc_page.rs => oc_page/mod.rs} (100%) rename lact-gui/src/app/root_stack/{thermals_page.rs => thermals_page/mod.rs} (100%) diff --git a/lact-gui/src/app.rs b/lact-gui/src/app/mod.rs similarity index 100% rename from lact-gui/src/app.rs rename to lact-gui/src/app/mod.rs diff --git a/lact-gui/src/app/root_stack/info_page.rs b/lact-gui/src/app/root_stack/info_page/mod.rs similarity index 100% rename from lact-gui/src/app/root_stack/info_page.rs rename to lact-gui/src/app/root_stack/info_page/mod.rs diff --git a/lact-gui/src/app/root_stack.rs b/lact-gui/src/app/root_stack/mod.rs similarity index 100% rename from lact-gui/src/app/root_stack.rs rename to lact-gui/src/app/root_stack/mod.rs diff --git a/lact-gui/src/app/root_stack/oc_page.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs similarity index 100% rename from lact-gui/src/app/root_stack/oc_page.rs rename to lact-gui/src/app/root_stack/oc_page/mod.rs diff --git a/lact-gui/src/app/root_stack/thermals_page.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs similarity index 100% rename from lact-gui/src/app/root_stack/thermals_page.rs rename to lact-gui/src/app/root_stack/thermals_page/mod.rs From 5c48d519f6b34508af46a8e0d1f8afe9fd2e8a8f Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sat, 19 Nov 2022 15:46:33 +0200 Subject: [PATCH 007/113] feat: show vulkan extensions --- Cargo.lock | 2 - lact-gui/Cargo.toml | 3 +- .../app/root_stack/info_page/vulkan_info.rs | 53 ++++++++++++++++--- lact-gui/src/client.rs | 2 +- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e5d8b1b..db7b71d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,11 +576,9 @@ name = "lact-gui" version = "0.1.0" dependencies = [ "anyhow", - "glib", "gtk", "lact-schema", "nix", - "pango", "serde", "serde_json", "tracing", diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml index 9db1e463..45f8e6e2 100644 --- a/lact-gui/Cargo.toml +++ b/lact-gui/Cargo.toml @@ -7,8 +7,7 @@ edition = "2021" [dependencies] lact-schema = { path = "../lact-schema" } gtk = "0.16" -pango = "0.16" -glib = "0.16" +# pango = "0.16" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } nix = "0.25" diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs index b84207d3..287c60f0 100644 --- a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs @@ -1,7 +1,6 @@ use gtk::prelude::*; use gtk::*; use lact_schema::VulkanInfo; -use std::collections::BTreeMap; use tracing::trace; #[derive(Clone)] @@ -10,6 +9,7 @@ pub struct VulkanInfoFrame { device_name_label: Label, version_label: Label, features_box: Box, + extensions_box: Box, } impl VulkanInfoFrame { @@ -25,6 +25,8 @@ impl VulkanInfoFrame { container.set_shadow_type(ShadowType::None); + let vbox = Box::new(Orientation::Vertical, 5); + let grid = Grid::new(); grid.set_margin_start(5); @@ -33,6 +35,7 @@ impl VulkanInfoFrame { grid.set_margin_top(5); grid.set_column_homogeneous(true); + grid.set_row_homogeneous(false); grid.set_row_spacing(7); grid.set_column_spacing(5); @@ -71,9 +74,11 @@ impl VulkanInfoFrame { grid.attach(&version_label, 2, 1, 3, 1); + vbox.pack_start(&grid, false, true, 5); + let features_expander = Expander::new(Some("Feature support")); - grid.attach(&features_expander, 0, 2, 5, 1); + vbox.pack_start(&features_expander, false, true, 5); let features_scrolled_window = ScrolledWindow::builder().build(); @@ -87,13 +92,32 @@ impl VulkanInfoFrame { features_expander.add(&features_scrolled_window); - container.add(&grid); + let extensions_box = Box::builder() + .orientation(Orientation::Vertical) + .spacing(5) + .halign(Align::Center) + .build(); + + let extensions_expander = Expander::builder() + .label("Extension support") + .child( + &ScrolledWindow::builder() + .vexpand(true) + .child(&extensions_box) + .build(), + ) + .build(); + + vbox.pack_start(&extensions_expander, false, true, 5); + + container.add(&vbox); Self { container, device_name_label, version_label, features_box, + extensions_box, } } @@ -105,11 +129,10 @@ impl VulkanInfoFrame { self.version_label .set_markup(&format!("{}", vulkan_info.api_version)); - let features: BTreeMap<_, _> = vulkan_info.supported_features.iter().collect(); - for (feature, supported) in features.into_iter() { + for (feature, supported) in &vulkan_info.supported_features { let vbox = Box::new(Orientation::Horizontal, 5); - let feature_name_label = Label::new(Some(feature)); + let feature_name_label = Label::new(Some(&feature)); vbox.pack_start(&feature_name_label, false, false, 0); @@ -118,9 +141,23 @@ impl VulkanInfoFrame { feature_supported_checkbutton.set_sensitive(false); feature_supported_checkbutton.set_active(*supported); - vbox.pack_start(&feature_supported_checkbutton, false, false, 0); + vbox.pack_end(&feature_supported_checkbutton, false, false, 0); + + self.features_box.pack_start(&vbox, false, false, 0); + } + + for (extension, supported) in &vulkan_info.supported_extensions { + let vbox = Box::new(Orientation::Horizontal, 5); + let extension_name_label = Label::new(Some(&extension)); + vbox.pack_start(&extension_name_label, false, false, 0); + + let extension_supported_checkbutton = CheckButton::builder() + .sensitive(false) + .active(*supported) + .build(); + vbox.pack_end(&extension_supported_checkbutton, false, false, 0); - self.features_box.pack_end(&vbox, false, false, 0); + self.extensions_box.pack_start(&vbox, false, false, 0); } } } diff --git a/lact-gui/src/client.rs b/lact-gui/src/client.rs index 071148ba..3bf3ce52 100644 --- a/lact-gui/src/client.rs +++ b/lact-gui/src/client.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Context}; use lact_schema::{request::Request, response::Response, DeviceInfo, DeviceListEntry, DeviceStats}; use nix::unistd::getuid; -use serde::{de::DeserializeOwned, Deserialize}; +use serde::Deserialize; use std::{ io::{BufRead, BufReader, Write}, marker::PhantomData, From 2116e2202c4e05bd94ac946a1471b30ea166a6e8 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sat, 19 Nov 2022 21:42:34 +0200 Subject: [PATCH 008/113] vram fix --- lact-gui/src/app/mod.rs | 26 +++++++++---------- lact-gui/src/app/root_stack/info_page/mod.rs | 18 +++++++------ .../app/root_stack/info_page/vulkan_info.rs | 8 +++--- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs index 8701e73d..77ad69a7 100644 --- a/lact-gui/src/app/mod.rs +++ b/lact-gui/src/app/mod.rs @@ -9,6 +9,7 @@ use std::time::Duration; use crate::client::DaemonClient; use apply_revealer::ApplyRevealer; +use glib::clone; use gtk::prelude::*; use gtk::*; use header::Header; @@ -249,12 +250,11 @@ impl App { // The loop that gets stats let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); - { - let daemon_connection = self.daemon_client.clone(); - thread::spawn(move || loop { + thread::spawn( + clone!(@strong self.daemon_client as daemon_client => move || loop { let gpu_id = current_gpu_id.read().unwrap(); - match daemon_connection + match daemon_client .get_device_stats(&gpu_id) .and_then(|stats| stats.inner()) { @@ -266,19 +266,19 @@ impl App { } } thread::sleep(Duration::from_millis(500)); - }); - } + }), + ); // Receiving stats into the gui event loop - { - let thermals_page = self.root_stack.thermals_page.clone(); - // let oc_page = self.root_stack.oc_page.clone(); - receiver.attach(None, move |msg| { + receiver.attach( + None, + clone!(@strong self.root_stack as root_stack, => move |msg| { match msg { GuiUpdateMsg::GpuStats(stats) => { trace!("New stats received, updating {stats:?}"); - thermals_page.set_stats(&stats); + root_stack.info_page.set_stats(&stats); + root_stack.thermals_page.set_stats(&stats); // oc_page.set_stats(&stats); } /*GuiUpdateMsg::FanControlInfo(fan_control_info) => { thermals_page.set_ventilation_info(fan_control_info) @@ -286,8 +286,8 @@ impl App { } glib::Continue(true) - }); - } + }), + ); } } diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs index 86819ce5..550e2ddb 100644 --- a/lact-gui/src/app/root_stack/info_page/mod.rs +++ b/lact-gui/src/app/root_stack/info_page/mod.rs @@ -2,7 +2,7 @@ mod vulkan_info; use gtk::prelude::*; use gtk::*; -use lact_schema::DeviceInfo; +use lact_schema::{DeviceInfo, DeviceStats}; use vulkan_info::VulkanInfoFrame; #[derive(Clone)] @@ -184,13 +184,6 @@ impl InformationPage { self.driver_label .set_markup(&format!("{}", gpu_info.driver)); - // TODO - // let vram_size = gpu_info - // .vram_size - // .map_or_else(|| "unknown".to_owned(), |size| size.to_string()); - // self.vram_size_label - // .set_markup(&format!("{vram_size}")); - let link_speed = gpu_info .link_info .current_speed @@ -210,4 +203,13 @@ impl InformationPage { self.container.show_all(); } + + pub fn set_stats(&self, stats: &DeviceStats) { + let vram_size = stats.total_vram.map_or_else( + || "unknown".to_owned(), + |size| (size / 1024 / 1024).to_string(), + ); + self.vram_size_label + .set_markup(&format!("{vram_size}MiB")); + } } diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs index 287c60f0..3b77d948 100644 --- a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs @@ -76,22 +76,22 @@ impl VulkanInfoFrame { vbox.pack_start(&grid, false, true, 5); - let features_expander = Expander::new(Some("Feature support")); - - vbox.pack_start(&features_expander, false, true, 5); + let features_expander = Expander::builder().label("Feature support").build(); let features_scrolled_window = ScrolledWindow::builder().build(); features_scrolled_window.set_vexpand(true); let features_box = Box::new(Orientation::Vertical, 5); - features_box.set_halign(Align::Center); + features_box.set_valign(Align::Fill); features_scrolled_window.add(&features_box); features_expander.add(&features_scrolled_window); + vbox.pack_start(&features_expander, false, true, 5); + let extensions_box = Box::builder() .orientation(Orientation::Vertical) .spacing(5) From 4068ebd34989d1fb1666b87de6498f6066cca967 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sat, 19 Nov 2022 22:23:47 +0200 Subject: [PATCH 009/113] somewhat working stats --- Cargo.lock | 2 +- lact-daemon/src/server/gpu_controller/mod.rs | 43 +++++++++--- lact-gui/src/app/mod.rs | 4 +- lact-gui/src/app/root_stack/info_page/mod.rs | 4 +- lact-gui/src/app/root_stack/mod.rs | 12 ++-- lact-gui/src/app/root_stack/oc_page/mod.rs | 18 ++--- .../oc_page/performance_level_frame.rs | 2 +- .../src/app/root_stack/oc_page/stats_grid.rs | 69 +++++++++++-------- .../src/app/root_stack/thermals_page/mod.rs | 8 +-- lact-schema/src/lib.rs | 41 +++++++++-- 10 files changed, 134 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db7b71d9..082458d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ dependencies = [ [[package]] name = "amdgpu-sysfs" version = "0.5.0" -source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=error#cbd84338e02c98a429be0cb8da66e05ee34c2ece" +source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=error#756d7818aabcc6e42b75cb4f32a4cb33fba867fb" dependencies = [ "serde", ] diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index 4ed3813d..4c43efa4 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -9,7 +9,10 @@ use amdgpu_sysfs::{ hw_mon::{FanControlMethod, HwMon}, }; use anyhow::{anyhow, Context}; -use lact_schema::{DeviceInfo, DeviceStats, GpuPciInfo, LinkInfo, PciInfo}; +use lact_schema::{ + ClockspeedStats, DeviceInfo, DeviceStats, FanStats, GpuPciInfo, LinkInfo, PciInfo, PowerStats, + VoltageStats, VramStats, +}; use pciid_parser::Database; use std::{ borrow::Cow, @@ -127,17 +130,35 @@ impl GpuController { pub fn get_stats(&self) -> anyhow::Result { Ok(DeviceStats { - fan_speed_current: self.hw_mon_and_then(HwMon::get_fan_current), - fan_speed_max: self.hw_mon_and_then(HwMon::get_fan_max), - fan_speed_min: self.hw_mon_and_then(HwMon::get_fan_min), - fan_control_enabled: self - .fan_control_handle - .lock() - .map_err(|err| anyhow!("Could not lock fan control mutex: {err}"))? - .is_some(), + fan_stats: FanStats { + fan_speed_current: self.hw_mon_and_then(HwMon::get_fan_current), + fan_speed_max: self.hw_mon_and_then(HwMon::get_fan_max), + fan_speed_min: self.hw_mon_and_then(HwMon::get_fan_min), + fan_control_enabled: self + .fan_control_handle + .lock() + .map_err(|err| anyhow!("Could not lock fan control mutex: {err}"))? + .is_some(), + }, + clockspeed_stats: ClockspeedStats { + gpu_clockspeed: self.hw_mon_and_then(HwMon::get_gpu_clockspeed), + vram_clockspeed: self.hw_mon_and_then(HwMon::get_vram_clockspeed), + }, + voltage_stats: VoltageStats { + gpu_voltage: self.hw_mon_and_then(HwMon::get_gpu_voltage), + northbridge_voltage: self.hw_mon_and_then(HwMon::get_northbridge_voltage), + }, + vram_stats: VramStats { + total_vram: self.handle.get_total_vram().ok(), + used_vram: self.handle.get_used_vram().ok(), + }, + power_stats: PowerStats { + power_average: self.hw_mon_and_then(HwMon::get_power_average), + power_cap_current: self.hw_mon_and_then(HwMon::get_power_cap), + power_cap_max: self.hw_mon_and_then(HwMon::get_power_cap_max), + power_cap_min: self.hw_mon_and_then(HwMon::get_power_cap_min), + }, temps: self.hw_mon_map(HwMon::get_temps).unwrap_or_default(), - total_vram: self.handle.get_total_vram().ok(), - used_vram: self.handle.get_used_vram().ok(), busy_percent: self.handle.get_busy_percent().ok(), performance_level: self.handle.get_power_force_performance_level().ok(), }) diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs index 77ad69a7..73283984 100644 --- a/lact-gui/src/app/mod.rs +++ b/lact-gui/src/app/mod.rs @@ -273,13 +273,13 @@ impl App { receiver.attach( None, - clone!(@strong self.root_stack as root_stack, => move |msg| { + clone!(@strong self.root_stack as root_stack => move |msg| { match msg { GuiUpdateMsg::GpuStats(stats) => { trace!("New stats received, updating {stats:?}"); root_stack.info_page.set_stats(&stats); root_stack.thermals_page.set_stats(&stats); - // oc_page.set_stats(&stats); + root_stack.oc_page.set_stats(&stats); } /*GuiUpdateMsg::FanControlInfo(fan_control_info) => { thermals_page.set_ventilation_info(fan_control_info) }*/ diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs index 550e2ddb..06d2f14d 100644 --- a/lact-gui/src/app/root_stack/info_page/mod.rs +++ b/lact-gui/src/app/root_stack/info_page/mod.rs @@ -205,11 +205,11 @@ impl InformationPage { } pub fn set_stats(&self, stats: &DeviceStats) { - let vram_size = stats.total_vram.map_or_else( + let vram_size = stats.vram_stats.total_vram.map_or_else( || "unknown".to_owned(), |size| (size / 1024 / 1024).to_string(), ); self.vram_size_label - .set_markup(&format!("{vram_size}MiB")); + .set_markup(&format!("{vram_size} MiB")); } } diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs index 0b232c29..60bd65f4 100644 --- a/lact-gui/src/app/root_stack/mod.rs +++ b/lact-gui/src/app/root_stack/mod.rs @@ -1,5 +1,5 @@ mod info_page; -// mod oc_page; +mod oc_page; mod software_page; mod thermals_page; @@ -7,7 +7,7 @@ use gtk::prelude::*; use gtk::*; use info_page::InformationPage; -// use oc_page::OcPage; +use oc_page::OcPage; use software_page::SoftwarePage; use thermals_page::ThermalsPage; @@ -17,7 +17,7 @@ pub struct RootStack { pub info_page: InformationPage, pub thermals_page: ThermalsPage, pub software_page: SoftwarePage, - // pub oc_page: OcPage, + pub oc_page: OcPage, } impl RootStack { @@ -28,9 +28,9 @@ impl RootStack { container.add_titled(&info_page.container, "info_page", "Information"); - // let oc_page = OcPage::new(); + let oc_page = OcPage::new(); - // container.add_titled(&oc_page.container, "oc_page", "OC"); + container.add_titled(&oc_page.container, "oc_page", "OC"); let thermals_page = ThermalsPage::new(); @@ -44,7 +44,7 @@ impl RootStack { container, info_page, thermals_page, - // oc_page, + oc_page, software_page, } } diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs index c792cb39..7017c63e 100644 --- a/lact-gui/src/app/root_stack/oc_page/mod.rs +++ b/lact-gui/src/app/root_stack/oc_page/mod.rs @@ -4,10 +4,9 @@ mod power_cap_frame; mod stats_grid; mod warning_frame; -use amdgpu_sysfs::gpu_handle::PerformanceLevel; use gtk::prelude::*; use gtk::*; -use lact_schema::{DeviceInfo, DeviceStats}; +use lact_schema::{DeviceInfo, DeviceStats, PerformanceLevel}; use performance_level_frame::PowerProfileFrame; use power_cap_frame::PowerCapFrame; use stats_grid::StatsGrid; @@ -62,9 +61,10 @@ impl OcPage { } pub fn connect_clocks_reset(&self, f: F) { - self.clocks_frame.connect_clocks_reset(move || { + /*self.clocks_frame.connect_clocks_reset(move || { f(); - }); + });*/ + todo!() } pub fn connect_settings_changed(&self, f: F) { @@ -76,10 +76,10 @@ impl OcPage { }); } { - let f = f.clone(); + /*let f = f.clone(); self.clocks_frame.connect_clocks_changed(move || { f(); - }) + })*/ } { self.power_cap_frame.connect_cap_changed(move || { @@ -98,7 +98,7 @@ impl OcPage { } } - pub fn get_performance_level(&self) -> Option { + pub fn get_performance_level(&self) -> Option { match self.performance_level_frame.get_visibility() { true => Some( self.performance_level_frame @@ -122,12 +122,12 @@ impl OcPage { .set_data(info.power_cap, info.power_cap_max);*/ } - pub fn get_clocks(&self) -> Option { + /*pub fn get_clocks(&self) -> Option { match self.clocks_frame.get_visibility() { true => Some(self.clocks_frame.get_settings()), false => None, } - } + }*/ pub fn get_power_cap(&self) -> Option { self.power_cap_frame.get_cap() diff --git a/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs index f7445bab..9d0eb644 100644 --- a/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs +++ b/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs @@ -1,6 +1,6 @@ -use amdgpu_sysfs::gpu_handle::PerformanceLevel; use gtk::prelude::*; use gtk::*; +use lact_schema::PerformanceLevel; #[derive(Clone)] pub struct PowerProfileFrame { diff --git a/lact-gui/src/app/root_stack/oc_page/stats_grid.rs b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs index 8f26f282..bd8ce234 100644 --- a/lact-gui/src/app/root_stack/oc_page/stats_grid.rs +++ b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs @@ -1,6 +1,6 @@ use gtk::prelude::*; use gtk::*; -use lact_schema::DeviceStats; +use lact_schema::{ClockspeedStats, DeviceStats, PowerStats, VoltageStats, VramStats}; #[derive(Clone)] pub struct StatsGrid { @@ -144,51 +144,62 @@ impl StatsGrid { } pub fn set_stats(&self, stats: &DeviceStats) { - self.vram_usage_bar.set_value( - stats.mem_used.unwrap_or_else(|| 0) as f64 - / stats.mem_total.unwrap_or_else(|| 0) as f64, - ); + let VramStats { + total_vram, + used_vram, + } = stats.vram_stats; + + self.vram_usage_bar + .set_value(used_vram.unwrap_or(0) as f64 / total_vram.unwrap_or(0) as f64); self.vram_usage_label.set_text(&format!( "{}/{} MiB", - stats.mem_used.unwrap_or_else(|| 0), - stats.mem_total.unwrap_or_else(|| 0) + used_vram.unwrap_or(0) / 1024 / 1024, + total_vram.unwrap_or(0) / 1024 / 1024, )); - self.gpu_clock_label.set_markup(&format!( - "{}MHz", - stats.gpu_freq.unwrap_or_else(|| 0) - )); + let ClockspeedStats { + gpu_clockspeed, + vram_clockspeed, + } = stats.clockspeed_stats; - self.vram_clock_label.set_markup(&format!( - "{}MHz", - stats.mem_freq.unwrap_or_else(|| 0) - )); + self.gpu_clock_label + .set_markup(&format!("{}MHz", gpu_clockspeed.unwrap_or(0))); + self.vram_clock_label + .set_markup(&format!("{}MHz", vram_clockspeed.unwrap_or(0))); + + let VoltageStats { gpu_voltage, .. } = stats.voltage_stats; self.gpu_voltage_label.set_markup(&format!( "{}V", - stats.voltage.unwrap_or_else(|| 0) as f64 / 1000f64 + gpu_voltage.unwrap_or(0) as f64 / 1000f64 )); + let PowerStats { + power_average, + power_cap_current, + power_cap_max, + power_cap_min, + } = stats.power_stats; + self.power_usage_label.set_markup(&format!( "{}/{}W", - stats.power_avg.unwrap_or_else(|| 0), - stats.power_cap.unwrap_or_else(|| 0) + power_average.unwrap_or(0.0), + power_cap_current.unwrap_or(0.0) )); - let temp = match stats.temperatures.get("junction") { - Some(temp) => Some(temp.current), - None => match stats.temperatures.get("edge") { - Some(temp) => Some(temp.current), - None => None, - }, - }; + let maybe_temp = stats + .temps + .get("junction") + .or_else(|| stats.temps.get("edge")); - if let Some(temp) = temp { + if let Some(temp) = maybe_temp.and_then(|temp| temp.current) { self.gpu_temperature_label - .set_markup(&format!("{}°C", temp)); + .set_markup(&format!("{temp}°C")); } - self.gpu_usage_label - .set_markup(&format!("{}%", stats.gpu_usage.unwrap_or_default())); + self.gpu_usage_label.set_markup(&format!( + "{}%", + stats.busy_percent.unwrap_or_default() + )); } } diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs index 7956798f..dd6119bc 100644 --- a/lact-gui/src/app/root_stack/thermals_page/mod.rs +++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs @@ -145,12 +145,12 @@ impl ThermalsPage { self.temp_label .set_markup(&format!("{temperatures_text}",)); - match stats.fan_speed_current { + match stats.fan_stats.fan_speed_current { Some(fan_speed_current) => self.fan_speed_label.set_markup(&format!( "{} RPM ({}%)", fan_speed_current, (fan_speed_current as f64 - / stats.fan_speed_max.unwrap_or(fan_speed_current) as f64 + / stats.fan_stats.fan_speed_max.unwrap_or(fan_speed_current) as f64 * 100.0) .round() )), @@ -159,9 +159,9 @@ impl ThermalsPage { self.fan_control_enabled_switch.set_visible(true); self.fan_control_enabled_switch - .set_active(!stats.fan_control_enabled); + .set_active(!stats.fan_stats.fan_control_enabled); - if stats.fan_control_enabled { + if stats.fan_stats.fan_control_enabled { self.fan_curve_frame.show(); } else { self.fan_curve_frame.hide(); diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index 0b532f0f..ed26485d 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -58,13 +58,46 @@ pub struct PciInfo { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DeviceStats { + pub fan_stats: FanStats, + pub clockspeed_stats: ClockspeedStats, + pub voltage_stats: VoltageStats, + pub vram_stats: VramStats, + pub power_stats: PowerStats, + pub temps: HashMap, + pub busy_percent: Option, + pub performance_level: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +pub struct FanStats { + pub fan_control_enabled: bool, pub fan_speed_current: Option, pub fan_speed_max: Option, pub fan_speed_min: Option, - pub fan_control_enabled: bool, - pub temps: HashMap, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +pub struct ClockspeedStats { + pub gpu_clockspeed: Option, + pub vram_clockspeed: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +pub struct VoltageStats { + pub gpu_voltage: Option, + pub northbridge_voltage: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +pub struct VramStats { pub total_vram: Option, pub used_vram: Option, - pub busy_percent: Option, - pub performance_level: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +pub struct PowerStats { + pub power_average: Option, + pub power_cap_current: Option, + pub power_cap_max: Option, + pub power_cap_min: Option, } From 21a9a7886e31ae412b4b66eb0b0a9ec7bc79344d Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sat, 19 Nov 2022 22:28:14 +0200 Subject: [PATCH 010/113] power stats --- lact-gui/src/app/root_stack/oc_page/mod.rs | 11 ++++++----- .../src/app/root_stack/oc_page/power_cap_frame.rs | 6 +++--- lact-gui/src/app/root_stack/oc_page/stats_grid.rs | 3 +-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs index 7017c63e..ddf5b90e 100644 --- a/lact-gui/src/app/root_stack/oc_page/mod.rs +++ b/lact-gui/src/app/root_stack/oc_page/mod.rs @@ -6,7 +6,7 @@ mod warning_frame; use gtk::prelude::*; use gtk::*; -use lact_schema::{DeviceInfo, DeviceStats, PerformanceLevel}; +use lact_schema::{DeviceInfo, DeviceStats, PerformanceLevel, PowerStats}; use performance_level_frame::PowerProfileFrame; use power_cap_frame::PowerCapFrame; use stats_grid::StatsGrid; @@ -58,6 +58,10 @@ impl OcPage { pub fn set_stats(&self, stats: &DeviceStats) { self.stats_grid.set_stats(stats); + self.power_cap_frame.set_data( + stats.power_stats.power_cap_current, + stats.power_stats.power_cap_max, + ); } pub fn connect_clocks_reset(&self, f: F) { @@ -108,7 +112,7 @@ impl OcPage { } } - pub fn set_info(&self, info: &DeviceInfo) { + pub fn set_power_stats(&self, power_stats: PowerStats) { // TODO /*match &info.clocks_table { Some(clocks_table) => { @@ -117,9 +121,6 @@ impl OcPage { } None => self.clocks_frame.hide(), }*/ - - /*self.power_cap_frame - .set_data(info.power_cap, info.power_cap_max);*/ } /*pub fn get_clocks(&self) -> Option { diff --git a/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs b/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs index 683eecb4..f9eedd3e 100644 --- a/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs +++ b/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs @@ -50,14 +50,14 @@ impl PowerCapFrame { } } - pub fn set_data(&self, power_cap: Option, power_cap_max: Option) { + pub fn set_data(&self, power_cap: Option, power_cap_max: Option) { if let Some(power_cap_max) = power_cap_max { - self.adjustment.set_upper(power_cap_max as f64); + self.adjustment.set_upper(power_cap_max); } else { self.container.set_visible(false); } if let Some(power_cap) = power_cap { - self.adjustment.set_value(power_cap as f64); + self.adjustment.set_value(power_cap); } else { self.container.set_visible(false); } diff --git a/lact-gui/src/app/root_stack/oc_page/stats_grid.rs b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs index bd8ce234..578b4c0a 100644 --- a/lact-gui/src/app/root_stack/oc_page/stats_grid.rs +++ b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs @@ -177,8 +177,7 @@ impl StatsGrid { let PowerStats { power_average, power_cap_current, - power_cap_max, - power_cap_min, + .. } = stats.power_stats; self.power_usage_label.set_markup(&format!( From a7fac939ac2981d60bdf7483cfb9b797aaed455d Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 20 Nov 2022 16:26:48 +0200 Subject: [PATCH 011/113] feat: power limit settings --- Cargo.lock | 2 +- lact-daemon/src/config.rs | 2 + lact-daemon/src/main.rs | 13 ++-- lact-daemon/src/server/gpu_controller/mod.rs | 1 + lact-daemon/src/server/handler.rs | 69 ++++++++++++++++++-- lact-daemon/src/server/mod.rs | 1 + lact-schema/src/lib.rs | 1 + lact-schema/src/request.rs | 3 +- 8 files changed, 79 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 082458d1..9d1eb965 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ dependencies = [ [[package]] name = "amdgpu-sysfs" version = "0.5.0" -source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=error#756d7818aabcc6e42b75cb4f32a4cb33fba867fb" +source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=error#4ffd5ee0be7fb644c50a6163c3eea6b3872333cd" dependencies = [ "serde", ] diff --git a/lact-daemon/src/config.rs b/lact-daemon/src/config.rs index 15894bb3..32228a80 100644 --- a/lact-daemon/src/config.rs +++ b/lact-daemon/src/config.rs @@ -30,6 +30,7 @@ impl Default for DaemonConfig { pub struct GpuConfig { pub fan_control_enabled: bool, pub fan_control_settings: Option, + pub power_cap: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -104,6 +105,7 @@ mod tests { temperature_key: "edge".to_owned(), interval_ms: 500, }), + ..Default::default() }, )] .into(), diff --git a/lact-daemon/src/main.rs b/lact-daemon/src/main.rs index d0ce23cf..45a3a6e2 100644 --- a/lact-daemon/src/main.rs +++ b/lact-daemon/src/main.rs @@ -8,7 +8,7 @@ use config::Config; use server::Server; use std::str::FromStr; use tokio::signal::ctrl_c; -use tracing::{warn, Level}; +use tracing::{debug_span, Instrument, Level}; #[tokio::main(flavor = "current_thread")] async fn main() -> anyhow::Result<()> { @@ -23,13 +23,12 @@ async fn main() -> anyhow::Result<()> { tokio::spawn(async move { ctrl_c().await.expect("Could not listen to shutdown signal"); - for controller in handler.gpu_controllers.values() { - if let Err(err) = controller.stop_fan_control().await { - warn!("Could not stop fan control on shutdown: {err}"); - } + async { + handler.cleanup().await; + socket::cleanup(); } - - socket::cleanup(); + .instrument(debug_span!("shutdown_cleanup")) + .await; std::process::exit(0); }); diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index 4c43efa4..291e8f34 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -157,6 +157,7 @@ impl GpuController { power_cap_current: self.hw_mon_and_then(HwMon::get_power_cap), power_cap_max: self.hw_mon_and_then(HwMon::get_power_cap_max), power_cap_min: self.hw_mon_and_then(HwMon::get_power_cap_min), + power_cap_default: self.hw_mon_and_then(HwMon::get_power_cap_default), }, temps: self.hw_mon_map(HwMon::get_temps).unwrap_or_default(), busy_percent: self.handle.get_busy_percent().ok(), diff --git a/lact-daemon/src/server/handler.rs b/lact-daemon/src/server/handler.rs index 21bb9ee9..d1785bd2 100644 --- a/lact-daemon/src/server/handler.rs +++ b/lact-daemon/src/server/handler.rs @@ -1,5 +1,5 @@ use super::gpu_controller::{fan_control::FanCurve, GpuController}; -use crate::config::{Config, FanControlSettings}; +use crate::config::{Config, FanControlSettings, GpuConfig}; use amdgpu_sysfs::sysfs::SysFS; use anyhow::{anyhow, Context}; use lact_schema::{DeviceInfo, DeviceListEntry, DeviceStats}; @@ -83,6 +83,16 @@ impl<'a> Handler { interval, )?; } + + if let Some(power_cap) = gpu_config.power_cap { + controller + .handle + .hw_monitors + .first() + .context("GPU has power cap defined but has no hardware monitor")? + .set_power_cap(power_cap) + .context("Could not set power cap")?; + } } else { info!("Could not find GPU with id {id} defined in configuration"); } @@ -94,6 +104,20 @@ impl<'a> Handler { }) } + fn edit_config(&self, f: F) -> anyhow::Result<()> { + let mut config_guard = self.config.write().map_err(|err| anyhow!("{err}"))?; + f(&mut config_guard); + config_guard.save()?; + Ok(()) + } + + fn edit_gpu_config(&self, id: String, f: F) -> anyhow::Result<()> { + self.edit_config(|config| { + let gpu_config = config.gpus.entry(id).or_default(); + f(gpu_config); + }) + } + fn controller_by_id(&self, id: &str) -> anyhow::Result<&GpuController> { Ok(self .gpu_controllers @@ -143,11 +167,48 @@ impl<'a> Handler { interval, )?; gpu_config.fan_control_enabled = true; - config_guard.save().context("Could not save config")?; - - Ok(()) + config_guard.save().context("Could not save config") } else { self.controller_by_id(id)?.stop_fan_control().await } } + + pub fn set_power_limit(&'a self, id: &str, limit: f64) -> anyhow::Result<()> { + self.controller_by_id(id)? + .handle + .hw_monitors + .first() + .context("GPU has no hardware monitor")? + .set_power_cap(limit)?; + + self.edit_gpu_config(id.to_owned(), |gpu_config| { + gpu_config.power_cap = Some(limit); + }) + } + + pub async fn cleanup(self) { + let config = self.config.read().unwrap().clone(); + for (id, gpu_config) in config.gpus { + if let Ok(controller) = self.controller_by_id(&id) { + if gpu_config.fan_control_enabled { + debug!("Stopping fan control"); + controller + .stop_fan_control() + .await + .expect("Could not stop fan control"); + } + + if let (Some(_), Some(hw_mon)) = + (gpu_config.power_cap, controller.handle.hw_monitors.first()) + { + if let Ok(default_cap) = hw_mon.get_power_cap_default() { + debug!("Setting power limit to default"); + hw_mon + .set_power_cap(default_cap) + .expect("Could not set power cap to default"); + } + } + } + } + } } diff --git a/lact-daemon/src/server/mod.rs b/lact-daemon/src/server/mod.rs index 14539267..bf6b9fa6 100644 --- a/lact-daemon/src/server/mod.rs +++ b/lact-daemon/src/server/mod.rs @@ -86,6 +86,7 @@ async fn handle_request<'a>(request: Request<'a>, handler: &'a Handler) -> anyho Request::SetFanControl { id, enabled } => { ok_response(handler.set_fan_control(id, enabled).await?) } + Request::SetPowerLimit { id, limit } => ok_response(handler.set_power_limit(id, limit)?), } } diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index ed26485d..e051074c 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -100,4 +100,5 @@ pub struct PowerStats { pub power_cap_current: Option, pub power_cap_max: Option, pub power_cap_min: Option, + pub power_cap_default: Option, } diff --git a/lact-schema/src/request.rs b/lact-schema/src/request.rs index 8ed56616..96e25731 100644 --- a/lact-schema/src/request.rs +++ b/lact-schema/src/request.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +#[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(tag = "command", content = "args", rename_all = "snake_case")] pub enum Request<'a> { Ping, @@ -8,4 +8,5 @@ pub enum Request<'a> { DeviceInfo { id: &'a str }, DeviceStats { id: &'a str }, SetFanControl { id: &'a str, enabled: bool }, + SetPowerLimit { id: &'a str, limit: f64 }, } From 94e35da8d4e87d2c87eb91e8b1d3a7c5863bb740 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 20 Nov 2022 19:18:47 +0200 Subject: [PATCH 012/113] add power cap reset --- lact-daemon/src/server/handler.rs | 16 +++++++++++----- lact-daemon/src/server/mod.rs | 2 +- lact-schema/src/request.rs | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lact-daemon/src/server/handler.rs b/lact-daemon/src/server/handler.rs index d1785bd2..a9c71ff6 100644 --- a/lact-daemon/src/server/handler.rs +++ b/lact-daemon/src/server/handler.rs @@ -173,16 +173,22 @@ impl<'a> Handler { } } - pub fn set_power_limit(&'a self, id: &str, limit: f64) -> anyhow::Result<()> { - self.controller_by_id(id)? + pub fn set_power_cap(&'a self, id: &str, maybe_cap: Option) -> anyhow::Result<()> { + let hw_mon = self + .controller_by_id(id)? .handle .hw_monitors .first() - .context("GPU has no hardware monitor")? - .set_power_cap(limit)?; + .context("GPU has no hardware monitor")?; + + let cap = match maybe_cap { + Some(cap) => cap, + None => hw_mon.get_power_cap_default()?, + }; + hw_mon.set_power_cap(cap)?; self.edit_gpu_config(id.to_owned(), |gpu_config| { - gpu_config.power_cap = Some(limit); + gpu_config.power_cap = maybe_cap; }) } diff --git a/lact-daemon/src/server/mod.rs b/lact-daemon/src/server/mod.rs index bf6b9fa6..bc447ece 100644 --- a/lact-daemon/src/server/mod.rs +++ b/lact-daemon/src/server/mod.rs @@ -86,7 +86,7 @@ async fn handle_request<'a>(request: Request<'a>, handler: &'a Handler) -> anyho Request::SetFanControl { id, enabled } => { ok_response(handler.set_fan_control(id, enabled).await?) } - Request::SetPowerLimit { id, limit } => ok_response(handler.set_power_limit(id, limit)?), + Request::SetPowerCap { id, cap } => ok_response(handler.set_power_cap(id, cap)?), } } diff --git a/lact-schema/src/request.rs b/lact-schema/src/request.rs index 96e25731..4129c82e 100644 --- a/lact-schema/src/request.rs +++ b/lact-schema/src/request.rs @@ -8,5 +8,5 @@ pub enum Request<'a> { DeviceInfo { id: &'a str }, DeviceStats { id: &'a str }, SetFanControl { id: &'a str, enabled: bool }, - SetPowerLimit { id: &'a str, limit: f64 }, + SetPowerCap { id: &'a str, cap: Option }, } From bb8094404ddaa67408a702c7bca87eca5093cc08 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 20 Nov 2022 19:51:43 +0200 Subject: [PATCH 013/113] better log --- lact-daemon/src/server/gpu_controller/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index 291e8f34..f48f75d4 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -207,12 +207,12 @@ impl GpuController { let notify_handle = self.fan_control_handle.clone(); let handle = tokio::spawn(async move { loop { - debug!("Fan control tick"); let mut temps = hw_mon.get_temps(); let temp = temps .remove(&temp_key) .expect("Could not get temperature by given key"); let target_rpm = curve.rpm_at_temp(temp, min_rpm, max_rpm); + debug!("Fan control tick: setting rpm to {target_rpm}"); if let Err(err) = hw_mon.set_fan_target(target_rpm) { error!("Could not set fan speed: {err}, disabling fan control"); From 41cc2cd266e89f8407b19b4e10296032dfe7583a Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 20 Nov 2022 20:09:36 +0200 Subject: [PATCH 014/113] somewhat working power cap --- Cargo.lock | 2 +- lact-daemon/src/server/gpu_controller/mod.rs | 4 +- lact-gui/src/app/mod.rs | 30 ++++++++----- lact-gui/src/app/root_stack/oc_page/mod.rs | 44 +++++++++---------- .../app/root_stack/oc_page/power_cap_frame.rs | 4 +- lact-gui/src/client.rs | 6 +++ 6 files changed, 51 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d1eb965..f7698425 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ dependencies = [ [[package]] name = "amdgpu-sysfs" version = "0.5.0" -source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=error#4ffd5ee0be7fb644c50a6163c3eea6b3872333cd" +source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=error#80cf493d20c36e27a54e5b34162d05767ec6c305" dependencies = [ "serde", ] diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index f48f75d4..7dea6382 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -21,7 +21,7 @@ use std::{ time::Duration, }; use tokio::{select, sync::Notify, task::JoinHandle, time::sleep}; -use tracing::{debug, error, info, warn}; +use tracing::{error, info, trace, warn}; type FanControlHandle = (Arc, JoinHandle<()>); @@ -212,7 +212,7 @@ impl GpuController { .remove(&temp_key) .expect("Could not get temperature by given key"); let target_rpm = curve.rpm_at_temp(temp, min_rpm, max_rpm); - debug!("Fan control tick: setting rpm to {target_rpm}"); + trace!("Fan control tick: setting rpm to {target_rpm}"); if let Err(err) = hw_mon.set_fan_target(target_rpm) { error!("Could not set fan speed: {err}, disabling fan control"); diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs index 73283984..2362f9eb 100644 --- a/lact-gui/src/app/mod.rs +++ b/lact-gui/src/app/mod.rs @@ -17,6 +17,9 @@ use lact_schema::DeviceStats; use root_stack::RootStack; use tracing::{debug, error, info, trace}; +// In ms +const STATS_POLL_INTERVAL: u64 = 250; + #[derive(Clone)] pub struct App { pub window: Window, @@ -98,10 +101,10 @@ impl App { let apply_revealer = self.apply_revealer.clone(); - // self.root_stack.oc_page.connect_settings_changed(move || { - // debug!("Settings changed, showing apply button"); - // apply_revealer.show(); - // }); + self.root_stack.oc_page.connect_settings_changed(move || { + debug!("Settings changed, showing apply button"); + apply_revealer.show(); + }); } { @@ -169,13 +172,13 @@ impl App { app.daemon_client .set_power_profile(gpu_id, profile) .expect("Failed to set power profile"); - } + }*/ if let Some(cap) = app.root_stack.oc_page.get_power_cap() { app.daemon_client - .set_power_cap(gpu_id, cap) + .set_power_cap(&gpu_id, Some(cap)) .expect("Failed to set power cap"); - }*/ + } app.set_info(&gpu_id); }); @@ -192,9 +195,16 @@ impl App { .get_device_info(gpu_id) .expect("Could not fetch info"); let info = info_buf.inner().unwrap(); - trace!("Setting info {info:?}"); + let stats_buf = self + .daemon_client + .get_device_stats(gpu_id) + .expect("Could not fetch stats"); + let stats = stats_buf.inner().unwrap(); + + trace!("Setting info {info:?} and stats {stats:?}"); self.root_stack.info_page.set_info(&info); + self.root_stack.oc_page.set_stats(&stats, true); // trace!("Setting clocks"); // self.root_stack.oc_page.set_info(&info); @@ -265,7 +275,7 @@ impl App { error!("Could not fetch stats: {err}"); } } - thread::sleep(Duration::from_millis(500)); + thread::sleep(Duration::from_millis(STATS_POLL_INTERVAL)); }), ); @@ -279,7 +289,7 @@ impl App { trace!("New stats received, updating {stats:?}"); root_stack.info_page.set_stats(&stats); root_stack.thermals_page.set_stats(&stats); - root_stack.oc_page.set_stats(&stats); + root_stack.oc_page.set_stats(&stats, false); } /*GuiUpdateMsg::FanControlInfo(fan_control_info) => { thermals_page.set_ventilation_info(fan_control_info) }*/ diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs index ddf5b90e..179232b5 100644 --- a/lact-gui/src/app/root_stack/oc_page/mod.rs +++ b/lact-gui/src/app/root_stack/oc_page/mod.rs @@ -4,6 +4,7 @@ mod power_cap_frame; mod stats_grid; mod warning_frame; +use glib::clone; use gtk::prelude::*; use gtk::*; use lact_schema::{DeviceInfo, DeviceStats, PerformanceLevel, PowerStats}; @@ -56,12 +57,14 @@ impl OcPage { } } - pub fn set_stats(&self, stats: &DeviceStats) { + pub fn set_stats(&self, stats: &DeviceStats, initial: bool) { self.stats_grid.set_stats(stats); - self.power_cap_frame.set_data( - stats.power_stats.power_cap_current, - stats.power_stats.power_cap_max, - ); + if initial { + self.power_cap_frame.set_data( + stats.power_stats.power_cap_current, + stats.power_stats.power_cap_max, + ); + } } pub fn connect_clocks_reset(&self, f: F) { @@ -72,24 +75,17 @@ impl OcPage { } pub fn connect_settings_changed(&self, f: F) { - { - let f = f.clone(); - self.performance_level_frame - .connect_power_profile_changed(move || { - f(); - }); - } - { - /*let f = f.clone(); - self.clocks_frame.connect_clocks_changed(move || { - f(); - })*/ - } - { - self.power_cap_frame.connect_cap_changed(move || { - f(); - }) - } + self.performance_level_frame + .connect_power_profile_changed(clone!(@strong f => move || { + f() + })); + /*let f = f.clone(); + self.clocks_frame.connect_clocks_changed(move || { + f(); + })*/ + self.power_cap_frame.connect_cap_changed(move || { + f(); + }) } pub fn set_performance_level(&self, profile: Option) { @@ -130,7 +126,7 @@ impl OcPage { } }*/ - pub fn get_power_cap(&self) -> Option { + pub fn get_power_cap(&self) -> Option { self.power_cap_frame.get_cap() } } diff --git a/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs b/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs index f9eedd3e..9ce3161f 100644 --- a/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs +++ b/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs @@ -63,13 +63,13 @@ impl PowerCapFrame { } } - pub fn get_cap(&self) -> Option { + pub fn get_cap(&self) -> Option { // Using match gives a warning that floats shouldn't be used in patterns let cap = self.adjustment.value(); if cap == 0.0 { None } else { - Some(cap as i64) + Some(cap) } } diff --git a/lact-gui/src/client.rs b/lact-gui/src/client.rs index 3bf3ce52..f3ef27ea 100644 --- a/lact-gui/src/client.rs +++ b/lact-gui/src/client.rs @@ -62,6 +62,12 @@ impl DaemonClient { Ok(()) } + pub fn set_power_cap(&self, id: &str, cap: Option) -> anyhow::Result<()> { + self.make_request(Request::SetPowerCap { id, cap })? + .inner()?; + Ok(()) + } + pub fn get_device_info(&self, id: &str) -> anyhow::Result> { self.make_request(Request::DeviceInfo { id }) } From a55326cf12388235c50410431b09e3699bd63472 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 20 Nov 2022 20:16:33 +0200 Subject: [PATCH 015/113] Don't run workflow in draft prs --- .github/workflows/rust.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f366bc34..68778ecb 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,6 +11,7 @@ env: jobs: build-test: + if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }} runs-on: ubuntu-20.04 steps: @@ -26,7 +27,7 @@ jobs: check-format: runs-on: ubuntu-20.04 - + if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }} steps: - uses: actions/checkout@v2 - name: install rustfmt From e4d7d046888d03d6f9f5309e93cc47eba334fe29 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 20 Nov 2022 21:08:26 +0200 Subject: [PATCH 016/113] minor fan control fixes --- lact-gui/src/app/mod.rs | 3 +- .../src/app/root_stack/thermals_page/mod.rs | 33 ++++++++----------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs index 2362f9eb..59869790 100644 --- a/lact-gui/src/app/mod.rs +++ b/lact-gui/src/app/mod.rs @@ -205,6 +205,7 @@ impl App { self.root_stack.info_page.set_info(&info); self.root_stack.oc_page.set_stats(&stats, true); + self.root_stack.thermals_page.set_stats(&stats, true); // trace!("Setting clocks"); // self.root_stack.oc_page.set_info(&info); @@ -288,7 +289,7 @@ impl App { GuiUpdateMsg::GpuStats(stats) => { trace!("New stats received, updating {stats:?}"); root_stack.info_page.set_stats(&stats); - root_stack.thermals_page.set_stats(&stats); + root_stack.thermals_page.set_stats(&stats, false); root_stack.oc_page.set_stats(&stats, false); } /*GuiUpdateMsg::FanControlInfo(fan_control_info) => { thermals_page.set_ventilation_info(fan_control_info) diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs index dd6119bc..2fe0ada5 100644 --- a/lact-gui/src/app/root_stack/thermals_page/mod.rs +++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs @@ -1,5 +1,6 @@ mod fan_curve_frame; +use glib::clone; use gtk::prelude::*; use gtk::*; use lact_schema::DeviceStats; @@ -129,7 +130,7 @@ impl ThermalsPage { } } - pub fn set_stats(&self, stats: &DeviceStats) { + pub fn set_stats(&self, stats: &DeviceStats, initial: bool) { let mut temperatures: Vec = stats .temps .iter() @@ -157,9 +158,11 @@ impl ThermalsPage { None => self.fan_speed_label.set_text("No fan detected"), } - self.fan_control_enabled_switch.set_visible(true); - self.fan_control_enabled_switch - .set_active(!stats.fan_stats.fan_control_enabled); + if initial { + self.fan_control_enabled_switch.set_visible(true); + self.fan_control_enabled_switch + .set_active(!stats.fan_stats.fan_control_enabled); + } if stats.fan_stats.fan_control_enabled { self.fan_curve_frame.show(); @@ -172,22 +175,14 @@ impl ThermalsPage { } pub fn connect_settings_changed(&self, f: F) { - // Fan control switch toggled - { - let f = f.clone(); - self.fan_control_enabled_switch - .connect_changed_active(move |_| { - f(); - }); - } - - // Fan curve adjusted - { - let f = f.clone(); - self.fan_curve_frame.connect_adjusted(move || { + self.fan_control_enabled_switch + .connect_changed_active(clone!(@strong f => move |_| { f(); - }); - } + })); + + self.fan_curve_frame.connect_adjusted(move || { + f(); + }); } pub fn get_thermals_settings(&self) -> ThermalsSettings { From 9be48ceb3c256b685639d720e3d1f2c7eb8470af Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 20 Nov 2022 22:32:37 +0200 Subject: [PATCH 017/113] show fan curve in stats --- lact-daemon/src/main.rs | 3 +- .../src/server/gpu_controller/fan_control.rs | 4 +- lact-daemon/src/server/gpu_controller/mod.rs | 57 ++++++++++--------- lact-gui/src/app/root_stack/info_page/mod.rs | 2 +- lact-gui/src/app/root_stack/oc_page/mod.rs | 6 +- .../src/app/root_stack/oc_page/stats_grid.rs | 18 +++--- .../src/app/root_stack/thermals_page/mod.rs | 8 +-- lact-schema/src/lib.rs | 46 ++++++++------- 8 files changed, 78 insertions(+), 66 deletions(-) diff --git a/lact-daemon/src/main.rs b/lact-daemon/src/main.rs index 45a3a6e2..82dff225 100644 --- a/lact-daemon/src/main.rs +++ b/lact-daemon/src/main.rs @@ -32,5 +32,6 @@ async fn main() -> anyhow::Result<()> { std::process::exit(0); }); - Ok(server.run().await) + server.run().await; + Ok(()) } diff --git a/lact-daemon/src/server/gpu_controller/fan_control.rs b/lact-daemon/src/server/gpu_controller/fan_control.rs index 75a8071c..da7221d6 100644 --- a/lact-daemon/src/server/gpu_controller/fan_control.rs +++ b/lact-daemon/src/server/gpu_controller/fan_control.rs @@ -1,10 +1,10 @@ use amdgpu_sysfs::hw_mon::Temperature; +use lact_schema::FanCurveMap; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; use tracing::error; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct FanCurve(BTreeMap); +pub struct FanCurve(pub FanCurveMap); impl FanCurve { pub fn rpm_at_temp(&self, temp: Temperature, min_rpm: u32, max_rpm: u32) -> u32 { diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index 7dea6382..c1c445ff 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -23,7 +23,7 @@ use std::{ use tokio::{select, sync::Notify, task::JoinHandle, time::sleep}; use tracing::{error, info, trace, warn}; -type FanControlHandle = (Arc, JoinHandle<()>); +type FanControlHandle = (Arc, JoinHandle<()>, FanCurve); pub struct GpuController { pub handle: GpuHandle, @@ -129,35 +129,39 @@ impl GpuController { } pub fn get_stats(&self) -> anyhow::Result { + let fan_control_guard = self + .fan_control_handle + .lock() + .map_err(|err| anyhow!("Could not lock fan control mutex: {err}"))?; + Ok(DeviceStats { - fan_stats: FanStats { - fan_speed_current: self.hw_mon_and_then(HwMon::get_fan_current), - fan_speed_max: self.hw_mon_and_then(HwMon::get_fan_max), - fan_speed_min: self.hw_mon_and_then(HwMon::get_fan_min), - fan_control_enabled: self - .fan_control_handle - .lock() - .map_err(|err| anyhow!("Could not lock fan control mutex: {err}"))? - .is_some(), + fan: FanStats { + control_enabled: fan_control_guard.is_some(), + curve: fan_control_guard + .as_ref() + .map(|(_, _, curve)| curve.0.clone()), + speed_current: self.hw_mon_and_then(HwMon::get_fan_current), + speed_max: self.hw_mon_and_then(HwMon::get_fan_max), + speed_min: self.hw_mon_and_then(HwMon::get_fan_min), }, - clockspeed_stats: ClockspeedStats { + clockspeed: ClockspeedStats { gpu_clockspeed: self.hw_mon_and_then(HwMon::get_gpu_clockspeed), vram_clockspeed: self.hw_mon_and_then(HwMon::get_vram_clockspeed), }, - voltage_stats: VoltageStats { - gpu_voltage: self.hw_mon_and_then(HwMon::get_gpu_voltage), - northbridge_voltage: self.hw_mon_and_then(HwMon::get_northbridge_voltage), + voltage: VoltageStats { + gpu: self.hw_mon_and_then(HwMon::get_gpu_voltage), + northbridge: self.hw_mon_and_then(HwMon::get_northbridge_voltage), }, - vram_stats: VramStats { - total_vram: self.handle.get_total_vram().ok(), - used_vram: self.handle.get_used_vram().ok(), + vram: VramStats { + total: self.handle.get_total_vram().ok(), + used: self.handle.get_used_vram().ok(), }, - power_stats: PowerStats { - power_average: self.hw_mon_and_then(HwMon::get_power_average), - power_cap_current: self.hw_mon_and_then(HwMon::get_power_cap), - power_cap_max: self.hw_mon_and_then(HwMon::get_power_cap_max), - power_cap_min: self.hw_mon_and_then(HwMon::get_power_cap_min), - power_cap_default: self.hw_mon_and_then(HwMon::get_power_cap_default), + power: PowerStats { + average: self.hw_mon_and_then(HwMon::get_power_average), + cap_current: self.hw_mon_and_then(HwMon::get_power_cap), + cap_max: self.hw_mon_and_then(HwMon::get_power_cap_max), + cap_min: self.hw_mon_and_then(HwMon::get_power_cap_min), + cap_default: self.hw_mon_and_then(HwMon::get_power_cap_default), }, temps: self.hw_mon_map(HwMon::get_temps).unwrap_or_default(), busy_percent: self.handle.get_busy_percent().ok(), @@ -203,6 +207,7 @@ impl GpuController { let notify = Arc::new(Notify::new()); let task_notify = notify.clone(); + let task_curve = curve.clone(); let notify_handle = self.fan_control_handle.clone(); let handle = tokio::spawn(async move { @@ -211,7 +216,7 @@ impl GpuController { let temp = temps .remove(&temp_key) .expect("Could not get temperature by given key"); - let target_rpm = curve.rpm_at_temp(temp, min_rpm, max_rpm); + let target_rpm = task_curve.rpm_at_temp(temp, min_rpm, max_rpm); trace!("Fan control tick: setting rpm to {target_rpm}"); if let Err(err) = hw_mon.set_fan_target(target_rpm) { @@ -234,7 +239,7 @@ impl GpuController { .take(); }); - *notify_guard = Some((notify, handle)); + *notify_guard = Some((notify, handle, curve)); info!( "Started fan control with interval {}ms", @@ -250,7 +255,7 @@ impl GpuController { .lock() .map_err(|err| anyhow!("Lock error: {err}"))? .take(); - if let Some((notify, handle)) = maybe_notify { + if let Some((notify, handle, _)) = maybe_notify { notify.notify_one(); handle.await?; } diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs index 06d2f14d..7720857b 100644 --- a/lact-gui/src/app/root_stack/info_page/mod.rs +++ b/lact-gui/src/app/root_stack/info_page/mod.rs @@ -205,7 +205,7 @@ impl InformationPage { } pub fn set_stats(&self, stats: &DeviceStats) { - let vram_size = stats.vram_stats.total_vram.map_or_else( + let vram_size = stats.vram.total.map_or_else( || "unknown".to_owned(), |size| (size / 1024 / 1024).to_string(), ); diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs index 179232b5..95e590c4 100644 --- a/lact-gui/src/app/root_stack/oc_page/mod.rs +++ b/lact-gui/src/app/root_stack/oc_page/mod.rs @@ -60,10 +60,8 @@ impl OcPage { pub fn set_stats(&self, stats: &DeviceStats, initial: bool) { self.stats_grid.set_stats(stats); if initial { - self.power_cap_frame.set_data( - stats.power_stats.power_cap_current, - stats.power_stats.power_cap_max, - ); + self.power_cap_frame + .set_data(stats.power.cap_current, stats.power.cap_max); } } diff --git a/lact-gui/src/app/root_stack/oc_page/stats_grid.rs b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs index 578b4c0a..9e25b7bb 100644 --- a/lact-gui/src/app/root_stack/oc_page/stats_grid.rs +++ b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs @@ -145,9 +145,9 @@ impl StatsGrid { pub fn set_stats(&self, stats: &DeviceStats) { let VramStats { - total_vram, - used_vram, - } = stats.vram_stats; + total: total_vram, + used: used_vram, + } = stats.vram; self.vram_usage_bar .set_value(used_vram.unwrap_or(0) as f64 / total_vram.unwrap_or(0) as f64); @@ -160,14 +160,16 @@ impl StatsGrid { let ClockspeedStats { gpu_clockspeed, vram_clockspeed, - } = stats.clockspeed_stats; + } = stats.clockspeed; self.gpu_clock_label .set_markup(&format!("{}MHz", gpu_clockspeed.unwrap_or(0))); self.vram_clock_label .set_markup(&format!("{}MHz", vram_clockspeed.unwrap_or(0))); - let VoltageStats { gpu_voltage, .. } = stats.voltage_stats; + let VoltageStats { + gpu: gpu_voltage, .. + } = stats.voltage; self.gpu_voltage_label.set_markup(&format!( "{}V", @@ -175,10 +177,10 @@ impl StatsGrid { )); let PowerStats { - power_average, - power_cap_current, + average: power_average, + cap_current: power_cap_current, .. - } = stats.power_stats; + } = stats.power; self.power_usage_label.set_markup(&format!( "{}/{}W", diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs index 2fe0ada5..911fb423 100644 --- a/lact-gui/src/app/root_stack/thermals_page/mod.rs +++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs @@ -146,12 +146,12 @@ impl ThermalsPage { self.temp_label .set_markup(&format!("{temperatures_text}",)); - match stats.fan_stats.fan_speed_current { + match stats.fan.speed_current { Some(fan_speed_current) => self.fan_speed_label.set_markup(&format!( "{} RPM ({}%)", fan_speed_current, (fan_speed_current as f64 - / stats.fan_stats.fan_speed_max.unwrap_or(fan_speed_current) as f64 + / stats.fan.speed_max.unwrap_or(fan_speed_current) as f64 * 100.0) .round() )), @@ -161,10 +161,10 @@ impl ThermalsPage { if initial { self.fan_control_enabled_switch.set_visible(true); self.fan_control_enabled_switch - .set_active(!stats.fan_stats.fan_control_enabled); + .set_active(!stats.fan.control_enabled); } - if stats.fan_stats.fan_control_enabled { + if stats.fan.control_enabled { self.fan_curve_frame.show(); } else { self.fan_curve_frame.hide(); diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index e051074c..33fad2e2 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -7,7 +7,12 @@ pub use amdgpu_sysfs::{gpu_handle::PerformanceLevel, hw_mon::Temperature}; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; -use std::{borrow::Cow, collections::HashMap}; +use std::{ + borrow::Cow, + collections::{BTreeMap, HashMap}, +}; + +pub type FanCurveMap = BTreeMap; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DeviceListEntry<'a> { @@ -58,22 +63,23 @@ pub struct PciInfo { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DeviceStats { - pub fan_stats: FanStats, - pub clockspeed_stats: ClockspeedStats, - pub voltage_stats: VoltageStats, - pub vram_stats: VramStats, - pub power_stats: PowerStats, + pub fan: FanStats, + pub clockspeed: ClockspeedStats, + pub voltage: VoltageStats, + pub vram: VramStats, + pub power: PowerStats, pub temps: HashMap, pub busy_percent: Option, pub performance_level: Option, } -#[derive(Serialize, Deserialize, Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct FanStats { - pub fan_control_enabled: bool, - pub fan_speed_current: Option, - pub fan_speed_max: Option, - pub fan_speed_min: Option, + pub control_enabled: bool, + pub curve: Option, + pub speed_current: Option, + pub speed_max: Option, + pub speed_min: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy)] @@ -84,21 +90,21 @@ pub struct ClockspeedStats { #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct VoltageStats { - pub gpu_voltage: Option, - pub northbridge_voltage: Option, + pub gpu: Option, + pub northbridge: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct VramStats { - pub total_vram: Option, - pub used_vram: Option, + pub total: Option, + pub used: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct PowerStats { - pub power_average: Option, - pub power_cap_current: Option, - pub power_cap_max: Option, - pub power_cap_min: Option, - pub power_cap_default: Option, + pub average: Option, + pub cap_current: Option, + pub cap_max: Option, + pub cap_min: Option, + pub cap_default: Option, } From 01eebc6d1fa1e94f03de63ea1546edfa2dd1a1aa Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Mon, 21 Nov 2022 15:42:14 +0200 Subject: [PATCH 018/113] feat: support setting the fan curve --- Cargo.lock | 5 +- lact-daemon/Cargo.toml | 4 +- .../src/server/gpu_controller/fan_control.rs | 96 +++++++++++++------ lact-daemon/src/server/gpu_controller/mod.rs | 59 ++++++------ lact-daemon/src/server/handler.rs | 74 +++++++++----- lact-daemon/src/server/mod.rs | 4 +- lact-gui/src/app/mod.rs | 6 +- lact-gui/src/client.rs | 13 ++- lact-schema/Cargo.toml | 4 +- lact-schema/src/request.rs | 20 +++- 10 files changed, 181 insertions(+), 104 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f7698425..5302eb8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,8 +25,9 @@ dependencies = [ [[package]] name = "amdgpu-sysfs" -version = "0.5.0" -source = "git+https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs?branch=error#80cf493d20c36e27a54e5b34162d05767ec6c305" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a5ed5bf383b6aea853667b97646ce7daeae53f83b82dcc3cdd3390614e3128" dependencies = [ "serde", ] diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index 5a81eed7..569f506d 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -6,9 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs", branch = "error", features = [ - "serde", -] } +amdgpu-sysfs = { version = "0.6.1", features = ["serde"] } anyhow = "1.0" bincode = "1.3" nix = "0.25" diff --git a/lact-daemon/src/server/gpu_controller/fan_control.rs b/lact-daemon/src/server/gpu_controller/fan_control.rs index da7221d6..76e0182a 100644 --- a/lact-daemon/src/server/gpu_controller/fan_control.rs +++ b/lact-daemon/src/server/gpu_controller/fan_control.rs @@ -1,21 +1,22 @@ use amdgpu_sysfs::hw_mon::Temperature; +use anyhow::anyhow; use lact_schema::FanCurveMap; use serde::{Deserialize, Serialize}; -use tracing::error; +use tracing::warn; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct FanCurve(pub FanCurveMap); impl FanCurve { - pub fn rpm_at_temp(&self, temp: Temperature, min_rpm: u32, max_rpm: u32) -> u32 { + pub fn pwm_at_temp(&self, temp: Temperature) -> u8 { let current = temp.current.expect("No current temp"); // This scenario is most likely unreachable as the kernel shuts down the GPU when it reaches critical temperature if temp.crit.filter(|crit| current > *crit).is_some() || temp.crit_hyst.filter(|hyst| current < *hyst).is_some() { - error!("GPU temperature is beyond critical values! {current}°C"); - return max_rpm; + warn!("GPU temperature is beyond critical values! {current}°C"); + return u8::MAX; } let current = current as i32; @@ -25,6 +26,7 @@ impl FanCurve { let percentage = match (maybe_lower, maybe_higher) { (Some((lower_temp, lower_speed)), Some((higher_temp, higher_speed))) => { let speed_ratio = (current - lower_temp) as f32 / (higher_temp - lower_temp) as f32; + println!("RATIO for temp {current}: {speed_ratio}"); lower_speed + (higher_speed - lower_speed) * speed_ratio } (Some((_, lower_speed)), None) => *lower_speed, @@ -32,7 +34,18 @@ impl FanCurve { (None, None) => panic!("Could not find fan speed on the curve! This is a bug."), }; - ((max_rpm - min_rpm) as f32 * percentage) as u32 + (u8::MAX as f32 * percentage) as u8 + } +} + +impl FanCurve { + pub fn validate(&self) -> anyhow::Result<()> { + for percentage in self.0.values() { + if !(0.0..=1.0).contains(percentage) { + return Err(anyhow!("Fan speed percentage must be between 0 and 1")); + } + } + Ok(()) } } @@ -57,44 +70,44 @@ mod tests { use super::FanCurve; use amdgpu_sysfs::hw_mon::Temperature; - fn simple_rpm(temp: f32, min_rpm: u32, max_rpm: u32) -> u32 { + fn simple_pwm(temp: f32) -> u8 { let curve = FanCurve([(0, 0.0), (100, 1.0)].into()); let temp = Temperature { current: Some(temp), crit: Some(150.0), crit_hyst: Some(-100.0), }; - curve.rpm_at_temp(temp, min_rpm, max_rpm) + curve.pwm_at_temp(temp) } #[test] fn simple_curve_middle() { - let rpm = simple_rpm(45.0, 0, 200); - assert_eq!(rpm, 90); + let pwm = simple_pwm(45.0); + assert_eq!(pwm, 114); } #[test] fn simple_curve_start() { - let rpm = simple_rpm(0.0, 0, 200); - assert_eq!(rpm, 0); + let pwm = simple_pwm(0.0); + assert_eq!(pwm, 0); } #[test] fn simple_curve_end() { - let rpm = simple_rpm(100.0, 0, 200); - assert_eq!(rpm, 200); + let pwm = simple_pwm(100.0); + assert_eq!(pwm, 255); } #[test] fn simple_curve_before() { - let rpm = simple_rpm(-5.0, 0, 200); - assert_eq!(rpm, 0); + let pwm = simple_pwm(-5.0); + assert_eq!(pwm, 0); } #[test] fn simple_curve_after() { - let rpm = simple_rpm(105.0, 0, 200); - assert_eq!(rpm, 200); + let pwm = simple_pwm(105.0); + assert_eq!(pwm, 255); } #[test] @@ -105,30 +118,51 @@ mod tests { crit: Some(90.0), crit_hyst: Some(0.0), }; - let rpm = curve.rpm_at_temp(temp, 0, 200); - assert_eq!(rpm, 200); + let pwm = curve.pwm_at_temp(temp); + assert_eq!(pwm, 255); + } + + #[test] + fn uneven_curve() { + let curve = FanCurve([(30, 0.0), (40, 0.1), (55, 0.9), (61, 1.0)].into()); + let pwm_at_temp = |current: f32| { + let temp = Temperature { + current: Some(current), + crit: Some(90.0), + crit_hyst: Some(0.0), + }; + curve.pwm_at_temp(temp) + }; + + assert_eq!(pwm_at_temp(30.0), 0); + assert_eq!(pwm_at_temp(35.0), 12); + assert_eq!(pwm_at_temp(40.0), 25); + assert_eq!(pwm_at_temp(47.0), 120); + assert_eq!(pwm_at_temp(52.0), 188); + assert_eq!(pwm_at_temp(53.0), 202); + assert_eq!(pwm_at_temp(54.0), 215); } #[test] fn default_curve() { let curve = FanCurve::default(); - let rpm_at_temp = |current: f32| { + let pwm_at_temp = |current: f32| { let temp = Temperature { current: Some(current), crit: Some(90.0), crit_hyst: Some(0.0), }; - curve.rpm_at_temp(temp, 0, 1000) + curve.pwm_at_temp(temp) }; - assert_eq!(rpm_at_temp(20.0), 0); - assert_eq!(rpm_at_temp(30.0), 0); - assert_eq!(rpm_at_temp(33.0), 60); - assert_eq!(rpm_at_temp(60.0), 500); - assert_eq!(rpm_at_temp(65.0), 625); - assert_eq!(rpm_at_temp(70.0), 750); - assert_eq!(rpm_at_temp(79.0), 975); - assert_eq!(rpm_at_temp(85.0), 1000); - assert_eq!(rpm_at_temp(100.0), 1000); - assert_eq!(rpm_at_temp(-5.0), 1000); + assert_eq!(pwm_at_temp(20.0), 0); + assert_eq!(pwm_at_temp(30.0), 0); + assert_eq!(pwm_at_temp(33.0), 15); + assert_eq!(pwm_at_temp(60.0), 127); + assert_eq!(pwm_at_temp(65.0), 159); + assert_eq!(pwm_at_temp(70.0), 191); + assert_eq!(pwm_at_temp(79.0), 248); + assert_eq!(pwm_at_temp(85.0), 255); + assert_eq!(pwm_at_temp(100.0), 255); + assert_eq!(pwm_at_temp(-5.0), 255); } } diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index c1c445ff..ace82a9d 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -21,14 +21,14 @@ use std::{ time::Duration, }; use tokio::{select, sync::Notify, task::JoinHandle, time::sleep}; -use tracing::{error, info, trace, warn}; +use tracing::{debug, error, info, trace, warn}; type FanControlHandle = (Arc, JoinHandle<()>, FanCurve); pub struct GpuController { pub handle: GpuHandle, pub pci_info: Option, - pub fan_control_handle: Arc>>, + pub fan_control_handle: Mutex>, } impl GpuController { @@ -88,7 +88,7 @@ impl GpuController { Ok(Self { handle, pci_info, - fan_control_handle: Arc::new(Mutex::new(None)), + fan_control_handle: Mutex::new(None), }) } @@ -177,12 +177,15 @@ impl GpuController { self.handle.hw_monitors.first().map(f) } - pub fn start_fan_control( + pub async fn start_fan_control( &self, curve: FanCurve, temp_key: String, interval: Duration, ) -> anyhow::Result<()> { + // Stop existing task to re-apply new curve + self.stop_fan_control(false).await?; + let hw_mon = self .handle .hw_monitors @@ -193,50 +196,36 @@ impl GpuController { .set_fan_control_method(FanControlMethod::Manual) .context("Could not set fan control method")?; - let max_rpm = hw_mon.get_fan_max().context("Could not get min RPM")?; - let min_rpm = hw_mon.get_fan_min().context("Could not get max RPM")?; - let mut notify_guard = self .fan_control_handle .lock() .map_err(|err| anyhow!("Lock error: {err}"))?; - if notify_guard.is_some() { - return Ok(()); - } - let notify = Arc::new(Notify::new()); let task_notify = notify.clone(); let task_curve = curve.clone(); + debug!("Using curve {curve:?}"); - let notify_handle = self.fan_control_handle.clone(); let handle = tokio::spawn(async move { loop { + select! { + _ = sleep(interval) => (), + _ = task_notify.notified() => break, + } + let mut temps = hw_mon.get_temps(); let temp = temps .remove(&temp_key) .expect("Could not get temperature by given key"); - let target_rpm = task_curve.rpm_at_temp(temp, min_rpm, max_rpm); - trace!("Fan control tick: setting rpm to {target_rpm}"); + let target_pwm = task_curve.pwm_at_temp(temp); + trace!("Fan control tick: setting pwm to {target_pwm}"); - if let Err(err) = hw_mon.set_fan_target(target_rpm) { + if let Err(err) = hw_mon.set_fan_pwm(target_pwm) { error!("Could not set fan speed: {err}, disabling fan control"); break; } - - select! { - _ = sleep(interval) => (), - _ = task_notify.notified() => break, - } - } - info!("Shutting down fan control"); - if let Err(err) = hw_mon.set_fan_control_method(FanControlMethod::Auto) { - error!("Could not set fan control back to automatic: {err}"); } - notify_handle - .lock() - .expect("Fan control mutex error") - .take(); + info!("Exited fan control task"); }); *notify_guard = Some((notify, handle, curve)); @@ -249,7 +238,7 @@ impl GpuController { Ok(()) } - pub async fn stop_fan_control(&self) -> anyhow::Result<()> { + pub async fn stop_fan_control(&self, reset_mode: bool) -> anyhow::Result<()> { let maybe_notify = self .fan_control_handle .lock() @@ -259,6 +248,18 @@ impl GpuController { notify.notify_one(); handle.await?; } + + if reset_mode { + let hw_mon = self + .handle + .hw_monitors + .first() + .cloned() + .context("This GPU has no monitor")?; + hw_mon + .set_fan_control_method(FanControlMethod::Auto) + .context("Could not set fan control back to automatic: {err}")?; + } Ok(()) } } diff --git a/lact-daemon/src/server/handler.rs b/lact-daemon/src/server/handler.rs index a9c71ff6..7043e4a2 100644 --- a/lact-daemon/src/server/handler.rs +++ b/lact-daemon/src/server/handler.rs @@ -2,7 +2,7 @@ use super::gpu_controller::{fan_control::FanCurve, GpuController}; use crate::config::{Config, FanControlSettings, GpuConfig}; use amdgpu_sysfs::sysfs::SysFS; use anyhow::{anyhow, Context}; -use lact_schema::{DeviceInfo, DeviceListEntry, DeviceStats}; +use lact_schema::{DeviceInfo, DeviceListEntry, DeviceStats, FanCurveMap}; use std::{ collections::HashMap, path::PathBuf, @@ -77,11 +77,13 @@ impl<'a> Handler { "Fan control is enabled but no settings are defined (invalid config?)", )?; let interval = Duration::from_millis(settings.interval_ms); - controller.start_fan_control( - settings.curve.clone(), - settings.temperature_key.clone(), - interval, - )?; + controller + .start_fan_control( + settings.curve.clone(), + settings.temperature_key.clone(), + interval, + ) + .await?; } if let Some(power_cap) = gpu_config.power_cap { @@ -147,30 +149,50 @@ impl<'a> Handler { self.controller_by_id(id)?.get_stats() } - pub async fn set_fan_control(&'a self, id: &str, enabled: bool) -> anyhow::Result<()> { - if enabled { - let mut config_guard = self.config.write().map_err(|err| anyhow!("{err}"))?; - let gpu_config = config_guard.gpus.entry(id.to_owned()).or_default(); - let settings = - gpu_config - .fan_control_settings - .get_or_insert_with(|| FanControlSettings { - curve: FanCurve::default(), + pub async fn set_fan_control( + &'a self, + id: &str, + enabled: bool, + curve: Option, + ) -> anyhow::Result<()> { + let settings = if enabled { + let settings = { + let curve = curve.map_or_else(FanCurve::default, |curve| FanCurve(curve)); + curve.validate()?; + + let mut config_guard = self.config.write().map_err(|err| anyhow!("{err}"))?; + let gpu_config = config_guard.gpus.entry(id.to_owned()).or_default(); + + if let Some(mut existing_settings) = gpu_config.fan_control_settings.clone() { + existing_settings.curve = curve; + existing_settings + } else { + FanControlSettings { + curve, temperature_key: "edge".to_owned(), interval_ms: 500, - }); + } + } + }; let interval = Duration::from_millis(settings.interval_ms); - self.controller_by_id(id)?.start_fan_control( - settings.curve.clone(), - settings.temperature_key.clone(), - interval, - )?; - gpu_config.fan_control_enabled = true; - config_guard.save().context("Could not save config") + self.controller_by_id(id)? + .start_fan_control( + settings.curve.clone(), + settings.temperature_key.clone(), + interval, + ) + .await?; + Some(settings) } else { - self.controller_by_id(id)?.stop_fan_control().await - } + self.controller_by_id(id)?.stop_fan_control(true).await?; + None + }; + + self.edit_gpu_config(id.to_owned(), |config| { + config.fan_control_enabled = enabled; + config.fan_control_settings = settings + }) } pub fn set_power_cap(&'a self, id: &str, maybe_cap: Option) -> anyhow::Result<()> { @@ -199,7 +221,7 @@ impl<'a> Handler { if gpu_config.fan_control_enabled { debug!("Stopping fan control"); controller - .stop_fan_control() + .stop_fan_control(true) .await .expect("Could not stop fan control"); } diff --git a/lact-daemon/src/server/mod.rs b/lact-daemon/src/server/mod.rs index bc447ece..9d0686f0 100644 --- a/lact-daemon/src/server/mod.rs +++ b/lact-daemon/src/server/mod.rs @@ -83,8 +83,8 @@ async fn handle_request<'a>(request: Request<'a>, handler: &'a Handler) -> anyho Request::ListDevices => ok_response(handler.list_devices()), Request::DeviceInfo { id } => ok_response(handler.get_device_info(id)?), Request::DeviceStats { id } => ok_response(handler.get_gpu_stats(id)?), - Request::SetFanControl { id, enabled } => { - ok_response(handler.set_fan_control(id, enabled).await?) + Request::SetFanControl { id, enabled, curve } => { + ok_response(handler.set_fan_control(id, enabled, curve).await?) } Request::SetPowerCap { id, cap } => ok_response(handler.set_power_cap(id, cap)?), } diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs index 59869790..c4e5d1dc 100644 --- a/lact-gui/src/app/mod.rs +++ b/lact-gui/src/app/mod.rs @@ -141,7 +141,11 @@ impl App { let thermals_settings = app.root_stack.thermals_page.get_thermals_settings(); app.daemon_client - .set_fan_control(&gpu_id, thermals_settings.automatic_fan_control_enabled) + .set_fan_control( + &gpu_id, + thermals_settings.automatic_fan_control_enabled, + None, + ) .expect("Could not set fan control"); // TODO diff --git a/lact-gui/src/client.rs b/lact-gui/src/client.rs index f3ef27ea..ad9be341 100644 --- a/lact-gui/src/client.rs +++ b/lact-gui/src/client.rs @@ -1,5 +1,7 @@ use anyhow::{anyhow, Context}; -use lact_schema::{request::Request, response::Response, DeviceInfo, DeviceListEntry, DeviceStats}; +use lact_schema::{ + request::Request, response::Response, DeviceInfo, DeviceListEntry, DeviceStats, FanCurveMap, +}; use nix::unistd::getuid; use serde::Deserialize; use std::{ @@ -56,8 +58,13 @@ impl DaemonClient { self.make_request(Request::ListDevices) } - pub fn set_fan_control(&self, id: &str, enabled: bool) -> anyhow::Result<()> { - self.make_request::<()>(Request::SetFanControl { id, enabled })? + pub fn set_fan_control( + &self, + id: &str, + enabled: bool, + curve: Option, + ) -> anyhow::Result<()> { + self.make_request::<()>(Request::SetFanControl { id, enabled, curve })? .inner()?; Ok(()) } diff --git a/lact-schema/Cargo.toml b/lact-schema/Cargo.toml index 117fb2cb..75fbdeab 100644 --- a/lact-schema/Cargo.toml +++ b/lact-schema/Cargo.toml @@ -4,9 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -amdgpu-sysfs = { git = "https://github.com/ilya-zlobintsev/amdgpu-sysfs-rs", branch = "error", features = [ - "serde", -] } +amdgpu-sysfs = { version = "*", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } indexmap = { version = "1.9", features = ["serde"] } diff --git a/lact-schema/src/request.rs b/lact-schema/src/request.rs index 4129c82e..220add5c 100644 --- a/lact-schema/src/request.rs +++ b/lact-schema/src/request.rs @@ -1,3 +1,4 @@ +use crate::FanCurveMap; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, PartialEq)] @@ -5,8 +6,19 @@ use serde::{Deserialize, Serialize}; pub enum Request<'a> { Ping, ListDevices, - DeviceInfo { id: &'a str }, - DeviceStats { id: &'a str }, - SetFanControl { id: &'a str, enabled: bool }, - SetPowerCap { id: &'a str, cap: Option }, + DeviceInfo { + id: &'a str, + }, + DeviceStats { + id: &'a str, + }, + SetFanControl { + id: &'a str, + enabled: bool, + curve: Option, + }, + SetPowerCap { + id: &'a str, + cap: Option, + }, } From 1e04c8025493800f22af7ce967bb780ebf938a5e Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Wed, 23 Nov 2022 11:27:23 +0200 Subject: [PATCH 019/113] wip --- lact-daemon/src/server/gpu_controller/mod.rs | 2 +- lact-gui/src/app/mod.rs | 137 ++++++++++-------- .../thermals_page/fan_curve_frame.rs | 7 +- .../src/app/root_stack/thermals_page/mod.rs | 32 ++-- 4 files changed, 98 insertions(+), 80 deletions(-) diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index ace82a9d..985708fd 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -258,7 +258,7 @@ impl GpuController { .context("This GPU has no monitor")?; hw_mon .set_fan_control_method(FanControlMethod::Auto) - .context("Could not set fan control back to automatic: {err}")?; + .context("Could not set fan control back to automatic")?; } Ok(()) } diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs index c4e5d1dc..966374fd 100644 --- a/lact-gui/src/app/mod.rs +++ b/lact-gui/src/app/mod.rs @@ -8,6 +8,7 @@ use std::thread; use std::time::Duration; use crate::client::DaemonClient; +use anyhow::Context; use apply_revealer::ApplyRevealer; use glib::clone; use gtk::prelude::*; @@ -127,66 +128,16 @@ impl App { })*/ } - // Apply settings - { - let current_gpu_id = current_gpu_id.clone(); - let app = self.clone(); - - self.apply_revealer.connect_apply_button_clicked(move || { - info!("Applying settings"); + self.apply_revealer.connect_apply_button_clicked( + clone!(@strong self as app, @strong current_gpu_id => move || { + if let Err(err) = app.apply_settings(current_gpu_id.clone()) { + show_error(err.context("Could not apply settings")); - let gpu_id = current_gpu_id.read().unwrap(); - - { - let thermals_settings = app.root_stack.thermals_page.get_thermals_settings(); - - app.daemon_client - .set_fan_control( - &gpu_id, - thermals_settings.automatic_fan_control_enabled, - None, - ) - .expect("Could not set fan control"); - - // TODO - /*app.daemon_client - .set_fan_curve(gpu_id, thermals_settings.curve) - .unwrap_or(println!("Failed to set fan curve"));*/ + let gpu_id = current_gpu_id.read().unwrap(); + app.set_info(&gpu_id) } - - /*if let Some(clocks_settings) = app.root_stack.oc_page.get_clocks() { - app.daemon_client - .set_gpu_max_power_state( - gpu_id, - clocks_settings.gpu_clock, - Some(clocks_settings.gpu_voltage), - ) - .expect("Failed to set GPU clockspeed/voltage"); - - app.daemon_client - .set_vram_max_clock(gpu_id, clocks_settings.vram_clock) - .expect("Failed to set VRAM Clock"); - - app.daemon_client - .commit_gpu_power_states(gpu_id) - .expect("Failed to commit power states"); - } - - if let Some(profile) = app.root_stack.oc_page.get_power_profile() { - app.daemon_client - .set_power_profile(gpu_id, profile) - .expect("Failed to set power profile"); - }*/ - - if let Some(cap) = app.root_stack.oc_page.get_power_cap() { - app.daemon_client - .set_power_cap(&gpu_id, Some(cap)) - .expect("Failed to set power cap"); - } - - app.set_info(&gpu_id); - }); - } + }), + ); self.start_stats_update_loop(current_gpu_id.clone()); @@ -304,9 +255,79 @@ impl App { }), ); } + + fn apply_settings(&self, current_gpu_id: Arc>) -> anyhow::Result<()> { + info!("Applying settings"); + + let gpu_id = current_gpu_id.read().unwrap(); + + /*let thermals_settings = self.root_stack.thermals_page.get_thermals_settings(); + + self.daemon_client + .set_fan_control( + &gpu_id, + thermals_settings.automatic_fan_control_enabled, + None, + ) + .context("Could not set fan control")?;*/ + + // TODO + /*self.daemon_client + .set_fan_curve(gpu_id, thermals_settings.curve) + .unwrap_or(println!("Failed to set fan curve"));*/ + + /*if let Some(clocks_settings) = self.root_stack.oc_page.get_clocks() { + self.daemon_client + .set_gpu_max_power_state( + gpu_id, + clocks_settings.gpu_clock, + Some(clocks_settings.gpu_voltage), + ) + .expect("Failed to set GPU clockspeed/voltage"); + + self.daemon_client + .set_vram_max_clock(gpu_id, clocks_settings.vram_clock) + .expect("Failed to set VRAM Clock"); + + self.daemon_client + .commit_gpu_power_states(gpu_id) + .expect("Failed to commit power states"); + } + + if let Some(profile) = self.root_stack.oc_page.get_power_profile() { + self.daemon_client + .set_power_profile(gpu_id, profile) + .expect("Failed to set power profile"); + }*/ + + if let Some(cap) = self.root_stack.oc_page.get_power_cap() { + self.daemon_client + .set_power_cap(&gpu_id, Some(cap)) + .context("Failed to set power cap")?; + } + + self.set_info(&gpu_id); + + Ok(()) + } } enum GuiUpdateMsg { // FanControlInfo(FanControlInfo), GpuStats(DeviceStats), } + +fn show_error(err: anyhow::Error) { + glib::idle_add(move || { + let text = format!("{err:?}"); + let diag = MessageDialog::builder() + .title("Error") + .message_type(MessageType::Error) + .text(&text) + .buttons(ButtonsType::Close) + .build(); + diag.run(); + diag.hide(); + glib::Continue(false) + }); +} diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs index 1395ef8e..12ddc803 100644 --- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs +++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs @@ -1,16 +1,13 @@ use gtk::prelude::*; use gtk::*; +use lact_schema::FanCurveMap; use std::collections::BTreeMap; use tracing::debug; #[derive(Clone)] pub struct FanCurveFrame { pub container: Frame, - adjustment_1: Adjustment, - adjustment_2: Adjustment, - adjustment_3: Adjustment, - adjustment_4: Adjustment, - adjustment_5: Adjustment, + curve: FanCurveMap, } impl FanCurveFrame { diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs index 911fb423..29024e7c 100644 --- a/lact-gui/src/app/root_stack/thermals_page/mod.rs +++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs @@ -1,4 +1,4 @@ -mod fan_curve_frame; +// mod fan_curve_frame; use glib::clone; use gtk::prelude::*; @@ -7,7 +7,7 @@ use lact_schema::DeviceStats; use std::collections::BTreeMap; use tracing::trace; -use fan_curve_frame::FanCurveFrame; +// use fan_curve_frame::FanCurveFrame; pub struct ThermalsSettings { pub automatic_fan_control_enabled: bool, @@ -20,7 +20,7 @@ pub struct ThermalsPage { temp_label: Label, fan_speed_label: Label, fan_control_enabled_switch: Switch, - fan_curve_frame: FanCurveFrame, + // fan_curve_frame: FanCurveFrame, } impl ThermalsPage { @@ -94,7 +94,7 @@ impl ThermalsPage { container.pack_start(&grid, false, false, 5); - let fan_curve_frame = FanCurveFrame::new(); + /*let fan_curve_frame = FanCurveFrame::new(); container.pack_start(&fan_curve_frame.container, true, true, 5); @@ -119,14 +119,14 @@ impl ThermalsPage { fan_curve_frame.show(); } }); - } + }*/ Self { container, temp_label, fan_speed_label, fan_control_enabled_switch, - fan_curve_frame, + // fan_curve_frame, } } @@ -162,12 +162,12 @@ impl ThermalsPage { self.fan_control_enabled_switch.set_visible(true); self.fan_control_enabled_switch .set_active(!stats.fan.control_enabled); - } - if stats.fan.control_enabled { - self.fan_curve_frame.show(); - } else { - self.fan_curve_frame.hide(); + /*if stats.fan.control_enabled { + self.fan_curve_frame.show(); + } else { + self.fan_curve_frame.hide(); + }*/ } // TODO @@ -180,12 +180,12 @@ impl ThermalsPage { f(); })); - self.fan_curve_frame.connect_adjusted(move || { + /*self.fan_curve_frame.connect_adjusted(move || { f(); - }); + });*/ } - pub fn get_thermals_settings(&self) -> ThermalsSettings { + /*pub fn get_thermals_settings(&self) -> ThermalsSettings { let automatic_fan_control_enabled = self.fan_control_enabled_switch.state(); let curve = self.fan_curve_frame.get_curve(); @@ -193,10 +193,10 @@ impl ThermalsPage { automatic_fan_control_enabled, curve, } - } + }*/ pub fn hide_fan_controls(&self) { self.fan_control_enabled_switch.set_visible(false); - self.fan_curve_frame.hide(); + // self.fan_curve_frame.hide(); } } From 7c4a1de9b35f52989ad2bd769af7b4f9e69c89a9 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Thu, 24 Nov 2022 14:04:44 +0200 Subject: [PATCH 020/113] feat: return clocks info --- Cargo.lock | 2 -- lact-daemon/Cargo.toml | 2 +- lact-daemon/src/server/gpu_controller/mod.rs | 10 ++++++-- lact-schema/Cargo.toml | 2 +- lact-schema/src/lib.rs | 24 +++++++++++++++++++- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5302eb8a..dc2d4977 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,8 +26,6 @@ dependencies = [ [[package]] name = "amdgpu-sysfs" version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a5ed5bf383b6aea853667b97646ce7daeae53f83b82dcc3cdd3390614e3128" dependencies = [ "serde", ] diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index 569f506d..d0ae3362 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -amdgpu-sysfs = { version = "0.6.1", features = ["serde"] } +amdgpu-sysfs = { path = "../../amdgpu-sysfs-rs", features = ["serde"] } anyhow = "1.0" bincode = "1.3" nix = "0.25" diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index 985708fd..afa837a1 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -10,8 +10,8 @@ use amdgpu_sysfs::{ }; use anyhow::{anyhow, Context}; use lact_schema::{ - ClockspeedStats, DeviceInfo, DeviceStats, FanStats, GpuPciInfo, LinkInfo, PciInfo, PowerStats, - VoltageStats, VramStats, + ClocksInfo, ClockspeedStats, DeviceInfo, DeviceStats, FanStats, GpuPciInfo, LinkInfo, PciInfo, + PowerStats, VoltageStats, VramStats, }; use pciid_parser::Database; use std::{ @@ -109,6 +109,10 @@ impl GpuController { let driver = self.handle.get_driver(); let vbios_version = self.handle.get_vbios_version().ok(); let link_info = self.get_link_info(); + let clocks_table = self.handle.get_power_table().ok(); + let clocks_info = clocks_table + .as_ref() + .map_or_else(Default::default, ClocksInfo::from); DeviceInfo { pci_info, @@ -116,6 +120,8 @@ impl GpuController { driver, vbios_version, link_info, + clocks_table, + clocks_info, } } diff --git a/lact-schema/Cargo.toml b/lact-schema/Cargo.toml index 75fbdeab..9d106186 100644 --- a/lact-schema/Cargo.toml +++ b/lact-schema/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -amdgpu-sysfs = { version = "*", features = ["serde"] } +amdgpu-sysfs = { path = "../../amdgpu-sysfs-rs", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } indexmap = { version = "1.9", features = ["serde"] } diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index 33fad2e2..c81689f0 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -3,7 +3,13 @@ pub mod response; #[cfg(test)] mod tests; -pub use amdgpu_sysfs::{gpu_handle::PerformanceLevel, hw_mon::Temperature}; +pub use amdgpu_sysfs::{ + gpu_handle::{ + overdrive::{ClocksTable, ClocksTableGen}, + PerformanceLevel, + }, + hw_mon::Temperature, +}; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; @@ -34,6 +40,22 @@ pub struct DeviceInfo<'a> { pub driver: &'a str, pub vbios_version: Option, pub link_info: LinkInfo, + pub clocks_table: Option, + pub clocks_info: ClocksInfo, +} + +#[derive(Serialize, Deserialize, Default, Debug, Clone, Copy)] +pub struct ClocksInfo { + pub max_sclk: Option, + pub max_mclk: Option, +} + +impl From<&T> for ClocksInfo { + fn from(table: &T) -> Self { + let max_sclk = table.get_max_sclk(); + let max_mclk = table.get_max_mclk(); + Self { max_sclk, max_mclk } + } } #[derive(Serialize, Deserialize, Debug, Clone)] From 8e096a51194be26936459ba6a7123bab439b89eb Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Thu, 24 Nov 2022 14:05:48 +0200 Subject: [PATCH 021/113] feat: show vulkan layers --- lact-daemon/src/server/vulkan.rs | 2 ++ lact-schema/src/lib.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/lact-daemon/src/server/vulkan.rs b/lact-daemon/src/server/vulkan.rs index 73e3c773..a57bf99b 100644 --- a/lact-daemon/src/server/vulkan.rs +++ b/lact-daemon/src/server/vulkan.rs @@ -16,6 +16,7 @@ pub fn get_vulkan_info<'a>(vendor_id: &'a str, device_id: &'a str) -> anyhow::Re let library = VulkanLibrary::new().map_err(|err| err.to_string())?; let instance = Instance::new(library, InstanceCreateInfo::default()) .map_err(|err| err.to_string())?; + let enabled_layers = instance.enabled_layers().to_vec(); let devices = instance .enumerate_physical_devices() .map_err(|err| err.to_string())?; @@ -38,6 +39,7 @@ pub fn get_vulkan_info<'a>(vendor_id: &'a str, device_id: &'a str) -> anyhow::Re .into_iter() .map(|(name, enabled)| (Cow::Borrowed(name), enabled)) .collect(), + enabled_layers, }; return Ok(info); } diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index c81689f0..440cdcff 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -71,6 +71,7 @@ pub struct VulkanInfo { pub device_name: String, pub api_version: String, pub driver_name: Option, + pub enabled_layers: Vec, pub supported_features: IndexMap, bool>, pub supported_extensions: IndexMap, bool>, } From bcd846d80114f9ee90dba651e2b3e2341c6c2a23 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Thu, 24 Nov 2022 14:20:31 +0200 Subject: [PATCH 022/113] feat: better vulkan driver info --- lact-daemon/src/server/vulkan.rs | 14 +++++++++----- .../src/app/root_stack/info_page/vulkan_info.rs | 4 ++-- lact-schema/src/lib.rs | 13 ++++++++++--- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lact-daemon/src/server/vulkan.rs b/lact-daemon/src/server/vulkan.rs index a57bf99b..8b8f9434 100644 --- a/lact-daemon/src/server/vulkan.rs +++ b/lact-daemon/src/server/vulkan.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use crate::fork::run_forked; -use lact_schema::VulkanInfo; +use lact_schema::{VulkanDriverInfo, VulkanInfo}; use vulkano::{ instance::{Instance, InstanceCreateInfo}, VulkanLibrary, @@ -24,17 +24,21 @@ pub fn get_vulkan_info<'a>(vendor_id: &'a str, device_id: &'a str) -> anyhow::Re for device in devices { let properties = device.properties(); // Not sure how this works with systems that have multiple identical GPUs - if properties.vendor_id == vendor_id && properties.device_id == device_id { + if (properties.vendor_id, properties.device_id) == (vendor_id, device_id) { let info = VulkanInfo { device_name: properties.device_name.clone(), api_version: device.api_version().to_string(), - driver_name: properties.driver_name.clone(), - supported_features: device + driver: VulkanDriverInfo { + version: properties.driver_version, + name: properties.driver_name.clone(), + info: properties.driver_info.clone(), + }, + features: device .supported_features() .into_iter() .map(|(name, enabled)| (Cow::Borrowed(name), enabled)) .collect(), - supported_extensions: device + extensions: device .supported_extensions() .into_iter() .map(|(name, enabled)| (Cow::Borrowed(name), enabled)) diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs index 3b77d948..0bc54779 100644 --- a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs @@ -129,7 +129,7 @@ impl VulkanInfoFrame { self.version_label .set_markup(&format!("{}", vulkan_info.api_version)); - for (feature, supported) in &vulkan_info.supported_features { + for (feature, supported) in &vulkan_info.features { let vbox = Box::new(Orientation::Horizontal, 5); let feature_name_label = Label::new(Some(&feature)); @@ -146,7 +146,7 @@ impl VulkanInfoFrame { self.features_box.pack_start(&vbox, false, false, 0); } - for (extension, supported) in &vulkan_info.supported_extensions { + for (extension, supported) in &vulkan_info.extensions { let vbox = Box::new(Orientation::Horizontal, 5); let extension_name_label = Label::new(Some(&extension)); vbox.pack_start(&extension_name_label, false, false, 0); diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index 440cdcff..09664cb2 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -70,10 +70,17 @@ pub struct LinkInfo { pub struct VulkanInfo { pub device_name: String, pub api_version: String, - pub driver_name: Option, + pub driver: VulkanDriverInfo, pub enabled_layers: Vec, - pub supported_features: IndexMap, bool>, - pub supported_extensions: IndexMap, bool>, + pub features: IndexMap, bool>, + pub extensions: IndexMap, bool>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct VulkanDriverInfo { + pub version: u32, + pub name: Option, + pub info: Option, } #[derive(Serialize, Deserialize, Debug, Clone)] From cd1a654973ed023d30366148e88b65f609eba917 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Thu, 24 Nov 2022 15:49:56 +0200 Subject: [PATCH 023/113] vulkan lists --- Cargo.lock | 6 +- lact-daemon/Cargo.toml | 2 +- lact-gui/Cargo.toml | 2 +- lact-gui/src/app/root_stack/info_page/mod.rs | 8 +- .../app/root_stack/info_page/vulkan_info.rs | 76 ++++++++++++++----- lact-schema/Cargo.toml | 2 +- 6 files changed, 66 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc2d4977..b04eee55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -553,7 +553,7 @@ checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "lact-daemon" -version = "0.1.0" +version = "0.2.0" dependencies = [ "amdgpu-sysfs", "anyhow", @@ -572,7 +572,7 @@ dependencies = [ [[package]] name = "lact-gui" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "gtk", @@ -586,7 +586,7 @@ dependencies = [ [[package]] name = "lact-schema" -version = "0.1.0" +version = "0.2.0" dependencies = [ "amdgpu-sysfs", "indexmap", diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index d0ae3362..f5764687 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lact-daemon" -version = "0.1.0" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml index 45f8e6e2..e1e43899 100644 --- a/lact-gui/Cargo.toml +++ b/lact-gui/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lact-gui" -version = "0.1.0" +version = "0.2.0" authors = ["Ilya Zlobintsev "] edition = "2021" diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs index 7720857b..94da7ea4 100644 --- a/lact-gui/src/app/root_stack/info_page/mod.rs +++ b/lact-gui/src/app/root_stack/info_page/mod.rs @@ -177,7 +177,7 @@ impl InformationPage { self.gpu_manufacturer_label .set_markup(&format!("{gpu_manufacturer}",)); - let vbios_version = gpu_info.vbios_version.as_deref().unwrap_or("unknown"); + let vbios_version = gpu_info.vbios_version.as_deref().unwrap_or("Unknown"); self.vbios_version_label .set_markup(&format!("{vbios_version}",)); @@ -188,12 +188,12 @@ impl InformationPage { .link_info .current_speed .as_deref() - .unwrap_or("unknown"); + .unwrap_or("Unknown"); let link_width = gpu_info .link_info .current_width .as_deref() - .unwrap_or("unknown"); + .unwrap_or("Unknown"); self.link_speed_label .set_markup(&format!("{link_speed} x{link_width}",)); @@ -206,7 +206,7 @@ impl InformationPage { pub fn set_stats(&self, stats: &DeviceStats) { let vram_size = stats.vram.total.map_or_else( - || "unknown".to_owned(), + || "Unknown".to_owned(), |size| (size / 1024 / 1024).to_string(), ); self.vram_size_label diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs index 0bc54779..063c8585 100644 --- a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs @@ -1,3 +1,4 @@ +use glib::clone; use gtk::prelude::*; use gtk::*; use lact_schema::VulkanInfo; @@ -8,8 +9,8 @@ pub struct VulkanInfoFrame { pub container: Frame, device_name_label: Label, version_label: Label, - features_box: Box, - extensions_box: Box, + features_listbox: ListBox, + extensions_listbox: ListBox, } impl VulkanInfoFrame { @@ -25,6 +26,9 @@ impl VulkanInfoFrame { container.set_shadow_type(ShadowType::None); + let features_listbox = ListBox::builder().halign(Align::Fill).build(); + let extensions_listbox = ListBox::builder().halign(Align::Fill).build(); + let vbox = Box::new(Orientation::Vertical, 5); let grid = Grid::new(); @@ -74,41 +78,57 @@ impl VulkanInfoFrame { grid.attach(&version_label, 2, 1, 3, 1); + let features_label = Label::builder() + .label("Features:") + .halign(Align::End) + .build(); + let show_features_button = Button::builder().label("Show").halign(Align::Start).build(); + show_features_button.connect_clicked(clone!(@strong features_listbox => move |_| { + show_list_window("Vulkan features", &features_listbox); + })); + + grid.attach(&features_label, 0, 2, 2, 1); + grid.attach(&show_features_button, 2, 2, 2, 1); + + let extensions_label = Label::builder() + .label("Extensions:") + .halign(Align::End) + .build(); + let show_extensions_button = Button::builder().label("Show").halign(Align::Start).build(); + show_extensions_button.connect_clicked(clone!(@strong extensions_listbox => move |_| { + show_list_window("Vulkan extensions", &extensions_listbox); + })); + + grid.attach(&extensions_label, 0, 3, 2, 1); + grid.attach(&show_extensions_button, 2, 3, 2, 1); + vbox.pack_start(&grid, false, true, 5); - let features_expander = Expander::builder().label("Feature support").build(); + /*let features_expander = Expander::builder().label("Feature support").build(); let features_scrolled_window = ScrolledWindow::builder().build(); features_scrolled_window.set_vexpand(true); - let features_box = Box::new(Orientation::Vertical, 5); - features_box.set_halign(Align::Center); - features_box.set_valign(Align::Fill); - features_scrolled_window.add(&features_box); + features_scrolled_window.add(&features_listbox); features_expander.add(&features_scrolled_window); vbox.pack_start(&features_expander, false, true, 5); - let extensions_box = Box::builder() - .orientation(Orientation::Vertical) - .spacing(5) - .halign(Align::Center) - .build(); let extensions_expander = Expander::builder() .label("Extension support") .child( &ScrolledWindow::builder() .vexpand(true) - .child(&extensions_box) + .child(&extensions_listbox) .build(), ) .build(); - vbox.pack_start(&extensions_expander, false, true, 5); + vbox.pack_start(&extensions_expander, false, true, 5);*/ container.add(&vbox); @@ -116,8 +136,8 @@ impl VulkanInfoFrame { container, device_name_label, version_label, - features_box, - extensions_box, + features_listbox, + extensions_listbox, } } @@ -129,7 +149,8 @@ impl VulkanInfoFrame { self.version_label .set_markup(&format!("{}", vulkan_info.api_version)); - for (feature, supported) in &vulkan_info.features { + self.features_listbox.children().clear(); + for (i, (feature, supported)) in vulkan_info.features.iter().enumerate() { let vbox = Box::new(Orientation::Horizontal, 5); let feature_name_label = Label::new(Some(&feature)); @@ -143,11 +164,14 @@ impl VulkanInfoFrame { vbox.pack_end(&feature_supported_checkbutton, false, false, 0); - self.features_box.pack_start(&vbox, false, false, 0); + self.features_listbox.insert(&vbox, i.try_into().unwrap()); } - for (extension, supported) in &vulkan_info.extensions { + self.extensions_listbox.children().clear(); + for (i, (extension, supported)) in vulkan_info.extensions.iter().enumerate() { let vbox = Box::new(Orientation::Horizontal, 5); + vbox.set_hexpand(true); + let extension_name_label = Label::new(Some(&extension)); vbox.pack_start(&extension_name_label, false, false, 0); @@ -157,7 +181,19 @@ impl VulkanInfoFrame { .build(); vbox.pack_end(&extension_supported_checkbutton, false, false, 0); - self.extensions_box.pack_start(&vbox, false, false, 0); + self.extensions_listbox.insert(&vbox, i.try_into().unwrap()); } } } + +fn show_list_window(title: &str, child: &ListBox) { + let window = Window::builder() + .type_(WindowType::Toplevel) + .title(title) + .width_request(500) + .height_request(700) + .build(); + let scroll = ScrolledWindow::builder().child(child).margin(10).build(); + window.add(&scroll); + window.show_all(); +} diff --git a/lact-schema/Cargo.toml b/lact-schema/Cargo.toml index 9d106186..ff35d25a 100644 --- a/lact-schema/Cargo.toml +++ b/lact-schema/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lact-schema" -version = "0.1.0" +version = "0.2.0" edition = "2021" [dependencies] From 5cd55b563a7810eb0892233591d487bcfd2fd7b1 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Thu, 24 Nov 2022 16:07:27 +0200 Subject: [PATCH 024/113] feat: show kernel version --- lact-gui/src/app/root_stack/software_page.rs | 40 ++++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/lact-gui/src/app/root_stack/software_page.rs b/lact-gui/src/app/root_stack/software_page.rs index 48e8e7eb..11192965 100644 --- a/lact-gui/src/app/root_stack/software_page.rs +++ b/lact-gui/src/app/root_stack/software_page.rs @@ -1,10 +1,10 @@ use gtk::prelude::*; use gtk::*; +use std::process::Command; #[derive(Debug, Clone)] pub struct SoftwarePage { pub container: Grid, - lact_version_label: Label, } impl SoftwarePage { @@ -16,12 +16,12 @@ impl SoftwarePage { container.set_margin_bottom(5); container.set_margin_top(5); - container.set_column_spacing(10); + container.set_column_spacing(5); container.attach( &{ let label = Label::new(None); - label.set_markup("LACT Version:"); + label.set_markup("LACT Version:"); label.set_halign(Align::End); label.set_hexpand(true); label @@ -39,16 +39,40 @@ impl SoftwarePage { false => "release", }; - lact_version_label.set_markup(&format!("{}-{}", lact_version, lact_release_type)); + lact_version_label.set_markup(&format!("{}-{}", lact_version, lact_release_type)); lact_version_label.set_hexpand(true); lact_version_label.set_halign(Align::Start); container.attach(&lact_version_label, 1, 0, 1, 1); - Self { - container, - lact_version_label, - } + container.attach( + &Label::builder() + .label("Kernel version:") + .halign(Align::End) + .hexpand(true) + .build(), + 0, + 1, + 1, + 1, + ); + let kernel_version_label = Label::builder() + .use_markup(true) + .label(&format!("{}", get_kernel_version().trim())) + .hexpand(true) + .halign(Align::Start) + .build(); + container.attach(&kernel_version_label, 1, 1, 1, 1); + + Self { container } } } + +fn get_kernel_version() -> String { + let output = Command::new("uname") + .arg("-r") + .output() + .expect("Could not run uname"); + String::from_utf8(output.stdout).expect("Invalid uname output") +} From d411cf425c8b3f098183fc167bd9c1785b25d930 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Thu, 24 Nov 2022 16:34:19 +0200 Subject: [PATCH 025/113] better pong response --- lact-daemon/src/server/mod.rs | 18 +++++++++++++----- lact-gui/src/client.rs | 4 +--- lact-schema/src/lib.rs | 14 ++++++++++++-- lact-schema/src/response.rs | 3 --- lact-schema/src/tests.rs | 15 +++++++++------ 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/lact-daemon/src/server/mod.rs b/lact-daemon/src/server/mod.rs index 9d0686f0..18145b9a 100644 --- a/lact-daemon/src/server/mod.rs +++ b/lact-daemon/src/server/mod.rs @@ -5,10 +5,7 @@ mod vulkan; use self::handler::Handler; use crate::{config::Config, socket}; -use lact_schema::{ - request::Request, - response::{Pong, Response}, -}; +use lact_schema::{Pong, Request, Response}; use serde::Serialize; use tokio::{ io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, @@ -79,7 +76,7 @@ async fn handle_stream(stream: UnixStream, handler: Handler) -> anyhow::Result<( #[instrument(level = "debug", skip(handler))] async fn handle_request<'a>(request: Request<'a>, handler: &'a Handler) -> anyhow::Result> { match request { - Request::Ping => ok_response(Pong), + Request::Ping => ok_response(ping()), Request::ListDevices => ok_response(handler.list_devices()), Request::DeviceInfo { id } => ok_response(handler.get_device_info(id)?), Request::DeviceStats { id } => ok_response(handler.get_gpu_stats(id)?), @@ -93,3 +90,14 @@ async fn handle_request<'a>(request: Request<'a>, handler: &'a Handler) -> anyho fn ok_response(data: T) -> anyhow::Result> { Ok(serde_json::to_vec(&Response::Ok(data))?) } + +fn ping() -> Pong<'static> { + let version = env!("CARGO_PKG_VERSION"); + let profile = if cfg!(debug_assertions) { + "debug" + } else { + "release" + }; + + Pong { version, profile } +} diff --git a/lact-gui/src/client.rs b/lact-gui/src/client.rs index ad9be341..79a37eba 100644 --- a/lact-gui/src/client.rs +++ b/lact-gui/src/client.rs @@ -1,7 +1,5 @@ use anyhow::{anyhow, Context}; -use lact_schema::{ - request::Request, response::Response, DeviceInfo, DeviceListEntry, DeviceStats, FanCurveMap, -}; +use lact_schema::{DeviceInfo, DeviceListEntry, DeviceStats, FanCurveMap, Request, Response}; use nix::unistd::getuid; use serde::Deserialize; use std::{ diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index 09664cb2..ef8046e8 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -1,8 +1,12 @@ -pub mod request; -pub mod response; +mod request; +mod response; + #[cfg(test)] mod tests; +pub use request::Request; +pub use response::Response; + pub use amdgpu_sysfs::{ gpu_handle::{ overdrive::{ClocksTable, ClocksTableGen}, @@ -20,6 +24,12 @@ use std::{ pub type FanCurveMap = BTreeMap; +#[derive(Serialize, Deserialize, Debug)] +pub struct Pong<'a> { + pub version: &'a str, + pub profile: &'a str, +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DeviceListEntry<'a> { pub id: &'a str, diff --git a/lact-schema/src/response.rs b/lact-schema/src/response.rs index b83e4dad..ae1f9cee 100644 --- a/lact-schema/src/response.rs +++ b/lact-schema/src/response.rs @@ -6,6 +6,3 @@ pub enum Response { Ok(T), Error(String), } - -#[derive(Serialize, Deserialize, Debug)] -pub struct Pong; diff --git a/lact-schema/src/tests.rs b/lact-schema/src/tests.rs index 77221a2d..3ca18370 100644 --- a/lact-schema/src/tests.rs +++ b/lact-schema/src/tests.rs @@ -1,7 +1,4 @@ -use crate::{ - request::Request, - response::{Pong, Response}, -}; +use crate::{Pong, Request, Response}; use serde_json::json; #[test] @@ -18,9 +15,15 @@ fn ping_requset() { fn pong_response() { let expected_response = json!({ "status": "ok", - "data": null + "data": { + "version": "0.0.1", + "profile": "debug" + } + }); + let response = Response::Ok(Pong { + version: "0.0.1", + profile: "debug", }); - let response = Response::Ok(Pong); assert_eq!(serde_json::to_value(&response).unwrap(), expected_response); } From b39ae49e5505659708f307b681d60cebd196fa0e Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sat, 26 Nov 2022 11:32:11 +0200 Subject: [PATCH 026/113] show power levels --- Cargo.lock | 33 ++++++++++---------- lact-daemon/Cargo.toml | 1 + lact-daemon/src/server/gpu_controller/mod.rs | 25 +++++++++++++-- lact-schema/Cargo.toml | 2 +- lact-schema/src/lib.rs | 2 ++ 5 files changed, 44 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b04eee55..3dc817a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,9 +16,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -38,9 +38,9 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" [[package]] name = "ash" -version = "0.37.0+1.3.209" +version = "0.37.1+1.3.235" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006ca68e0f2b03f22d6fa9f2860f85aed430d257fec20f8879b2145e7c7ae1a6" +checksum = "911015c962d56e2e4052f40182ca5462ba60a3d2ff04e827c365a0ab3d65726d" dependencies = [ "libloading", ] @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cairo-rs" @@ -186,9 +186,9 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" dependencies = [ "cfg-if", "crossbeam-utils", @@ -196,9 +196,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] @@ -558,6 +558,7 @@ dependencies = [ "amdgpu-sysfs", "anyhow", "bincode", + "indexmap", "lact-schema", "nix", "pciid-parser", @@ -786,9 +787,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a528564cc62c19a7acac4d81e01f39e53e25e17b934878f4c6d25cc2836e62f8" +checksum = "5f400b0f7905bf702f9f3dc3df5a121b16c54e9e8012c082905fdf09a931861a" dependencies = [ "thiserror", "ucd-trie", @@ -961,9 +962,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8b3801309262e8184d9687fb697586833e939767aea0dda89f5a8e650e8bd7" +checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" dependencies = [ "itoa", "ryu", @@ -1227,7 +1228,7 @@ dependencies = [ [[package]] name = "vulkano" version = "0.32.0" -source = "git+https://github.com/vulkano-rs/vulkano#89e1c56db4c94752e9444483ebd1c12bdb135128" +source = "git+https://github.com/vulkano-rs/vulkano#ebcea02c1e9fd4db62d6dd52c058544351140e36" dependencies = [ "ahash", "ash", @@ -1237,9 +1238,9 @@ dependencies = [ "half", "heck", "indexmap", - "lazy_static", "libloading", "objc", + "once_cell", "parking_lot", "proc-macro2", "quote", diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index f5764687..9a8aaa52 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -27,3 +27,4 @@ tracing = "0.1" tracing-subscriber = "0.3" vulkano = { git = "https://github.com/vulkano-rs/vulkano" } lact-schema = { path = "../lact-schema" } +indexmap = { version = "1.9", features = ["serde"] } diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index afa837a1..4b798a56 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -5,10 +5,11 @@ use super::vulkan::get_vulkan_info; use crate::fork::run_forked; use amdgpu_sysfs::{ error::Error, - gpu_handle::GpuHandle, + gpu_handle::{GpuHandle, PowerLevels, PowerStateKind}, hw_mon::{FanControlMethod, HwMon}, }; use anyhow::{anyhow, Context}; +use indexmap::IndexMap; use lact_schema::{ ClocksInfo, ClockspeedStats, DeviceInfo, DeviceStats, FanStats, GpuPciInfo, LinkInfo, PciInfo, PowerStats, VoltageStats, VramStats, @@ -25,6 +26,13 @@ use tracing::{debug, error, info, trace, warn}; type FanControlHandle = (Arc, JoinHandle<()>, FanCurve); +const POWER_LEVEL_TYPES: [PowerStateKind; 4] = [ + PowerStateKind::CoreClock, + PowerStateKind::MemoryClock, + PowerStateKind::PcieSpeed, + PowerStateKind::SOCClock, +]; + pub struct GpuController { pub handle: GpuHandle, pub pci_info: Option, @@ -109,7 +117,7 @@ impl GpuController { let driver = self.handle.get_driver(); let vbios_version = self.handle.get_vbios_version().ok(); let link_info = self.get_link_info(); - let clocks_table = self.handle.get_power_table().ok(); + let clocks_table = self.handle.get_clocks_table().ok(); let clocks_info = clocks_table .as_ref() .map_or_else(Default::default, ClocksInfo::from); @@ -172,6 +180,7 @@ impl GpuController { temps: self.hw_mon_map(HwMon::get_temps).unwrap_or_default(), busy_percent: self.handle.get_busy_percent().ok(), performance_level: self.handle.get_power_force_performance_level().ok(), + power_levels: self.read_power_levels(), }) } @@ -268,4 +277,16 @@ impl GpuController { } Ok(()) } + + fn read_power_levels(&self) -> IndexMap { + let mut map = IndexMap::with_capacity(POWER_LEVEL_TYPES.len()); + + for kind in POWER_LEVEL_TYPES { + if let Ok(levels) = self.handle.get_power_levels(kind) { + map.insert(kind, levels); + } + } + + map + } } diff --git a/lact-schema/Cargo.toml b/lact-schema/Cargo.toml index ff35d25a..413ee7dd 100644 --- a/lact-schema/Cargo.toml +++ b/lact-schema/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] amdgpu-sysfs = { path = "../../amdgpu-sysfs-rs", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } -indexmap = { version = "1.9", features = ["serde"] } +indexmap = { version = "*", features = ["serde"] } [dev-dependencies] serde_json = "1.0" diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index ef8046e8..97b1a13a 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -4,6 +4,7 @@ mod response; #[cfg(test)] mod tests; +use amdgpu_sysfs::gpu_handle::{PowerLevels, PowerStateKind}; pub use request::Request; pub use response::Response; @@ -111,6 +112,7 @@ pub struct DeviceStats { pub temps: HashMap, pub busy_percent: Option, pub performance_level: Option, + pub power_levels: IndexMap, } #[derive(Serialize, Deserialize, Debug, Clone)] From 0f635975bbc21d48d289d9a97abe8667d24b25a1 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sat, 31 Dec 2022 15:32:59 +0200 Subject: [PATCH 027/113] new clocks levels logic --- Cargo.lock | 169 ++++++++++++------- lact-daemon/Cargo.toml | 5 +- lact-daemon/src/server/gpu_controller/mod.rs | 26 +-- lact-gui/Cargo.toml | 2 +- lact-schema/src/lib.rs | 6 +- 5 files changed, 115 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3dc817a3..fc3082bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,16 +25,17 @@ dependencies = [ [[package]] name = "amdgpu-sysfs" -version = "0.6.1" +version = "0.7.0" dependencies = [ + "enum_dispatch", "serde", ] [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "ash" @@ -118,9 +119,9 @@ checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "cairo-rs" -version = "0.16.3" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "247e1183fa769ac22121f92276dae52f89acaf297f24b1320019f439b6e3b46f" +checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" dependencies = [ "bitflags", "cairo-sys-rs", @@ -209,13 +210,25 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "enum_dispatch" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1693044dcf452888dd3a6a6a0dab67f0652094e3920dfe029a54d2f37d9b7394" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "field-offset" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" dependencies = [ - "memoffset", + "memoffset 0.6.5", "rustc_version", ] @@ -299,9 +312,9 @@ dependencies = [ [[package]] name = "gdk" -version = "0.16.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0a4a7aa015962d02634258715164977eb151b48fd250dcac48fab8d312a5aa" +checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1" dependencies = [ "bitflags", "cairo-rs", @@ -315,9 +328,9 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.16.3" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba3e42776d1466938add08211734738d5c76e863a25b7a8064c4433a74a1a26" +checksum = "c3578c60dee9d029ad86593ed88cb40f35c1b83360e12498d055022385dd9a05" dependencies = [ "bitflags", "gdk-pixbuf-sys", @@ -369,9 +382,9 @@ dependencies = [ [[package]] name = "gio" -version = "0.16.3" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d4a17d999e6e4e05d87c2bb05b7140d47769bc53211711a33e2f91536458714" +checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092" dependencies = [ "bitflags", "futures-channel", @@ -402,9 +415,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.16.3" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50feee2f1e73be50e6634c901bfced69a0937c5e4e4673067ade85e093fa9bd7" +checksum = "ddd4df61a866ed7259d6189b8bcb1464989a77f1d85d25d002279bbe9dd38b2f" dependencies = [ "bitflags", "futures-channel", @@ -460,9 +473,9 @@ dependencies = [ [[package]] name = "gtk" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca4ab4a5a19d45748f405d2e00201e86d351e80cc13b9d43dfb9be35033a8bd6" +checksum = "e4d3507d43908c866c805f74c9dd593c0ce7ba5c38e576e41846639cdcd4bee6" dependencies = [ "atk", "bitflags", @@ -547,9 +560,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "lact-daemon" @@ -558,7 +571,6 @@ dependencies = [ "amdgpu-sysfs", "anyhow", "bincode", - "indexmap", "lact-schema", "nix", "pciid-parser", @@ -603,9 +615,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libloading" @@ -669,6 +681,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "mio" version = "0.8.5" @@ -683,16 +704,16 @@ dependencies = [ [[package]] name = "nix" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb" +checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" dependencies = [ - "autocfg", "bitflags", "cfg-if", "libc", - "memoffset", + "memoffset 0.7.1", "pin-utils", + "static_assertions", ] [[package]] @@ -716,9 +737,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "overload" @@ -728,9 +749,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pango" -version = "0.16.3" +version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6a83cd4015382dbb0f4fcf3ab7b277d4885711a62b2f2c1e6582a120094edad" +checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" dependencies = [ "bitflags", "gio", @@ -764,9 +785,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" dependencies = [ "cfg-if", "libc", @@ -787,9 +808,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f400b0f7905bf702f9f3dc3df5a121b16c54e9e8012c082905fdf09a931861a" +checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" dependencies = [ "thiserror", "ucd-trie", @@ -850,18 +871,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -912,9 +933,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "scopeguard" @@ -942,18 +963,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.147" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -962,9 +983,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "itoa", "ryu", @@ -973,9 +994,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.14" +version = "0.9.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d232d893b10de3eb7258ff01974d6ee20663d8e833263c99409d4b13a0209da" +checksum = "92b5b431e8907b50339b51223b97d102db8d987ced36f6e4d03621db9316c834" dependencies = [ "indexmap", "itoa", @@ -1027,11 +1048,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" -version = "1.0.103" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", @@ -1053,18 +1080,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -1082,9 +1109,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" dependencies = [ "autocfg", "bytes", @@ -1095,14 +1122,14 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ "proc-macro2", "quote", @@ -1111,9 +1138,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" dependencies = [ "serde", ] @@ -1188,15 +1215,15 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unsafe-libyaml" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68" +checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2" [[package]] name = "valuable" @@ -1228,7 +1255,7 @@ dependencies = [ [[package]] name = "vulkano" version = "0.32.0" -source = "git+https://github.com/vulkano-rs/vulkano#ebcea02c1e9fd4db62d6dd52c058544351140e36" +source = "git+https://github.com/vulkano-rs/vulkano#21f850dc992a7e3a96409a6c849b981682427cdb" dependencies = [ "ahash", "ash", @@ -1250,6 +1277,18 @@ dependencies = [ "smallvec", "thread_local", "vk-parse", + "vulkano_macros", +] + +[[package]] +name = "vulkano_macros" +version = "0.32.0" +source = "git+https://github.com/vulkano-rs/vulkano#21f850dc992a7e3a96409a6c849b981682427cdb" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index 9a8aaa52..f0bb87c5 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -9,12 +9,12 @@ edition = "2021" amdgpu-sysfs = { path = "../../amdgpu-sysfs-rs", features = ["serde"] } anyhow = "1.0" bincode = "1.3" -nix = "0.25" +nix = "0.26" pciid-parser = { version = "0.6", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yaml = "0.9" -tokio = { version = "1.21.2", features = [ +tokio = { version = "1.23.0", features = [ "rt", "macros", "net", @@ -27,4 +27,3 @@ tracing = "0.1" tracing-subscriber = "0.3" vulkano = { git = "https://github.com/vulkano-rs/vulkano" } lact-schema = { path = "../lact-schema" } -indexmap = { version = "1.9", features = ["serde"] } diff --git a/lact-daemon/src/server/gpu_controller/mod.rs b/lact-daemon/src/server/gpu_controller/mod.rs index 4b798a56..37c1b76e 100644 --- a/lact-daemon/src/server/gpu_controller/mod.rs +++ b/lact-daemon/src/server/gpu_controller/mod.rs @@ -5,11 +5,10 @@ use super::vulkan::get_vulkan_info; use crate::fork::run_forked; use amdgpu_sysfs::{ error::Error, - gpu_handle::{GpuHandle, PowerLevels, PowerStateKind}, + gpu_handle::GpuHandle, hw_mon::{FanControlMethod, HwMon}, }; use anyhow::{anyhow, Context}; -use indexmap::IndexMap; use lact_schema::{ ClocksInfo, ClockspeedStats, DeviceInfo, DeviceStats, FanStats, GpuPciInfo, LinkInfo, PciInfo, PowerStats, VoltageStats, VramStats, @@ -26,13 +25,6 @@ use tracing::{debug, error, info, trace, warn}; type FanControlHandle = (Arc, JoinHandle<()>, FanCurve); -const POWER_LEVEL_TYPES: [PowerStateKind; 4] = [ - PowerStateKind::CoreClock, - PowerStateKind::MemoryClock, - PowerStateKind::PcieSpeed, - PowerStateKind::SOCClock, -]; - pub struct GpuController { pub handle: GpuHandle, pub pci_info: Option, @@ -180,7 +172,9 @@ impl GpuController { temps: self.hw_mon_map(HwMon::get_temps).unwrap_or_default(), busy_percent: self.handle.get_busy_percent().ok(), performance_level: self.handle.get_power_force_performance_level().ok(), - power_levels: self.read_power_levels(), + core_clock_levels: self.handle.get_core_clock_levels().ok(), + memory_clock_levels: self.handle.get_memory_clock_levels().ok(), + pcie_clock_levels: self.handle.get_pcie_clock_levels().ok(), }) } @@ -277,16 +271,4 @@ impl GpuController { } Ok(()) } - - fn read_power_levels(&self) -> IndexMap { - let mut map = IndexMap::with_capacity(POWER_LEVEL_TYPES.len()); - - for kind in POWER_LEVEL_TYPES { - if let Ok(levels) = self.handle.get_power_levels(kind) { - map.insert(kind, levels); - } - } - - map - } } diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml index e1e43899..59e24413 100644 --- a/lact-gui/Cargo.toml +++ b/lact-gui/Cargo.toml @@ -10,7 +10,7 @@ gtk = "0.16" # pango = "0.16" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } -nix = "0.25" +nix = "0.26" anyhow = "1.0" serde_json = "1.0" serde = "1.0" diff --git a/lact-schema/src/lib.rs b/lact-schema/src/lib.rs index 97b1a13a..2b645c1d 100644 --- a/lact-schema/src/lib.rs +++ b/lact-schema/src/lib.rs @@ -4,7 +4,7 @@ mod response; #[cfg(test)] mod tests; -use amdgpu_sysfs::gpu_handle::{PowerLevels, PowerStateKind}; +use amdgpu_sysfs::gpu_handle::PowerLevels; pub use request::Request; pub use response::Response; @@ -112,7 +112,9 @@ pub struct DeviceStats { pub temps: HashMap, pub busy_percent: Option, pub performance_level: Option, - pub power_levels: IndexMap, + pub core_clock_levels: Option>, + pub memory_clock_levels: Option>, + pub pcie_clock_levels: Option>, } #[derive(Serialize, Deserialize, Debug, Clone)] From 2e25a5b5fbd0ea7240e9c44a33d75e35a7039081 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 1 Jan 2023 20:16:16 +0200 Subject: [PATCH 028/113] add makefile --- Makefile | 15 +++++++++++++++ lact-gui/src/client.rs | 2 ++ lactd.service | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..ba3bde9a --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +export CARGO_TARGET_DIR ?= ./target +DESTDIR ?= /usr/local + +build-release: + cargo build --release + +install: + install -Dm755 target/release/lact-daemon ${DESTDIR}/bin/lact-daemon + install -Dm755 target/release/lact-gui ${DESTDIR}/bin/lact-gui + install -Dm755 lactd.service ${DESTDIR}/lib/systemd/system/lactd.service + +uninstall: + rm ${DESTDIR}/bin/lact-daemon + rm ${DESTDIR}/bin/lact-gui + rm ${DESTDIR}/lib/systemd/system/lactd.service diff --git a/lact-gui/src/client.rs b/lact-gui/src/client.rs index 79a37eba..13e8f860 100644 --- a/lact-gui/src/client.rs +++ b/lact-gui/src/client.rs @@ -10,6 +10,7 @@ use std::{ path::PathBuf, sync::{Arc, Mutex}, }; +use tracing::info; #[derive(Clone)] pub struct DaemonClient { @@ -20,6 +21,7 @@ impl DaemonClient { pub fn connect() -> anyhow::Result { let path = get_socket_path().context("Could not connect to daemon: socket file not found")?; + info!("Connecting to service at {path:?}"); let stream = UnixStream::connect(path).context("Could not connect to daemon")?; let reader = BufReader::new(stream.try_clone()?); diff --git a/lactd.service b/lactd.service index 3d38ec31..140144ec 100644 --- a/lactd.service +++ b/lactd.service @@ -3,7 +3,7 @@ Description=AMDGPU Control Daemon After=multi-user.target [Service] -ExecStart=/usr/local/bin/lact-daemon +ExecStart=lact-daemon [Install] WantedBy=graphical.target From 67957a4673f4e9c78f01846cd82a2c0f295bf9fd Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 1 Jan 2023 20:28:19 +0200 Subject: [PATCH 029/113] refactor: move lact-client to a separate crate --- Cargo.lock | 14 +++++++++++++- Cargo.toml | 2 +- lact-client/Cargo.toml | 12 ++++++++++++ lact-gui/src/client.rs => lact-client/src/lib.rs | 4 +++- lact-gui/Cargo.toml | 2 +- lact-gui/src/app/header.rs | 2 +- lact-gui/src/app/mod.rs | 4 ++-- lact-gui/src/app/root_stack/info_page/mod.rs | 2 +- .../src/app/root_stack/info_page/vulkan_info.rs | 2 +- lact-gui/src/app/root_stack/oc_page/mod.rs | 2 +- .../root_stack/oc_page/performance_level_frame.rs | 2 +- lact-gui/src/app/root_stack/oc_page/stats_grid.rs | 2 +- .../root_stack/thermals_page/fan_curve_frame.rs | 2 +- lact-gui/src/app/root_stack/thermals_page/mod.rs | 2 +- lact-gui/src/main.rs | 3 +-- 15 files changed, 41 insertions(+), 16 deletions(-) create mode 100644 lact-client/Cargo.toml rename lact-gui/src/client.rs => lact-client/src/lib.rs (96%) diff --git a/Cargo.lock b/Cargo.lock index fc3082bd..53dd676d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,6 +564,18 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +[[package]] +name = "lact-client" +version = "0.1.0" +dependencies = [ + "anyhow", + "lact-schema", + "nix", + "serde", + "serde_json", + "tracing", +] + [[package]] name = "lact-daemon" version = "0.2.0" @@ -589,7 +601,7 @@ version = "0.2.0" dependencies = [ "anyhow", "gtk", - "lact-schema", + "lact-client", "nix", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 960cc6bf..3cf776ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "2" -members = ["lact-daemon", "lact-schema", "lact-gui"] +members = ["lact-*"] diff --git a/lact-client/Cargo.toml b/lact-client/Cargo.toml new file mode 100644 index 00000000..7108e3a2 --- /dev/null +++ b/lact-client/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "lact-client" +version = "0.1.0" +edition = "2021" + +[dependencies] +lact-schema = { path = "../lact-schema" } +anyhow = "1.0.68" +nix = { version = "0.26.1", default-features = false } +serde = "1.0.152" +tracing = "0.1.37" +serde_json = "1.0.91" diff --git a/lact-gui/src/client.rs b/lact-client/src/lib.rs similarity index 96% rename from lact-gui/src/client.rs rename to lact-client/src/lib.rs index 13e8f860..16cf72e3 100644 --- a/lact-gui/src/client.rs +++ b/lact-client/src/lib.rs @@ -1,6 +1,8 @@ +pub use lact_schema as schema; + use anyhow::{anyhow, Context}; -use lact_schema::{DeviceInfo, DeviceListEntry, DeviceStats, FanCurveMap, Request, Response}; use nix::unistd::getuid; +use schema::{DeviceInfo, DeviceListEntry, DeviceStats, FanCurveMap, Request, Response}; use serde::Deserialize; use std::{ io::{BufRead, BufReader, Write}, diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml index 59e24413..f65c971d 100644 --- a/lact-gui/Cargo.toml +++ b/lact-gui/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Ilya Zlobintsev "] edition = "2021" [dependencies] -lact-schema = { path = "../lact-schema" } +lact-client = { path = "../lact-client" } gtk = "0.16" # pango = "0.16" tracing = "0.1" diff --git a/lact-gui/src/app/header.rs b/lact-gui/src/app/header.rs index 2b89005f..c560c6f9 100644 --- a/lact-gui/src/app/header.rs +++ b/lact-gui/src/app/header.rs @@ -1,6 +1,6 @@ use gtk::prelude::*; use gtk::*; -use lact_schema::DeviceListEntry; +use lact_client::schema::DeviceListEntry; use pango::EllipsizeMode; #[derive(Clone)] diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs index 966374fd..9bec8ca4 100644 --- a/lact-gui/src/app/mod.rs +++ b/lact-gui/src/app/mod.rs @@ -7,14 +7,14 @@ use std::sync::{Arc, RwLock}; use std::thread; use std::time::Duration; -use crate::client::DaemonClient; use anyhow::Context; use apply_revealer::ApplyRevealer; use glib::clone; use gtk::prelude::*; use gtk::*; use header::Header; -use lact_schema::DeviceStats; +use lact_client::schema::DeviceStats; +use lact_client::DaemonClient; use root_stack::RootStack; use tracing::{debug, error, info, trace}; diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs index 94da7ea4..f078dda6 100644 --- a/lact-gui/src/app/root_stack/info_page/mod.rs +++ b/lact-gui/src/app/root_stack/info_page/mod.rs @@ -2,7 +2,7 @@ mod vulkan_info; use gtk::prelude::*; use gtk::*; -use lact_schema::{DeviceInfo, DeviceStats}; +use lact_client::schema::{DeviceInfo, DeviceStats}; use vulkan_info::VulkanInfoFrame; #[derive(Clone)] diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs index 063c8585..c1388c33 100644 --- a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs @@ -1,7 +1,7 @@ use glib::clone; use gtk::prelude::*; use gtk::*; -use lact_schema::VulkanInfo; +use lact_client::schema::VulkanInfo; use tracing::trace; #[derive(Clone)] diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs index 95e590c4..8a85f7ed 100644 --- a/lact-gui/src/app/root_stack/oc_page/mod.rs +++ b/lact-gui/src/app/root_stack/oc_page/mod.rs @@ -7,7 +7,7 @@ mod warning_frame; use glib::clone; use gtk::prelude::*; use gtk::*; -use lact_schema::{DeviceInfo, DeviceStats, PerformanceLevel, PowerStats}; +use lact_client::schema::{DeviceInfo, DeviceStats, PerformanceLevel, PowerStats}; use performance_level_frame::PowerProfileFrame; use power_cap_frame::PowerCapFrame; use stats_grid::StatsGrid; diff --git a/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs index 9d0eb644..0a6cc0a7 100644 --- a/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs +++ b/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs @@ -1,6 +1,6 @@ use gtk::prelude::*; use gtk::*; -use lact_schema::PerformanceLevel; +use lact_client::schema::PerformanceLevel; #[derive(Clone)] pub struct PowerProfileFrame { diff --git a/lact-gui/src/app/root_stack/oc_page/stats_grid.rs b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs index 9e25b7bb..59145da8 100644 --- a/lact-gui/src/app/root_stack/oc_page/stats_grid.rs +++ b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs @@ -1,6 +1,6 @@ use gtk::prelude::*; use gtk::*; -use lact_schema::{ClockspeedStats, DeviceStats, PowerStats, VoltageStats, VramStats}; +use lact_client::schema::{ClockspeedStats, DeviceStats, PowerStats, VoltageStats, VramStats}; #[derive(Clone)] pub struct StatsGrid { diff --git a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs index 12ddc803..d986addf 100644 --- a/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs +++ b/lact-gui/src/app/root_stack/thermals_page/fan_curve_frame.rs @@ -1,6 +1,6 @@ use gtk::prelude::*; use gtk::*; -use lact_schema::FanCurveMap; +use lact_client::schema::FanCurveMap; use std::collections::BTreeMap; use tracing::debug; diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs index 29024e7c..cb854cbe 100644 --- a/lact-gui/src/app/root_stack/thermals_page/mod.rs +++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs @@ -3,7 +3,7 @@ use glib::clone; use gtk::prelude::*; use gtk::*; -use lact_schema::DeviceStats; +use lact_client::schema::DeviceStats; use std::collections::BTreeMap; use tracing::trace; diff --git a/lact-gui/src/main.rs b/lact-gui/src/main.rs index 37a77bfd..de86a58c 100644 --- a/lact-gui/src/main.rs +++ b/lact-gui/src/main.rs @@ -1,9 +1,8 @@ mod app; -mod client; use anyhow::{anyhow, Context}; use app::App; -use client::DaemonClient; +use lact_client::DaemonClient; use tracing::metadata::LevelFilter; use tracing_subscriber::EnvFilter; From 5fe5f2e03ecf03d9987d316f51162c9679d143bc Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 1 Jan 2023 21:15:30 +0200 Subject: [PATCH 030/113] wip cli --- Cargo.lock | 154 +++++++++++++++++++++++++++++++++++++++++++ lact-cli/Cargo.toml | 9 +++ lact-cli/src/args.rs | 35 ++++++++++ lact-cli/src/main.rs | 51 ++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 lact-cli/Cargo.toml create mode 100644 lact-cli/src/args.rs create mode 100644 lact-cli/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 53dd676d..37b95e8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,6 +142,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" + [[package]] name = "cfg-expr" version = "0.11.0" @@ -157,6 +163,43 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -222,6 +265,27 @@ dependencies = [ "syn", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "field-offset" version = "0.3.4" @@ -547,6 +611,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "indexmap" version = "1.9.2" @@ -558,12 +631,43 @@ dependencies = [ "serde", ] +[[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys", +] + [[package]] name = "itoa" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +[[package]] +name = "lact-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "lact-client", +] + [[package]] name = "lact-client" version = "0.1.0" @@ -641,6 +745,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -753,6 +863,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + [[package]] name = "overload" version = "0.1.1" @@ -943,6 +1059,20 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.36.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.12" @@ -1066,6 +1196,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "1.0.107" @@ -1090,6 +1226,15 @@ dependencies = [ "version-compare", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.38" @@ -1325,6 +1470,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/lact-cli/Cargo.toml b/lact-cli/Cargo.toml new file mode 100644 index 00000000..cd5f3036 --- /dev/null +++ b/lact-cli/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "lact-cli" +version = "0.1.0" +edition = "2021" + +[dependencies] +lact-client = { path = "../lact-client" } +anyhow = "1.0.68" +clap = { version = "4.0.32", features = ["derive"] } diff --git a/lact-cli/src/args.rs b/lact-cli/src/args.rs new file mode 100644 index 00000000..9ff36052 --- /dev/null +++ b/lact-cli/src/args.rs @@ -0,0 +1,35 @@ +use clap::{Parser, Subcommand}; +use lact_client::DaemonClient; + +#[derive(Parser)] +#[command(author, version, about)] +pub struct Args { + pub gpu_id: Option, + #[command(subcommand)] + pub subcommand: Command, +} + +#[derive(Subcommand)] +pub enum Command { + /// List GPUs + ListGpus, + /// Show GPU info + Info, +} + +impl Args { + pub fn gpu_ids(&self, client: &DaemonClient) -> Vec { + match self.gpu_id { + Some(ref id) => vec![id.clone()], + None => { + let buffer = client.list_devices().expect("Could not list GPUs"); + buffer + .inner() + .expect("Could not deserialize GPUs response") + .into_iter() + .map(|entry| entry.id.to_owned()) + .collect() + } + } + } +} diff --git a/lact-cli/src/main.rs b/lact-cli/src/main.rs new file mode 100644 index 00000000..0a2f04b2 --- /dev/null +++ b/lact-cli/src/main.rs @@ -0,0 +1,51 @@ +mod args; + +use anyhow::{Context, Result}; +use args::{Args, Command}; +use clap::Parser; +use lact_client::DaemonClient; + +fn main() -> Result<()> { + let args = Args::parse(); + let client = DaemonClient::connect()?; + + let f = match args.subcommand { + Command::ListGpus => list_gpus, + Command::Info => info, + }; + f(&args, &client) +} + +fn list_gpus(args: &Args, client: &DaemonClient) -> Result<()> { + let buffer = client.list_devices()?; + for entry in buffer.inner()? { + let id = entry.id; + if let Some(name) = entry.name { + println!("{id} ({name})"); + } else { + println!("{id}"); + } + } + Ok(()) +} + +fn info(args: &Args, client: &DaemonClient) -> Result<()> { + for id in args.gpu_ids(&client) { + let info_buffer = client.get_device_info(&id)?; + let info = info_buffer.inner()?; + let pci_info = info.pci_info.context("GPU reports no pci info")?; + + if let Some(ref vendor) = pci_info.device_pci_info.vendor { + println!("GPU Vendor: {vendor}"); + } + if let Some(ref model) = pci_info.device_pci_info.model { + println!("GPU Model: {model}"); + } + println!("Driver in use: {}", info.driver); + if let Some(ref vbios_version) = info.vbios_version { + println!("VBIOS version: {vbios_version}"); + } + println!("Link: {:?}", info.link_info); + } + Ok(()) +} From 0b4c42da124d5e3cc981734759323154b968c7a5 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Sun, 1 Jan 2023 23:25:37 +0200 Subject: [PATCH 031/113] update packaging --- .gitignore | 1 + Makefile | 8 +++++++- lact.desktop | 5 ----- res/lact.desktop | 7 +++++++ res/lact.png | Bin 0 -> 44437 bytes lactd.service => res/lactd.service | 0 6 files changed, 15 insertions(+), 6 deletions(-) delete mode 100644 lact.desktop create mode 100644 res/lact.desktop create mode 100644 res/lact.png rename lactd.service => res/lactd.service (100%) diff --git a/.gitignore b/.gitignore index 40c3a7b2..18e76c91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target +AppDir/ *.glade\~ diff --git a/Makefile b/Makefile index ba3bde9a..2645043f 100644 --- a/Makefile +++ b/Makefile @@ -7,9 +7,15 @@ build-release: install: install -Dm755 target/release/lact-daemon ${DESTDIR}/bin/lact-daemon install -Dm755 target/release/lact-gui ${DESTDIR}/bin/lact-gui - install -Dm755 lactd.service ${DESTDIR}/lib/systemd/system/lactd.service + install -Dm755 target/release/lact-cli ${DESTDIR}/bin/lact-cli + install -Dm755 res/lactd.service ${DESTDIR}/lib/systemd/system/lactd.service + install -Dm755 res/lact.desktop ${DESTDIR}/share/applications/lact.desktop + install -Dm755 res/lact.png ${DESTDIR}/share/pixmaps/lact.png uninstall: rm ${DESTDIR}/bin/lact-daemon rm ${DESTDIR}/bin/lact-gui + rm ${DESTDIR}/bin/lact-cli rm ${DESTDIR}/lib/systemd/system/lactd.service + rm ${DESTDIR}/share/applications/lact.desktop + rm ${DESTDIR}/share/pixmaps/lact.png diff --git a/lact.desktop b/lact.desktop deleted file mode 100644 index 532759fa..00000000 --- a/lact.desktop +++ /dev/null @@ -1,5 +0,0 @@ -[Desktop Entry] -Type=Application -Name=LACT -Description=AMDGPU Control Application -Exec=lact-gui \ No newline at end of file diff --git a/res/lact.desktop b/res/lact.desktop new file mode 100644 index 00000000..3d0772c0 --- /dev/null +++ b/res/lact.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=LACT +GenericName=AMDGPU Control Application +Exec=lact-gui +Icon=lact +Categories=System; diff --git a/res/lact.png b/res/lact.png new file mode 100644 index 0000000000000000000000000000000000000000..450d9fd571bdb84956324e1b4c71b34ab9fdc6ea GIT binary patch literal 44437 zcmeEt<9nQ2`*xD1abq^NZKttqHg0UYu^TtG-PpEmV`3*0=bi4opFiR~j^Bs-!_2H} z;kwU@>zr^!d5JG@U*W*Oz`jUHiYkMFf#3aoV4y(Xj4f_OfIiq+h=?d!7#o9uQN_8$ z@ydMqh8`@4#-0aDU5_jk*BcfLu7oTTEtW<^EPl0M&A~_DMA=YvHFe=U_3h`v zW5I~!nxc-~`NYSrJtWf0nhY1p?>qe@6nKn}rx(fja&gVH+@-l1^*PY(;DN^;^z`0T zd`6?-!PSXbj*nUnlRIt$W-QE~7#3K6zYLTbQ~)OOu4=Bn*DB8VP3GI~P9z=z3J`}8 zzQ++s2N6-zR4C`jYFbLMylioMqAC7brosV#ykerCfimMhv`+5v?=ZC|QGPscK0oPq&q;3PMD2k9EDI##l*7#rJ`EC2P zGY|QRzf3NoVMg32e(6bV&H#IdJ z{v(Y}1VKiF+NQyfnvXR+^kf|}KJ(h50+Y3tNuDj8HZ8S_A*5zq>gI)EZ$ZRCCb?fG z%=iQp0SE0lfKSA~K+MSeW@G{GRZBpC+KGGMVNsa^7*FO*d_z;)TarM&!D;Cq>tGxL zS!H?0=jGJE^Us=h0bmQaWRH$551+b{Ti()HnmA!=;g?d>X>f!ASJ$!Cto3pjF&yJ zwe4Lm$_BB2Hdqi0cAnEbC12`8Xdh_bD;R{fNxevFFE(W7{7B+OHD`)snuYwtSYiYq z@R(s|t2C@aqJ;oSD)PYmT5cJV$7n}>F%Krq!iv$#^r+m!xuq;R6?;N@zxNp}^lJ$B-UJf>nAcVR3NfL6bUBc?+h@{^rAcAWVLe5YY} zlzfN8e_*kIlsx0>lh3Xrsr6IstbWgvJeM+0G)F>byL`julAAy78!&n!C|j_&QlI`Z zTGx}ip!64HIlobeYUk`@PTXswx}*h~ykXt{D6Z+&yaGkGMgFut;iNmLQJ}Nng$F z-4f4l#(cM;{b^$p7IY_M1d9l3^M_;`8={xqS<$Hh&&#S4Epf8nYhh9MdxNQ;B?ZWgW@eUuFwkavJP4@;LiKD-|H5>(25c^r z&yDt#hE6vXEB>D?-YuBZEW4&O4)X~mg?Gv9biK*V(oGT)}U!%QHZ({7SSoWJ2@|I&|eXwzOq zJKA=u4q6bOiKvnovKP$qzg>&#L zAQJ&#O1B0I1${DLN`6l*wV%IV+B?zE z?{OjN$bPJ}%e5Rv4wacXLl1Zr_g@!5M1_BB8sRE~-!6bLKa^_aiOh z6S$iej+veZdCbS^k|L*>>?WIK|MgJdlEqrn-oT{~lHKcx28`RuaO7o<@7@sZ#2~Z5 zGpc^q9%B1zA7P%$P)z%=NM#twFAGR6(y-6t=SE{{^r&9xlBXXj$1DjGf|npyb~`mf zv&RuP?l(+ov9fU>iz(0{hyp3OUnlsiq)Q29X};xQt{>;&{YLGR^?sX;TiH`lUiEWK zqz+r|8pi%~_lVIOJUgB~QYC2KhrJ$lu>d9!3Sytk5sf=SH_5JYl2#bYzmrVmXlg!! ze=9`l3J` zv63lS9)jn~B|tLx{kH2xZ<$h%jq8usH8;;-_&e5M;gO2g_){QFuL@iDyD!QZ@FcPFd}pVg%PLKKFfq zKxp}XyWjb6oQE=LWeCYHEy&&GleT>(M=dj%GhSc@YHLQ;D`9DO)4=rhoDn2Io#nEv z@8&J0CHuWTbs2ghBlS{EuFUsSksNDeB6W&x{Jc}4^KCZ{tNnotBMzld8hU`Tkbj2Q z1xaY!*sw*5wc8|&-dEpzY|M4KXhuL*P}zzTH^MAqdRDdk+jXqNU}aP9`NUWsngwS( z!3jgM(@Lm8$`Yl=uX?%49#yTIEsiZ+-aCZv-zQzLyz*YA<%BiUJjMOxEjK?d{fx&;i)mydghq&v z_x`{U-+!dgDo^p`%81zgx;@w>z*2j<{MKqRXf*nz68U45(0vzHw&Uzi;w@DHv_YJ) zOwI8NQ(>5$v7-G@!9*u3QPd8NI zdhhcp*dD#Gljd2W+>O?2A)mKi%?K9h1k3xcuRHzT;P1GKrBJ9aAct-Cn{|JAF2ZCE zLKtyfVOadAjQ7|T}>9xBVP9txTlWhNEUaS z)3YwMo+toM5YRS(SuT)AB*QXm^}yjbAcH=)OI#RM-!JqVenTUq7z+KD|3)eE8ChJL)DFF zxrkeeDkmZlLBeJ7ZxnsUh$k%Q<5}^-iaD)M? zO8Mj5x7LO9y9FjIB1ndB2?|C_e@vyMhWX$H_rpk;E}~eOxS_0CE*Il*d z+i&Kwi{_IbL*3_p_53IcMP%RH5T8tk_Ve*seR@d#)&+}Z|Ekdy@p1V5+wYkWi^B*Z zLYQCkV$GGLlxC1YvU&k5`h6*Gm?xh#+Tl)y$^s?Kn29X1|vT5k}1%`~x z@)dYs6Bt@ySgV>zjNkZxb=mCj@r@g8_;!J2B{2J}5c|T8dG>o04!w>t6enqM{LZCU z_sZ9H5hjxu;YB1u+cj3z6U`{=puo{xT_oF+kp1DUZQ={Fo@^TSc1idI<4aBRB=(1? zmc*xxZS`vF!>E*NoH`gC7OGaBjkq48S;Q#2i=;>Wk+&GF<2>o-Cz4`^xmP$sa7SY9 zxOK^xanVRTO6Ss}VzR^859xvO=PVZ18M28v3rK9G(|H}1KRIk_%)qdEd9#B%15 zi74Z7N8#z`0DNpr~my?n!cI{7h9EH}DhoxDBP%e>> zEty0-?MSNp;+m3hN<@;(erm*Qf__ujb`}G7IB?GD^t>nYb9_Fz6tT zMG5C_QPZY{*RZ7yUnPV{i8+US_~=-k>&y> zROOE)N`U}+#;%Ncj4kiH%DCmSr6*3z2vhqxiN@*G-PLq_b>Ip4uyq_aIk71 zIz2?K=83)|RSVljNRil)GI-P3Cq)_s+H7as+$rAJ?wpl6{G`d!-uJmDKwv>>sXfXz zHTW~EJqfeD3@9;@H!1X>f(VZB-%)n7!?3PnmJpAzMC{iQopcId7kdjs(bap1|}!%w3Wd$LpvJkeUjoKrog-_?DYw z(cC?~WQ?7MD24JQW})@9x{(DH73~W71LmKmg}YbhROUl2cm%bAvMm=*yapcc!wD;fC1z4UJZlm~Ma)fxs+V=SU7 zq?9`nMCR`zY}Qpr1x65O6p@Jf_sC{QPkA5E<}fD}?F#!BWAJ>%owHT-yB7Z&DO@0r z;sP`xb%jTPo1}QO1_=V}$!xhY#FX*lS~ab~RGo&yMEELoBjlcLt>%2pE3@$%-oP>WBV?$& z0!lLm!cimgleh+x+^MOTJxVbt^~mm@ zguiuT28k#eqH<5Y(ZyNC>FTX(G0&G#^&eAn)F#s95{VEq{c&?75 zbR&Q4=ONmkJ~yJ zGxG~1-@#-*C>cn#zDrJyvw*bjl9&#f23(qW+I~mFeIEVh@NCqvgLIRSU1JB$<#Li*?7mLzm303RM#vwXN(h;TtC_5RrQqM#9^w+UulrTvgF& zf))~w)H&KZMdPh3=@p~+f}r_doStSTWgeG|594lOuseXK;XKJ%2FT=)pR z3UHsKWan3`o%Q8lKotIoxi<1)o~>+8?0AF1HEDD18()L@i(HO8`CI+*Q;-eUETgQ` zFb>4qV5~x$^Q)gzU z%a)F8$}F_os1BKjj)_@$@DLP^!N&8_e!J%bZ(O*g>7k5t-U9M|JF4V$&8aIbz3jb< z`6>Q=J=MH-Pi8f4f!JoVEuu&mlp3!Sf0x;ARO12mlnPTC&SCAEwUo#Ml8p0h9hny{ z`K!pENt_)|6#l^8V^T+9p^aofx$xZzNHXYnRwrGOd0b2+EW03WrV`LpllC4LzNK@& zvSaW zbdfpfkUH9*ye!gatW%gX%U*icY~=eqCLO>2v_gb6veEenbcfYnxa$B)Xnm&tIx7>v zN9i0ABB!A??liaK-{)F!+>^CX4Dh@3B8rhOh(3{vnUS~#z%$Cy#tQrV4zW*QkmT0A{Wh`LU;?Y{=4iQ4 z07Dilq;Ye2CYDaO<{u3yF5|oglTKPGnY_dA=%j$kvvAHyhU8$Io#RH}8PwPzT-Uc3 zzM(#|X6fncO74+<a94=c)#H?=4^pfKt~Exua0NE&SYssm>Hjsi${5H3rp=e%j~eCEN0Y^EWqCuUD}o zvG%!iBewQaYgxaPLNhWXd`lV%$P$+p$=y|?Ox~qhGaIjS_A`BPyW})!i~#q*hR3=s zJ`Qnq?(nquM!C)FS$EhtH-)}HJfD1B=$63q@M%Fk>A)zjYdxu~cs{k8_7c$;O3xSI zYk^zal&Pz$lRp=0k4oCq0{%d(S&O}@9d0G7Foo38;E zVd>&l=v6<|Y|BS&j@Kw`U+kneH(VLHpVz{XdCp;^1srNt2GKdN$voCZqR%6<1QV92 zClVgd6;>nH2rhPE1+yn6Gl-mx7W_uht@WB~_J{4K&&?}`J>0LrHi z=k70AUKwMGPNVRUtpq50Q+F7`0V5RW?l*@}HTdKG0zC!$nW`EDy$gEe&Nr&c-!$WC zI=k&CM$meysFY0=wk3BTD8mJOt2dLcZc~qdRTm58-+MbaZ-USgr_sb|KWy>5o{zZ> zpar|P`w+(@5A@$>Vv$d#OLwbF)wK2@EazRq8B}obw@;mQE7;}UqSyS2YEDepR zLsMo5z)85MJZyFfe&XO}Qxoun`n)dR8pGGE;8GO#2;Hm+QeDbTn2_C1V6?0Z+4`K- zyBckQp_DHqY~FiP&#iF7EOyI|ZqehK>uX@82`T@5A!8vM!_he9#y-PddJ5r4X=_15 zB)#bnZvM)ajh$s;_(^ctorWcN;6MbT^p*W1~IF71Kr`zpl1T0U~&Ob!F#e73LWo zD&Ikr`)+Z8T|!5Ph8=WGuJhE3jnUzokEJ3oMNg%KO?x7pcN}lg{OsFB@+aK5r9_@Ix&+zFL zHsAVIk}Yj<#J4R*KJZ$iG-}+10l)-S-Db0FCI z{qsn1@7Iow&@LUAjc~TZc@^f}=DnJy0j$hn`CBS`NP>?u^En>33)Lw96T`lUh-prk z+%5{^KxKB=jO<43^(;o~JPLXSiRB8D0^NJpLHb|;X3DnCGlG(8ns&4N z8s}%$1f)CWuWs)#@ZiQZlpzj)i5o*gLwH86c#XW&ewiun5W(18uN@&zlQGO1b~20fZk4$JE=8OP5)Bx}RdU6BU>eUzVWKYQLxLBQ{N_F%FOP%9vAMFn(aM!}gd6!~*=SkkIYaNbl;iFq0$x8) zP7{(AM#ke9oaoS=*&q9~%zI#6Jmx~agbf?9>YZMp{2AUC;K^KD4rN7qsvjaBSrJGU z!*E+N*1k5cw25yp7QWip1+Ih=#(*O>OWLAqOtiVpy(JZ)O8Bqa1l?}_tV`q)FI;;C zWcv~5hz)SdKH$#DMr@$N6t%%-#l}wWn)O|ly(=Mck6+03%QpIllLAJ^uJ$v8lpwu9r>-@E3r@N~ z{6In=s^|4xp)oz|*#6Uy;5*8afnNuyhp$4xod!AfP@SVSb=>>{&`AS+vnqyR_wrKu zz0ryA1z3k#Dntleh6=lGa>-P0d*SAH<408GlhHau$@7pLZa2L(%Xhq}+R*b`nwwh3 zDxyivwlaAwkUNT>QsP|qxAJ?lI*T6=Us55wuElO8_~Zi7Nxh5tlx9`sKtlTp?D}va zBb+F3N3Mb8e-m0B2n=r&MQ;9hf59hpu3m}^CG^FXAn+htB)2yihDGw=7^!2m5kYyc z0;sfwyYZfXxk!p+L;}j&BmwY5!kQfAp}2;L&ZJUKr*0quHSQR1WiR8_%zM|JAwS}e3M7vStxve9U@|SulFQ68@Mg0%p92J&EXT&=bj};sG?$mJ<_5wdJDL*Mr&fC!l% z0yb0kj5mATI+0L(8lMNt`}0zlSs{dcJju{@eyl)5E6=mWxNe>U^;ju#Q=L3HrGtH$ ze42tU(XSG7MYT(^9Lz)pdYX(F!FTte`Jii@5&c$nr^E3I80z)cM(YVN|fbw!Htme!&Sb>;Qb~A85fMQZoVCK3)FY8 zzB#*)Wi-8qjhs}ru(^3rl4xiyZvG)N{y#1NX|dNu%g|S5BT@Ci9O|-cB}3f&8d);#XhVngVpdAfVU|2kN{d;P_^m$P-_$|RT$urn&7Apd z8m(Lj5TTr)CoPvs1g#o+sW#dx&Pxgwr-Yibt?csz>8-Bf`LLp*sfuljA zb*jsnO_LUuzI*Ng@I}Sajs~0jdHlR-E6Y{zA@Lg(`X5pF?-SXATc~?2L96-Y(Q@Zr1wN`=6>Uu4XT1jdiifE5r-W!r z19qKHMLj`SeiDJLxgw!o?IPBFxn9odh$UE9HBd&h*vPlJ)ZMCaf?Kv(moT!Wk6@z% zV9_Wvg{g3ogm%dvpb5RFCByFT?_n3J%j=n4&om>`KCcWp*H5-2POH$jVUyP~`W(x| zJXm#_yPh{J72Oe_8*f>qw=U6@1nFO$)}C=}x1Lr7mhjeTmz;sRn&GSWAwjN%s*Tz| zeHEqioTAq}L9zm@7JH zrbuP<-RTYHk{9l}(VH%SxeM}tc3i5BLx-rZ$>eb}V>b z>-AusmCB#86vNnr9n=;ufT79-2LF10Lx%jJIKbM-mrx;vGRzLq1!_4*xCVFtH{ABz zKzDRi#EUoZkl^HZ5XU9nuJiiIf2Go3|k(9nZ#@u>y4vQR{{kj zfyvy_r32|6bYx8Uf^FM+OkuB3hm95u-gOnsxE+FPOEq|iA(wun6dm8nML|=-vEg}I z*?g22$u{Zn?Oyv`O}zuDw3tg`s9TCM)1*?bjbjVnj$Ezle6306Ps1-vdHF|24F!kr zmrXMw98ZnYqa(?(>N+1MW8-}CmjjDv`dkbjedZcf;i<0A>OD(GE9Th)XDfBrU?OZ*4p*s-|%3fodP*rovp_~PP)al5r>?h ztd|z?*EgN5=fx(vvKFVfW|zJk7F6=dnc9PWu7!3-LHW6(z@Js^o#>wn^uAX(F{jn_ z|DeWlvdA`dtiDgyv8^RzGZP<7LiKzfXUB`wHIZIk78nXfZ!6CYIn z3Yl|Hox{x*;5(U}LKiT4vQ7K5XUM*UbIZG~!R1t)@^G~NcDulLFPc;P6Ex!Z(W5d* zlPw`-?hY{~8z5}O$ei=KUDT7Yqjb9r{!oHUuqj z1wQPX2OE%9DLQ5J@d^-h_;>>bn)`W>(a%Z!>#EAJzhtLjdUptnUk^{W-T6wIOC?Y$ z3^qILZQnoOC&m+OYd<6%t&mOYmtq;l;^{KX0$i; z@*_iM4sC}Z5oRf_Tgf+`E>;F+1P`4olwZ%vFs4T8_y2A1gLp#~AVDmvDNMsQlPgVq51zpwmJTVwuGc>bZ6<-mXpl<}whJOya*U)C5E$Sd$b{X7v@|sKgE;2j-=6Pl=xD7pjEzZcTMjVi7Z)j%bUM`jLucY&bQW$dHq!hH zKG40L^pV(F!z$Ci01i+V2H`W&Q)jL0UwkUo|FsyKCCk45AS?|FLa6eIjZOK#2o;(E zK~%q9ul$S3-|c`fN)E!POlea?<$w4Wi}XkJoN=w>kp2H}6aJoSe<~o#D%Jl}AW=F< z9hBjXOQ*qO2humCcXmj@!Z1*Q$L0mx|JgG-IY_0{≧FddnnnFzy!0|LR2%(o?v* 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 Date: Sun, 1 Jan 2023 23:39:26 +0200 Subject: [PATCH 032/113] remove outdated launch file --- .vscode/launch.json | 138 -------------------------------------------- 1 file changed, 138 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index eb007c82..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,138 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in library 'daemon'", - "cargo": { - "args": [ - "test", - "--no-run", - "--lib", - "--package=daemon" - ], - "filter": { - "name": "daemon", - "kind": "lib" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug executable 'daemon'", - "cargo": { - "args": [ - "build", - "--bin=daemon", - "--package=daemon" - ], - "filter": { - "name": "daemon", - "kind": "bin" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in executable 'daemon'", - "cargo": { - "args": [ - "test", - "--no-run", - "--bin=daemon", - "--package=daemon" - ], - "filter": { - "name": "daemon", - "kind": "bin" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug executable 'cli'", - "cargo": { - "args": [ - "build", - "--bin=cli", - "--package=cli" - ], - "filter": { - "name": "cli", - "kind": "bin" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in executable 'cli'", - "cargo": { - "args": [ - "test", - "--no-run", - "--bin=cli", - "--package=cli" - ], - "filter": { - "name": "cli", - "kind": "bin" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug executable 'gui'", - "cargo": { - "args": [ - "build", - "--bin=gui", - "--package=gui" - ], - "filter": { - "name": "gui", - "kind": "bin" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in executable 'gui'", - "cargo": { - "args": [ - "test", - "--no-run", - "--bin=gui", - "--package=gui" - ], - "filter": { - "name": "gui", - "kind": "bin" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - } - ] -} \ No newline at end of file From 2d06b917946f0855a56485bea418ab1e745e0068 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Wed, 4 Jan 2023 15:34:53 +0200 Subject: [PATCH 033/113] disable fan control switch when there is no fan --- lact-cli/src/main.rs | 2 +- lact-gui/src/app/root_stack/thermals_page/mod.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lact-cli/src/main.rs b/lact-cli/src/main.rs index 0a2f04b2..2b2b6b52 100644 --- a/lact-cli/src/main.rs +++ b/lact-cli/src/main.rs @@ -16,7 +16,7 @@ fn main() -> Result<()> { f(&args, &client) } -fn list_gpus(args: &Args, client: &DaemonClient) -> Result<()> { +fn list_gpus(_: &Args, client: &DaemonClient) -> Result<()> { let buffer = client.list_devices()?; for entry in buffer.inner()? { let id = entry.id; diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs index cb854cbe..4e113115 100644 --- a/lact-gui/src/app/root_stack/thermals_page/mod.rs +++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs @@ -160,6 +160,8 @@ impl ThermalsPage { if initial { self.fan_control_enabled_switch.set_visible(true); + self.fan_control_enabled_switch + .set_sensitive(stats.fan.speed_current.is_some()); self.fan_control_enabled_switch .set_active(!stats.fan.control_enabled); From 54ff100bddb61df478347bff2c6dcb685401dabb Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Wed, 4 Jan 2023 20:44:12 +0200 Subject: [PATCH 034/113] feat: wip gtk4 --- Cargo.lock | 167 +++++++++++------- lact-gui/Cargo.toml | 3 +- lact-gui/src/app/apply_revealer.rs | 2 +- lact-gui/src/app/header.rs | 5 +- lact-gui/src/app/mod.rs | 151 ++++++++-------- lact-gui/src/app/root_stack/info_page/mod.rs | 2 - .../app/root_stack/info_page/vulkan_info.rs | 27 ++- lact-gui/src/app/root_stack/mod.rs | 9 +- lact-gui/src/app/root_stack/oc_page/mod.rs | 10 +- .../oc_page/performance_level_frame.rs | 10 +- .../app/root_stack/oc_page/power_cap_frame.rs | 10 +- .../src/app/root_stack/oc_page/stats_grid.rs | 26 +-- .../app/root_stack/oc_page/warning_frame.rs | 6 +- .../src/app/root_stack/thermals_page/mod.rs | 7 +- lact-gui/src/main.rs | 4 +- 15 files changed, 240 insertions(+), 199 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37b95e8f..7f73bc5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,30 +46,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "atk" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf" -dependencies = [ - "atk-sys", - "bitflags", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ad703eb64dc058024f0e57ccfa069e15a413b98dbd50a1a950e743b7f11148" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -374,22 +350,6 @@ dependencies = [ "slab", ] -[[package]] -name = "gdk" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1" -dependencies = [ - "bitflags", - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - [[package]] name = "gdk-pixbuf" version = "0.16.7" @@ -417,10 +377,26 @@ dependencies = [ ] [[package]] -name = "gdk-sys" -version = "0.16.0" +name = "gdk4" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2181330ebf9d091f8ea7fed6877f7adc92114128592e1fdaeb1da28e0d01e9" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk-pixbuf", + "gdk4-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk4-sys" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76354f97a913e55b984759a997b693aa7dc71068c9e98bcce51aa167a0a5c5a" +checksum = "de55cb49432901fe2b3534177fa06844665b9b0911d85d8601a8d8b88b7791db" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -536,51 +512,88 @@ dependencies = [ ] [[package]] -name = "gtk" -version = "0.16.2" +name = "graphene-rs" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ecb4d347e6d09820df3bdfd89a74a8eec07753a06bb92a3aac3ad31d04447b" +dependencies = [ + "glib", + "graphene-sys", + "libc", +] + +[[package]] +name = "graphene-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9aa82337d3972b4eafdea71e607c23f47be6f27f749aab613f1ad8ddbe6dcd6" +dependencies = [ + "glib-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gsk4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4d3507d43908c866c805f74c9dd593c0ce7ba5c38e576e41846639cdcd4bee6" +checksum = "591239f5c52ca803b222124ac9c47f230cd180cee9b114c4d672e4a94b74f491" dependencies = [ - "atk", "bitflags", "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", + "gdk4", "glib", - "gtk-sys", - "gtk3-macros", + "graphene-rs", + "gsk4-sys", "libc", - "once_cell", "pango", - "pkg-config", ] [[package]] -name = "gtk-sys" -version = "0.16.0" +name = "gsk4-sys" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b5f8946685d5fe44497007786600c2f368ff6b1e61a16251c89f72a97520a3" +checksum = "195a63f0be42529f98c3eb3bae0decfd0428ba2cc683b3e20ced88f340904ec5" dependencies = [ - "atk-sys", "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", + "gdk4-sys", "glib-sys", "gobject-sys", + "graphene-sys", "libc", "pango-sys", "system-deps", ] [[package]] -name = "gtk3-macros" -version = "0.16.0" +name = "gtk4" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd89dba65def483a233dc4fdd3f3dab01576e3d83f80f6c9303ebe421661855e" +dependencies = [ + "bitflags", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk-pixbuf", + "gdk4", + "gio", + "glib", + "graphene-rs", + "gsk4", + "gtk4-macros", + "gtk4-sys", + "libc", + "once_cell", + "pango", +] + +[[package]] +name = "gtk4-macros" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfd6557b1018b773e43c8de9d0d13581d6b36190d0501916cbec4731db5ccff" +checksum = "832687a415d9d8bc11fe9c17dda1bf13ee262c41b995dd4df1d1cce33cead405" dependencies = [ "anyhow", "proc-macro-crate", @@ -590,6 +603,25 @@ dependencies = [ "syn", ] +[[package]] +name = "gtk4-sys" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e370564e3fdacff7cffc99f7366b6a4689feb44e819d3ccee598a9a215b71605" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk4-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "graphene-sys", + "gsk4-sys", + "libc", + "pango-sys", + "system-deps", +] + [[package]] name = "half" version = "2.1.0" @@ -704,9 +736,10 @@ name = "lact-gui" version = "0.2.0" dependencies = [ "anyhow", - "gtk", + "gtk4", "lact-client", "nix", + "once_cell", "serde", "serde_json", "tracing", diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml index f65c971d..6c1a5a45 100644 --- a/lact-gui/Cargo.toml +++ b/lact-gui/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" [dependencies] lact-client = { path = "../lact-client" } -gtk = "0.16" +gtk = { version = "0.5", package = "gtk4" } +once_cell = "1.17.0" # pango = "0.16" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/lact-gui/src/app/apply_revealer.rs b/lact-gui/src/app/apply_revealer.rs index b940a690..96aaf30e 100644 --- a/lact-gui/src/app/apply_revealer.rs +++ b/lact-gui/src/app/apply_revealer.rs @@ -17,7 +17,7 @@ impl ApplyRevealer { apply_button.set_label("Apply"); - container.add(&apply_button); + container.set_child(Some(&apply_button)); Self { container, diff --git a/lact-gui/src/app/header.rs b/lact-gui/src/app/header.rs index c560c6f9..bf93f329 100644 --- a/lact-gui/src/app/header.rs +++ b/lact-gui/src/app/header.rs @@ -14,9 +14,10 @@ impl Header { pub fn new() -> Self { let container = HeaderBar::new(); - container.set_custom_title(Some(&Grid::new())); // Bad workaround to hide the title + // TODO Check if this is this still needed + container.set_title_widget(Some(&Grid::new())); // Bad workaround to hide the title - container.set_show_close_button(true); + container.set_show_title_buttons(true); let gpu_selector = ComboBoxText::new(); container.pack_start(&gpu_selector); diff --git a/lact-gui/src/app/mod.rs b/lact-gui/src/app/mod.rs index 9bec8ca4..ac323a78 100644 --- a/lact-gui/src/app/mod.rs +++ b/lact-gui/src/app/mod.rs @@ -10,6 +10,7 @@ use std::time::Duration; use anyhow::Context; use apply_revealer::ApplyRevealer; use glib::clone; +use gtk::gio::ApplicationFlags; use gtk::prelude::*; use gtk::*; use header::Header; @@ -23,7 +24,8 @@ const STATS_POLL_INTERVAL: u64 = 250; #[derive(Clone)] pub struct App { - pub window: Window, + application: Application, + pub window: ApplicationWindow, pub header: Header, root_stack: RootStack, apply_revealer: ApplyRevealer, @@ -32,19 +34,21 @@ pub struct App { impl App { pub fn new(daemon_client: DaemonClient) -> Self { - let window = Window::new(WindowType::Toplevel); + let application = Application::new(None, ApplicationFlags::default()); let header = Header::new(); + let window = ApplicationWindow::builder() + .title("LACT") + .default_width(500) + .default_height(600) + .build(); window.set_titlebar(Some(&header.container)); - window.set_title("LACT"); - - window.set_default_size(500, 600); - window.connect_delete_event(move |_, _| { - main_quit(); - Inhibit(false) - }); + // window.connect_close_request(move |_, _| { + // // main_quit(); + // Inhibit(false) + // }); let root_stack = RootStack::new(); @@ -52,15 +56,16 @@ impl App { let root_box = Box::new(Orientation::Vertical, 5); - root_box.add(&root_stack.container); + root_box.append(&root_stack.container); let apply_revealer = ApplyRevealer::new(); - root_box.add(&apply_revealer.container); + root_box.append(&apply_revealer.container); - window.add(&root_box); + window.set_child(Some(&root_box)); App { + application, window, header, root_stack, @@ -69,79 +74,87 @@ impl App { } } - pub fn run(&self) -> anyhow::Result<()> { - self.window.show_all(); + pub fn run(self) -> anyhow::Result<()> { + self.application + .connect_activate(clone!(@strong self as app => move |_| { + app.window.set_application(Some(&app.application)); - let current_gpu_id = Arc::new(RwLock::new(String::new())); + let current_gpu_id = Arc::new(RwLock::new(String::new())); - { - let current_gpu_id = current_gpu_id.clone(); - let app = self.clone(); - - self.header.connect_gpu_selection_changed(move |gpu_id| { - info!("GPU Selection changed"); - app.set_info(&gpu_id); - *current_gpu_id.write().unwrap() = gpu_id; - }); - } + { + let current_gpu_id = current_gpu_id.clone(); - let devices_buf = self.daemon_client.list_devices()?; - let devices = devices_buf.inner()?; - self.header.set_devices(&devices); + app.header.connect_gpu_selection_changed(clone!(@strong app => move |gpu_id| { + info!("GPU Selection changed"); + app.set_info(&gpu_id); + *current_gpu_id.write().unwrap() = gpu_id; + })); + } - // Show apply button on setting changes - { - let apply_revealer = self.apply_revealer.clone(); + let devices_buf = app + .daemon_client + .list_devices() + .expect("Could not list devices"); + let devices = devices_buf.inner().expect("Could not access devices"); + app.header.set_devices(&devices); - self.root_stack - .thermals_page - .connect_settings_changed(move || { - debug!("Settings changed, showing apply button"); - apply_revealer.show(); - }); + // Show apply button on setting changes + { + let apply_revealer = app.apply_revealer.clone(); - let apply_revealer = self.apply_revealer.clone(); + app.root_stack + .thermals_page + .connect_settings_changed(move || { + debug!("Settings changed, showing apply button"); + apply_revealer.show(); + }); - self.root_stack.oc_page.connect_settings_changed(move || { - debug!("Settings changed, showing apply button"); - apply_revealer.show(); - }); - } + let apply_revealer = app.apply_revealer.clone(); - { - let app = self.clone(); - let current_gpu_id = current_gpu_id.clone(); + app.root_stack.oc_page.connect_settings_changed(move || { + debug!("Settings changed, showing apply button"); + apply_revealer.show(); + }); + } - // TODO - /*self.root_stack.oc_page.connect_clocks_reset(move || { - info!("Resetting clocks, but not applying"); + { + let app = app.clone(); + let current_gpu_id = current_gpu_id.clone(); - let gpu_id = current_gpu_id.load(Ordering::SeqCst); + // TODO + /*app.root_stack.oc_page.connect_clocks_reset(move || { + info!("Resetting clocks, but not applying"); - app.daemon_client - .reset_gpu_power_states(gpu_id) - .expect("Failed to reset clocks"); + let gpu_id = current_gpu_id.load(Ordering::SeqCst); - app.set_info(gpu_id); + app.daemon_client + .reset_gpu_power_states(gpu_id) + .expect("Failed to reset clocks"); - app.apply_revealer.show(); - })*/ - } + app.set_info(gpu_id); - self.apply_revealer.connect_apply_button_clicked( - clone!(@strong self as app, @strong current_gpu_id => move || { - if let Err(err) = app.apply_settings(current_gpu_id.clone()) { - show_error(err.context("Could not apply settings")); - - let gpu_id = current_gpu_id.read().unwrap(); - app.set_info(&gpu_id) + app.apply_revealer.show(); + })*/ } - }), - ); - self.start_stats_update_loop(current_gpu_id.clone()); + app.apply_revealer.connect_apply_button_clicked( + clone!(@strong app as app, @strong current_gpu_id => move || { + if let Err(err) = app.apply_settings(current_gpu_id.clone()) { + show_error(err.context("Could not apply settings")); + + let gpu_id = current_gpu_id.read().unwrap(); + app.set_info(&gpu_id) + } + }), + ); + + app.start_stats_update_loop(current_gpu_id.clone()); - Ok(gtk::main()) + app.window.show(); + })); + + self.application.run(); + Ok(()) } fn set_info(&self, gpu_id: &str) { @@ -326,7 +339,7 @@ fn show_error(err: anyhow::Error) { .text(&text) .buttons(ButtonsType::Close) .build(); - diag.run(); + diag.set_modal(true); diag.hide(); glib::Continue(false) }); diff --git a/lact-gui/src/app/root_stack/info_page/mod.rs b/lact-gui/src/app/root_stack/info_page/mod.rs index f078dda6..7d65ad86 100644 --- a/lact-gui/src/app/root_stack/info_page/mod.rs +++ b/lact-gui/src/app/root_stack/info_page/mod.rs @@ -200,8 +200,6 @@ impl InformationPage { if let Some(vulkan_info) = &gpu_info.vulkan_info { self.vulkan_info_frame.set_info(vulkan_info); } - - self.container.show_all(); } pub fn set_stats(&self, stats: &DeviceStats) { diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs index c1388c33..7d7fe572 100644 --- a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info.rs @@ -22,9 +22,9 @@ impl VulkanInfoFrame { label.set_markup("Vulkan Information"); label })); - container.set_label_align(0.5, 0.5); + container.set_label_align(0.5); - container.set_shadow_type(ShadowType::None); + // container.set_shadow_type(ShadowType::None); // TODO let features_listbox = ListBox::builder().halign(Align::Fill).build(); let extensions_listbox = ListBox::builder().halign(Align::Fill).build(); @@ -102,7 +102,7 @@ impl VulkanInfoFrame { grid.attach(&extensions_label, 0, 3, 2, 1); grid.attach(&show_extensions_button, 2, 3, 2, 1); - vbox.pack_start(&grid, false, true, 5); + vbox.prepend(&grid); /*let features_expander = Expander::builder().label("Feature support").build(); @@ -130,7 +130,7 @@ impl VulkanInfoFrame { vbox.pack_start(&extensions_expander, false, true, 5);*/ - container.add(&vbox); + container.set_child(Some(&vbox)); Self { container, @@ -149,37 +149,37 @@ impl VulkanInfoFrame { self.version_label .set_markup(&format!("{}", vulkan_info.api_version)); - self.features_listbox.children().clear(); + // self.features_listbox.children().clear(); for (i, (feature, supported)) in vulkan_info.features.iter().enumerate() { let vbox = Box::new(Orientation::Horizontal, 5); let feature_name_label = Label::new(Some(&feature)); - vbox.pack_start(&feature_name_label, false, false, 0); + vbox.append(&feature_name_label); let feature_supported_checkbutton = CheckButton::new(); feature_supported_checkbutton.set_sensitive(false); feature_supported_checkbutton.set_active(*supported); - vbox.pack_end(&feature_supported_checkbutton, false, false, 0); + vbox.append(&feature_supported_checkbutton); self.features_listbox.insert(&vbox, i.try_into().unwrap()); } - self.extensions_listbox.children().clear(); + // self.extensions_listbox.children().clear(); for (i, (extension, supported)) in vulkan_info.extensions.iter().enumerate() { let vbox = Box::new(Orientation::Horizontal, 5); vbox.set_hexpand(true); let extension_name_label = Label::new(Some(&extension)); - vbox.pack_start(&extension_name_label, false, false, 0); + vbox.append(&extension_name_label); let extension_supported_checkbutton = CheckButton::builder() .sensitive(false) .active(*supported) .build(); - vbox.pack_end(&extension_supported_checkbutton, false, false, 0); + vbox.append(&extension_supported_checkbutton); self.extensions_listbox.insert(&vbox, i.try_into().unwrap()); } @@ -188,12 +188,11 @@ impl VulkanInfoFrame { fn show_list_window(title: &str, child: &ListBox) { let window = Window::builder() - .type_(WindowType::Toplevel) .title(title) .width_request(500) .height_request(700) .build(); - let scroll = ScrolledWindow::builder().child(child).margin(10).build(); - window.add(&scroll); - window.show_all(); + let scroll = ScrolledWindow::builder().child(child).build(); + window.set_child(Some(&scroll)); + window.show(); } diff --git a/lact-gui/src/app/root_stack/mod.rs b/lact-gui/src/app/root_stack/mod.rs index 60bd65f4..94a9bd4f 100644 --- a/lact-gui/src/app/root_stack/mod.rs +++ b/lact-gui/src/app/root_stack/mod.rs @@ -3,7 +3,6 @@ mod oc_page; mod software_page; mod thermals_page; -use gtk::prelude::*; use gtk::*; use info_page::InformationPage; @@ -26,19 +25,19 @@ impl RootStack { let info_page = InformationPage::new(); - container.add_titled(&info_page.container, "info_page", "Information"); + container.add_titled(&info_page.container, Some("info_page"), "Information"); let oc_page = OcPage::new(); - container.add_titled(&oc_page.container, "oc_page", "OC"); + container.add_titled(&oc_page.container, Some("oc_page"), "OC"); let thermals_page = ThermalsPage::new(); - container.add_titled(&thermals_page.container, "thermals_page", "Thermals"); + container.add_titled(&thermals_page.container, Some("thermals_page"), "Thermals"); let software_page = SoftwarePage::new(); - container.add_titled(&software_page.container, "software_page", "Software"); + container.add_titled(&software_page.container, Some("software_page"), "Software"); Self { container, diff --git a/lact-gui/src/app/root_stack/oc_page/mod.rs b/lact-gui/src/app/root_stack/oc_page/mod.rs index 8a85f7ed..93e2af3d 100644 --- a/lact-gui/src/app/root_stack/oc_page/mod.rs +++ b/lact-gui/src/app/root_stack/oc_page/mod.rs @@ -7,7 +7,7 @@ mod warning_frame; use glib::clone; use gtk::prelude::*; use gtk::*; -use lact_client::schema::{DeviceInfo, DeviceStats, PerformanceLevel, PowerStats}; +use lact_client::schema::{DeviceStats, PerformanceLevel, PowerStats}; use performance_level_frame::PowerProfileFrame; use power_cap_frame::PowerCapFrame; use stats_grid::StatsGrid; @@ -29,19 +29,19 @@ impl OcPage { let warning_frame = WarningFrame::new(); - container.pack_start(&warning_frame.container, false, true, 5); + container.append(&warning_frame.container); let stats_grid = StatsGrid::new(); - container.pack_start(&stats_grid.container, false, true, 5); + container.append(&stats_grid.container); let power_cap_frame = PowerCapFrame::new(); - container.pack_start(&power_cap_frame.container, false, true, 0); + container.append(&power_cap_frame.container); let power_profile_frame = PowerProfileFrame::new(); - container.pack_start(&power_profile_frame.container, false, true, 0); + container.append(&power_profile_frame.container); // let clocks_frame = ClocksFrame::new(); diff --git a/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs b/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs index 0a6cc0a7..7e772f15 100644 --- a/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs +++ b/lact-gui/src/app/root_stack/oc_page/performance_level_frame.rs @@ -13,14 +13,14 @@ impl PowerProfileFrame { pub fn new() -> Self { let container = Frame::new(None); - container.set_shadow_type(ShadowType::None); + // container.set_shadow_type(ShadowType::None); container.set_label_widget(Some(&{ let label = Label::new(None); label.set_markup("Power Profile"); label })); - container.set_label_align(0.2, 0.0); + container.set_label_align(0.2); let root_box = Box::new(Orientation::Horizontal, 5); @@ -30,11 +30,11 @@ impl PowerProfileFrame { combo_box.append(Some("1"), "Highest clocks"); combo_box.append(Some("2"), "Lowest clocks"); - root_box.pack_start(&combo_box, false, true, 5); + root_box.append(&combo_box); let description_label = Label::new(Some("A description is supposed to be here")); - root_box.pack_start(&description_label, false, true, 5); + root_box.append(&description_label); { let description_label = description_label.clone(); @@ -49,7 +49,7 @@ impl PowerProfileFrame { }); } - container.add(&root_box); + container.set_child(Some(&root_box)); Self { container, combo_box, diff --git a/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs b/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs index 9ce3161f..fa9e1596 100644 --- a/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs +++ b/lact-gui/src/app/root_stack/oc_page/power_cap_frame.rs @@ -12,20 +12,20 @@ impl PowerCapFrame { pub fn new() -> Self { let container = Frame::new(None); - container.set_shadow_type(ShadowType::None); + // container.set_shadow_type(ShadowType::None); container.set_label_widget(Some(&{ let label = Label::new(None); label.set_markup("Power Usage Limit"); label })); - container.set_label_align(0.2, 0.0); + container.set_label_align(0.2); let root_box = Box::new(Orientation::Horizontal, 0); let label = Label::new(None); - root_box.pack_start(&label, false, true, 5); + root_box.append(&label); let adjustment = Adjustment::new(0.0, 0.0, 0.0, 1.0, 10.0, 0.0); { @@ -39,9 +39,9 @@ impl PowerCapFrame { scale.set_draw_value(false); - root_box.pack_start(&scale, true, true, 5); + root_box.append(&scale); - container.add(&root_box); + container.set_child(Some(&root_box)); Self { container, diff --git a/lact-gui/src/app/root_stack/oc_page/stats_grid.rs b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs index 59145da8..88130053 100644 --- a/lact-gui/src/app/root_stack/oc_page/stats_grid.rs +++ b/lact-gui/src/app/root_stack/oc_page/stats_grid.rs @@ -37,7 +37,7 @@ impl StatsGrid { vram_usage_label.set_text("0/0 MiB"); - vram_usage_overlay.add(&vram_usage_bar); + vram_usage_overlay.set_child(Some(&vram_usage_bar)); vram_usage_overlay.add_overlay(&vram_usage_label); container.attach(&vram_usage_overlay, 1, 0, 2, 1); @@ -47,11 +47,11 @@ impl StatsGrid { { let gpu_clock_box = Box::new(Orientation::Horizontal, 5); - gpu_clock_box.pack_start(&Label::new(Some("GPU Clock:")), false, false, 2); + gpu_clock_box.append(&Label::new(Some("GPU Clock:"))); gpu_clock_label.set_markup("0MHz"); - gpu_clock_box.pack_start(&gpu_clock_label, false, false, 2); + gpu_clock_box.append(&gpu_clock_label); gpu_clock_box.set_halign(Align::Center); @@ -62,11 +62,11 @@ impl StatsGrid { { let vram_clock_box = Box::new(Orientation::Horizontal, 5); - vram_clock_box.pack_start(&Label::new(Some("VRAM Clock:")), false, false, 2); + vram_clock_box.append(&Label::new(Some("VRAM Clock:"))); vram_clock_label.set_markup("0MHz"); - vram_clock_box.pack_start(&vram_clock_label, false, false, 2); + vram_clock_box.append(&vram_clock_label); vram_clock_box.set_halign(Align::Center); @@ -76,11 +76,11 @@ impl StatsGrid { { let gpu_voltage_box = Box::new(Orientation::Horizontal, 5); - gpu_voltage_box.pack_start(&Label::new(Some("GPU Voltage:")), false, false, 2); + gpu_voltage_box.append(&Label::new(Some("GPU Voltage:"))); gpu_voltage_label.set_markup("0.000V"); - gpu_voltage_box.pack_start(&gpu_voltage_label, false, false, 2); + gpu_voltage_box.append(&gpu_voltage_label); gpu_voltage_box.set_halign(Align::Center); @@ -91,11 +91,11 @@ impl StatsGrid { { let power_usage_box = Box::new(Orientation::Horizontal, 5); - power_usage_box.pack_start(&Label::new(Some("Power Usage:")), false, false, 2); + power_usage_box.append(&Label::new(Some("Power Usage:"))); power_usage_label.set_markup("00/000W"); - power_usage_box.pack_start(&power_usage_label, false, false, 2); + power_usage_box.append(&power_usage_label); power_usage_box.set_halign(Align::Center); @@ -106,11 +106,11 @@ impl StatsGrid { { let gpu_temperature_box = Box::new(Orientation::Horizontal, 5); - gpu_temperature_box.pack_start(&Label::new(Some("GPU Temperature:")), false, false, 2); + gpu_temperature_box.append(&Label::new(Some("GPU Temperature:"))); // gpu_temperature_label.set_markup("0°C"); - gpu_temperature_box.pack_start(&gpu_temperature_label, false, false, 2); + gpu_temperature_box.append(&gpu_temperature_label); gpu_temperature_box.set_halign(Align::Center); @@ -121,9 +121,9 @@ impl StatsGrid { { let gpu_usage_box = Box::new(Orientation::Horizontal, 5); - gpu_usage_box.pack_start(&Label::new(Some("GPU Usage:")), false, false, 2); + gpu_usage_box.append(&Label::new(Some("GPU Usage:"))); - gpu_usage_box.pack_start(&gpu_usage_label, false, false, 2); + gpu_usage_box.append(&gpu_usage_label); gpu_usage_box.set_halign(Align::Center); diff --git a/lact-gui/src/app/root_stack/oc_page/warning_frame.rs b/lact-gui/src/app/root_stack/oc_page/warning_frame.rs index 70a3456e..b63da5d8 100644 --- a/lact-gui/src/app/root_stack/oc_page/warning_frame.rs +++ b/lact-gui/src/app/root_stack/oc_page/warning_frame.rs @@ -10,15 +10,15 @@ impl WarningFrame { pub fn new() -> Self { let container = Frame::new(Some("Overclocking information")); - container.set_label_align(0.3, 0.5); + container.set_label_align(0.3); let warning_label = Label::new(None); - warning_label.set_line_wrap(true); + warning_label.set_wrap(true); warning_label.set_markup("Overclocking support is not enabled! To enable overclocking support, you need to add amdgpu.ppfeaturemask=0xffffffff to your kernel boot options. Look for the documentation of your distro."); warning_label.set_selectable(true); - container.add(&warning_label); + container.set_child(Some(&warning_label)); Self { container } } diff --git a/lact-gui/src/app/root_stack/thermals_page/mod.rs b/lact-gui/src/app/root_stack/thermals_page/mod.rs index 4e113115..a4522144 100644 --- a/lact-gui/src/app/root_stack/thermals_page/mod.rs +++ b/lact-gui/src/app/root_stack/thermals_page/mod.rs @@ -5,8 +5,6 @@ use gtk::prelude::*; use gtk::*; use lact_client::schema::DeviceStats; use std::collections::BTreeMap; -use tracing::trace; - // use fan_curve_frame::FanCurveFrame; pub struct ThermalsSettings { @@ -92,7 +90,7 @@ impl ThermalsPage { grid.attach(&fan_control_enabled_switch, 2, 2, 1, 1); - container.pack_start(&grid, false, false, 5); + container.prepend(&grid); /*let fan_curve_frame = FanCurveFrame::new(); @@ -178,8 +176,9 @@ impl ThermalsPage { pub fn connect_settings_changed(&self, f: F) { self.fan_control_enabled_switch - .connect_changed_active(clone!(@strong f => move |_| { + .connect_state_set(clone!(@strong f => move |_, _| { f(); + Inhibit(false) })); /*self.fan_curve_frame.connect_adjusted(move || { diff --git a/lact-gui/src/main.rs b/lact-gui/src/main.rs index de86a58c..fbe5e59b 100644 --- a/lact-gui/src/main.rs +++ b/lact-gui/src/main.rs @@ -20,7 +20,5 @@ fn main() -> anyhow::Result<()> { let app = App::new(connection); - app.run()?; - - Ok(()) + app.run() } From 86c92e8cb511b8b6d4ef56e83b22661c791cd6f3 Mon Sep 17 00:00:00 2001 From: Ilya Zlobintsev Date: Wed, 4 Jan 2023 21:43:05 +0200 Subject: [PATCH 035/113] new vulkan window --- Cargo.lock | 1 + lact-gui/Cargo.toml | 1 + .../vulkan_info/feature_model/imp.rs | 54 ++++++ .../vulkan_info/feature_model/mod.rs | 19 ++ .../{vulkan_info.rs => vulkan_info/mod.rs} | 162 +++++++++++++----- 5 files changed, 190 insertions(+), 47 deletions(-) create mode 100644 lact-gui/src/app/root_stack/info_page/vulkan_info/feature_model/imp.rs create mode 100644 lact-gui/src/app/root_stack/info_page/vulkan_info/feature_model/mod.rs rename lact-gui/src/app/root_stack/info_page/{vulkan_info.rs => vulkan_info/mod.rs} (50%) diff --git a/Cargo.lock b/Cargo.lock index 7f73bc5c..de9734b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -737,6 +737,7 @@ version = "0.2.0" dependencies = [ "anyhow", "gtk4", + "indexmap", "lact-client", "nix", "once_cell", diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml index 6c1a5a45..53505d5b 100644 --- a/lact-gui/Cargo.toml +++ b/lact-gui/Cargo.toml @@ -15,3 +15,4 @@ nix = "0.26" anyhow = "1.0" serde_json = "1.0" serde = "1.0" +indexmap = "1.9.2" diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_model/imp.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_model/imp.rs new file mode 100644 index 00000000..b7ef7b04 --- /dev/null +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_model/imp.rs @@ -0,0 +1,54 @@ +use gio::subclass::prelude::*; +use gtk::{ + gio, + glib::{self, ParamSpec, ParamSpecBoolean, ParamSpecString}, + prelude::*, +}; +use once_cell::sync::Lazy; +use std::cell::{Cell, RefCell}; + +#[derive(Debug, Default)] +pub struct FeatureModel { + pub name: RefCell, + pub supported: Cell, +} + +#[glib::object_subclass] +impl ObjectSubclass for FeatureModel { + const NAME: &'static str = "VulkanFeatureModel"; + type Type = super::FeatureModel; +} + +impl ObjectImpl for FeatureModel { + fn properties() -> &'static [glib::ParamSpec] { + static PROPERTIES: Lazy> = Lazy::new(|| { + vec![ + ParamSpecString::builder("name").build(), + ParamSpecBoolean::builder("supported").build(), + ] + }); + PROPERTIES.as_ref() + } + + fn set_property(&self, _id: usize, value: &glib::Value, pspec: &ParamSpec) { + match pspec.name() { + "name" => { + let name = value.get().expect("Name needs to be a string"); + self.name.replace(name); + } + "supported" => { + let supported = value.get().expect("Supported needs to be a bool"); + self.supported.replace(supported); + } + _ => unimplemented!(), + } + } + + fn property(&self, _id: usize, pspec: &ParamSpec) -> glib::Value { + match pspec.name() { + "name" => self.name.borrow().to_value(), + "supported" => self.supported.get().to_value(), + _ => unimplemented!(), + } + } +} diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_model/mod.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_model/mod.rs new file mode 100644 index 00000000..c55f6c8b --- /dev/null +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/feature_model/mod.rs @@ -0,0 +1,19 @@ +mod imp; + +use std::borrow::Cow; + +use gtk::glib::{self, Object}; +use indexmap::IndexMap; + +glib::wrapper! { + pub struct FeatureModel(ObjectSubclass); +} + +impl FeatureModel { + pub fn new(name: String, supported: bool) -> Self { + Object::builder() + .property("name", name) + .property("supported", supported) + .build() + } +} diff --git a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs similarity index 50% rename from lact-gui/src/app/root_stack/info_page/vulkan_info.rs rename to lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs index 7d7fe572..ce48730a 100644 --- a/lact-gui/src/app/root_stack/info_page/vulkan_info.rs +++ b/lact-gui/src/app/root_stack/info_page/vulkan_info/mod.rs @@ -1,7 +1,12 @@ +mod feature_model; + +use self::feature_model::FeatureModel; use glib::clone; use gtk::prelude::*; use gtk::*; use lact_client::schema::VulkanInfo; +use std::cell::RefCell; +use std::rc::Rc; use tracing::trace; #[derive(Clone)] @@ -9,8 +14,8 @@ pub struct VulkanInfoFrame { pub container: Frame, device_name_label: Label, version_label: Label, - features_listbox: ListBox, - extensions_listbox: ListBox, + features: Rc>>, + extensions: Rc>>, } impl VulkanInfoFrame { @@ -24,10 +29,10 @@ impl VulkanInfoFrame { })); container.set_label_align(0.5); - // container.set_shadow_type(ShadowType::None); // TODO + let features = Rc::new(RefCell::new(Vec::new())); + let extensions = Rc::new(RefCell::new(Vec::new())); - let features_listbox = ListBox::builder().halign(Align::Fill).build(); - let extensions_listbox = ListBox::builder().halign(Align::Fill).build(); + // container.set_shadow_type(ShadowType::None); // TODO let vbox = Box::new(Orientation::Vertical, 5); @@ -83,8 +88,8 @@ impl VulkanInfoFrame { .halign(Align::End) .build(); let show_features_button = Button::builder().label("Show").halign(Align::Start).build(); - show_features_button.connect_clicked(clone!(@strong features_listbox => move |_| { - show_list_window("Vulkan features", &features_listbox); + show_features_button.connect_clicked(clone!(@strong features => move |_| { + show_list_window("Vulkan features", &features.borrow()); })); grid.attach(&features_label, 0, 2, 2, 1); @@ -95,8 +100,8 @@ impl VulkanInfoFrame { .halign(Align::End) .build(); let show_extensions_button = Button::builder().label("Show").halign(Align::Start).build(); - show_extensions_button.connect_clicked(clone!(@strong extensions_listbox => move |_| { - show_list_window("Vulkan extensions", &extensions_listbox); + show_extensions_button.connect_clicked(clone!(@strong extensions => move |_| { + show_list_window("Vulkan extensions", &extensions.borrow()); })); grid.attach(&extensions_label, 0, 3, 2, 1); @@ -136,8 +141,8 @@ impl VulkanInfoFrame { container, device_name_label, version_label, - features_listbox, - extensions_listbox, + features, + extensions, } } @@ -149,50 +154,113 @@ impl VulkanInfoFrame { self.version_label .set_markup(&format!("{}", vulkan_info.api_version)); - // self.features_listbox.children().clear(); - for (i, (feature, supported)) in vulkan_info.features.iter().enumerate() { - let vbox = Box::new(Orientation::Horizontal, 5); - - let feature_name_label = Label::new(Some(&feature)); - - vbox.append(&feature_name_label); + let features_vec: Vec<_> = vulkan_info + .features + .iter() + .map(|(name, supported)| FeatureModel::new(name.to_string(), *supported)) + .collect(); + self.features.replace(features_vec); + + let extensions_vec: Vec<_> = vulkan_info + .extensions + .iter() + .map(|(name, supported)| FeatureModel::new(name.to_string(), *supported)) + .collect(); + self.extensions.replace(extensions_vec); + } +} - let feature_supported_checkbutton = CheckButton::new(); +fn show_list_window(title: &str, items: &[FeatureModel]) { + let window = Window::builder() + .title(title) + .width_request(500) + .height_request(700) + .build(); - feature_supported_checkbutton.set_sensitive(false); - feature_supported_checkbutton.set_active(*supported); + let base_model = gio::ListStore::new(FeatureModel::static_type()); + base_model.extend_from_slice(&items); - vbox.append(&feature_supported_checkbutton); + let expression = PropertyExpression::new(FeatureModel::static_type(), Expression::NONE, "name"); + let filter = StringFilter::builder() + .match_mode(StringFilterMatchMode::Substring) + .ignore_case(true) + .expression(expression) + .build(); - self.features_listbox.insert(&vbox, i.try_into().unwrap()); + let entry = SearchEntry::builder().hexpand(true).build(); + entry.connect_search_changed(clone!(@weak filter => move |entry| { + if entry.text().is_empty() { + filter.set_search(None); + } else { + filter.set_search(Some(entry.text().as_str())); } + })); + let search_bar = SearchBar::builder() + .child(&entry) + .search_mode_enabled(true) + .build(); + search_bar.grab_focus(); - // self.extensions_listbox.children().clear(); - for (i, (extension, supported)) in vulkan_info.extensions.iter().enumerate() { - let vbox = Box::new(Orientation::Horizontal, 5); - vbox.set_hexpand(true); - - let extension_name_label = Label::new(Some(&extension)); - vbox.append(&extension_name_label); + let filter_model = FilterListModel::builder() + .model(&base_model) + .filter(&filter) + .incremental(true) + .build(); - let extension_supported_checkbutton = CheckButton::builder() - .sensitive(false) - .active(*supported) - .build(); - vbox.append(&extension_supported_checkbutton); + let selection_model = NoSelection::new(Some(&filter_model)); - self.extensions_listbox.insert(&vbox, i.try_into().unwrap()); - } - } -} + let factory = gtk::SignalListItemFactory::new(); -fn show_list_window(title: &str, child: &ListBox) { - let window = Window::builder() - .title(title) - .width_request(500) - .height_request(700) + factory.connect_setup(move |_factory, item| { + let item = item.downcast_ref::().unwrap(); + item.set_activatable(false); + let label = Label::builder() + .margin_top(5) + .margin_bottom(5) + .selectable(true) + .hexpand(true) + .halign(Align::Start) + .build(); + let checkbox = CheckButton::builder().sensitive(false).build(); + let vbox = Box::builder() + .orientation(Orientation::Horizontal) + .margin_start(10) + .margin_end(10) + .build(); + vbox.append(&label); + vbox.append(&checkbox); + item.set_child(Some(&vbox)); + }); + + factory.connect_bind(move |_factory, item| { + let item = item.downcast_ref::().unwrap(); + let model = item.item().and_downcast::().unwrap(); + + let vbox = item.child().and_downcast::().unwrap(); + let children = vbox.observe_children(); + let label = children.item(0).and_downcast::