Skip to main content

smudge_platform/
renderer.rs

1//! The renderer a window presents with, and the environment override on it.
2
3use std::env;
4
5use crate::error::PlatformError;
6
7/// The environment variable that overrides the renderer choice.
8///
9/// Its value is `cpu`, `gpu` or `auto`. Setting it lets a machine switch
10/// renderer without a rebuild, which is how a check of the automatic choice
11/// runs on a machine with only a software adapter.
12pub const RENDERER_ENVIRONMENT: &str = "SMUDGE_RENDERER";
13
14/// Which renderer a window presents with.
15///
16/// The CPU renderer keeps a pixmap and repaints only the frame's damage. The
17/// GPU renderer draws the whole frame on every present, with no damage
18/// tracking. Both draw the same display list.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20pub enum RendererChoice {
21    /// The CPU renderer. Always available.
22    Cpu,
23    /// The GPU renderer through vello.
24    ///
25    /// Needs the `gpu` feature and an adapter that can present to the window.
26    /// A run with this choice fails when either is missing; it never falls
27    /// back on its own.
28    Gpu,
29    /// The GPU renderer when the window's adapter is a real GPU, and the CPU
30    /// renderer otherwise: when the adapter is a software rasterizer such as
31    /// llvmpipe or SwiftShader, when no adapter exists, when the window
32    /// surface cannot present GPU frames, or while the damage overlay is on.
33    /// The default in builds with the `gpu` feature.
34    Auto,
35}
36
37impl Default for RendererChoice {
38    fn default() -> RendererChoice {
39        // The default depends on the feature: `Auto` with the gpu feature and
40        // `Cpu` without it, which a derive cannot switch by feature.
41        if cfg!(feature = "gpu") {
42            RendererChoice::Auto
43        } else {
44            RendererChoice::Cpu
45        }
46    }
47}
48
49impl RendererChoice {
50    /// Returns the choice with the environment override applied.
51    ///
52    /// [`RENDERER_ENVIRONMENT`], when it holds `cpu`, `gpu` or `auto`,
53    /// replaces this choice; any other non-empty value fails the run, and an
54    /// unset or empty variable leaves the choice alone.
55    ///
56    /// # Errors
57    ///
58    /// Returns [`PlatformError::Renderer`] when the environment variable
59    /// holds an unknown value.
60    pub(crate) fn with_env_override(self) -> Result<RendererChoice, PlatformError> {
61        let value = env::var(RENDERER_ENVIRONMENT).ok();
62        match env_choice(value.as_deref()) {
63            Ok(Some(overridden)) => Ok(overridden),
64            Ok(None) => Ok(self),
65            Err(value) => Err(PlatformError::Renderer(format!(
66                "{RENDERER_ENVIRONMENT} is set to {value:?}; expected cpu, gpu or auto"
67            ))),
68        }
69    }
70}
71
72/// Parses one override value, or `None` when the variable is unset or empty.
73fn env_choice(value: Option<&str>) -> Result<Option<RendererChoice>, String> {
74    match value {
75        None | Some("") => Ok(None),
76        Some("cpu") => Ok(Some(RendererChoice::Cpu)),
77        Some("gpu") => Ok(Some(RendererChoice::Gpu)),
78        Some("auto") => Ok(Some(RendererChoice::Auto)),
79        Some(other) => Err(String::from(other)),
80    }
81}
82
83#[cfg(test)]
84#[path = "renderer_tests.rs"]
85mod tests;