feat: implement historical charts window (#301)

* feat: Implement graph widget in OC tab

* fix: Moved legend to lower left corner

* fix: Fix graph jumping around before getting data for 60 secs

* fix: Fix vertical part of graph jumping around

* chore: Refactoring GraphData to be it's separate struct

* chore: Rename Graph to Plot

* feat: Implement throttling histogram in Plot

* fix: Fix throttling data not being filtered out

* fix: Draw lines in front of throttling histogram and use deep orange non-transparent color

* feat: Cubic spline interpolation for plot

* chrone: Lightly refactor cubic sampling

* feat: Supersample the plot area and reconfigure sizes accordingly

* feat: WIP graphs window

* feat: improvements

* feat: add clockspeed plot

* feat: avoid using JSON to send graph info

* perf: trim clockspeed graph data

* chore: remove unused feature

* perf: use NaiveDateTime for the plot

* feat: add power usage plot

* fix: reset power plot

* dev: add benchmark for drawing plots

* perf: use raw timestamps instead of NaiveDateTime in plot

* fix: avoid running gtk tests in ci

* chore: switch away from git source for plotters-cairo

---------

Co-authored-by: Alik Aslanyan <inline0@pm.me>
This commit is contained in:
Ilya Zlobintsev 2024-04-22 19:21:00 +03:00 committed by GitHub
parent d5af85020d
commit 50fea14480
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 1150 additions and 44 deletions

View File

@ -25,7 +25,7 @@ jobs:
- name: Build
run: cargo build
- name: Run tests
run: cargo test --verbose --no-default-features
run: cargo test --verbose --no-default-features -p lact
check-format:
runs-on: ubuntu-22.04

526
Cargo.lock generated
View File

@ -70,6 +70,12 @@ dependencies = [
"libc",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
version = "0.6.13"
@ -360,6 +366,12 @@ dependencies = [
"syn 2.0.52",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.5.0"
@ -390,6 +402,12 @@ dependencies = [
"system-deps",
]
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.0.88"
@ -432,6 +450,33 @@ dependencies = [
"windows-targets 0.52.4",
]
[[package]]
name = "ciborium"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
[[package]]
name = "ciborium-ll"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "4.5.1"
@ -460,7 +505,7 @@ version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro2",
"quote",
"syn 2.0.52",
@ -487,6 +532,12 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "const-cstr"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6"
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -503,6 +554,19 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "core-graphics"
version = "0.22.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"core-graphics-types",
"foreign-types",
"libc",
]
[[package]]
name = "core-graphics-types"
version = "0.1.3"
@ -514,6 +578,18 @@ dependencies = [
"libc",
]
[[package]]
name = "core-text"
version = "19.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25"
dependencies = [
"core-foundation",
"core-graphics",
"foreign-types",
"libc",
]
[[package]]
name = "core2"
version = "0.4.0"
@ -541,6 +617,61 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "criterion"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
"anes",
"cast",
"ciborium",
"clap",
"criterion-plot",
"is-terminal",
"itertools 0.10.5",
"num-traits",
"once_cell",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools 0.10.5",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.11"
@ -649,6 +780,54 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "dlib"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
dependencies = [
"libloading 0.8.2",
]
[[package]]
name = "dwrote"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b"
dependencies = [
"lazy_static",
"libc",
"winapi",
"wio",
]
[[package]]
name = "either"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
[[package]]
name = "endi"
version = "1.1.0"
@ -780,12 +959,79 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "float-ord"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "font-kit"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21fe28504d371085fae9ac7a3450f0b289ab71e07c8e57baa3fb68b9e57d6ce5"
dependencies = [
"bitflags 1.3.2",
"byteorder",
"core-foundation",
"core-graphics",
"core-text",
"dirs-next",
"dwrote",
"float-ord",
"freetype",
"lazy_static",
"libc",
"log",
"pathfinder_geometry",
"pathfinder_simd",
"walkdir",
"winapi",
"yeslogic-fontconfig-sys",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "freetype"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efc8599a3078adf8edeb86c71e9f8fa7d88af5ca31e806a867756081f90f5d83"
dependencies = [
"freetype-sys",
"libc",
]
[[package]]
name = "freetype-sys"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66ee28c39a43d89fbed8b4798fb4ba56722cfd2b5af81f9326c27614ba88ecd5"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "futures"
version = "0.3.30"
@ -911,9 +1157,9 @@ dependencies = [
[[package]]
name = "gdk4"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6771942f85a2beaa220c64739395e4401b9fab4a52aba9b503fa1e6ed4d4d806"
checksum = "9100b25604183f2fd97f55ef087fae96ab4934d7215118a35303e422688e6e4b"
dependencies = [
"cairo-rs",
"gdk-pixbuf",
@ -926,9 +1172,9 @@ dependencies = [
[[package]]
name = "gdk4-sys"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1eb95854fab65072023a7814434f003db571d6e45c287c0b0c540c1c78bdf6ae"
checksum = "d0b76874c40bb8d1c7d03a7231e23ac75fa577a456cd53af32ec17ec8f121626"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
@ -970,9 +1216,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "gio"
version = "0.19.2"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eae10b27b6dd27e22ed0d812c6387deba295e6fc004a8b379e459b663b05a02"
checksum = "c64947d08d7fbb03bf8ad1f25a8ac6cf4329bc772c9b7e5abe7bf9493c81194f"
dependencies = [
"futures-channel",
"futures-core",
@ -1001,9 +1247,9 @@ dependencies = [
[[package]]
name = "glib"
version = "0.19.2"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab9e86540b5d8402e905ad4ce7d6aa544092131ab564f3102175af176b90a053"
checksum = "01e191cc1af1f35b9699213107068cd3fe05d9816275ac118dc785a0dd8faebf"
dependencies = [
"bitflags 2.4.2",
"futures-channel",
@ -1023,11 +1269,11 @@ dependencies = [
[[package]]
name = "glib-macros"
version = "0.19.2"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f5897ca27a83e4cdc7b4666850bade0a2e73e17689aabafcc9acddad9d823b8"
checksum = "9972bb91643d589c889654693a4f1d07697fdcb5d104b5c44fb68649ba1bf68d"
dependencies = [
"heck",
"heck 0.5.0",
"proc-macro-crate",
"proc-macro2",
"quote",
@ -1080,9 +1326,9 @@ dependencies = [
[[package]]
name = "gsk4"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e8ce8dee0fd87a11002214b1204ff18c9272fbd530408f0884a0f9b25dc31de"
checksum = "c65036fc8f99579e8cb37b12487969b707ab23ec8ab953682ff347cbd15d396e"
dependencies = [
"cairo-rs",
"gdk4",
@ -1095,9 +1341,9 @@ dependencies = [
[[package]]
name = "gsk4-sys"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2660a652da5b662d43924df19ba40d73f015ed427329ef51d2b1360a4e0dc0e4"
checksum = "bd24c814379f9c3199dc53e52253ee8d0f657eae389ab282c330505289d24738"
dependencies = [
"cairo-sys-rs",
"gdk4-sys",
@ -1111,9 +1357,9 @@ dependencies = [
[[package]]
name = "gtk4"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d26ffa3ec6316ccaa1df62d3e7f5bae1637c0acbb43f250fabef38319f73c64"
checksum = "aa82753b8c26277e4af1446c70e35b19aad4fb794a7b143859e7eeb9a4025d83"
dependencies = [
"cairo-rs",
"field-offset",
@ -1132,9 +1378,9 @@ dependencies = [
[[package]]
name = "gtk4-macros"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8b86439e9896f6f3f47c3d8077c5c8205174078760afdabd9098a8e9e937d97"
checksum = "40300bf071d2fcd4c94eacc09e84ec6fe73129d2ceb635cf7e55b026b5443567"
dependencies = [
"anyhow",
"proc-macro-crate",
@ -1146,9 +1392,9 @@ dependencies = [
[[package]]
name = "gtk4-sys"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2abc0a6d356d59a3806021829ce6ed3e70bba3509b41a535fedcb09fae13fbc0"
checksum = "0db1b104138f087ccdc81d2c332de5dd049b89de3d384437cc1093b17cd2da18"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
@ -1195,6 +1441,18 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hex"
version = "0.4.3"
@ -1261,6 +1519,35 @@ dependencies = [
"libc",
]
[[package]]
name = "is-terminal"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.10"
@ -1269,9 +1556,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "js-sys"
version = "0.3.68"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
@ -1363,10 +1650,17 @@ version = "0.5.4"
dependencies = [
"amdgpu-sysfs",
"anyhow",
"chrono",
"criterion",
"gtk4",
"itertools 0.12.1",
"lact-client",
"lact-daemon",
"lact-gui",
"lact-schema",
"libadwaita",
"plotters",
"plotters-cairo",
"pretty_assertions",
"tracing",
"tracing-subscriber",
@ -1484,6 +1778,16 @@ dependencies = [
"windows-targets 0.52.4",
]
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.4.2",
"libc",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
@ -1648,6 +1952,12 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "ordered-stream"
version = "0.2.0"
@ -1675,9 +1985,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "pango"
version = "0.19.2"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7809e8af4df8d024a066106b72ca6bc7253a484ae3867041a96103ef8a13188d"
checksum = "b1264d13deb823cc652f26cfe59afb1ec4b9db2a5bd27c41b738c879cc1bfaa1"
dependencies = [
"gio",
"glib",
@ -1726,6 +2036,25 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "pathfinder_geometry"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3"
dependencies = [
"log",
"pathfinder_simd",
]
[[package]]
name = "pathfinder_simd"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0444332826c70dc47be74a7c6a5fc44e23a7905ad6858d4162b658320455ef93"
dependencies = [
"rustc_version",
]
[[package]]
name = "pciid-parser"
version = "0.7.2"
@ -1764,6 +2093,49 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "plotters"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"chrono",
"font-kit",
"lazy_static",
"num-traits",
"pathfinder_geometry",
"plotters-backend",
"plotters-svg",
"ttf-parser",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-cairo"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12e5e8951c0078885c8e31976d3d73c4d7c44b35f249a3fce2095153c160409"
dependencies = [
"cairo-rs",
"plotters-backend",
]
[[package]]
name = "plotters-svg"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]]
name = "polling"
version = "3.5.0"
@ -1887,6 +2259,26 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
@ -1896,6 +2288,17 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_users"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]]
name = "regex"
version = "1.10.3"
@ -2204,7 +2607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331"
dependencies = [
"cfg-expr",
"heck",
"heck 0.4.1",
"pkg-config",
"toml",
"version-compare",
@ -2302,6 +2705,16 @@ dependencies = [
"time-core",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "tokio"
version = "1.36.0"
@ -2437,6 +2850,12 @@ dependencies = [
"tracing-log",
]
[[package]]
name = "ttf-parser"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff"
[[package]]
name = "typenum"
version = "1.17.0"
@ -2523,7 +2942,7 @@ dependencies = [
"core-graphics-types",
"crossbeam-queue",
"half",
"heck",
"heck 0.4.1",
"indexmap",
"libloading 0.8.2",
"objc",
@ -2558,9 +2977,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@ -2568,9 +2987,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
@ -2583,9 +3002,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -2593,9 +3012,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
@ -2606,9 +3025,19 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.91"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "web-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
@ -2800,6 +3229,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "wio"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
dependencies = [
"winapi",
]
[[package]]
name = "xattr"
version = "1.3.1"
@ -2833,6 +3271,18 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "yeslogic-fontconfig-sys"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386"
dependencies = [
"const-cstr",
"dlib",
"once_cell",
"pkg-config",
]
[[package]]
name = "zbus"
version = "4.1.2"

View File

@ -25,3 +25,7 @@ nix = { version = "0.28.0", default-features = false }
strip = "symbols"
opt-level = "s"
lto = true
[profile.bench]
strip = false
debug = true

View File

@ -7,6 +7,7 @@ edition = "2021"
[features]
default = ["gtk-tests"]
gtk-tests = []
bench = []
[dependencies]
lact-client = { path = "../lact-client" }
@ -22,5 +23,23 @@ adw = { package = "libadwaita", version = "0.6.0", features = [
"v1_4",
], optional = true }
plotters = { version = "0.3.5", default-features = false, features = [
"datetime",
"line_series",
"ttf",
"histogram",
"full_palette",
] }
plotters-cairo = "0.6.0"
chrono = "0.4"
itertools = "0.12.1"
[dev-dependencies]
criterion = "0.5.1"
pretty_assertions = "1.4.0"
lact-gui = { path = ".", features = ["bench"] }
lact-schema = { path = "../lact-schema", features = ["args"] }
[[bench]]
name = "gui"
harness = false

44
lact-gui/benches/gui.rs Normal file
View File

@ -0,0 +1,44 @@
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use gtk::glib::{subclass::types::ObjectSubclassIsExt, Object};
use lact_gui::app::{Plot, PlotData};
use plotters::backend::SVGBackend;
pub fn criterion_benchmark(c: &mut Criterion) {
gtk::init().unwrap();
let mut plot_data = PlotData::default();
let mut time = chrono::NaiveDateTime::new(
chrono::NaiveDate::from_yo_opt(2024, 1).unwrap(),
chrono::NaiveTime::default(),
);
for value in (0..100).step_by(5) {
plot_data.push_line_series_with_time("value", value as f64, time);
time += chrono::TimeDelta::seconds(2);
}
let plot: Plot = Object::builder().build();
*plot.data_mut() = plot_data.clone();
let imp = plot.imp();
c.bench_function("plot_pdf", |b| {
b.iter(|| {
let mut buf = String::new();
let plotters_backend = SVGBackend::with_string(&mut buf, (1000, 1000));
imp.plot_pdf(plotters_backend).unwrap();
})
});
c.bench_function("trim_plot_data", |b| {
b.iter_batched(
|| plot_data.clone(),
|mut data| {
data.trim_data(60);
},
BatchSize::SmallInput,
)
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

View File

@ -0,0 +1,155 @@
pub(crate) mod plot;
use self::plot::PlotData;
use glib::Object;
use gtk::{
glib::{self, subclass::types::ObjectSubclassIsExt},
prelude::WidgetExt,
};
use lact_client::schema::DeviceStats;
const GRAPH_WIDTH_SECONDS: i64 = 60;
glib::wrapper! {
pub struct GraphsWindow(ObjectSubclass<imp::GraphsWindow>)
@extends gtk::Box, gtk::Widget, gtk::Window,
@implements gtk::Orientable, gtk::Accessible, gtk::Buildable;
}
impl GraphsWindow {
pub fn new() -> Self {
Object::builder().build()
}
pub fn set_stats(&self, stats: &DeviceStats) {
let imp = self.imp();
let mut temperature_plot = imp.temperature_plot.data_mut();
let mut clockspeed_plot = imp.clockspeed_plot.data_mut();
let mut power_plot = imp.power_plot.data_mut();
let throttling_plots = [&mut temperature_plot, &mut clockspeed_plot, &mut power_plot];
match &stats.throttle_info {
Some(throttle_info) => {
if throttle_info.is_empty() {
for plot in throttling_plots {
plot.push_throttling("No", false);
}
} else {
let type_text: Vec<String> = throttle_info
.iter()
.map(|(throttle_type, details)| {
format!("{throttle_type} ({})", details.join(", "))
})
.collect();
let text = type_text.join(", ");
for plot in throttling_plots {
plot.push_throttling(&text, true);
}
}
}
None => {
for plot in throttling_plots {
plot.push_throttling("Unknown", false);
}
}
}
for (name, value) in &stats.temps {
temperature_plot.push_line_series(name, value.current.unwrap_or(0.0) as f64);
}
if let Some(average) = stats.power.average {
power_plot.push_line_series("Average", average);
}
if let Some(current) = stats.power.current {
power_plot.push_line_series("Current", current);
}
if let Some(limit) = stats.power.cap_current {
power_plot.push_line_series("Limit", limit);
}
if let Some(point) = stats.clockspeed.gpu_clockspeed {
clockspeed_plot.push_line_series("GPU (Avg)", point as f64);
}
if let Some(point) = stats.clockspeed.current_gfxclk {
clockspeed_plot.push_line_series("GPU (Trgt)", point as f64);
}
if let Some(point) = stats.clockspeed.vram_clockspeed {
clockspeed_plot.push_line_series("VRAM", point as f64);
}
temperature_plot.trim_data(GRAPH_WIDTH_SECONDS);
clockspeed_plot.trim_data(GRAPH_WIDTH_SECONDS);
power_plot.trim_data(GRAPH_WIDTH_SECONDS);
imp.temperature_plot.queue_draw();
imp.clockspeed_plot.queue_draw();
imp.power_plot.queue_draw();
}
pub fn clear(&self) {
let imp = self.imp();
*imp.temperature_plot.data_mut() = PlotData::default();
*imp.clockspeed_plot.data_mut() = PlotData::default();
*imp.power_plot.data_mut() = PlotData::default();
imp.temperature_plot.queue_draw();
imp.clockspeed_plot.queue_draw();
imp.power_plot.queue_draw();
}
}
impl Default for GraphsWindow {
fn default() -> Self {
Self::new()
}
}
mod imp {
use super::plot::Plot;
use gtk::{
glib::{self, subclass::InitializingObject},
prelude::*,
subclass::{
prelude::*,
widget::{CompositeTemplateClass, WidgetImpl},
},
CompositeTemplate,
};
#[derive(CompositeTemplate, Default)]
#[template(file = "ui/graphs_window.blp")]
pub struct GraphsWindow {
#[template_child]
pub(super) temperature_plot: TemplateChild<Plot>,
#[template_child]
pub(super) clockspeed_plot: TemplateChild<Plot>,
#[template_child]
pub(super) power_plot: TemplateChild<Plot>,
}
#[glib::object_subclass]
impl ObjectSubclass for GraphsWindow {
const NAME: &'static str = "GraphsWindow";
type Type = super::GraphsWindow;
type ParentType = gtk::Window;
fn class_init(class: &mut Self::Class) {
Plot::ensure_type();
class.bind_template();
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
impl ObjectImpl for GraphsWindow {}
impl WidgetImpl for GraphsWindow {}
impl WindowImpl for GraphsWindow {}
impl ApplicationWindowImpl for GraphsWindow {}
}

View File

@ -0,0 +1,87 @@
use itertools::Itertools;
#[derive(Clone, Copy)]
pub struct CubicSplineSegment {
a: f64,
b: f64,
c: f64,
d: f64,
x: i64,
}
impl CubicSplineSegment {
// Create a new cubic spline segment
fn new(a: f64, b: f64, c: f64, d: f64, x: i64) -> Self {
Self { a, b, c, d, x }
}
// Evaluate the cubic spline at a given point x
pub fn evaluate(&self, x: i64) -> f64 {
let dx = (x - self.x) as f64;
self.a + self.b * dx + self.c * dx.powi(2) + self.d * dx.powi(3)
}
}
pub type TimePeriod = (i64, i64);
// Define a function to perform cubic spline interpolation
pub fn cubic_spline_interpolation<'a, I>(iter: I) -> Vec<(TimePeriod, CubicSplineSegment)>
where
I: IntoIterator<Item = (&'a i64, &'a f64)> + 'a,
{
let data: Vec<_> = iter.into_iter().collect();
let n = data.len();
// Compute differences between consecutive x values
let mut dx = Vec::with_capacity(n - 1);
for i in 1..n {
let x_diff = (*data[i].0 - *data[i - 1].0) as f64;
dx.push(x_diff);
}
// Compute differences between consecutive y values
let dy: Vec<f64> = data.iter().map(|&(_, y)| *y).collect();
// Compute second derivatives using Thomas algorithm
let mut d = vec![0.0; n];
let mut s = vec![0.0; n - 1];
let mut q = vec![0.0; n];
let mut p = vec![0.0; n];
for i in 1..(n - 1) {
s[i] = dx[i - 1] / (dx[i - 1] + dx[i]);
q[i] = 1.0 - s[i];
p[i] = 6.0 * ((dy[i + 1] - dy[i]) / dx[i] - (dy[i] - dy[i - 1]) / dx[i - 1])
/ (dx[i - 1] + dx[i]);
}
for i in 1..(n - 1) {
let temp = q[i] * d[i - 1] + 2.0;
d[i] = -s[i] / temp;
p[i] = (p[i] - q[i] * p[i - 1]) / temp;
}
for i in (1..(n - 1)).rev() {
d[i] = d[i] * d[i + 1] + p[i];
}
data.iter()
.zip(
// Construct cubic spline segments
(0..(n - 1)).map(|i| {
let a = dy[i];
let b = (dy[i + 1] - dy[i]) / dx[i] - dx[i] * (2.0 * d[i] + d[i + 1]) / 6.0;
let c = d[i] / 2.0;
let d = (d[i + 1] - d[i]) / (6.0 * dx[i]);
CubicSplineSegment::new(a, b, c, d, *data[i].0)
}),
)
// Group 2 closest points together
.tuple_windows::<(_, _)>()
// Get first time, second time and their corresponding interpolation segment
.map(|(((first_time, _), segment), ((second_time, _), _))| {
((**first_time, **second_time), segment)
})
.collect()
}

View File

@ -0,0 +1,247 @@
use super::cubic_spline::cubic_spline_interpolation;
use anyhow::Context;
use chrono::NaiveDateTime;
use glib::Properties;
use gtk::{glib, prelude::*, subclass::prelude::*};
use itertools::Itertools;
use plotters::prelude::*;
use plotters::style::colors::full_palette::DEEPORANGE_100;
use plotters_cairo::CairoBackend;
use std::cell::Cell;
use std::cell::RefCell;
use std::cmp::max;
use std::collections::BTreeMap;
use tracing::error;
#[derive(Properties, Default)]
#[properties(wrapper_type = super::Plot)]
pub struct Plot {
#[property(get, set)]
title: RefCell<String>,
#[property(get, set)]
value_suffix: RefCell<String>,
#[property(get, set)]
y_label_area_size: Cell<u32>,
pub(super) data: RefCell<PlotData>,
}
#[glib::object_subclass]
impl ObjectSubclass for Plot {
const NAME: &'static str = "Plot";
type Type = super::Plot;
type ParentType = gtk::Widget;
}
#[glib::derived_properties]
impl ObjectImpl for Plot {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.set_height_request(250);
obj.set_hexpand(true);
obj.set_vexpand(true);
}
}
impl WidgetImpl for Plot {
fn snapshot(&self, snapshot: &gtk::Snapshot) {
let width = self.obj().width() as u32;
let height = self.obj().height() as u32;
if width == 0 || height == 0 {
return;
}
let bounds = gtk::graphene::Rect::new(0.0, 0.0, width as f32, height as f32);
let cr = snapshot.append_cairo(&bounds);
// Supersample the plot area
let backend = CairoBackend::new(&cr, (width * 2, height * 2)).unwrap();
if let Err(err) = self.plot_pdf(backend) {
error!("Failed to plot PDF chart: {err:?}")
}
}
}
#[derive(Default)]
#[cfg_attr(feature = "bench", derive(Clone))]
pub struct PlotData {
line_series: BTreeMap<String, BTreeMap<i64, f64>>,
throttling: BTreeMap<i64, (String, bool)>,
}
impl PlotData {
pub fn push_line_series(&mut self, name: &str, point: f64) {
self.push_line_series_with_time(name, point, chrono::Local::now().naive_local());
}
pub fn push_line_series_with_time(&mut self, name: &str, point: f64, time: NaiveDateTime) {
self.line_series
.entry(name.to_owned())
.or_default()
.insert(time.timestamp_millis(), point);
}
pub fn push_throttling(&mut self, name: &str, point: bool) {
self.throttling.insert(
chrono::Local::now().naive_local().timestamp_millis(),
(name.to_owned(), point),
);
}
pub fn line_series_iter(&self) -> impl Iterator<Item = (&String, &BTreeMap<i64, f64>)> {
self.line_series.iter()
}
pub fn throttling_iter(&self) -> impl Iterator<Item = (i64, &str, bool)> {
self.throttling
.iter()
.map(|(time, (name, point))| (*time, name.as_str(), *point))
}
pub fn trim_data(&mut self, last_seconds: i64) {
// Limit data to N seconds
for data in self.line_series.values_mut() {
let maximum_point = data
.last_key_value()
.map(|(date_time, _)| *date_time)
.unwrap_or_default();
data.retain(|time_point, _| ((maximum_point - *time_point) / 1000) < last_seconds);
}
self.line_series.retain(|_, data| !data.is_empty());
// Limit data to N seconds
let maximum_point = self
.throttling
.last_key_value()
.map(|(date_time, _)| *date_time)
.unwrap_or_default();
self.throttling
.retain(|time_point, _| ((maximum_point - *time_point) / 1000) < last_seconds);
}
}
impl Plot {
pub fn plot_pdf<'a, DB>(&self, backend: DB) -> anyhow::Result<()>
where
DB: DrawingBackend + 'a,
<DB as plotters::prelude::DrawingBackend>::ErrorType: 'static,
{
let root = backend.into_drawing_area();
let data = self.data.borrow();
let start_date = data
.line_series_iter()
.filter_map(|(_, data)| Some(data.first_key_value()?.0))
.min()
.cloned()
.unwrap_or_default();
let end_date = data
.line_series_iter()
.map(|(_, value)| value)
.filter_map(|data| Some(data.last_key_value()?.0))
.max()
.cloned()
.unwrap_or_default();
let mut maximum_value = data
.line_series_iter()
.flat_map(|(_, data)| data.values())
.max_by(|x, y| x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal))
.cloned()
.unwrap_or_default();
if maximum_value < 100.0f64 {
maximum_value = 100.0f64;
}
root.fill(&WHITE)?;
let mut chart = ChartBuilder::on(&root)
.x_label_area_size(40)
.y_label_area_size(self.y_label_area_size.get())
.margin(20)
.caption(self.title.borrow().as_str(), ("sans-serif", 30))
.build_cartesian_2d(
start_date..max(end_date, start_date + 60 * 1000),
0f64..maximum_value,
)?;
chart
.configure_mesh()
.x_label_formatter(&|date_time| {
let date_time = NaiveDateTime::from_timestamp_millis(*date_time).unwrap();
date_time.format("%H:%M:%S").to_string()
})
.y_label_formatter(&|x| format!("{x}{}", self.value_suffix.borrow()))
.x_labels(5)
.y_labels(10)
.label_style(("sans-serif", 30))
.draw()
.context("Failed to draw mesh")?;
// Draw the throttling histogram
chart
.draw_series(
data.throttling_iter()
// Group segments of consecutive enabled/disabled throttlings
.group_by(|(_, _, point)| *point)
.into_iter()
// Filter only when throttling is enabled
.filter_map(|(point, group_iter)| point.then_some(group_iter))
// Get last and first times
.filter_map(|mut group_iter| {
let first = group_iter.next()?;
Some((first, group_iter.last().unwrap_or(first)))
})
// Filter out redundant data
.map(|((start, name, _), (end, _, _))| ((start, end), name))
.map(|((start_time, end_time), _)| {
let mut bar = Rectangle::new(
[(start_time, 0f64), (end_time, maximum_value)],
DEEPORANGE_100.filled(),
);
bar.set_margin(0, 0, 5, 5);
bar
}),
)
.context("Failed to draw throttling histogram")?;
for (idx, (caption, data)) in (0..).zip(data.line_series_iter()) {
chart
.draw_series(LineSeries::new(
cubic_spline_interpolation(data.iter())
.into_iter()
.flat_map(|((first_time, second_time), segment)| {
// Interpolate in intervals of one millisecond
(first_time..second_time).map(move |current_date| {
(current_date, segment.evaluate(current_date))
})
}),
Palette99::pick(idx).stroke_width(1),
))
.context("Failed to draw series")?
.label(caption)
.legend(move |(x, y)| {
Rectangle::new([(x - 10, y - 10), (x + 10, y + 10)], Palette99::pick(idx))
});
}
chart
.configure_series_labels()
.margin(30)
.label_font(("sans-serif", 30))
.position(SeriesLabelPosition::LowerRight)
.background_style(WHITE.mix(0.8))
.border_style(BLACK)
.draw()
.context("Failed to draw series labels")?;
root.present()?;
Ok(())
}
}

View File

@ -0,0 +1,19 @@
mod cubic_spline;
mod imp;
use std::cell::RefMut;
pub use imp::PlotData;
use gtk::glib::{self, subclass::types::ObjectSubclassIsExt};
glib::wrapper! {
pub struct Plot(ObjectSubclass<imp::Plot>)
@extends gtk::Widget;
}
impl Plot {
pub fn data_mut(&self) -> RefMut<'_, PlotData> {
self.imp().data.borrow_mut()
}
}

View File

@ -22,6 +22,10 @@ impl Header {
container.pack_start(&gpu_selector);
let menu = gio::Menu::new();
menu.append(
Some("Show historical charts"),
Some("app.show-graphs-window"),
);
menu.append(
Some("Generate debug snapshot"),
Some("app.generate-debug-snapshot"),

View File

@ -1,9 +1,14 @@
mod apply_revealer;
mod graphs_window;
mod header;
mod info_row;
mod page_section;
mod root_stack;
#[cfg(feature = "bench")]
pub use graphs_window::plot::{Plot, PlotData};
use self::graphs_window::GraphsWindow;
use crate::{create_connection, APP_ID, GUI_VERSION};
use anyhow::{anyhow, Context};
use apply_revealer::ApplyRevealer;
@ -27,13 +32,14 @@ use tracing::{debug, error, trace, warn};
const STATS_POLL_INTERVAL: u64 = 250;
#[derive(Clone)]
pub struct App {
pub(crate) struct App {
application: Application,
pub window: ApplicationWindow,
pub header: Header,
root_stack: RootStack,
apply_revealer: ApplyRevealer,
daemon_client: DaemonClient,
graphs_window: GraphsWindow,
}
impl App {
@ -78,6 +84,8 @@ impl App {
window.set_child(Some(&root_box));
let graphs_window = GraphsWindow::new();
App {
application,
window,
@ -85,6 +93,7 @@ impl App {
root_stack,
apply_revealer,
daemon_client,
graphs_window,
}
}
@ -100,6 +109,7 @@ impl App {
app.set_info(&gpu_id);
*current_gpu_id.borrow_mut() = gpu_id;
debug!("Updated current GPU id");
app.graphs_window.clear();
}));
let devices_buf = app
@ -178,11 +188,17 @@ impl App {
let disable_overdive_action = ActionEntry::builder("disable-overdrive")
.activate(clone!(@strong app => move |_, _, _| {
app.disable_overclocking()
app.disable_overclocking();
}))
.build();
app.application.add_action_entries([snapshot_action, disable_overdive_action]);
let show_graphs_window_action = ActionEntry::builder("show-graphs-window")
.activate(clone!(@strong app => move |_, _, _| {
app.graphs_window.show();
}))
.build();
app.application.add_action_entries([snapshot_action, disable_overdive_action, show_graphs_window_action]);
app.start_stats_update_loop(current_gpu_id);
@ -334,7 +350,7 @@ impl App {
fn start_stats_update_loop(&self, current_gpu_id: Rc<RefCell<String>>) {
// The loop that gets stats
glib::spawn_future_local(
clone!(@strong self.daemon_client as daemon_client, @strong self.root_stack as root_stack => async move {
clone!(@strong self.daemon_client as daemon_client, @strong self.root_stack as root_stack, @strong self.graphs_window as graphs_window => async move {
loop {
{
let gpu_id = current_gpu_id.borrow();
@ -348,6 +364,7 @@ impl App {
root_stack.info_page.set_stats(&stats);
root_stack.thermals_page.set_stats(&stats, false);
root_stack.oc_page.set_stats(&stats, false);
graphs_window.set_stats(&stats);
}
Err(err) => {
error!("Could not fetch stats: {err}");

View File

@ -1,4 +1,4 @@
mod app;
pub mod app;
use anyhow::{anyhow, Context};
use app::App;

View File

@ -0,0 +1,54 @@
using Gtk 4.0;
template $GraphsWindow: Window {
default-height: 600;
default-width: 600;
title: "Historical data";
hide-on-close: true;
Grid {
margin-top: 10;
margin-bottom: 10;
margin-start: 10;
margin-end: 10;
row-spacing: 20;
column-spacing: 20;
$Plot temperature_plot {
title: "Temperature";
hexpand: true;
value-suffix: "°C";
y-label-area-size: 80;
layout {
column: 0;
row: 0;
}
}
$Plot clockspeed_plot {
title: "Clockspeed";
hexpand: true;
value-suffix: "MHz";
y-label-area-size: 140;
layout {
column: 0;
row: 1;
}
}
$Plot power_plot {
title: "Power usage";
hexpand: true;
value-suffix: "W";
y-label-area-size: 80;
layout {
column: 0;
row: 2;
}
}
}
}

View File

@ -82,5 +82,11 @@ template $GpuStatsSection: $PageSection {
value: bind template.throttling;
}
}
}
Button {
label: "Show historical charts";
action-name: "app.show-graphs-window";
}
}