mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
App: a second copy opening, or OpenGL that fails, no longer strands Window Graphics
Two copies opened close together made the second take the first's launch-pending marker for a crash and switch Window Graphics to OpenGL. Where OpenGL can't open a window (NVIDIA on X11: "gl not compatible with provided surface"), every later launch then failed, with no way to reach the setting. The marker is now locked while its launch runs, so a locked marker is a launch still opening, not a crash. And a window that fails to open on OpenGL puts Window Graphics back to Automatic. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
fbc4080f53
commit
384c8cc094
2 files changed
+53
-8
No files matched your search
@@ -5,8 +5,11 @@
|
||||
//! can't be caught, so each launch leaves a marker in the settings folder until its window has
|
||||
//! drawn a frame. A launch that finds the previous one's marker switches Window Graphics to
|
||||
//! OpenGL, which those drivers run, and says so once the window is up. `WGPU_BACKEND` (wgpu's
|
||||
//! own override) still wins over both.
|
||||
//! own override) still wins over both. The marker is locked while its launch runs, so a second
|
||||
//! copy opened meanwhile isn't taken for a crash; and a window OpenGL can't open either puts
|
||||
//! Window Graphics back to Automatic ([`opengl_failed`]) rather than failing every launch.
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use effectcraft_engine::prefs::{PREFS_FILE, Prefs};
|
||||
@@ -23,6 +26,8 @@ pub struct Launch {
|
||||
/// Switched to OpenGL because the previous launch never drew: the message to show.
|
||||
pub notice: Option<String>,
|
||||
marker: Option<PathBuf>,
|
||||
/// Holds the marker's lock until the window draws (or the process ends).
|
||||
lock: Option<File>,
|
||||
}
|
||||
|
||||
impl Launch {
|
||||
@@ -34,8 +39,10 @@ impl Launch {
|
||||
let prefs_path = dir.join(PREFS_FILE);
|
||||
let mut prefs = std::fs::read_to_string(&prefs_path).map(|t| Prefs::from_json(&t)).unwrap_or_default();
|
||||
let marker = dir.join(MARKER);
|
||||
// Locked by another launch that is still opening its window: not a crash.
|
||||
let held = File::open(&marker).is_ok_and(|f| f.try_lock().is_err());
|
||||
let mut notice = None;
|
||||
if marker.exists() && prefs.startup.window_graphics == "auto" && cfg!(not(target_os = "macos")) {
|
||||
if marker.exists() && !held && prefs.startup.window_graphics == "auto" && cfg!(not(target_os = "macos")) {
|
||||
prefs.startup.window_graphics = "gl".into();
|
||||
match std::fs::create_dir_all(dir).and_then(|()| std::fs::write(&prefs_path, prefs.to_json())) {
|
||||
Ok(()) => log::warn!("the last launch never drew its window: switching Window Graphics to OpenGL"),
|
||||
@@ -49,18 +56,20 @@ impl Launch {
|
||||
);
|
||||
}
|
||||
let backends = (prefs.startup.window_graphics == "gl").then_some(Backends::GL);
|
||||
let marker = match std::fs::create_dir_all(dir).and_then(|()| std::fs::write(&marker, "")) {
|
||||
Ok(()) => Some(marker),
|
||||
let (marker, lock) = match std::fs::create_dir_all(dir).and_then(|()| File::create(&marker)) {
|
||||
// Where locks aren't available the guard works as before.
|
||||
Ok(f) => (Some(marker), f.try_lock().is_ok().then_some(f)),
|
||||
Err(e) => {
|
||||
log::warn!("launch marker: {e}");
|
||||
None
|
||||
(None, None)
|
||||
}
|
||||
};
|
||||
Launch { backends, notice, marker }
|
||||
Launch { backends, notice, marker, lock }
|
||||
}
|
||||
|
||||
/// The window drew its first frame: the launch made it.
|
||||
pub fn drawn(&mut self) {
|
||||
self.lock = None;
|
||||
if let Some(m) = self.marker.take()
|
||||
&& let Err(e) = std::fs::remove_file(&m)
|
||||
{
|
||||
@@ -69,6 +78,18 @@ impl Launch {
|
||||
}
|
||||
}
|
||||
|
||||
/// The window couldn't open with OpenGL (an error, not a crash): back to Automatic, and no marker.
|
||||
pub fn opengl_failed(dir: Option<&Path>) {
|
||||
let Some(dir) = dir else { return };
|
||||
let prefs_path = dir.join(PREFS_FILE);
|
||||
let mut prefs = std::fs::read_to_string(&prefs_path).map(|t| Prefs::from_json(&t)).unwrap_or_default();
|
||||
prefs.startup.window_graphics = "auto".into();
|
||||
if let Err(e) = std::fs::write(&prefs_path, prefs.to_json()) {
|
||||
log::warn!("saving Window Graphics failed: {e}");
|
||||
}
|
||||
let _ = std::fs::remove_file(dir.join(MARKER));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -89,7 +110,7 @@ mod tests {
|
||||
l.drawn();
|
||||
assert!(!dir.join(MARKER).exists());
|
||||
// A launch that crashes before drawing (it never calls `drawn`)…
|
||||
let _crashed = Launch::begin(Some(&dir), false);
|
||||
drop(Launch::begin(Some(&dir), false));
|
||||
// …makes the next one use OpenGL and say so, once.
|
||||
let mut l = Launch::begin(Some(&dir), false);
|
||||
if cfg!(target_os = "macos") {
|
||||
@@ -108,4 +129,23 @@ mod tests {
|
||||
assert!(!dir.join(MARKER).exists());
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// A second copy opened while the first is still opening isn't a crash; OpenGL that can't
|
||||
/// open a window either goes back to Automatic instead of failing every launch.
|
||||
#[test]
|
||||
fn a_second_copy_or_failed_opengl_doesnt_strand_window_graphics() {
|
||||
let dir = std::env::temp_dir().join(format!("ec-launch-guard-2-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
let _first = Launch::begin(Some(&dir), false);
|
||||
assert_eq!(Launch::begin(Some(&dir), false).backends, None);
|
||||
assert_eq!(window_graphics(&dir), "auto");
|
||||
drop(_first);
|
||||
if cfg!(not(target_os = "macos")) {
|
||||
assert_eq!(Launch::begin(Some(&dir), false).backends, Some(Backends::GL), "a real crash still switches");
|
||||
opengl_failed(Some(&dir));
|
||||
assert_eq!(window_graphics(&dir), "auto");
|
||||
assert_eq!(Launch::begin(Some(&dir), false).backends, None);
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,12 @@ fn main() -> eframe::Result {
|
||||
);
|
||||
match &result {
|
||||
Ok(()) => log::info!("the window closed"),
|
||||
Err(e) => startup_failed(e, !on_gl && cfg!(not(target_os = "macos"))),
|
||||
Err(e) => {
|
||||
if on_gl {
|
||||
launch_guard::opengl_failed(effectcraft_host::config_dir().as_deref());
|
||||
}
|
||||
startup_failed(e, !on_gl && cfg!(not(target_os = "macos")));
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user