diff --git a/apps/designcraft/src/gpu_limits.rs b/apps/designcraft/src/gpu_limits.rs index 5615e5650..13eca4719 100644 --- a/apps/designcraft/src/gpu_limits.rs +++ b/apps/designcraft/src/gpu_limits.rs @@ -6,6 +6,11 @@ //! (9048×5757 between 250 % and 100 % monitors), then failed `Surface::configure`, and wgpu's //! default error handler panics: the app closed with the documents in it. DirectX 12, Vulkan and //! Metal hardware offer 16384. +//! +//! The same goes for buffers: egui-wgpu doubles its vertex buffer when a frame outgrows it, and +//! wgpu's default largest buffer is 256 MiB. A frame of a 160-page catalogue with 137 MB of vertices +//! asked for a 275 MB buffer, and egui-wgpu panicked when the device refused it. DirectX 12 and +//! Vulkan hardware offer far more. use eframe::{ NativeOptions, @@ -14,21 +19,23 @@ use eframe::{ }; use std::sync::{Arc, Mutex, PoisonError}; -/// `required` with the largest texture side the adapter's `supported` limits offer. Taking the -/// adapter's own value also never asks a smaller (downlevel) adapter for more than it has. -pub fn surface_texture_limits(mut required: Limits, supported: &Limits) -> Limits { +/// `required` with the largest texture side and buffer the adapter's `supported` limits offer. +/// Taking the adapter's own values also never asks a smaller (downlevel) adapter for more than it +/// has. +pub fn adapter_size_limits(mut required: Limits, supported: &Limits) -> Limits { required.max_texture_dimension_2d = supported.max_texture_dimension_2d; + required.max_buffer_size = supported.max_buffer_size; required } -/// Makes the device eframe creates ask for the adapter's largest texture side -/// ([`surface_texture_limits`]), keeping eframe's other device requirements. -pub fn request_adapter_texture_size(options: &mut NativeOptions) { +/// Makes the device eframe creates ask for the adapter's largest texture side and buffer +/// ([`adapter_size_limits`]), keeping eframe's other device requirements. +pub fn request_adapter_sizes(options: &mut NativeOptions) { if let WgpuSetup::CreateNew(create) = &mut options.wgpu_options.wgpu_setup { let device_descriptor = Arc::clone(&create.device_descriptor); create.device_descriptor = Arc::new(move |adapter| { let mut descriptor = device_descriptor(adapter); - descriptor.required_limits = surface_texture_limits(descriptor.required_limits, &adapter.limits()); + descriptor.required_limits = adapter_size_limits(descriptor.required_limits, &adapter.limits()); descriptor }); } @@ -62,7 +69,7 @@ mod tests { let eframe_request = Limits { max_texture_dimension_2d: 8192, ..base.clone() }; for side in [4096, 8192, 16384, 32768] { let supported = Limits { max_texture_dimension_2d: side, ..base.clone() }; - let limits = surface_texture_limits(eframe_request.clone(), &supported); + let limits = adapter_size_limits(eframe_request.clone(), &supported); assert_eq!(limits.max_texture_dimension_2d, side); // The drag's 9048×5757 surface fits from 16384 up. assert_eq!(side >= 16384, 9048 <= limits.max_texture_dimension_2d); @@ -73,12 +80,27 @@ mod tests { } } + #[test] + fn largest_buffer_is_the_adapters_own() { + // egui-wgpu grew its vertex buffer to 275 MB for 137 MB of vertices: more than wgpu's + // default 256 MiB, within what DirectX 12 hardware offers. A downlevel adapter offering + // less is asked for no more. + let eframe_request = Limits::default(); + for size in [128 << 20, 256 << 20, 2 << 30] { + let supported = Limits { max_buffer_size: size, ..Limits::default() }; + let limits = adapter_size_limits(eframe_request.clone(), &supported); + assert_eq!(limits.max_buffer_size, size); + assert_eq!(size >= 2 << 30, limits.max_buffer_size >= 275_114_400); + assert!(limits.check_limits(&supported)); + } + } + #[test] fn native_options_keep_creating_their_own_device() { // The wrapper only replaces the device descriptor: eframe still creates the instance, // adapter and device, and main.rs can still choose the backends. let mut options = NativeOptions::default(); - request_adapter_texture_size(&mut options); + request_adapter_sizes(&mut options); assert!(matches!(options.wgpu_options.wgpu_setup, WgpuSetup::CreateNew(_))); } } diff --git a/apps/designcraft/src/main.rs b/apps/designcraft/src/main.rs index 53f4a454e..ab6970332 100644 --- a/apps/designcraft/src/main.rs +++ b/apps/designcraft/src/main.rs @@ -319,9 +319,9 @@ fn main() -> std::process::ExitCode { if let Some(icon) = app_icon() { options.viewport = options.viewport.with_icon(icon); } - // The adapter's largest texture side instead of eframe's 8192, so a window across several - // monitors keeps its surface (see `gpu_limits`). - gpu_limits::request_adapter_texture_size(&mut options); + // The adapter's largest texture side and buffer instead of eframe's 8192 and wgpu's 256 MiB, so a + // window across several monitors keeps its surface and a large frame its vertex buffer (see `gpu_limits`). + gpu_limits::request_adapter_sizes(&mut options); // The graphics backend, decided before the window exists: a driver that faults takes the // process down before any Rust code can catch it (see `gpu::backend`). let fallback = gpu::backend::Fallback::begin( @@ -400,6 +400,12 @@ fn main() -> std::process::ExitCode { info.driver, info.driver_info ); + let limits = rs.device.limits(); + log::info!( + "graphics: largest texture side {}, largest buffer {} MiB", + limits.max_texture_dimension_2d, + limits.max_buffer_size >> 20 + ); created.started_with(info.backend); created.created(&cc.egui_ctx); if !skip.is_empty() {