mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 02:19:56 -06:00
Fix #4150: Windows doesn't start OpenGL unless a start needs it
wgpu's OpenGL backend on Windows keeps a hidden window on a helper thread that pumps no messages (gfx-rs/wgpu#10504). Windows hands an input-language change to every UI thread of the process and waits for each, so switching the keyboard to another layout hung the whole app, even while it drew with Direct3D 12. OpenGL was started on every Windows start, as the fallback. A Windows start now lists Direct3D 12 alone. OpenGL is still listed where it is the fallback: after a start crashed on an adapter or while listing them (#2243), after a restart that left an adapter out, and when Direct3D 12 lists no adapter at all (Windows 7). WGPU_BACKEND still picks the backends as before.
This commit is contained in:
@@ -44,6 +44,9 @@ clipboard-win = { workspace = true }
|
||||
# GPU that shows it: drawing on a GPU without a monitor reset the display driver. Safe wrappers over
|
||||
# Win32 (MIT); the `user` feature is User32.dll only. Only the wgpu renderer chooses an adapter.
|
||||
winsafe = { version = "0.0.29", default-features = false, features = ["user"], optional = true }
|
||||
# Waits for Direct3D 12's adapter list before the window starts (src/gpu.rs, #4150). MIT OR
|
||||
# Apache-2.0, already in the build through eframe.
|
||||
pollster = "1"
|
||||
|
||||
# DirectWrite's system font collection and GDI's font list (src/system_fonts.rs): fonts that font
|
||||
# services such as Adobe Fonts load in place (#579, #1258). MIT OR Apache-2.0, already in the build
|
||||
|
||||
@@ -126,13 +126,45 @@ pub fn preferred_compositor(pref: Option<&str>, env: Option<PowerPreference>) ->
|
||||
if automatic(pref, env) { crate::compositor_gpu::compositor_gpu() } else { None }
|
||||
}
|
||||
|
||||
/// The graphics backends the window starts with: on Windows Direct3D 12, falling back to OpenGL,
|
||||
/// unless `WGPU_BACKEND` names others (`env_set`; `default` then holds them). Creating a Vulkan
|
||||
/// instance loads every installed Vulkan driver into the process, and a faulty one (Intel's
|
||||
/// `igvk64.dll`, #806) crashed the app before its window appeared. DX12 is Windows' own backend,
|
||||
/// as in PdfCraft. Elsewhere, `default`.
|
||||
pub fn backends(default: Backends, env_set: bool) -> Backends {
|
||||
if cfg!(windows) && !env_set { Backends::DX12 | Backends::GL } else { default }
|
||||
/// The graphics backends the window starts with: on Windows Direct3D 12, with OpenGL beside it
|
||||
/// only when `opengl` ([`windows_opengl`]), unless `WGPU_BACKEND` names others (`env_set`;
|
||||
/// `default` then holds them). Creating a Vulkan instance loads every installed Vulkan driver into
|
||||
/// the process, and a faulty one (Intel's `igvk64.dll`, #806) crashed the app before its window
|
||||
/// appeared. DX12 is Windows' own backend, as in PdfCraft. Elsewhere, `default`.
|
||||
pub fn backends(default: Backends, env_set: bool, opengl: bool) -> Backends {
|
||||
match (cfg!(windows) && !env_set, opengl) {
|
||||
(true, true) => Backends::DX12 | Backends::GL,
|
||||
(true, false) => Backends::DX12,
|
||||
(false, _) => default,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a Windows start lists OpenGL's adapters beside Direct3D 12's: only when it leaves an
|
||||
/// adapter out (an earlier start crashed on it, or a restart failed on it) or DX12 lists none
|
||||
/// (Windows 7). wgpu's OpenGL backend keeps a hidden window on a thread that pumps no messages,
|
||||
/// and Windows waits on it when the input language changes, so the whole app hung on a switch to
|
||||
/// another keyboard layout (#4150, gfx-rs/wgpu#10504), even drawing with DX12.
|
||||
pub fn windows_opengl(left_out: &LeftOut) -> bool {
|
||||
left_out.opengl_only || !left_out.adapters.is_empty() || !skipped().is_empty() || !dx12_lists_adapters()
|
||||
}
|
||||
|
||||
/// Whether Direct3D 12 lists any adapter, WARP's software one included. A driver that crashes here
|
||||
/// crashes a start while it lists the adapters, which the next start answers with OpenGL alone.
|
||||
#[cfg(windows)]
|
||||
fn dx12_lists_adapters() -> bool {
|
||||
let mut descriptor = wgpu::InstanceDescriptor::new_without_display_handle();
|
||||
descriptor.backends = Backends::DX12;
|
||||
// FXC is part of Windows, as for the window's own instance (#1194).
|
||||
descriptor.backend_options.dx12.shader_compiler = wgpu::Dx12Compiler::Fxc;
|
||||
let instance = wgpu::Instance::new(descriptor);
|
||||
let found = pollster::block_on(instance.enumerate_adapters(Backends::DX12)).len();
|
||||
log::info!("Direct3D 12 lists {found} graphics adapters");
|
||||
found > 0
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn dx12_lists_adapters() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// The largest texture side to ask the device for, which bounds the window's surface: the
|
||||
|
||||
@@ -451,13 +451,25 @@ fn a_windows_gpu_renders_through_dx12_before_vulkan() {
|
||||
#[test]
|
||||
fn windows_never_loads_vulkan_drivers_unless_asked() {
|
||||
let all = Backends::all();
|
||||
let start = backends(all, false);
|
||||
let start = backends(all, false, false);
|
||||
let fallback = backends(all, false, true);
|
||||
if cfg!(windows) {
|
||||
assert_eq!(start, Backends::DX12 | Backends::GL, "#806");
|
||||
assert_eq!(start, Backends::DX12, "#806, and no OpenGL unless needed (#4150)");
|
||||
assert_eq!(fallback, Backends::DX12 | Backends::GL);
|
||||
} else {
|
||||
assert_eq!(start, all);
|
||||
assert_eq!((start, fallback), (all, all));
|
||||
}
|
||||
assert_eq!(backends(Backends::VULKAN, true), Backends::VULKAN, "WGPU_BACKEND=vulkan still picks it");
|
||||
assert_eq!(backends(Backends::VULKAN, true, false), Backends::VULKAN, "WGPU_BACKEND=vulkan still picks it");
|
||||
assert_eq!(backends(Backends::GL, true, false), Backends::GL, "WGPU_BACKEND=gl still picks it");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn windows_lists_opengl_once_a_start_leaves_an_adapter_out() {
|
||||
let crashed = LeftOut { adapters: vec!["Dx12:8086:3ea0".into()], ..LeftOut::default() };
|
||||
assert!(windows_opengl(&crashed), "#2243: the next start can fall back to OpenGL");
|
||||
let listing = LeftOut { opengl_only: true, ..LeftOut::default() };
|
||||
assert!(windows_opengl(&listing));
|
||||
assert_eq!(listing.backends(backends(Backends::all(), false, true)), Backends::GL);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -313,8 +313,9 @@ fn main() -> std::process::ExitCode {
|
||||
// Metal on an Iris Pro, #651). `WGPU_VALIDATION_INDIRECT_CALL=1` turns it back on.
|
||||
create.instance_descriptor.flags =
|
||||
(eframe::wgpu::InstanceFlags::from_build_config() - eframe::wgpu::InstanceFlags::VALIDATION_INDIRECT_CALL).with_env();
|
||||
create.instance_descriptor.backends =
|
||||
left_out.backends(gpu::backends(create.instance_descriptor.backends, std::env::var_os("WGPU_BACKEND").is_some()));
|
||||
let env_set = std::env::var_os("WGPU_BACKEND").is_some();
|
||||
let opengl = cfg!(windows) && !env_set && gpu::windows_opengl(&left_out);
|
||||
create.instance_descriptor.backends = left_out.backends(gpu::backends(create.instance_descriptor.backends, env_set, opengl));
|
||||
// DX12 shaders compile with FXC, part of Windows: wgpu's default loads whichever
|
||||
// dxcompiler.dll the search path finds, and one without its dxil.dll fails every shader,
|
||||
// losing the device before the first frame (#1194). `WGPU_DX12_COMPILER` picks another.
|
||||
|
||||
Reference in New Issue
Block a user