From d9a55d19d61e67788cc04acc763182c8910bf13f Mon Sep 17 00:00:00 2001 From: r31lin <257763872+r31lin@users.noreply.github.com> Date: Fri, 30 Oct 2026 11:52:42 +0100 Subject: [PATCH 1/2] Ask the graphics device for the adapter's largest buffer egui-wgpu doubles its vertex buffer when a frame outgrows it, and wgpu's default largest buffer is 256 MiB. Opening a 160-page InDesign catalogue produced a frame with 137 MB of vertices; egui-wgpu asked for a 275 MB buffer, the device refused it and the app stopped. The device now takes the adapter's own largest buffer, as it already did for the texture side (2047 MiB on DirectX 12 here), and the log says both limits. Co-Authored-By: Claude Opus 5.5 --- apps/designcraft/src/gpu_limits.rs | 40 +++++++++++++++++++++++------- apps/designcraft/src/main.rs | 8 +++++- 2 files changed, 38 insertions(+), 10 deletions(-) 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 4b4a1ab7f..180aca13e 100644 --- a/apps/designcraft/src/main.rs +++ b/apps/designcraft/src/main.rs @@ -316,7 +316,7 @@ fn main() -> std::process::ExitCode { } // 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); + 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( @@ -395,6 +395,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() { From a2d745db009da671102f4ddff99e094cc5ae4dee Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Tue, 3 Nov 2026 13:40:17 +0000 Subject: [PATCH 2/2] Apply review fixes - apps/designcraft/src/main.rs --- apps/designcraft/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/designcraft/src/main.rs b/apps/designcraft/src/main.rs index 180aca13e..aa8d83222 100644 --- a/apps/designcraft/src/main.rs +++ b/apps/designcraft/src/main.rs @@ -314,8 +314,8 @@ 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`). + // 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`).