mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2026-08-17 16:34:54 -05:00
feat: show OpenCL info, move Vulkan to software tab (#553)
* feat: fetch OpenCL info
* feat: show opencl info in gui
* chore: move vulkan info to software page, refactor
* refactor: unify hw info page into one component
* chore: update generated-sources
* feat: show more information
* fix: disable dynamic feature in OpenCL
* Revert "fix: disable dynamic feature in OpenCL"
This reverts commit 18c9d93ed7.
* fix: add dependency
* fix: set correct dependencies
* fix: opencl package name
* fix: fedora package name
* fix: add rhel repo
* chore: update readme
This commit is contained in:
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Update repos
|
||||
run: sudo apt update
|
||||
- name: Install dependencies
|
||||
run: sudo apt install -y libgtk-4-dev pkg-config libvulkan-dev libdrm-dev curl fuse3 libfuse-dev
|
||||
run: sudo apt install -y libgtk-4-dev pkg-config libvulkan-dev libdrm-dev curl fuse3 libfuse-dev ocl-icd-libopencl1 ocl-icd-opencl-dev
|
||||
- name: Update pci.ids
|
||||
run: sudo curl -o /usr/share/hwdata/pci.ids https://pci-ids.ucw.cz/v2.2/pci.ids
|
||||
- name: Build
|
||||
|
||||
Generated
+67
-2
@@ -310,6 +310,18 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cl3"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a50168bca6baeb04c8aec60adba5917221034d96275f6b199be66a3969519f1"
|
||||
dependencies = [
|
||||
"dlopen2",
|
||||
"libc",
|
||||
"opencl-sys",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.8.1"
|
||||
@@ -537,6 +549,29 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlopen2"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6"
|
||||
dependencies = [
|
||||
"dlopen2_derive",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlopen2_derive"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "easy_fuser"
|
||||
version = "0.4.0"
|
||||
@@ -1308,6 +1343,7 @@ dependencies = [
|
||||
"bindgen",
|
||||
"bitflags 2.9.0",
|
||||
"chrono",
|
||||
"cl3",
|
||||
"copes",
|
||||
"divan",
|
||||
"easy_fuser",
|
||||
@@ -1671,7 +1707,7 @@ dependencies = [
|
||||
"libloading",
|
||||
"nvml-wrapper-sys",
|
||||
"static_assertions",
|
||||
"thiserror",
|
||||
"thiserror 1.0.69",
|
||||
"wrapcenum-derive",
|
||||
]
|
||||
|
||||
@@ -1698,6 +1734,15 @@ version = "1.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
|
||||
[[package]]
|
||||
name = "opencl-sys"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6edabf61284e58c345821019106a2e68f8a61eb732c0d2b214a78af24499855c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ordered-stream"
|
||||
version = "0.2.0"
|
||||
@@ -2338,7 +2383,16 @@ version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2352,6 +2406,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread-priority"
|
||||
version = "1.2.0"
|
||||
|
||||
@@ -164,13 +164,14 @@ Dependencies:
|
||||
- hwdata
|
||||
- libdrm
|
||||
- vulkan-tools
|
||||
- ocl-icd
|
||||
|
||||
Command to install all dependencies:
|
||||
|
||||
- Fedora:
|
||||
`sudo dnf install rust cargo make git clang gtk4-devel libdrm-devel vulkan-tools`
|
||||
`sudo dnf install rust cargo make git clang gtk4-devel libdrm-devel vulkan-tools ocl-icd-devel`
|
||||
- Arch:
|
||||
`sudo pacman -S --needed base-devel git clang make rust gtk4 hwdata vulkan-tools`
|
||||
`sudo pacman -S --needed base-devel git clang make rust gtk4 hwdata vulkan-tools ocl-icd`
|
||||
|
||||
Steps:
|
||||
|
||||
|
||||
@@ -440,6 +440,19 @@
|
||||
"dest": "cargo/vendor/chrono-0.4.40",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/cl3/cl3-0.13.0.crate",
|
||||
"sha256": "0a50168bca6baeb04c8aec60adba5917221034d96275f6b199be66a3969519f1",
|
||||
"dest": "cargo/vendor/cl3-0.13.0"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"0a50168bca6baeb04c8aec60adba5917221034d96275f6b199be66a3969519f1\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/cl3-0.13.0",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
@@ -757,6 +770,32 @@
|
||||
"dest": "cargo/vendor/divan-macros-0.1.17",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/dlopen2/dlopen2-0.7.0.crate",
|
||||
"sha256": "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6",
|
||||
"dest": "cargo/vendor/dlopen2-0.7.0"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/dlopen2-0.7.0",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/dlopen2_derive/dlopen2_derive-0.4.0.crate",
|
||||
"sha256": "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54",
|
||||
"dest": "cargo/vendor/dlopen2_derive-0.4.0"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/dlopen2_derive-0.4.0",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
@@ -2093,6 +2132,19 @@
|
||||
"dest": "cargo/vendor/once_cell-1.20.3",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/opencl-sys/opencl-sys-0.6.0.crate",
|
||||
"sha256": "6edabf61284e58c345821019106a2e68f8a61eb732c0d2b214a78af24499855c",
|
||||
"dest": "cargo/vendor/opencl-sys-0.6.0"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"6edabf61284e58c345821019106a2e68f8a61eb732c0d2b214a78af24499855c\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/opencl-sys-0.6.0",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
@@ -3029,6 +3081,19 @@
|
||||
"dest": "cargo/vendor/thiserror-1.0.69",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/thiserror/thiserror-2.0.12.crate",
|
||||
"sha256": "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708",
|
||||
"dest": "cargo/vendor/thiserror-2.0.12"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/thiserror-2.0.12",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
@@ -3042,6 +3107,19 @@
|
||||
"dest": "cargo/vendor/thiserror-impl-1.0.69",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/thiserror-impl/thiserror-impl-2.0.12.crate",
|
||||
"sha256": "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d",
|
||||
"dest": "cargo/vendor/thiserror-impl-2.0.12"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/thiserror-impl-2.0.12",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
@@ -4062,4 +4140,4 @@
|
||||
"dest": "cargo",
|
||||
"dest-filename": "config"
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -47,6 +47,7 @@ notify = { version = "8.0.0", default-features = false }
|
||||
copes = { git = "https://gitlab.com/corectrl/copes" }
|
||||
libloading = "0.8.6"
|
||||
tempfile = "3.17.1"
|
||||
cl3 = { version = "0.13.0", features = ["static"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod gpu_controller;
|
||||
pub mod handler;
|
||||
mod opencl;
|
||||
mod profiles;
|
||||
pub(crate) mod system;
|
||||
mod vulkan;
|
||||
|
||||
@@ -17,6 +17,7 @@ pub const VENDOR_NVIDIA: &str = "10DE";
|
||||
|
||||
use crate::bindings::intel::IntelDrm;
|
||||
use amdgpu_sysfs::gpu_handle::power_profile_mode::PowerProfileModesTable;
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Context;
|
||||
use futures::{future::LocalBoxFuture, FutureExt};
|
||||
use lact_schema::{
|
||||
@@ -81,6 +82,30 @@ impl CommonControllerInfo {
|
||||
self.pci_slot_name
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_slot_info(&self) -> anyhow::Result<PciSlotInfo> {
|
||||
let [domain, bus, dev, func] = self
|
||||
.pci_slot_name
|
||||
.split([':', '.'])
|
||||
.map(|part| u16::from_str_radix(part, 16).context("Could not parse pci slot name part"))
|
||||
.collect::<anyhow::Result<Vec<_>>>()?
|
||||
.try_into()
|
||||
.map_err(|err| anyhow!("Invalid pci slot name format {err:?}"))?;
|
||||
|
||||
Ok(PciSlotInfo {
|
||||
domain,
|
||||
bus,
|
||||
dev,
|
||||
func,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PciSlotInfo {
|
||||
pub domain: u16,
|
||||
pub bus: u16,
|
||||
pub dev: u16,
|
||||
pub func: u16,
|
||||
}
|
||||
|
||||
pub(crate) fn init_controller(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use super::{CommonControllerInfo, FanControlHandle, GpuController, VENDOR_AMD};
|
||||
use crate::server::{gpu_controller::fan_control::FanCurveExt, vulkan::get_vulkan_info};
|
||||
use crate::server::{
|
||||
gpu_controller::fan_control::FanCurveExt, opencl::get_opencl_info, vulkan::get_vulkan_info,
|
||||
};
|
||||
use amdgpu_sysfs::{
|
||||
error::Error,
|
||||
gpu_handle::{
|
||||
@@ -407,7 +409,7 @@ impl AmdGpuController {
|
||||
.enumerate()
|
||||
.map(|(i, value)| {
|
||||
let i = u8::try_from(i).unwrap();
|
||||
let enabled = enabled_states.map_or(true, |enabled| enabled.contains(&i));
|
||||
let enabled = enabled_states.is_none_or(|enabled| enabled.contains(&i));
|
||||
PowerState {
|
||||
enabled,
|
||||
min_value: None,
|
||||
@@ -599,6 +601,7 @@ impl GpuController for AmdGpuController {
|
||||
let vbios_version = self.get_full_vbios_version();
|
||||
let link_info = self.get_link_info();
|
||||
let drm_info = self.get_drm_info();
|
||||
let opencl_info = get_opencl_info(&self.common);
|
||||
|
||||
DeviceInfo {
|
||||
pci_info,
|
||||
@@ -606,6 +609,7 @@ impl GpuController for AmdGpuController {
|
||||
driver,
|
||||
vbios_version,
|
||||
link_info,
|
||||
opencl_info,
|
||||
drm_info,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
drm_i915_gem_memory_class_I915_MEMORY_CLASS_DEVICE,
|
||||
drm_xe_memory_class_DRM_XE_MEM_REGION_CLASS_VRAM, IntelDrm,
|
||||
},
|
||||
server::vulkan::get_vulkan_info,
|
||||
server::{opencl::get_opencl_info, vulkan::get_vulkan_info},
|
||||
};
|
||||
use amdgpu_sysfs::{gpu_handle::power_profile_mode::PowerProfileModesTable, hw_mon::Temperature};
|
||||
use anyhow::{anyhow, Context};
|
||||
@@ -588,6 +588,7 @@ impl GpuController for IntelGpuController {
|
||||
vbios_version: None,
|
||||
link_info: LinkInfo::default(),
|
||||
drm_info: Some(drm_info),
|
||||
opencl_info: get_opencl_info(&self.common),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
mod driver;
|
||||
|
||||
use super::{CommonControllerInfo, FanControlHandle, GpuController};
|
||||
use crate::server::{gpu_controller::fan_control::FanCurveExt, vulkan::get_vulkan_info};
|
||||
use crate::server::{
|
||||
gpu_controller::fan_control::FanCurveExt, opencl::get_opencl_info, vulkan::get_vulkan_info,
|
||||
};
|
||||
use amdgpu_sysfs::{gpu_handle::power_profile_mode::PowerProfileModesTable, hw_mon::Temperature};
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use driver::DriverHandle;
|
||||
@@ -322,6 +324,7 @@ impl GpuController for NvidiaGpuController {
|
||||
output
|
||||
}),
|
||||
},
|
||||
opencl_info: get_opencl_info(&self.common),
|
||||
drm_info: Some(DrmInfo {
|
||||
device_name: device.name().ok(),
|
||||
pci_revision_id: None,
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
use super::gpu_controller::{CommonControllerInfo, PciSlotInfo};
|
||||
use anyhow::anyhow;
|
||||
use cl3::{
|
||||
device,
|
||||
ext::{
|
||||
cl_device_info, CL_DEVICE_GLOBAL_MEM_SIZE, CL_DEVICE_LOCAL_MEM_SIZE,
|
||||
CL_DEVICE_MAX_COMPUTE_UNITS, CL_DEVICE_MAX_WORK_GROUP_SIZE, CL_DEVICE_NAME,
|
||||
CL_DEVICE_OPENCL_C_VERSION, CL_DEVICE_PCI_BUS_INFO_KHR, CL_DEVICE_TYPE_ALL,
|
||||
CL_DEVICE_VERSION, CL_DRIVER_VERSION, CL_PLATFORM_NAME,
|
||||
},
|
||||
platform,
|
||||
};
|
||||
use lact_schema::OpenCLInfo;
|
||||
use std::ffi::c_void;
|
||||
use tracing::error;
|
||||
|
||||
pub fn get_opencl_info(info: &CommonControllerInfo) -> Option<OpenCLInfo> {
|
||||
match try_get_opencl_info(info) {
|
||||
Ok(info) => info,
|
||||
Err(err) => {
|
||||
error!("could not get OpenCL info: {err}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_get_opencl_info(info: &CommonControllerInfo) -> anyhow::Result<Option<OpenCLInfo>> {
|
||||
let slot_info = info.get_slot_info()?;
|
||||
|
||||
let Some((platform, device)) = find_matching_device(&slot_info)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let platform_name = platform::get_platform_info(platform, CL_PLATFORM_NAME)
|
||||
.map_err(|err| anyhow!("Could not get platform name: {err}"))?
|
||||
.to_string()
|
||||
.replace('\0', "");
|
||||
|
||||
let device_name = get_info_string(device, CL_DEVICE_NAME)?;
|
||||
let version = get_info_string(device, CL_DEVICE_VERSION)?;
|
||||
let driver_version = get_info_string(device, CL_DRIVER_VERSION)?;
|
||||
let c_version = get_info_string(device, CL_DEVICE_OPENCL_C_VERSION)?;
|
||||
|
||||
let compute_units = device::get_device_info(device, CL_DEVICE_MAX_COMPUTE_UNITS)
|
||||
.map_err(|err| anyhow!("Could not get device cu count: {err}"))?
|
||||
.to_uint();
|
||||
|
||||
let workgroup_size = device::get_device_info(device, CL_DEVICE_MAX_WORK_GROUP_SIZE)
|
||||
.map_err(|err| anyhow!("Could not get device cu count: {err}"))?
|
||||
.to_size();
|
||||
|
||||
let global_memory = device::get_device_info(device, CL_DEVICE_GLOBAL_MEM_SIZE)
|
||||
.map_err(|err| anyhow!("Could not get device memory: {err}"))?
|
||||
.to_ulong();
|
||||
|
||||
let local_memory = device::get_device_info(device, CL_DEVICE_LOCAL_MEM_SIZE)
|
||||
.map_err(|err| anyhow!("Could not get device memory: {err}"))?
|
||||
.to_ulong();
|
||||
|
||||
Ok(Some(OpenCLInfo {
|
||||
platform_name,
|
||||
device_name,
|
||||
version,
|
||||
driver_version,
|
||||
c_version,
|
||||
workgroup_size,
|
||||
compute_units,
|
||||
global_memory,
|
||||
local_memory,
|
||||
}))
|
||||
}
|
||||
|
||||
fn find_matching_device(
|
||||
slot_info: &PciSlotInfo,
|
||||
) -> anyhow::Result<Option<(*mut c_void, *mut c_void)>> {
|
||||
let platforms = platform::get_platform_ids()
|
||||
.map_err(|err| anyhow!("Could not get platform list: {err}"))?;
|
||||
|
||||
for platform in platforms {
|
||||
let devices = device::get_device_ids(platform, CL_DEVICE_TYPE_ALL)
|
||||
.map_err(|err| anyhow!("Could not get device list: {err}"))?;
|
||||
for device in devices {
|
||||
let raw_bus_info = device::get_device_info(device, CL_DEVICE_PCI_BUS_INFO_KHR)
|
||||
.map_err(|err| anyhow!("Could not get bus info: {err}"))?
|
||||
.to_vec_uchar();
|
||||
let bus_info = device::get_device_pci_bus_info_khr(&raw_bus_info);
|
||||
|
||||
if bus_info.pci_bus == u32::from(slot_info.bus)
|
||||
&& bus_info.pci_device == u32::from(slot_info.dev)
|
||||
&& bus_info.pci_domain == u32::from(slot_info.domain)
|
||||
&& bus_info.pci_function == u32::from(slot_info.func)
|
||||
{
|
||||
return Ok(Some((platform, device)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn get_info_string(device: *mut c_void, param: cl_device_info) -> anyhow::Result<String> {
|
||||
let mut string = device::get_device_info(device, param)
|
||||
.map_err(|err| anyhow!("Could not fetch property {param:0x}: {err}"))?
|
||||
.to_string();
|
||||
string.pop();
|
||||
Ok(string)
|
||||
}
|
||||
+18
-2
@@ -35,7 +35,7 @@ use msg::AppMsg;
|
||||
use pages::{
|
||||
info_page::InformationPage,
|
||||
oc_page::{OcPage, OcPageMsg},
|
||||
software_page::SoftwarePage,
|
||||
software_page::{SoftwarePage, SoftwarePageMsg},
|
||||
thermals_page::ThermalsPage,
|
||||
PageUpdate,
|
||||
};
|
||||
@@ -86,7 +86,7 @@ impl AsyncComponent for AppModel {
|
||||
#[root]
|
||||
gtk::ApplicationWindow::builder()
|
||||
.titlebar(>k::HeaderBar::new())
|
||||
.default_height(900)
|
||||
.default_height(800)
|
||||
.default_width(60)
|
||||
.icon_name(APP_ID)
|
||||
.title("LACT")
|
||||
@@ -460,6 +460,8 @@ impl AppModel {
|
||||
update,
|
||||
initial: true,
|
||||
});
|
||||
self.software_page
|
||||
.emit(SoftwarePageMsg::DeviceInfo(info.clone()));
|
||||
|
||||
let vram_clock_ratio = info
|
||||
.drm_info
|
||||
@@ -1097,3 +1099,17 @@ async fn create_connection() -> anyhow::Result<(DaemonClient, Option<anyhow::Err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_friendly_size(bytes: u64) -> String {
|
||||
const NAMES: &[&str] = &["bytes", "KiB", "MiB", "GiB"];
|
||||
|
||||
let mut size = bytes as f64;
|
||||
|
||||
let mut i = 0;
|
||||
while size > 2048.0 && i < NAMES.len() - 1 {
|
||||
size /= 1024.0;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
format!("{size:.1$} {}", NAMES[i], (size.fract() != 0.0) as usize)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,38 @@ impl InfoRow {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InfoRowItem {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
pub note: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[relm4::factory(pub)]
|
||||
impl relm4::factory::FactoryComponent for InfoRowItem {
|
||||
type Init = Self;
|
||||
type ParentWidget = gtk::Box;
|
||||
type CommandOutput = ();
|
||||
type Input = ();
|
||||
type Output = ();
|
||||
|
||||
fn init_model(
|
||||
init: Self::Init,
|
||||
_index: &Self::Index,
|
||||
_sender: relm4::FactorySender<Self>,
|
||||
) -> Self {
|
||||
init
|
||||
}
|
||||
|
||||
view! {
|
||||
InfoRow {
|
||||
set_selectable: true,
|
||||
set_name: self.name.clone(),
|
||||
set_value: self.value.clone(),
|
||||
set_info_text: self.note.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InfoRow {
|
||||
fn default() -> Self {
|
||||
Object::builder().build()
|
||||
|
||||
@@ -59,7 +59,7 @@ mod imp {
|
||||
#[local_ref]
|
||||
obj {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 5,
|
||||
set_spacing: 10,
|
||||
set_margin_horizontal: 5,
|
||||
|
||||
#[local_ref]
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
mod hardware_info;
|
||||
mod vulkan_info;
|
||||
|
||||
use self::hardware_info::HardwareInfoSection;
|
||||
use super::{values_grid, PageUpdate};
|
||||
use crate::app::page_section::PageSection;
|
||||
use super::PageUpdate;
|
||||
use crate::app::{info_row::InfoRow, page_section::PageSection};
|
||||
use gtk::prelude::*;
|
||||
use relm4::{Component, ComponentController, ComponentParts, ComponentSender, RelmWidgetExt};
|
||||
use vulkan_info::VulkanInfoFrame;
|
||||
use lact_schema::{DeviceInfo, DeviceStats};
|
||||
use relm4::{prelude::FactoryVecDeque, ComponentParts, ComponentSender, RelmWidgetExt};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct InformationPage {
|
||||
hardware_info: relm4::Controller<HardwareInfoSection>,
|
||||
vulkan_info: VulkanInfoFrame,
|
||||
values_list: FactoryVecDeque<InfoRowItem>,
|
||||
device_info: Option<Arc<DeviceInfo>>,
|
||||
device_stats: Option<Arc<DeviceStats>>,
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl Component for InformationPage {
|
||||
impl relm4::SimpleComponent for InformationPage {
|
||||
type Init = ();
|
||||
type Input = PageUpdate;
|
||||
type Output = ();
|
||||
type CommandOutput = ();
|
||||
|
||||
view! {
|
||||
gtk::ScrolledWindow {
|
||||
@@ -29,23 +26,12 @@ impl Component for InformationPage {
|
||||
set_spacing: 15,
|
||||
set_margin_horizontal: 20,
|
||||
|
||||
model.hardware_info.widget(),
|
||||
|
||||
#[name = "vulkan_section"]
|
||||
PageSection::new("Vulkan Information") -> PageSection {
|
||||
set_spacing: 10,
|
||||
set_margin_start: 15,
|
||||
|
||||
append = &model.vulkan_info.container.clone(),
|
||||
PageSection::new("Hardware Information") {
|
||||
append = &model.values_list.widget().clone() -> gtk::Box {
|
||||
set_spacing: 10,
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
}
|
||||
},
|
||||
|
||||
#[name = "vulkan_unavailable_label"]
|
||||
gtk::Label {
|
||||
set_label: "Vulkan is not available on this GPU",
|
||||
set_visible: false,
|
||||
set_margin_horizontal: 10,
|
||||
set_halign: gtk::Align::Start,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,12 +41,10 @@ impl Component for InformationPage {
|
||||
root: Self::Root,
|
||||
_sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let hardware_info = HardwareInfoSection::builder().launch(()).detach();
|
||||
let vulkan_info = VulkanInfoFrame::new();
|
||||
|
||||
let model = Self {
|
||||
hardware_info,
|
||||
vulkan_info,
|
||||
values_list: FactoryVecDeque::builder().launch_default().detach(),
|
||||
device_info: None,
|
||||
device_stats: None,
|
||||
};
|
||||
|
||||
let widgets = view_output!();
|
||||
@@ -68,27 +52,72 @@ impl Component for InformationPage {
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
fn update_with_view(
|
||||
&mut self,
|
||||
widgets: &mut Self::Widgets,
|
||||
msg: Self::Input,
|
||||
_sender: ComponentSender<Self>,
|
||||
_root: &Self::Root,
|
||||
) {
|
||||
self.hardware_info.emit(msg.clone());
|
||||
|
||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
|
||||
match msg {
|
||||
PageUpdate::Info(gpu_info) => {
|
||||
if let Some(vulkan_info) = &gpu_info.vulkan_info {
|
||||
self.vulkan_info.set_info(vulkan_info);
|
||||
self.vulkan_info.container.show();
|
||||
widgets.vulkan_unavailable_label.hide();
|
||||
} else {
|
||||
self.vulkan_info.container.hide();
|
||||
widgets.vulkan_unavailable_label.show();
|
||||
PageUpdate::Info(device_info) => {
|
||||
self.device_info = Some(device_info);
|
||||
}
|
||||
PageUpdate::Stats(device_stats) => {
|
||||
self.device_stats = Some(device_stats);
|
||||
}
|
||||
}
|
||||
self.update_items();
|
||||
}
|
||||
}
|
||||
|
||||
impl InformationPage {
|
||||
fn update_items(&mut self) {
|
||||
self.values_list.guard().clear();
|
||||
|
||||
if let Some(info) = &self.device_info {
|
||||
let mut values_list = self.values_list.guard();
|
||||
for (name, value) in info.info_elements(self.device_stats.as_deref()) {
|
||||
if let Some(value) = value {
|
||||
let note = if name == "Card Model" && !value.starts_with("Unknown ") {
|
||||
Some("The card displayed here may be of a sibling model, e.g. XT vs XTX variety. This is normal, as such models often use the same device ID, and it is not possible to differentiate between them.")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
values_list.push_back(InfoRowItem {
|
||||
name: format!("{name}:"),
|
||||
value,
|
||||
note,
|
||||
});
|
||||
}
|
||||
}
|
||||
PageUpdate::Stats(_stats) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InfoRowItem {
|
||||
name: String,
|
||||
value: String,
|
||||
note: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[relm4::factory]
|
||||
impl relm4::factory::FactoryComponent for InfoRowItem {
|
||||
type Init = Self;
|
||||
type ParentWidget = gtk::Box;
|
||||
type CommandOutput = ();
|
||||
type Input = ();
|
||||
type Output = ();
|
||||
|
||||
fn init_model(
|
||||
init: Self::Init,
|
||||
_index: &Self::Index,
|
||||
_sender: relm4::FactorySender<Self>,
|
||||
) -> Self {
|
||||
init
|
||||
}
|
||||
|
||||
view! {
|
||||
InfoRow {
|
||||
set_selectable: true,
|
||||
set_name: self.name.clone(),
|
||||
set_value: self.value.clone(),
|
||||
set_info_text: self.note.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
use crate::app::{info_row::InfoRow, page_section::PageSection, pages::PageUpdate};
|
||||
use gtk::prelude::{BoxExt, OrientableExt, WidgetExt};
|
||||
use lact_schema::{DeviceInfo, DeviceStats};
|
||||
use relm4::RelmWidgetExt;
|
||||
use relm4::{prelude::FactoryVecDeque, ComponentParts, ComponentSender};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct HardwareInfoSection {
|
||||
values_list: FactoryVecDeque<InfoRowItem>,
|
||||
device_info: Option<Arc<DeviceInfo>>,
|
||||
device_stats: Option<Arc<DeviceStats>>,
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl relm4::SimpleComponent for HardwareInfoSection {
|
||||
type Init = ();
|
||||
type Input = PageUpdate;
|
||||
type Output = ();
|
||||
|
||||
view! {
|
||||
PageSection::new("Hardware Information") {
|
||||
set_margin_start: 15,
|
||||
|
||||
append = &model.values_list.widget().clone() -> gtk::Box {
|
||||
set_spacing: 10,
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_margin_horizontal: 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
_init: Self::Init,
|
||||
root: Self::Root,
|
||||
_sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let model = Self {
|
||||
values_list: FactoryVecDeque::builder().launch_default().detach(),
|
||||
device_info: None,
|
||||
device_stats: None,
|
||||
};
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
|
||||
match msg {
|
||||
PageUpdate::Info(device_info) => {
|
||||
self.device_info = Some(device_info);
|
||||
}
|
||||
PageUpdate::Stats(device_stats) => {
|
||||
self.device_stats = Some(device_stats);
|
||||
}
|
||||
}
|
||||
self.update_items();
|
||||
}
|
||||
}
|
||||
|
||||
impl HardwareInfoSection {
|
||||
fn update_items(&mut self) {
|
||||
self.values_list.guard().clear();
|
||||
|
||||
if let Some(info) = &self.device_info {
|
||||
let mut values_list = self.values_list.guard();
|
||||
for (name, value) in info.info_elements(self.device_stats.as_deref()) {
|
||||
if let Some(value) = value {
|
||||
let note = if name == "Card Model" && !value.starts_with("Unknown ") {
|
||||
Some("The card displayed here may be of a sibling model, e.g. XT vs XTX variety. This is normal, as such models often use the same device ID, and it is not possible to differentiate between them.")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
values_list.push_back(InfoRowItem {
|
||||
name: format!("{name}:"),
|
||||
value,
|
||||
note,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InfoRowItem {
|
||||
name: String,
|
||||
value: String,
|
||||
note: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[relm4::factory]
|
||||
impl relm4::factory::FactoryComponent for InfoRowItem {
|
||||
type Init = Self;
|
||||
type ParentWidget = gtk::Box;
|
||||
type CommandOutput = ();
|
||||
type Input = ();
|
||||
type Output = ();
|
||||
|
||||
fn init_model(
|
||||
init: Self::Init,
|
||||
_index: &Self::Index,
|
||||
_sender: relm4::FactorySender<Self>,
|
||||
) -> Self {
|
||||
init
|
||||
}
|
||||
|
||||
view! {
|
||||
InfoRow {
|
||||
set_selectable: true,
|
||||
set_name: self.name.clone(),
|
||||
set_value: self.value.clone(),
|
||||
set_info_text: self.note.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
mod feature_window;
|
||||
|
||||
use super::values_grid;
|
||||
use crate::app::pages::{label_row, values_row};
|
||||
use feature_window::{VulkanFeature, VulkanFeaturesWindow};
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk::*;
|
||||
use lact_client::schema::VulkanInfo;
|
||||
use relm4::{Component, ComponentController};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use tracing::trace;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VulkanInfoFrame {
|
||||
pub container: Box,
|
||||
device_name_label: Label,
|
||||
version_label: Label,
|
||||
driver_name_label: Label,
|
||||
driver_version_label: Label,
|
||||
features: Rc<RefCell<Vec<VulkanFeature>>>,
|
||||
extensions: Rc<RefCell<Vec<VulkanFeature>>>,
|
||||
}
|
||||
|
||||
impl VulkanInfoFrame {
|
||||
pub fn new() -> Self {
|
||||
let container = Box::new(Orientation::Vertical, 0);
|
||||
|
||||
let features: Rc<RefCell<Vec<VulkanFeature>>> = Rc::default();
|
||||
let extensions: Rc<RefCell<Vec<VulkanFeature>>> = Rc::default();
|
||||
|
||||
let grid = values_grid();
|
||||
grid.set_margin_start(0);
|
||||
grid.set_margin_end(0);
|
||||
|
||||
let device_name_label = label_row("Device name", &grid, 0, 0, true);
|
||||
let version_label = label_row("Vulkan version:", &grid, 1, 0, true);
|
||||
let driver_name_label = label_row("Driver name:", &grid, 2, 0, true);
|
||||
let driver_version_label = label_row("Driver version:", &grid, 3, 0, true);
|
||||
|
||||
let show_features_button = Button::builder().label("Show").halign(Align::End).build();
|
||||
show_features_button.connect_clicked(clone!(
|
||||
#[strong]
|
||||
features,
|
||||
move |_| {
|
||||
show_features_window("Vulkan features", features.clone());
|
||||
}
|
||||
));
|
||||
values_row("Features:", &grid, &show_features_button, 4, 0);
|
||||
|
||||
let show_extensions_button = Button::builder().label("Show").halign(Align::End).build();
|
||||
show_extensions_button.connect_clicked(clone!(
|
||||
#[strong]
|
||||
extensions,
|
||||
move |_| {
|
||||
show_features_window("Vulkan extensions", extensions.clone());
|
||||
}
|
||||
));
|
||||
values_row("Extensions:", &grid, &show_extensions_button, 5, 0);
|
||||
|
||||
container.append(&grid);
|
||||
|
||||
Self {
|
||||
container,
|
||||
device_name_label,
|
||||
version_label,
|
||||
driver_name_label,
|
||||
driver_version_label,
|
||||
features,
|
||||
extensions,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_info(&self, vulkan_info: &VulkanInfo) {
|
||||
trace!("setting vulkan info: {:?}", vulkan_info);
|
||||
|
||||
self.device_name_label
|
||||
.set_markup(&format!("<b>{}</b>", vulkan_info.device_name));
|
||||
self.version_label
|
||||
.set_markup(&format!("<b>{}</b>", vulkan_info.api_version));
|
||||
|
||||
self.driver_name_label.set_markup(&format!(
|
||||
"<b>{}</b>",
|
||||
vulkan_info.driver.name.as_deref().unwrap_or_default(),
|
||||
));
|
||||
|
||||
self.driver_version_label.set_markup(&format!(
|
||||
"<b>{}</b>",
|
||||
vulkan_info.driver.info.as_deref().unwrap_or_default(),
|
||||
));
|
||||
|
||||
let mut features = self.features.borrow_mut();
|
||||
features.clear();
|
||||
|
||||
for (name, supported) in &vulkan_info.features {
|
||||
let feature = VulkanFeature {
|
||||
name: name.to_string(),
|
||||
supported: *supported,
|
||||
};
|
||||
features.push(feature);
|
||||
}
|
||||
|
||||
let mut extensions = self.extensions.borrow_mut();
|
||||
extensions.clear();
|
||||
for (name, supported) in &vulkan_info.extensions {
|
||||
let extension = VulkanFeature {
|
||||
name: name.to_string(),
|
||||
supported: *supported,
|
||||
};
|
||||
extensions.push(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_features_window(title: &str, values: Rc<RefCell<Vec<VulkanFeature>>>) {
|
||||
let features = values.borrow().iter().cloned().collect();
|
||||
|
||||
let mut window_controller = VulkanFeaturesWindow::builder()
|
||||
.launch((features, title.to_owned()))
|
||||
.detach();
|
||||
window_controller.detach_runtime();
|
||||
window_controller.widget().present();
|
||||
}
|
||||
@@ -19,8 +19,6 @@ impl relm4::SimpleComponent for GpuStatsSection {
|
||||
|
||||
view! {
|
||||
PageSection::new("Statistics") {
|
||||
set_spacing: 10,
|
||||
|
||||
append = >k::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_spacing: 5,
|
||||
|
||||
@@ -1,30 +1,190 @@
|
||||
use crate::{app::info_row::InfoRow, GUI_VERSION};
|
||||
use gtk::prelude::*;
|
||||
use lact_client::schema::{SystemInfo, GIT_COMMIT};
|
||||
use relm4::{ComponentParts, ComponentSender, SimpleComponent};
|
||||
use std::{fmt::Write, rc::Rc};
|
||||
mod vulkan;
|
||||
|
||||
pub struct SoftwarePage {}
|
||||
use crate::{
|
||||
app::{format_friendly_size, info_row::InfoRow, page_section::PageSection},
|
||||
GUI_VERSION,
|
||||
};
|
||||
use gtk::prelude::*;
|
||||
use indexmap::IndexMap;
|
||||
use lact_client::schema::{SystemInfo, GIT_COMMIT};
|
||||
use lact_schema::DeviceInfo;
|
||||
use relm4::{Component, ComponentController, ComponentParts, ComponentSender, RelmWidgetExt};
|
||||
use std::{fmt::Write, rc::Rc, sync::Arc};
|
||||
use vulkan::feature_window::{VulkanFeature, VulkanFeaturesWindow};
|
||||
|
||||
pub struct SoftwarePage {
|
||||
device_info: Option<Arc<DeviceInfo>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SoftwarePageMsg {
|
||||
DeviceInfo(Arc<DeviceInfo>),
|
||||
ShowVulkanFeatures,
|
||||
ShowVulkanExtensions,
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for SoftwarePage {
|
||||
impl relm4::SimpleComponent for SoftwarePage {
|
||||
type Init = (Rc<SystemInfo>, bool);
|
||||
type Input = ();
|
||||
type Input = SoftwarePageMsg;
|
||||
type Output = ();
|
||||
|
||||
view! {
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 10,
|
||||
set_margin_start: 5,
|
||||
set_margin_end: 5,
|
||||
set_margin_top: 5,
|
||||
set_margin_bottom: 5,
|
||||
gtk::ScrolledWindow {
|
||||
set_hscrollbar_policy: gtk::PolicyType::Never,
|
||||
|
||||
append = &InfoRow::new_selectable("LACT Daemon:", &daemon_version),
|
||||
append = &InfoRow::new_selectable("LACT GUI:", &gui_version),
|
||||
append = &InfoRow::new_selectable("Kernel Version:", &system_info.kernel_version),
|
||||
}
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 15,
|
||||
set_margin_horizontal: 20,
|
||||
|
||||
PageSection::new("System") {
|
||||
append = &InfoRow::new_selectable("LACT Daemon:", &daemon_version),
|
||||
append = &InfoRow::new_selectable("LACT GUI:", &gui_version),
|
||||
append = &InfoRow::new_selectable("Kernel Version:", &system_info.kernel_version),
|
||||
},
|
||||
|
||||
match model.device_info.as_ref().and_then(|info| info.vulkan_info.as_ref()) {
|
||||
Some(info) => {
|
||||
PageSection::new("Vulkan") {
|
||||
append = &InfoRow {
|
||||
set_name: "Device Name:",
|
||||
#[watch]
|
||||
set_value: info.device_name.as_str(),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "API Version:",
|
||||
#[watch]
|
||||
set_value: info.api_version.as_str(),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "Driver Name:",
|
||||
#[watch]
|
||||
set_value: info.driver.name.as_deref().unwrap_or_default(),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "Driver Version:",
|
||||
#[watch]
|
||||
set_value: info.driver.info.as_deref().unwrap_or_default(),
|
||||
set_selectable: true,
|
||||
},
|
||||
|
||||
append = >k::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_hexpand: true,
|
||||
|
||||
append = >k::Label {
|
||||
set_halign: gtk::Align::Start,
|
||||
set_hexpand: true,
|
||||
set_label: "Features:"
|
||||
},
|
||||
|
||||
append = >k::Button {
|
||||
connect_clicked => SoftwarePageMsg::ShowVulkanFeatures,
|
||||
set_label: "Show",
|
||||
}
|
||||
},
|
||||
|
||||
append = >k::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_hexpand: true,
|
||||
|
||||
append = >k::Label {
|
||||
set_halign: gtk::Align::Start,
|
||||
set_hexpand: true,
|
||||
set_label: "Extensions:"
|
||||
},
|
||||
|
||||
append = >k::Button {
|
||||
connect_clicked => SoftwarePageMsg::ShowVulkanExtensions,
|
||||
set_label: "Show",
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
None => {
|
||||
PageSection::new("Vulkan") {
|
||||
append = >k::Label {
|
||||
set_label: "Vulkan device not found",
|
||||
set_halign: gtk::Align::Start,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
match model.device_info.as_ref().and_then(|info| info.opencl_info.as_ref()) {
|
||||
Some(info) => {
|
||||
PageSection::new("OpenCL") {
|
||||
append = &InfoRow {
|
||||
set_name: "Platform Name:",
|
||||
#[watch]
|
||||
set_value: info.platform_name.as_str(),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "Device Name:",
|
||||
#[watch]
|
||||
set_value: info.device_name.as_str(),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "Version:",
|
||||
#[watch]
|
||||
set_value: info.version.as_str(),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "Driver Version:",
|
||||
#[watch]
|
||||
set_value: info.driver_version.as_str(),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "OpenCL C Version:",
|
||||
#[watch]
|
||||
set_value: info.c_version.as_str(),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "Compute Units:",
|
||||
#[watch]
|
||||
set_value: info.compute_units.to_string(),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "Workgroup Size:",
|
||||
#[watch]
|
||||
set_value: info.workgroup_size.to_string(),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "Global Memory:",
|
||||
#[watch]
|
||||
set_value: format_friendly_size(info.global_memory),
|
||||
set_selectable: true,
|
||||
},
|
||||
append = &InfoRow {
|
||||
set_name: "Local Memory:",
|
||||
#[watch]
|
||||
set_value: format_friendly_size(info.local_memory),
|
||||
set_selectable: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
None => {
|
||||
PageSection::new("OpenCL") {
|
||||
append = >k::Label {
|
||||
set_label: "OpenCL device not found",
|
||||
set_halign: gtk::Align::Start,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
fn init(
|
||||
@@ -32,7 +192,7 @@ impl SimpleComponent for SoftwarePage {
|
||||
root: Self::Root,
|
||||
_sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let model = Self {};
|
||||
let model = Self { device_info: None };
|
||||
|
||||
let mut daemon_version = format!("{}-{}", system_info.version, system_info.profile);
|
||||
if embedded {
|
||||
@@ -53,4 +213,42 @@ impl SimpleComponent for SoftwarePage {
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {
|
||||
match msg {
|
||||
SoftwarePageMsg::DeviceInfo(info) => {
|
||||
self.device_info = Some(info);
|
||||
}
|
||||
SoftwarePageMsg::ShowVulkanFeatures => {
|
||||
if let Some(info) = &self.device_info {
|
||||
if let Some(vulkan_info) = &info.vulkan_info {
|
||||
show_features_window("Vulkan Features", &vulkan_info.features);
|
||||
}
|
||||
}
|
||||
}
|
||||
SoftwarePageMsg::ShowVulkanExtensions => {
|
||||
if let Some(info) = &self.device_info {
|
||||
if let Some(vulkan_info) = &info.vulkan_info {
|
||||
show_features_window("Vulkan Extensions", &vulkan_info.extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_features_window(title: &str, values: &IndexMap<String, bool>) {
|
||||
let values = values
|
||||
.into_iter()
|
||||
.map(|(name, &supported)| VulkanFeature {
|
||||
name: name.clone(),
|
||||
supported,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut window_controller = VulkanFeaturesWindow::builder()
|
||||
.launch((values, title.to_owned()))
|
||||
.detach();
|
||||
window_controller.detach_runtime();
|
||||
window_controller.widget().present();
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod feature_window;
|
||||
@@ -97,6 +97,7 @@ pub struct GpuPciInfo {
|
||||
pub struct DeviceInfo {
|
||||
pub pci_info: Option<GpuPciInfo>,
|
||||
pub vulkan_info: Option<VulkanInfo>,
|
||||
pub opencl_info: Option<OpenCLInfo>,
|
||||
pub driver: String,
|
||||
pub vbios_version: Option<String>,
|
||||
pub link_info: LinkInfo,
|
||||
@@ -411,6 +412,19 @@ pub struct VulkanDriverInfo {
|
||||
pub driver_version: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct OpenCLInfo {
|
||||
pub platform_name: String,
|
||||
pub device_name: String,
|
||||
pub version: String,
|
||||
pub driver_version: String,
|
||||
pub c_version: String,
|
||||
pub compute_units: u32,
|
||||
pub workgroup_size: usize,
|
||||
pub global_memory: u64,
|
||||
pub local_memory: u64,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct PciInfo {
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
FROM quay.io/rockylinux/rockylinux:8
|
||||
RUN dnf install curl --allowerasing -y # Fix for curl-minimal conflict
|
||||
RUN dnf install -y dnf-plugins-core rocky-repos
|
||||
RUN dnf config-manager --enable devel
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
FROM quay.io/rockylinux/rockylinux:9
|
||||
RUN dnf install curl --allowerasing -y # Fix for curl-minimal conflict
|
||||
RUN dnf install -y dnf-plugins-core rocky-repos
|
||||
RUN dnf config-manager --enable devel
|
||||
|
||||
@@ -12,16 +12,16 @@ metadata:
|
||||
conflicts: [ lact, lact-libadwaita ]
|
||||
depends:
|
||||
all: [ hwdata, vulkan-tools ]
|
||||
debian-12+ubuntu-2204+ubuntu-2404: [ libdrm-amdgpu1, libdrm2 ]
|
||||
fedora-40+fedora-41+fedora-42+rhel-8+rhel-9: [ libdrm ]
|
||||
arch: [ libdrm ]
|
||||
opensuse-tumbleweed: [ libdrm ]
|
||||
debian-12+ubuntu-2204+ubuntu-2404: [ libdrm-amdgpu1, libdrm2, ocl-icd-opencl-dev ]
|
||||
fedora-40+fedora-41+fedora-42+rhel-8+rhel-9: [ libdrm, ocl-icd-devel ]
|
||||
arch: [ libdrm, ocl-icd ]
|
||||
opensuse-tumbleweed: [ libdrm, ocl-icd-devel ]
|
||||
build_depends:
|
||||
all: [ curl, make, clang, git, vulkan-tools ]
|
||||
debian-12+ubuntu-2204+ubuntu-2404: [ pkg-config, build-essential, libdrm-dev, dbus ]
|
||||
fedora-40+fedora-41+fedora-42+rhel-8+rhel-9: [ gcc, libdrm-devel, dbus ]
|
||||
arch: [ libdrm, dbus ]
|
||||
opensuse-tumbleweed: [ libdrm-devel ]
|
||||
debian-12+ubuntu-2204+ubuntu-2404: [ pkg-config, build-essential, libdrm-dev, dbus, ocl-icd-opencl-dev ]
|
||||
fedora-40+fedora-41+fedora-42+rhel-8+rhel-9: [ gcc, libdrm-devel, dbus, ocl-icd-devel ]
|
||||
arch: [ libdrm, dbus, ocl-icd ]
|
||||
opensuse-tumbleweed: [ libdrm-devel, ocl-icd-devel ]
|
||||
all_images: true
|
||||
env:
|
||||
RUSTUP_URL: https://sh.rustup.rs
|
||||
@@ -29,7 +29,7 @@ configure:
|
||||
steps:
|
||||
- cmd: curl -o /tmp/install_rust.sh $RUSTUP_URL
|
||||
- cmd: sh /tmp/install_rust.sh -y
|
||||
- cmd: bash -c "source $HOME/.cargo/env && rustup toolchain install 1.82.0"
|
||||
- cmd: bash -c "source $HOME/.cargo/env && rustup toolchain install 1.85.1"
|
||||
- cmd: pacman -Syu --noconfirm
|
||||
pkg: true
|
||||
build:
|
||||
|
||||
@@ -12,14 +12,14 @@ metadata:
|
||||
conflicts: [ lact, lact-headless ]
|
||||
depends:
|
||||
all: [ hwdata, vulkan-tools ]
|
||||
fedora-40+fedora-41+fedora-42: [ gtk4, libdrm, libadwaita ]
|
||||
arch: [ gtk4, libadwaita ]
|
||||
opensuse-tumbleweed: [ libdrm, libadwaita ]
|
||||
fedora-40+fedora-41+fedora-42: [ gtk4, libdrm, libadwaita ocl-icd-devel ]
|
||||
arch: [ gtk4, libadwaita, ocl-icd ]
|
||||
opensuse-tumbleweed: [ libdrm, libadwaita, ocl-icd-devel ]
|
||||
build_depends:
|
||||
all: [ curl, make, clang, git, vulkan-tools ]
|
||||
fedora-40+fedora-41+fedora-42: [ gtk4-devel, gcc, libdrm-devel, libadwaita-devel, dbus ]
|
||||
arch: [ gtk4, libadwaita, dbus ]
|
||||
opensuse-tumbleweed: [ libdrm-devel, libadwaita-devel ]
|
||||
fedora-40+fedora-41+fedora-42: [ gtk4-devel, gcc, libdrm-devel, libadwaita-devel, dbus, ocl-icd-devel ]
|
||||
arch: [ gtk4, libadwaita, dbus, ocl-icd ]
|
||||
opensuse-tumbleweed: [ libdrm-devel, libadwaita-devel, ocl-icd-devel ]
|
||||
all_images: true
|
||||
env:
|
||||
RUSTUP_URL: https://sh.rustup.rs
|
||||
@@ -27,7 +27,7 @@ configure:
|
||||
steps:
|
||||
- cmd: curl -o /tmp/install_rust.sh $RUSTUP_URL
|
||||
- cmd: sh /tmp/install_rust.sh -y
|
||||
- cmd: bash -c "source $HOME/.cargo/env && rustup toolchain install 1.82.0"
|
||||
- cmd: bash -c "source $HOME/.cargo/env && rustup toolchain install 1.85.1"
|
||||
- cmd: pacman -Syu --noconfirm
|
||||
pkg: true
|
||||
build:
|
||||
|
||||
@@ -12,16 +12,16 @@ metadata:
|
||||
conflicts: [ lact-headless, lact-libadwaita ]
|
||||
depends:
|
||||
all: [ hwdata, vulkan-tools ]
|
||||
debian-12+ubuntu-2204+ubuntu-2404: [ libgtk-4-1, libdrm2, libdrm-amdgpu1 ]
|
||||
fedora-40+fedora-41+fedora-42: [ gtk4, libdrm ]
|
||||
arch: [ gtk4 ]
|
||||
opensuse-tumbleweed: [ gtk4 ]
|
||||
debian-12+ubuntu-2204+ubuntu-2404: [ libgtk-4-1, libdrm2, libdrm-amdgpu1, ocl-icd-opencl-dev ]
|
||||
fedora-40+fedora-41+fedora-42: [ gtk4, libdrm, ocl-icd-devel ]
|
||||
arch: [ gtk4, ocl-icd ]
|
||||
opensuse-tumbleweed: [ gtk4, ocl-icd-devel ]
|
||||
build_depends:
|
||||
all: [ curl, make, clang, git, vulkan-tools ]
|
||||
debian-12+ubuntu-2204+ubuntu-2404: [ libgtk-4-dev, pkg-config, build-essential, libdrm-dev, dbus ]
|
||||
fedora-40+fedora-41+fedora-42: [ gtk4-devel, gcc, libdrm-devel, dbus ]
|
||||
arch: [ gtk4, dbus ]
|
||||
opensuse-tumbleweed: [ gtk4-devel, libdrm-devel ]
|
||||
debian-12+ubuntu-2204+ubuntu-2404: [ libgtk-4-dev, pkg-config, build-essential, libdrm-dev, dbus, ocl-icd-opencl-dev ]
|
||||
fedora-40+fedora-41+fedora-42: [ gtk4-devel, gcc, libdrm-devel, dbus, ocl-icd-devel ]
|
||||
arch: [ gtk4, dbus, ocl-icd ]
|
||||
opensuse-tumbleweed: [ gtk4-devel, libdrm-devel, ocl-icd-devel ]
|
||||
all_images: true
|
||||
env:
|
||||
RUSTUP_URL: https://sh.rustup.rs
|
||||
@@ -29,7 +29,7 @@ configure:
|
||||
steps:
|
||||
- cmd: curl -o /tmp/install_rust.sh $RUSTUP_URL
|
||||
- cmd: sh /tmp/install_rust.sh -y
|
||||
- cmd: bash -c "source $HOME/.cargo/env && rustup toolchain install 1.82.0"
|
||||
- cmd: bash -c "source $HOME/.cargo/env && rustup toolchain install 1.85.1"
|
||||
- cmd: pacman -Syu --noconfirm
|
||||
pkg: true
|
||||
build:
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "1.82.0"
|
||||
channel = "1.85.1"
|
||||
|
||||
Reference in New Issue
Block a user