diff --git a/apps/vectorcraft/src/gpu.rs b/apps/vectorcraft/src/gpu.rs index 431d40346..00f92405e 100644 --- a/apps/vectorcraft/src/gpu.rs +++ b/apps/vectorcraft/src/gpu.rs @@ -182,11 +182,14 @@ fn any_hardware(kinds: &[DeviceType]) -> bool { kinds.iter().any(|k| *k != DeviceType::Cpu) } -/// The largest texture side to ask the device for, which bounds the window's surface: the -/// adapter's own limit when it is above eframe's `default` (8192 px), so a window larger than -/// that keeps its surface (#1459). Never below `default`, which is what was asked for before. -pub fn surface_limit(default: u32, adapter: u32) -> u32 { - default.max(adapter) +/// The limits to ask the device for: what eframe `asked` for, lowered to what the `adapter` offers +/// wherever it offers less, so a GPU short of one of them still gets a device (#2105). The texture +/// side, which bounds the window's surface, is the adapter's own, above eframe's 8192 px when the +/// adapter allows more, so a window larger than that keeps its surface (#1459). +pub fn device_limits(asked: wgpu::Limits, adapter: &wgpu::Limits) -> wgpu::Limits { + let mut limits = asked.or_worse_values_from(adapter); + limits.max_texture_dimension_2d = adapter.max_texture_dimension_2d; + limits } /// One adapter, as [`adapter_order`] sees it. diff --git a/apps/vectorcraft/src/gpu/tests.rs b/apps/vectorcraft/src/gpu/tests.rs index 3d13b5157..b1511a232 100644 --- a/apps/vectorcraft/src/gpu/tests.rs +++ b/apps/vectorcraft/src/gpu/tests.rs @@ -5,11 +5,34 @@ fn gpu(name: &str, backend: Backend, device_type: DeviceType) -> Candidate { } /// #1459: a 9048 px wide window (two display scales' ratio past 8192) fits an adapter that -/// takes 16384 px textures; an adapter with less than eframe asks for changes nothing. +/// takes 16384 px textures. #[test] fn the_surface_may_be_as_large_as_the_adapter_allows() { - assert_eq!(surface_limit(8192, 16384), 16384); - assert_eq!(surface_limit(8192, 4096), 8192); + let adapter = wgpu::Limits { max_texture_dimension_2d: 16384, ..wgpu::Limits::default() }; + let asked = wgpu::Limits { max_texture_dimension_2d: 8192, ..wgpu::Limits::default() }; + assert_eq!(device_limits(asked, &adapter).max_texture_dimension_2d, 16384); +} + +/// #2105: a GPU that offers less than eframe asks for on any limit (here a Raspberry Pi 5's V3D 7.1: +/// 15 inter-stage variables on Vulkan, 4096 px textures on OpenGL) gets a device with what it +/// offers, instead of failing and leaving the window to the software rasterizer. A GPU that offers +/// more is asked for no more than before, except for the texture side. +#[test] +fn the_device_asks_for_no_more_than_the_adapter_offers() { + let asked = wgpu::Limits { max_texture_dimension_2d: 8192, ..wgpu::Limits::default() }; + let vulkan = wgpu::Limits { max_inter_stage_shader_variables: 15, max_texture_dimension_2d: 7680, ..wgpu::Limits::default() }; + let limits = device_limits(asked.clone(), &vulkan); + assert_eq!((limits.max_inter_stage_shader_variables, limits.max_texture_dimension_2d), (15, 7680)); + assert!(limits.check_limits(&vulkan), "{limits:?}"); + let gl = wgpu::Limits { max_texture_dimension_2d: 4096, ..wgpu::Limits::downlevel_webgl2_defaults() }; + let webgl2 = wgpu::Limits { max_texture_dimension_2d: 8192, ..wgpu::Limits::downlevel_webgl2_defaults() }; + let limits = device_limits(webgl2.clone(), &gl); + assert_eq!(limits.max_texture_dimension_2d, 4096); + assert!(limits.check_limits(&gl), "{limits:?}"); + let ample = wgpu::Limits { max_texture_dimension_2d: 8192, ..wgpu::Limits::default() }; + assert_eq!(device_limits(asked.clone(), &le), asked); + let better = wgpu::Limits { max_inter_stage_shader_variables: 31, max_texture_dimension_2d: 8192, ..wgpu::Limits::default() }; + assert_eq!(device_limits(asked.clone(), &better), asked, "never more than eframe asked for"); } fn names(c: &[Candidate], order: &[usize]) -> Vec { diff --git a/apps/vectorcraft/src/main.rs b/apps/vectorcraft/src/main.rs index a7d86d201..8bb9600af 100644 --- a/apps/vectorcraft/src/main.rs +++ b/apps/vectorcraft/src/main.rs @@ -346,14 +346,15 @@ fn main() -> std::process::ExitCode { // losing the device before the first frame (#1194). `WGPU_DX12_COMPILER` picks another. create.instance_descriptor.backend_options.dx12.shader_compiler = eframe::wgpu::Dx12Compiler::from_env().unwrap_or(eframe::wgpu::Dx12Compiler::Fxc); - // The window's surface may be as large as the adapter allows, not eframe's fixed - // 8192 px: a window stretched over several monitors, or grown by a display scale - // change, otherwise fails to configure its surface and the app closes (#1459). + // The device asks for no more than the adapter offers, so a GPU short of one of + // eframe's limits still gets a device (#2105). The window's surface may be as large as + // the adapter allows, not eframe's fixed 8192 px: a window stretched over several + // monitors, or grown by a display scale change, otherwise fails to configure its + // surface and the app closes (#1459). let device = create.device_descriptor.clone(); create.device_descriptor = std::sync::Arc::new(move |adapter: &eframe::wgpu::Adapter| { let mut descriptor = device(adapter); - let limit = &mut descriptor.required_limits.max_texture_dimension_2d; - *limit = gpu::surface_limit(*limit, adapter.limits().max_texture_dimension_2d); + descriptor.required_limits = gpu::device_limits(descriptor.required_limits, &adapter.limits()); descriptor }); }