use std::fmt;
use serde_json::Value;
use crate::gpu::{RenderingEngine, get_backend_status};
use crate::native::error::NativeError;
use crate::native::pixels::SurfaceOptions;
use crate::native::surface::NativeSurface;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum RenderEngine {
#[default]
Auto,
Cpu,
Gpu,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EngineKind {
Cpu,
Gpu,
}
impl fmt::Display for EngineKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Cpu => "cpu",
Self::Gpu => "gpu",
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NativeEngineStatus {
pub renderer: EngineKind,
pub api: Option<String>,
pub device: String,
pub driver: Option<String>,
pub threads: usize,
pub is_gpu_available: bool,
pub error: Option<String>,
}
#[derive(Debug, Default)]
pub struct NativeBackend {
_private: (),
}
impl NativeBackend {
pub fn new() -> Self {
Self { _private: () }
}
pub fn create_surface(
&self,
width: u32,
height: u32,
options: SurfaceOptions,
) -> Result<NativeSurface, NativeError> {
NativeSurface::new(width, height, options)
}
pub fn engine_status(&self, engine: RenderEngine) -> NativeEngineStatus {
engine_status(engine)
}
}
pub(crate) fn resolve_engine(engine: RenderEngine) -> Result<RenderingEngine, NativeError> {
match engine {
RenderEngine::Auto => Ok(RenderingEngine::default()),
RenderEngine::Cpu => Ok(RenderingEngine::CPU),
RenderEngine::Gpu => {
if RenderingEngine::GPU.selectable() {
Ok(RenderingEngine::GPU)
} else {
Err(NativeError::EngineUnavailable {
engine,
reason: RenderingEngine::GPU
.lacks_gpu_support()
.unwrap_or_else(|| "GPU backend not selectable".to_string()),
})
}
}
}
}
pub(crate) fn engine_kind_from(engine: RenderingEngine) -> EngineKind {
match engine {
RenderingEngine::CPU => EngineKind::Cpu,
RenderingEngine::GPU => EngineKind::Gpu,
}
}
fn engine_status(engine: RenderEngine) -> NativeEngineStatus {
let raw = get_backend_status();
let is_gpu_available = raw
.get("gpuAvailable")
.and_then(Value::as_bool)
.unwrap_or(false);
let renderer = match (engine, is_gpu_available) {
(RenderEngine::Cpu, _) | (RenderEngine::Auto, false) | (RenderEngine::Gpu, false) => {
EngineKind::Cpu
}
(RenderEngine::Auto, true) | (RenderEngine::Gpu, true) => EngineKind::Gpu,
};
let device = match (renderer, is_gpu_available) {
(EngineKind::Cpu, true) => {
"CPU-based renderer (manually selected)".to_string()
}
_ => raw
.get("device")
.and_then(Value::as_str)
.unwrap_or("unknown device")
.to_string(),
};
let api = if matches!(renderer, EngineKind::Gpu) {
raw.get("api").and_then(Value::as_str).map(str::to_owned)
} else {
None
};
let driver = raw
.get("driver")
.and_then(Value::as_str)
.filter(|s| !s.is_empty() && *s != "N/A")
.map(str::to_owned);
let threads = raw
.get("threads")
.and_then(Value::as_u64)
.map(|n| n as usize)
.unwrap_or_else(rayon::current_num_threads);
let error = raw.get("error").and_then(Value::as_str).map(str::to_owned);
NativeEngineStatus {
renderer,
api,
device,
driver,
threads,
is_gpu_available,
error,
}
}