diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index d974f9ca016..c346d5bce11 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -268,6 +268,8 @@ pub enum WgpuSetup { /// /// This is generally a "software" implementation on the system. /// In particular useful for running on CI without a GPU and as a reference for testing. + /// Note that not all platforms/setups may have a fallback adapter. + /// /// Defaults to `false`. force_fallback_adapter: bool, diff --git a/crates/egui_kittest/src/snapshot.rs b/crates/egui_kittest/src/snapshot.rs index 5be6c275419..814da104d3a 100644 --- a/crates/egui_kittest/src/snapshot.rs +++ b/crates/egui_kittest/src/snapshot.rs @@ -1,9 +1,13 @@ use crate::Harness; use image::ImageError; + use std::fmt::Display; use std::io::ErrorKind; use std::path::{Path, PathBuf}; +#[cfg(feature = "wgpu")] +use egui_wgpu::wgpu; + #[non_exhaustive] pub struct SnapshotOptions { /// The threshold for the image comparison. @@ -14,6 +18,13 @@ pub struct SnapshotOptions { /// The path where the snapshots will be saved. /// The default is `tests/snapshots`. pub output_path: PathBuf, + + /// Configures the wgpu renderer setup. + /// + /// Note that no [`wgpu::Surface`] is needed for the snapshot tests, + /// therefore adapter creation doesn't have to be constrained on compatible surfaces. + #[cfg(feature = "wgpu")] + pub wgpu_setup: egui_wgpu::WgpuSetup, } impl Default for SnapshotOptions { @@ -21,10 +32,34 @@ impl Default for SnapshotOptions { Self { threshold: 0.6, output_path: PathBuf::from("tests/snapshots"), + #[cfg(feature = "wgpu")] + wgpu_setup: default_wgpu_setup(), } } } +#[cfg(feature = "wgpu")] +fn default_wgpu_setup() -> egui_wgpu::WgpuSetup { + egui_wgpu::WgpuSetup::CreateNew { + supported_backends: wgpu::util::backend_bits_from_env().unwrap_or( + wgpu::Backends::all().intersection(wgpu::Backends::BROWSER_WEBGPU.complement()), + ), + power_preference: wgpu::PowerPreference::HighPerformance, + + // It would be nice to force a fallback adapter here. + // However, they're not guaranteed to be available on all platforms. + force_fallback_adapter: false, + + device_descriptor: std::sync::Arc::new(|_| wgpu::DeviceDescriptor { + label: Some("egui-kittest"), + ..Default::default() + }), + trace_path: std::env::var("WGPU_TRACE") + .ok() + .map(std::path::PathBuf::from), + } +} + impl SnapshotOptions { /// Create a new [`SnapshotOptions`] with the default values. pub fn new() -> Self { @@ -185,6 +220,8 @@ pub fn try_image_snapshot_options( let SnapshotOptions { threshold, output_path, + #[cfg(feature = "wgpu")] + wgpu_setup: _, } = options; let path = output_path.join(format!("{name}.png")); @@ -333,7 +370,7 @@ impl Harness<'_, State> { name: &str, options: &SnapshotOptions, ) -> Result<(), SnapshotError> { - let image = crate::wgpu::TestRenderer::new().render(self); + let image = crate::wgpu::TestRenderer::new(&options.wgpu_setup).render(self); try_image_snapshot_options(&image, name, options) } @@ -346,7 +383,7 @@ impl Harness<'_, State> { /// Returns a [`SnapshotError`] if the image does not match the snapshot or if there was an error /// reading or writing the snapshot. pub fn try_wgpu_snapshot(&self, name: &str) -> Result<(), SnapshotError> { - let image = crate::wgpu::TestRenderer::new().render(self); + let image = crate::wgpu::TestRenderer::new(&default_wgpu_setup()).render(self); try_image_snapshot(&image, name) } diff --git a/crates/egui_kittest/src/wgpu.rs b/crates/egui_kittest/src/wgpu.rs index 2e775390e73..14da14742aa 100644 --- a/crates/egui_kittest/src/wgpu.rs +++ b/crates/egui_kittest/src/wgpu.rs @@ -3,7 +3,7 @@ use std::{iter::once, sync::Arc}; use image::RgbaImage; use egui_wgpu::{ - wgpu::{self, Backends, InstanceDescriptor, StoreOp, TextureFormat}, + wgpu::{self, StoreOp, TextureFormat}, ScreenDescriptor, }; @@ -16,32 +16,41 @@ pub struct TestRenderer { dithering: bool, } -impl Default for TestRenderer { - fn default() -> Self { - Self::new() - } -} - impl TestRenderer { - /// Create a new [`TestRenderer`] using a default [`wgpu::Instance`]. - pub fn new() -> Self { - let instance = wgpu::Instance::new(InstanceDescriptor::default()); - - let adapters = instance.enumerate_adapters(Backends::all()); - let adapter = adapters.first().expect("No adapter found"); - - let (device, queue) = pollster::block_on(adapter.request_device( - &wgpu::DeviceDescriptor { - label: Some("Egui Device"), - memory_hints: Default::default(), - required_limits: Default::default(), - required_features: Default::default(), - }, - None, - )) - .expect("Failed to create device"); + /// Create a new [`TestRenderer`] using a [`egui_wgpu::WgpuSetup`]. + pub fn new(wgpu_setup: &egui_wgpu::WgpuSetup) -> Self { + let (device, queue) = match wgpu_setup { + egui_wgpu::WgpuSetup::CreateNew { + supported_backends, + power_preference, + force_fallback_adapter, + device_descriptor, + trace_path, + } => { + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: *supported_backends, + ..Default::default() + }); + let adapter = + pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { + power_preference: *power_preference, + force_fallback_adapter: *force_fallback_adapter, + compatible_surface: None, + })) + .expect("No adapter found"); + + let device_descriptor = device_descriptor(&adapter); + let (device, queue) = pollster::block_on( + adapter.request_device(&device_descriptor, trace_path.as_deref()), + ) + .expect("Failed to request device"); + + (Arc::new(device), Arc::new(queue)) + } + egui_wgpu::WgpuSetup::Existing { device, queue, .. } => (device.clone(), queue.clone()), + }; - Self::create(Arc::new(device), Arc::new(queue)) + Self::create(device, queue) } /// Create a new [`TestRenderer`] using the provided [`wgpu::Device`] and [`wgpu::Queue`].