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    /// The adapter comes from the Vulkan, Metal or DX12 backend first, and
27    /// from any backend, OpenGL included, when none of those can present to
28    /// the window. A run with this choice fails when no adapter is found
29    /// either way; it never falls back to the CPU renderer on its own.
30    Gpu,
31    /// The GPU renderer when the window's adapter is a real GPU, and the CPU
32    /// renderer otherwise: when the adapter is a software rasterizer such as
33    /// llvmpipe or SwiftShader, when no Vulkan, Metal or DX12 adapter exists,
34    /// when the window surface cannot present GPU frames, or while the damage
35    /// overlay is on. This choice never uses OpenGL, which serves the GPU
36    /// renderer's compute shaders poorly. The default in builds with the
37    /// `gpu` feature.
38    Auto,
39}
40
41impl Default for RendererChoice {
42    fn default() -> RendererChoice {
43        // The default depends on the feature: `Auto` with the gpu feature and
44        // `Cpu` without it, which a derive cannot switch by feature.
45        if cfg!(feature = "gpu") {
46            RendererChoice::Auto
47        } else {
48            RendererChoice::Cpu
49        }
50    }
51}
52
53impl RendererChoice {
54    /// Returns the choice with the environment override applied.
55    ///
56    /// [`RENDERER_ENVIRONMENT`], when it holds `cpu`, `gpu` or `auto`,
57    /// replaces this choice; any other non-empty value fails the run, and an
58    /// unset or empty variable leaves the choice alone.
59    ///
60    /// # Errors
61    ///
62    /// Returns [`PlatformError::Renderer`] when the environment variable
63    /// holds an unknown value.
64    pub(crate) fn with_env_override(self) -> Result<RendererChoice, PlatformError> {
65        let value = env::var(RENDERER_ENVIRONMENT).ok();
66        match env_choice(value.as_deref()) {
67            Ok(Some(overridden)) => Ok(overridden),
68            Ok(None) => Ok(self),
69            Err(value) => Err(PlatformError::Renderer(format!(
70                "{RENDERER_ENVIRONMENT} is set to {value:?}; expected cpu, gpu or auto"
71            ))),
72        }
73    }
74}
75
76/// Parses one override value, or `None` when the variable is unset or empty.
77fn env_choice(value: Option<&str>) -> Result<Option<RendererChoice>, String> {
78    match value {
79        None | Some("") => Ok(None),
80        Some("cpu") => Ok(Some(RendererChoice::Cpu)),
81        Some("gpu") => Ok(Some(RendererChoice::Gpu)),
82        Some("auto") => Ok(Some(RendererChoice::Auto)),
83        Some(other) => Err(String::from(other)),
84    }
85}
86
87#[cfg(test)]
88#[path = "renderer_tests.rs"]
89mod tests;