Krolyxon
Commit e515e5a

Branches

Tags

Refs →

remagesrcrenderbackend.rs

97 lines · 2.9 KB · Rust

1//! Remembers which wgpu backend won the adapter search, so the next start initialises only that
2//! one. Probing Vulkan and GL together costs tens of milliseconds before the first frame.
3
4use std::path::PathBuf;
5
6/// The backend and adapter of the last full probe.
7#[derive(Clone, Debug, PartialEq)]
8pub struct Choice {
9 pub backend: wgpu::Backend,
10 pub adapter: String,
11}
12
13impl Choice {
14 fn parse(text: &str) -> Option<Self> {
15 let mut lines = text.lines();
16 let backend = match lines.next()? {
17 "vulkan" => wgpu::Backend::Vulkan,
18 "gl" => wgpu::Backend::Gl,
19 _ => return None,
20 };
21 let adapter = lines.next().filter(|a| !a.is_empty())?.to_string();
22 Some(Self { backend, adapter })
23 }
24
25 fn encode(&self) -> Option<String> {
26 let backend = match self.backend {
27 wgpu::Backend::Vulkan => "vulkan",
28 wgpu::Backend::Gl => "gl",
29 _ => return None,
30 };
31 Some(format!("{backend}\n{}\n", self.adapter))
32 }
33}
34
35fn path() -> Option<PathBuf> {
36 Some(dirs::cache_dir()?.join("remage").join("backend"))
37}
38
39/// Whether the environment steers the adapter search; the choice is then neither used nor kept.
40pub fn steered() -> bool {
41 ["WGPU_BACKEND", "WGPU_POWER_PREF", "WGPU_ADAPTER_NAME"]
42 .iter()
43 .any(|v| std::env::var_os(v).is_some())
44}
45
46pub fn load() -> Option<Choice> {
47 if steered() {
48 return None;
49 }
50 Choice::parse(&std::fs::read_to_string(path()?).ok()?)
51}
52
53/// Remember `choice` (or forget the old one, for a backend that can't be asked for alone).
54pub fn store(choice: &Choice) {
55 let Some(path) = path() else { return };
56 let Some(text) = choice.encode() else {
57 let _ = std::fs::remove_file(&path);
58 return;
59 };
60 let write = || -> std::io::Result<()> {
61 if let Some(dir) = path.parent() {
62 std::fs::create_dir_all(dir)?;
63 }
64 let tmp = path.with_extension(format!("{}.tmp", std::process::id()));
65 std::fs::write(&tmp, text)?;
66 std::fs::rename(&tmp, &path)
67 };
68 if let Err(e) = write() {
69 log::debug!("backend cache not written: {e}");
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn round_trip() {
79 let c = Choice {
80 backend: wgpu::Backend::Gl,
81 adapter: "Mesa Intel(R) Graphics (RPL-S)".into(),
82 };
83 assert_eq!(Choice::parse(&c.encode().unwrap()), Some(c));
84 assert_eq!(
85 Choice::parse("vulkan\nNVIDIA\n").map(|c| c.backend),
86 Some(wgpu::Backend::Vulkan)
87 );
88 for bad in ["", "gl", "gl\n", "metal\nApple M1\n", "garbage"] {
89 assert_eq!(Choice::parse(bad), None, "{bad:?}");
90 }
91 let other = Choice {
92 backend: wgpu::Backend::Metal,
93 adapter: "x".into(),
94 };
95 assert_eq!(other.encode(), None);
96 }
97}