From 18a65ed521daf3b05abc67a38bc6f47950cdf686 Mon Sep 17 00:00:00 2001 From: "zed-zippy[bot]" <234243425+zed-zippy[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 21:01:07 +0000 Subject: [PATCH] Avoid panicking in wgpu rendering during resize (#50169) (cherry-pick to preview) (#50343) Cherry-pick of #50169 to preview ---- Fixes Zed-5AW Fixes Zed-5AP Claude believes this is the right fix, but would love someone who knows more about graphics than me to take a look: @reflectronic / @zortax? The panic is: ``` wgpu error: Validation Error Caused by: In Texture::create_view Texture with 'path_intermediate' label is invalid gpui::platform::wgpu::wgpu_renderer::WgpuRenderer::create_path_intermediate (wgpu_renderer.rs:742) gpui::platform::wgpu::wgpu_renderer::WgpuRenderer::update_drawable_size (wgpu_renderer.rs:784) gpui::platform::linux::x11::window::X11WindowStatePtr::set_bounds (window.rs:1169) gpui::platform::linux::x11::client::X11Client::handle_event (client.rs:902) ``` or: ``` wgpu error: Validation Error Caused by: In Texture::create_view Texture with 'path_intermediate' label is invalid gpui::platform::wgpu::wgpu_renderer::WgpuRenderer::create_path_intermediate (wgpu_renderer.rs:742) gpui::platform::wgpu::wgpu_renderer::WgpuRenderer::new (wgpu_renderer.rs:274) gpui::platform::linux::x11::window::X11WindowState::new::{{closure}} (window.rs:698) gpui::platform::linux::x11::window::X11WindowState::new (window.rs:488) gpui::platform::linux::x11::window::X11Window::new (window.rs:814) gpui::platform::linux::x11::client::X11Client::open_window (client.rs:1514) gpui::platform::linux::platform::::open_window (platform.rs:289) gpui::window::Window::new (window.rs:1119) gpui::app::App::open_window::{{closure}} (app.rs:1025) gpui::app::App::update (app.rs:835) gpui::app::App::open_window (app.rs:1022) ``` I haven't seen a Wayland equivalent (not sure if that's because it doesn't happen on Wayalnd or because I havent' seen it yet) Release Notes: - Linux: Fixed a panic in the new WPGU renderer during resize Co-authored-by: Conrad Irwin --- crates/gpui_wgpu/src/wgpu_renderer.rs | 105 +++++++++++++++----------- 1 file changed, 60 insertions(+), 45 deletions(-) diff --git a/crates/gpui_wgpu/src/wgpu_renderer.rs b/crates/gpui_wgpu/src/wgpu_renderer.rs index 14f267deb87f482c8e7905144eca97ecf2dec68f..ee9cf05bb76802ff4858715f8a5efdcac9bdba7d 100644 --- a/crates/gpui_wgpu/src/wgpu_renderer.rs +++ b/crates/gpui_wgpu/src/wgpu_renderer.rs @@ -107,8 +107,8 @@ pub struct WgpuRenderer { instance_buffer: wgpu::Buffer, instance_buffer_capacity: u64, storage_buffer_alignment: u64, - path_intermediate_texture: wgpu::Texture, - path_intermediate_view: wgpu::TextureView, + path_intermediate_texture: Option, + path_intermediate_view: Option, path_msaa_texture: Option, path_msaa_view: Option, rendering_params: RenderingParameters, @@ -272,23 +272,6 @@ impl WgpuRenderer { mapped_at_creation: false, }); - let (path_intermediate_texture, path_intermediate_view) = Self::create_path_intermediate( - &device, - surface_format, - config.size.width.0 as u32, - config.size.height.0 as u32, - ); - - let (path_msaa_texture, path_msaa_view) = Self::create_msaa_if_needed( - &device, - surface_format, - config.size.width.0 as u32, - config.size.height.0 as u32, - rendering_params.path_sample_count, - ) - .map(|(t, v)| (Some(t), Some(v))) - .unwrap_or((None, None)); - let globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("globals_bind_group"), layout: &bind_group_layouts.globals, @@ -354,10 +337,12 @@ impl WgpuRenderer { instance_buffer, instance_buffer_capacity: initial_instance_buffer_capacity, storage_buffer_alignment, - path_intermediate_texture, - path_intermediate_view, - path_msaa_texture, - path_msaa_view, + // Defer intermediate texture creation to first draw call via ensure_intermediate_textures(). + // This avoids panics when the device/surface is in an invalid state during initialization. + path_intermediate_texture: None, + path_intermediate_view: None, + path_msaa_texture: None, + path_msaa_view: None, rendering_params, dual_source_blending, adapter_info, @@ -786,7 +771,9 @@ impl WgpuRenderer { } // Destroy old textures before allocating new ones to avoid GPU memory spikes - self.path_intermediate_texture.destroy(); + if let Some(ref texture) = self.path_intermediate_texture { + texture.destroy(); + } if let Some(ref texture) = self.path_msaa_texture { texture.destroy(); } @@ -795,28 +782,44 @@ impl WgpuRenderer { self.surface_config.height = height.max(1); self.surface.configure(&self.device, &self.surface_config); - let (path_intermediate_texture, path_intermediate_view) = - Self::create_path_intermediate( - &self.device, - self.surface_config.format, - self.surface_config.width, - self.surface_config.height, - ); - self.path_intermediate_texture = path_intermediate_texture; - self.path_intermediate_view = path_intermediate_view; - - let (path_msaa_texture, path_msaa_view) = Self::create_msaa_if_needed( + // Invalidate intermediate textures - they will be lazily recreated + // in draw() after we confirm the surface is healthy. This avoids + // panics when the device/surface is in an invalid state during resize. + self.path_intermediate_texture = None; + self.path_intermediate_view = None; + self.path_msaa_texture = None; + self.path_msaa_view = None; + } + } + + fn ensure_intermediate_textures(&mut self) { + if self.path_intermediate_texture.is_some() { + return; + } + + let (path_intermediate_texture, path_intermediate_view) = { + let (t, v) = Self::create_path_intermediate( &self.device, self.surface_config.format, self.surface_config.width, self.surface_config.height, - self.rendering_params.path_sample_count, - ) - .map(|(t, v)| (Some(t), Some(v))) - .unwrap_or((None, None)); - self.path_msaa_texture = path_msaa_texture; - self.path_msaa_view = path_msaa_view; - } + ); + (Some(t), Some(v)) + }; + self.path_intermediate_texture = path_intermediate_texture; + self.path_intermediate_view = path_intermediate_view; + + let (path_msaa_texture, path_msaa_view) = Self::create_msaa_if_needed( + &self.device, + self.surface_config.format, + self.surface_config.width, + self.surface_config.height, + self.rendering_params.path_sample_count, + ) + .map(|(t, v)| (Some(t), Some(v))) + .unwrap_or((None, None)); + self.path_msaa_texture = path_msaa_texture; + self.path_msaa_view = path_msaa_view; } pub fn update_transparency(&mut self, transparent: bool) { @@ -875,6 +878,10 @@ impl WgpuRenderer { return; } }; + + // Now that we know the surface is healthy, ensure intermediate textures exist + self.ensure_intermediate_textures(); + let frame_view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); @@ -1264,11 +1271,15 @@ impl WgpuRenderer { vec![PathSprite { bounds }] }; + let Some(path_intermediate_view) = self.path_intermediate_view.as_ref() else { + return true; + }; + let sprite_data = unsafe { Self::instance_bytes(&sprites) }; self.draw_instances_with_texture( sprite_data, sprites.len() as u32, - &self.path_intermediate_view, + path_intermediate_view, &self.pipelines.paths, instance_offset, pass, @@ -1312,10 +1323,14 @@ impl WgpuRenderer { }], }); + let Some(path_intermediate_view) = self.path_intermediate_view.as_ref() else { + return true; + }; + let (target_view, resolve_target) = if let Some(ref msaa_view) = self.path_msaa_view { - (msaa_view, Some(&self.path_intermediate_view)) + (msaa_view, Some(path_intermediate_view)) } else { - (&self.path_intermediate_view, None) + (path_intermediate_view, None) }; {