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;