diff --git a/crates/gpu/examples/bench_stack_reuse.rs b/crates/gpu/examples/bench_stack_reuse.rs index 30745c3794..2014cee902 100644 --- a/crates/gpu/examples/bench_stack_reuse.rs +++ b/crates/gpu/examples/bench_stack_reuse.rs @@ -17,7 +17,8 @@ fn render(comp: &mut Compositor, device: &wgpu::Device, queue: &wgpu::Queue, doc fn main() -> Result<(), Box> { let quick = std::env::args().any(|a| a == "--quick"); let size = if quick { Size::new(1024, 768) } else { Size::new(6000, 4000) }; - let adapter = pollster::block_on(wgpu::Instance::default().request_adapter(&Default::default()))?; + let adapter = + pollster::block_on(wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()).request_adapter(&Default::default()))?; let (device, queue) = pollster::block_on(adapter.request_device(&Default::default()))?; let format = Compositor::preferred_acc_format(&adapter); println!("adapter={:?}, accumulation={format:?}, size={}x{}", adapter.get_info(), size.width, size.height); diff --git a/crates/gpu/tests/it/device_loss.rs b/crates/gpu/tests/it/device_loss.rs index 3875bd4a89..2e8839de12 100644 --- a/crates/gpu/tests/it/device_loss.rs +++ b/crates/gpu/tests/it/device_loss.rs @@ -12,7 +12,7 @@ use photocraft_raster::Surface; use crate::GPU_LOCK; fn device() -> Option<(wgpu::Adapter, wgpu::Device, wgpu::Queue)> { - let instance = wgpu::Instance::default(); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()); let adapter = match pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default())) { Ok(a) => a, Err(e) => { diff --git a/crates/gpu/tests/it/parity.rs b/crates/gpu/tests/it/parity.rs index 901a21365b..2667ad3a45 100644 --- a/crates/gpu/tests/it/parity.rs +++ b/crates/gpu/tests/it/parity.rs @@ -136,7 +136,10 @@ fn gpu() -> Option { /// The returned guard holds [`GPU_LOCK`] until the device is dropped. fn any_device() -> Option<(wgpu::Adapter, wgpu::Device, wgpu::Queue, std::sync::MutexGuard<'static, ()>)> { let lock = GPU_LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner); - let adapter = pollster::block_on(wgpu::Instance::default().request_adapter(&wgpu::RequestAdapterOptions::default())).ok()?; + let adapter = pollster::block_on( + wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()).request_adapter(&wgpu::RequestAdapterOptions::default()), + ) + .ok()?; let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor::default())).ok()?; Some((adapter, device, queue, lock)) } diff --git a/crates/gpu/tests/it/timing.rs b/crates/gpu/tests/it/timing.rs index 76348695c5..68ebc9d0be 100644 --- a/crates/gpu/tests/it/timing.rs +++ b/crates/gpu/tests/it/timing.rs @@ -38,7 +38,7 @@ fn block_on(f: F) -> F::Output { /// A real GPU with timestamp queries, or `None` with the reason printed. fn gpu() -> Option<(wgpu::Device, wgpu::Queue, Compositor)> { let options = wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, ..Default::default() }; - let Ok(adapter) = block_on(wgpu::Instance::default().request_adapter(&options)) else { + let Ok(adapter) = block_on(wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()).request_adapter(&options)) else { eprintln!("SKIP gpu timing: no adapter"); return None; }; diff --git a/crates/gpu/tests/it/uniform_limits.rs b/crates/gpu/tests/it/uniform_limits.rs index 90dfa57aed..e562a8ca9d 100644 --- a/crates/gpu/tests/it/uniform_limits.rs +++ b/crates/gpu/tests/it/uniform_limits.rs @@ -20,7 +20,7 @@ fn fills_size(count: usize, size: Size) -> Document { } fn constrained_device() -> Option<(wgpu::Adapter, wgpu::Device, wgpu::Queue)> { - let instance = wgpu::Instance::default(); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()); let adapter = match pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default())) { Ok(adapter) => adapter, Err(error) => { diff --git a/crates/gpu/tests/it/vector_mask.rs b/crates/gpu/tests/it/vector_mask.rs index d0d7e7a012..feb8597b8e 100644 --- a/crates/gpu/tests/it/vector_mask.rs +++ b/crates/gpu/tests/it/vector_mask.rs @@ -6,7 +6,7 @@ use photocraft_doc::{Adjustment, Document, Effect, Layer, LayerContent, LayerMas use photocraft_geom::Rect; fn gpu() -> Option<(wgpu::Device, wgpu::Queue, photocraft_gpu::Compositor)> { - let instance = wgpu::Instance::default(); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()); let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default())).ok()?; let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor::default())).ok()?; // Exact comparison needs 32-bit float targets (see parity.rs); skip on adapters without them. diff --git a/crates/testkit/src/gpu.rs b/crates/testkit/src/gpu.rs index 700908ecfb..3bb5df1ed8 100644 --- a/crates/testkit/src/gpu.rs +++ b/crates/testkit/src/gpu.rs @@ -11,6 +11,13 @@ //! - **libvulkan** with the NVIDIA and lavapipe drivers both installed: `wgpu::Instance::new` on //! several threads segfaults. //! +//! Separately, the **Intel Vulkan driver** on Windows (`igvk64.dll`) crashes a process that so +//! much as creates a wgpu instance with Vulkan in it (#4), even on one thread. The app leaves +//! Vulkan out on Intel; tests use wgpu's defaults, so on such a machine run them with +//! `WGPU_BACKEND=dx12`. Tests that build their own instance read it through +//! `InstanceDescriptor::new_without_display_handle_from_env`; `wgpu::Instance::default()` +//! ignores it. +//! //! So every test that builds a GPU harness (or its own wgpu device) holds [`gpu_lock`] for its //! whole run, harness included. Under nextest each test is its own process and the lock costs //! nothing; `.config/nextest.toml` caps how many of those run at once instead. diff --git a/crates/ui-egui/examples/bevel_bench.rs b/crates/ui-egui/examples/bevel_bench.rs index 15c76e6d35..4d022b77f7 100644 --- a/crates/ui-egui/examples/bevel_bench.rs +++ b/crates/ui-egui/examples/bevel_bench.rs @@ -101,7 +101,7 @@ struct Gpu { } fn gpu() -> Option { - let instance = wgpu::Instance::default(); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()); let adapter = block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, ..Default::default() })) .ok()?; diff --git a/crates/ui-egui/examples/fx_bench.rs b/crates/ui-egui/examples/fx_bench.rs index d51fcf610c..af4036e384 100644 --- a/crates/ui-egui/examples/fx_bench.rs +++ b/crates/ui-egui/examples/fx_bench.rs @@ -136,7 +136,7 @@ struct Gpu { } fn gpu() -> Option { - let instance = wgpu::Instance::default(); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()); let adapter = block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, ..Default::default() })) .ok()?; diff --git a/crates/ui-egui/examples/interactive_bench.rs b/crates/ui-egui/examples/interactive_bench.rs index 2ffe3c1ce1..efeff0927d 100644 --- a/crates/ui-egui/examples/interactive_bench.rs +++ b/crates/ui-egui/examples/interactive_bench.rs @@ -44,7 +44,7 @@ struct Gpu { } fn gpu() -> Option { - let instance = wgpu::Instance::default(); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()); let adapter = block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, ..Default::default() })) .ok()?; diff --git a/crates/ui-egui/tests/it/adjust_preview_gpu.rs b/crates/ui-egui/tests/it/adjust_preview_gpu.rs index d3880b04fd..2fdae54cbe 100644 --- a/crates/ui-egui/tests/it/adjust_preview_gpu.rs +++ b/crates/ui-egui/tests/it/adjust_preview_gpu.rs @@ -23,7 +23,9 @@ fn block_on(f: F) -> F::Output { } fn gpu() -> Option<(wgpu::Device, wgpu::Queue, Compositor)> { - let adapter = match block_on(wgpu::Instance::default().request_adapter(&wgpu::RequestAdapterOptions::default())) { + let adapter = match block_on( + wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()).request_adapter(&wgpu::RequestAdapterOptions::default()), + ) { Ok(a) => a, Err(e) => { eprintln!("skipping adjustment preview GPU parity: no adapter ({e})"); diff --git a/crates/ui-egui/tests/it/layout_perf.rs b/crates/ui-egui/tests/it/layout_perf.rs index 2b5cf73bf1..b8f43f4733 100644 --- a/crates/ui-egui/tests/it/layout_perf.rs +++ b/crates/ui-egui/tests/it/layout_perf.rs @@ -232,7 +232,9 @@ fn layout_document_composites_on_the_gpu_like_the_cpu() { std::thread::yield_now(); } } - let Ok(adapter) = block_on(wgpu::Instance::default().request_adapter(&wgpu::RequestAdapterOptions::default())) else { + let Ok(adapter) = + block_on(wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle_from_env()).request_adapter(&wgpu::RequestAdapterOptions::default())) + else { eprintln!("skipping: no GPU adapter"); return; }; diff --git a/docs/development.md b/docs/development.md index a9e9fc486e..4a88e44630 100644 --- a/docs/development.md +++ b/docs/development.md @@ -158,7 +158,7 @@ By default PhotoCraft's own crates log at `info` and everything else at `warn`. | `PHOTOCRAFT_CPU_CANVAS=1` | Force the CPU canvas path instead of the wgpu shader canvas | | `PHOTOCRAFT_NATIVE_WAYLAND=1` | Linux: stay on native Wayland when a pen is attached (by default the window then opens through Xwayland, because Wayland gives the app no pen input; #639). A pen is an input device with `BTN_TOOL_PEN` and the `ABS_X`/`ABS_Y` axes; virtual keyboards that claim every key are not pens (#1072) | | `PHOTOCRAFT_GPU_ADAPTER=nvidia` | Linux: use the graphics adapter whose name contains this (case-insensitive); overrides `performance.gpuAdapter` for one launch | -| `WGPU_BACKEND=dx12` | Pick the wgpu backend(s) (`vulkan`, `dx12`, `metal`, `gl`); overrides `performance.gpuBackend` and the startup fallback | +| `WGPU_BACKEND=dx12` | Pick the wgpu backend(s) (`vulkan`, `dx12`, `metal`, `gl`); overrides `performance.gpuBackend` and the startup fallback. Tests read it too: on Windows with an Intel GPU, run them with `WGPU_BACKEND=dx12`, since loading the Intel Vulkan driver crashes the test process with `STATUS_ACCESS_VIOLATION` (#4) | | `WGPU_DX12_COMPILER=fxc` | DX12 shader compiler (`fxc`, `dxc`, `auto`); `dxc` and `auto` look `dxcompiler.dll` up through the DLL search path | | `PHOTOCRAFT_GPU_TILE=2048` | Force GPU canvas tiling (tests tile seams) | | `PHOTOCRAFT_TEXEL_GUARD=1` (or `=0`) | Force on (off) uploading egui's fully transparent texels as alpha 1/255. On by default only for Intel Iris Xe on DX12, whose driver shows stale memory in all-zero texture blocks: red squares in brush previews and tool icons (`ui-egui/src/texel_guard.rs`; check a GPU with `cargo run --release -p photocraft-gpu --example zero_block_probe -- dx12 400`) |