Add a renderer escape hatch (wgpu default, glow fallback)

The desktop forces the wgpu/Vulkan renderer because glow/OpenGL crashes
on NVIDIA + Wayland maximize. But on some systems wgpu creates the window
and never presents a frame, so nothing appears. Keep wgpu the default but
compile glow back in and let it be selected via `--renderer glow` or
`YT_OFFLINE_RENDERER=glow` for those machines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Luna 2026-06-08 00:04:24 -07:00
parent 2692fa2c9e
commit 9ba375f7b1
4 changed files with 166 additions and 8 deletions

70
Cargo.lock generated
View file

@ -744,6 +744,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "cgl"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "chacha20" name = "chacha20"
version = "0.10.0" version = "0.10.0"
@ -1048,6 +1057,9 @@ dependencies = [
"egui-wgpu", "egui-wgpu",
"egui-winit", "egui-winit",
"egui_glow", "egui_glow",
"glow 0.14.2",
"glutin",
"glutin-winit",
"image", "image",
"js-sys", "js-sys",
"log", "log",
@ -1489,6 +1501,63 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "glutin"
version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12124de845cacfebedff80e877bb37b5b75c34c5a4c89e47e1cdd67fb6041325"
dependencies = [
"bitflags 2.11.1",
"cfg_aliases 0.2.1",
"cgl",
"dispatch2",
"glutin_egl_sys",
"glutin_glx_sys",
"glutin_wgl_sys",
"libloading",
"objc2 0.6.4",
"objc2-app-kit 0.3.2",
"objc2-core-foundation",
"objc2-foundation 0.3.2",
"once_cell",
"raw-window-handle",
"wayland-sys",
"windows-sys 0.52.0",
"x11-dl",
]
[[package]]
name = "glutin-winit"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f"
dependencies = [
"cfg_aliases 0.2.1",
"glutin",
"raw-window-handle",
"winit",
]
[[package]]
name = "glutin_egl_sys"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c4680ba6195f424febdc3ba46e7a42a0e58743f2edb115297b86d7f8ecc02d2"
dependencies = [
"gl_generator",
"windows-sys 0.52.0",
]
[[package]]
name = "glutin_glx_sys"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7bb2938045a88b612499fbcba375a77198e01306f52272e692f8c1f3751185"
dependencies = [
"gl_generator",
"x11-dl",
]
[[package]] [[package]]
name = "glutin_wgl_sys" name = "glutin_wgl_sys"
version = "0.6.1" version = "0.6.1"
@ -2366,6 +2435,7 @@ dependencies = [
"bitflags 2.11.1", "bitflags 2.11.1",
"block2 0.6.2", "block2 0.6.2",
"objc2 0.6.4", "objc2 0.6.4",
"objc2-core-foundation",
"objc2-core-graphics", "objc2-core-graphics",
"objc2-foundation 0.3.2", "objc2-foundation 0.3.2",
] ]

View file

@ -10,12 +10,13 @@ authors = ["InannaBeloved <anassaeneroi@pm.me>"]
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
# Use the wgpu (Vulkan/Metal/DX) renderer instead of the default glow # Default to the wgpu (Vulkan/Metal/DX) renderer instead of the default glow
# (OpenGL) one. NVIDIA + Wayland crashes the GL surface on window resize # (OpenGL) one. NVIDIA + Wayland crashes the GL surface on window resize
# (Glutin EGL_BAD_ALLOC / OutOfMemory on maximize); wgpu's Vulkan path # (Glutin EGL_BAD_ALLOC / OutOfMemory on maximize); wgpu's Vulkan path
# reconfigures the swapchain cleanly. We drop the `glow` default feature # reconfigures the swapchain cleanly. Keep glow compiled in as an escape
# and keep the rest (accesskit, fonts, wayland, x11). # hatch for machines where wgpu/Vulkan creates a window but never presents
eframe = { version = "0.29", default-features = false, features = ["accesskit", "default_fonts", "wayland", "x11", "wgpu"] } # a frame (`YT_OFFLINE_RENDERER=glow` or `--renderer glow`).
eframe = { version = "0.29", default-features = false, features = ["accesskit", "default_fonts", "wayland", "x11", "wgpu", "glow"] }
image = { version = "0.25", default-features = false, features = ["webp", "jpeg", "png"] } image = { version = "0.25", default-features = false, features = ["webp", "jpeg", "png"] }
toml = "0.8" toml = "0.8"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }

View file

@ -96,6 +96,27 @@ handles the resize cleanly. Make sure you have a working Vulkan driver
(`vulkan-icd-loader` + your GPU's Vulkan package), which any desktop with (`vulkan-icd-loader` + your GPU's Vulkan package), which any desktop with
working graphics already has. working graphics already has.
## The desktop window opens but stays blank
The desktop defaults to the wgpu/Vulkan renderer. On some systems with a
broken Vulkan stack, the OS can create the window while wgpu never
presents a frame. Try the OpenGL renderer:
```bash
YT_OFFLINE_RENDERER=glow yt-offline
```
or:
```bash
yt-offline --renderer glow
```
If that works, update/reinstall your Vulkan driver later and switch back
to the default renderer. On NVIDIA + Wayland, prefer the default wgpu
renderer when possible because OpenGL has historically crashed on window
maximize.
## The web UI looks like an old version after an upgrade ## The web UI looks like an old version after an upgrade
The SPA is served `Cache-Control: no-store`, so a hard reload The SPA is served `Cache-Control: no-store`, so a hard reload

View file

@ -36,6 +36,33 @@ mod vtt;
mod web; mod web;
mod ytdlp_bin; mod ytdlp_bin;
fn renderer_from_name(requested: Option<&str>) -> eframe::Renderer {
match requested.map(|s| s.trim().to_ascii_lowercase()).as_deref() {
Some("glow" | "opengl" | "gl") => eframe::Renderer::Glow,
Some("wgpu" | "vulkan" | "vk") | None => eframe::Renderer::Wgpu,
Some(name) => {
eprintln!(
"Warning: unknown renderer '{name}' (expected 'wgpu' or 'glow'); using wgpu."
);
eframe::Renderer::Wgpu
}
}
}
fn renderer_from_args(args: &[String]) -> eframe::Renderer {
let requested = args
.iter()
.position(|a| a == "--renderer")
.and_then(|pos| args.get(pos + 1).map(String::as_str));
if requested.is_some() {
renderer_from_name(requested)
} else {
let env_renderer = std::env::var("YT_OFFLINE_RENDERER").ok();
renderer_from_name(env_renderer.as_deref())
}
}
fn main() -> eframe::Result<()> { fn main() -> eframe::Result<()> {
let args: Vec<String> = std::env::args().collect(); let args: Vec<String> = std::env::args().collect();
@ -82,10 +109,11 @@ fn main() -> eframe::Result<()> {
.with_inner_size([1280.0, 820.0]) .with_inner_size([1280.0, 820.0])
.with_min_inner_size([800.0, 500.0]) .with_min_inner_size([800.0, 500.0])
.with_title("yt-offline"), .with_title("yt-offline"),
// Force the wgpu (Vulkan) renderer. The default glow/OpenGL path // Default to wgpu (Vulkan): the glow/OpenGL path crashes on some
// crashes on NVIDIA + Wayland when the window is maximized // NVIDIA + Wayland maximizes (Glutin EGL_BAD_ALLOC). Keep a launch
// (Glutin EGL_BAD_ALLOC). wgpu reconfigures the swapchain cleanly. // escape hatch for systems where Vulkan/wgpu opens a blank window:
renderer: eframe::Renderer::Wgpu, // `YT_OFFLINE_RENDERER=glow yt-offline` or `yt-offline --renderer glow`.
renderer: renderer_from_args(&args),
..Default::default() ..Default::default()
}; };
eframe::run_native( eframe::run_native(
@ -94,3 +122,41 @@ fn main() -> eframe::Result<()> {
Box::new(|cc| Ok(Box::new(app::App::new(cc, tray)))), Box::new(|cc| Ok(Box::new(app::App::new(cc, tray)))),
) )
} }
#[cfg(test)]
mod tests {
#[test]
fn renderer_name_accepts_default_and_aliases() {
assert_eq!(super::renderer_from_name(None), eframe::Renderer::Wgpu);
assert_eq!(
super::renderer_from_name(Some("wgpu")),
eframe::Renderer::Wgpu
);
assert_eq!(
super::renderer_from_name(Some("vulkan")),
eframe::Renderer::Wgpu
);
assert_eq!(
super::renderer_from_name(Some("glow")),
eframe::Renderer::Glow
);
assert_eq!(
super::renderer_from_name(Some("opengl")),
eframe::Renderer::Glow
);
assert_eq!(
super::renderer_from_name(Some("unknown")),
eframe::Renderer::Wgpu
);
}
#[test]
fn renderer_arg_overrides_env_parser_path() {
let args = vec![
"yt-offline".to_string(),
"--renderer".to_string(),
"gl".to_string(),
];
assert_eq!(super::renderer_from_args(&args), eframe::Renderer::Glow);
}
}