From 2757aa41402c24a5b815b87fea7f7ba8c1db5184 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 27 Feb 2026 13:59:28 -0700 Subject: [PATCH] Clamp window size on wgpu (#50329) Fixes ZED-59P Release Notes: - Linux: Fix panic when requested window size was larger than supported by your GPU --- crates/gpui_linux/src/linux/wayland/window.rs | 6 +++ crates/gpui_linux/src/linux/x11/window.rs | 34 ++++++++------- crates/gpui_wgpu/src/wgpu_renderer.rs | 42 ++++++++++++++++--- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/crates/gpui_linux/src/linux/wayland/window.rs b/crates/gpui_linux/src/linux/wayland/window.rs index 4a4c4060bdc31b95bd4b90d930afdc54727a9667..dd8e0b27c32ca9d15152028e686b065165a9e0c1 100644 --- a/crates/gpui_linux/src/linux/wayland/window.rs +++ b/crates/gpui_linux/src/linux/wayland/window.rs @@ -345,6 +345,12 @@ impl WaylandWindowState { if let Some(title) = options.titlebar.and_then(|titlebar| titlebar.title) { xdg_state.toplevel.set_title(title.to_string()); } + // Set max window size based on the GPU's maximum texture dimension. + // This prevents the window from being resized larger than what the GPU can render. + let max_texture_size = renderer.max_texture_size() as i32; + xdg_state + .toplevel + .set_max_size(max_texture_size, max_texture_size); } Ok(Self { diff --git a/crates/gpui_linux/src/linux/x11/window.rs b/crates/gpui_linux/src/linux/x11/window.rs index 0ddd6e7adff84908e6a1c06d661347d39bdc5c9e..55da1d89947eb9a39937b9e70b05ab71aceb6525 100644 --- a/crates/gpui_linux/src/linux/x11/window.rs +++ b/crates/gpui_linux/src/linux/x11/window.rs @@ -497,21 +497,6 @@ impl X11WindowState { ), )?; - if let Some(size) = params.window_min_size { - let mut size_hints = WmSizeHints::new(); - let min_size = (f32::from(size.width) as i32, f32::from(size.height) as i32); - size_hints.min_size = Some(min_size); - check_reply( - || { - format!( - "X11 change of WM_SIZE_HINTS failed. min_size: {:?}", - min_size - ) - }, - size_hints.set_normal_hints(xcb, x_window), - )?; - } - let reply = get_reply(|| "X11 GetGeometry failed.", xcb.get_geometry(x_window))?; if reply.x == 0 && reply.y == 0 { bounds.origin.x.0 += 2; @@ -697,6 +682,25 @@ impl X11WindowState { WgpuRenderer::new(gpu_context, &raw_window, config)? }; + // Set max window size hints based on the GPU's maximum texture dimension. + // This prevents the window from being resized larger than what the GPU can render. + let max_texture_size = renderer.max_texture_size(); + let mut size_hints = WmSizeHints::new(); + if let Some(size) = params.window_min_size { + size_hints.min_size = + Some((f32::from(size.width) as i32, f32::from(size.height) as i32)); + } + size_hints.max_size = Some((max_texture_size as i32, max_texture_size as i32)); + check_reply( + || { + format!( + "X11 change of WM_SIZE_HINTS failed. max_size: {:?}", + max_texture_size + ) + }, + size_hints.set_normal_hints(xcb, x_window), + )?; + let display = Rc::new(X11Display::new(xcb, scale_factor, x_screen_index)?); Ok(Self { diff --git a/crates/gpui_wgpu/src/wgpu_renderer.rs b/crates/gpui_wgpu/src/wgpu_renderer.rs index 23de2f55e0707b2f706ecb6ae977e4b08850d894..6e4169e34b4706dbdcdfc88238c170ec484180be 100644 --- a/crates/gpui_wgpu/src/wgpu_renderer.rs +++ b/crates/gpui_wgpu/src/wgpu_renderer.rs @@ -118,6 +118,7 @@ pub struct WgpuRenderer { adapter_info: wgpu::AdapterInfo, transparent_alpha_mode: wgpu::CompositeAlphaMode, opaque_alpha_mode: wgpu::CompositeAlphaMode, + max_texture_size: u32, } impl WgpuRenderer { @@ -239,11 +240,27 @@ impl WgpuRenderer { opaque_alpha_mode }; + let device = Arc::clone(&context.device); + let max_texture_size = device.limits().max_texture_dimension_2d; + + let requested_width = config.size.width.0 as u32; + let requested_height = config.size.height.0 as u32; + let clamped_width = requested_width.min(max_texture_size); + let clamped_height = requested_height.min(max_texture_size); + + if clamped_width != requested_width || clamped_height != requested_height { + warn!( + "Requested surface size ({}, {}) exceeds maximum texture dimension {}. \ + Clamping to ({}, {}). Window content may not fill the entire window.", + requested_width, requested_height, max_texture_size, clamped_width, clamped_height + ); + } + let surface_config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface_format, - width: config.size.width.0 as u32, - height: config.size.height.0 as u32, + width: clamped_width.max(1), + height: clamped_height.max(1), present_mode: wgpu::PresentMode::Fifo, desired_maximum_frame_latency: 2, alpha_mode, @@ -251,7 +268,6 @@ impl WgpuRenderer { }; surface.configure(&context.device, &surface_config); - let device = Arc::clone(&context.device); let queue = Arc::clone(&context.queue); let dual_source_blending = context.supports_dual_source_blending(); @@ -374,6 +390,7 @@ impl WgpuRenderer { adapter_info, transparent_alpha_mode, opaque_alpha_mode, + max_texture_size, }) } @@ -811,6 +828,17 @@ impl WgpuRenderer { let height = size.height.0 as u32; if width != self.surface_config.width || height != self.surface_config.height { + let clamped_width = width.min(self.max_texture_size); + let clamped_height = height.min(self.max_texture_size); + + if clamped_width != width || clamped_height != height { + warn!( + "Requested surface size ({}, {}) exceeds maximum texture dimension {}. \ + Clamping to ({}, {}). Window content may not fill the entire window.", + width, height, self.max_texture_size, clamped_width, clamped_height + ); + } + // Wait for any in-flight GPU work to complete before destroying textures if let Err(e) = self.device.poll(wgpu::PollType::Wait { submission_index: None, @@ -827,8 +855,8 @@ impl WgpuRenderer { texture.destroy(); } - self.surface_config.width = width.max(1); - self.surface_config.height = height.max(1); + self.surface_config.width = clamped_width.max(1); + self.surface_config.height = clamped_height.max(1); self.surface.configure(&self.device, &self.surface_config); // Invalidate intermediate textures - they will be lazily recreated @@ -917,6 +945,10 @@ impl WgpuRenderer { } } + pub fn max_texture_size(&self) -> u32 { + self.max_texture_size + } + pub fn draw(&mut self, scene: &Scene) { self.atlas.before_frame();