wallr-core 0.8.0

Core library for wallr - wallpaper engine, GPU transitions, theme provider dispatch, and video playback.
Documentation
use crate::video::error::{VideoError, VideoResult};
use std::fmt;

pub use wallr_common::types::GpuSelection;

#[derive(Debug, Clone)]
pub struct AdapterInfo {
    pub name: String,
    pub backend: wgpu::Backend,
    pub device_type: wgpu::DeviceType,
    pub driver: String,
    pub driver_info: String,
}

impl AdapterInfo {
    pub fn is_integrated(&self) -> bool {
        self.device_type == wgpu::DeviceType::IntegratedGpu
    }

    pub fn is_discrete(&self) -> bool {
        self.device_type == wgpu::DeviceType::DiscreteGpu
    }
}

impl fmt::Display for AdapterInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{} ({:?}, {:?}) - {}",
            self.name, self.device_type, self.backend, self.driver
        )
    }
}

pub async fn detect_adapters(instance: &wgpu::Instance) -> Vec<AdapterInfo> {
    let mut adapters = Vec::new();
    for adapter in instance.enumerate_adapters(wgpu::Backends::all()).await {
        let info = adapter.get_info();
        adapters.push(AdapterInfo {
            name: info.name.clone(),
            backend: info.backend,
            device_type: info.device_type,
            driver: info.driver.clone(),
            driver_info: info.driver_info.clone(),
        });
    }
    tracing::info!("Detected {} GPU adapter(s)", adapters.len());
    for (i, a) in adapters.iter().enumerate() {
        tracing::info!("  [{}] {}", i, a);
    }
    adapters
}

pub async fn select_adapter(
    instance: &wgpu::Instance,
    preference: &GpuSelection,
) -> VideoResult<wgpu::Adapter> {
    let adapters: Vec<_> = instance
        .enumerate_adapters(wgpu::Backends::all())
        .await
        .into_iter()
        .map(|adapter| {
            let info = adapter.get_info();
            (
                adapter,
                AdapterInfo {
                    name: info.name,
                    backend: info.backend,
                    device_type: info.device_type,
                    driver: info.driver,
                    driver_info: info.driver_info,
                },
            )
        })
        .collect();
    if adapters.is_empty() {
        return Err(VideoError::AdapterNotFound(
            "No GPU adapters detected".to_string(),
        ));
    }

    let selected = match preference {
        GpuSelection::Auto => adapters
            .iter()
            .find(|(_, info)| info.is_integrated())
            .or_else(|| adapters.first()),
        GpuSelection::Integrated => adapters.iter().find(|(_, info)| info.is_integrated()),
        GpuSelection::Discrete => adapters.iter().find(|(_, info)| info.is_discrete()),
        GpuSelection::Named(name) => {
            let requested = name.to_ascii_lowercase();
            adapters
                .iter()
                .find(|(_, info)| info.name.to_ascii_lowercase().contains(&requested))
        }
    };

    let selected = selected.ok_or_else(|| {
        VideoError::AdapterNotFound(format!("No adapter matching preference: {}", preference))
    })?;

    tracing::info!("Selected GPU adapter: {}", selected.1);
    Ok(selected.0.clone())
}

pub fn adapter_diagnostics(adapter: &wgpu::Adapter) -> String {
    let info = adapter.get_info();
    format!(
        "GPU: {} ({:?})\nBackend: {:?}\nDriver: {}\nDriver Info: {}",
        info.name, info.device_type, info.backend, info.driver, info.driver_info
    )
}