From 90bbaafc620530924de3b71c4d1e5a27ecd3d220 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Tue, 17 Feb 2026 10:34:56 +0000 Subject: [PATCH 1/3] Handle WGPU SurfaceErrors better Signed-off-by: Nico Burns --- crates/anyrender_vello/src/window_renderer.rs | 17 ++++++-- .../src/window_renderer.rs | 17 ++++++-- crates/wgpu_context/src/surface_renderer.rs | 41 +++++++++++-------- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/crates/anyrender_vello/src/window_renderer.rs b/crates/anyrender_vello/src/window_renderer.rs index f1331ab..aae3da7 100644 --- a/crates/anyrender_vello/src/window_renderer.rs +++ b/crates/anyrender_vello/src/window_renderer.rs @@ -10,7 +10,7 @@ use vello::{ AaConfig, AaSupport, RenderParams, Renderer as VelloRenderer, RendererOptions, Scene as VelloScene, }; -use wgpu::{Features, Limits, PresentMode, TextureFormat, TextureUsages}; +use wgpu::{Features, Limits, PresentMode, SurfaceError, TextureFormat, TextureUsages}; use wgpu_context::{ DeviceHandle, SurfaceRenderer, SurfaceRendererConfiguration, TextureConfiguration, WGPUContext, }; @@ -207,7 +207,16 @@ impl WindowRenderer for VelloWindowRenderer { }); timer.record_time("cmd"); - let texture_view = render_surface.target_texture_view(); + match render_surface.ensure_current_surface_texture() { + Ok(_) => {} + Err(SurfaceError::Timeout | SurfaceError::Lost | SurfaceError::Outdated) => return, + Err(SurfaceError::OutOfMemory) => panic!("Out of memory"), + Err(SurfaceError::Other) => panic!("Unknown error getting surface"), + }; + + let texture_view = render_surface + .target_texture_view() + .expect("handled errorss from ensure_current_surface_texture above"); state .renderer .render_to_texture( @@ -227,7 +236,9 @@ impl WindowRenderer for VelloWindowRenderer { drop(texture_view); - render_surface.maybe_blit_and_present(); + render_surface + .maybe_blit_and_present() + .expect("handled errorss from ensure_current_surface_texture above"); timer.record_time("present"); render_surface diff --git a/crates/anyrender_vello_hybrid/src/window_renderer.rs b/crates/anyrender_vello_hybrid/src/window_renderer.rs index e6e216d..5ec218b 100644 --- a/crates/anyrender_vello_hybrid/src/window_renderer.rs +++ b/crates/anyrender_vello_hybrid/src/window_renderer.rs @@ -10,7 +10,7 @@ use vello_hybrid::{ RenderSettings, RenderSize, RenderTargetConfig, Renderer as VelloHybridRenderer, Scene as VelloHybridScene, }; -use wgpu::{CommandEncoderDescriptor, Features, Limits, PresentMode, TextureFormat}; +use wgpu::{CommandEncoderDescriptor, Features, Limits, PresentMode, SurfaceError, TextureFormat}; use wgpu_context::{DeviceHandle, SurfaceRenderer, SurfaceRendererConfiguration, WGPUContext}; use crate::{VelloHybridScenePainter, scene::ImageManager}; @@ -226,7 +226,16 @@ impl WindowRenderer for VelloHybridWindowRenderer { }); timer.record_time("cmd"); - let texture_view = render_surface.target_texture_view(); + match render_surface.ensure_current_surface_texture() { + Ok(_) => {} + Err(SurfaceError::Timeout | SurfaceError::Lost | SurfaceError::Outdated) => return, + Err(SurfaceError::OutOfMemory) => panic!("Out of memory"), + Err(SurfaceError::Other) => panic!("Unknown error getting surface"), + }; + + let texture_view = render_surface + .target_texture_view() + .expect("handled errorss from ensure_current_surface_texture above"); state .renderer @@ -247,7 +256,9 @@ impl WindowRenderer for VelloHybridWindowRenderer { drop(texture_view); - render_surface.maybe_blit_and_present(); + render_surface + .maybe_blit_and_present() + .expect("handled errorss from ensure_current_surface_texture above"); timer.record_time("present"); render_surface diff --git a/crates/wgpu_context/src/surface_renderer.rs b/crates/wgpu_context/src/surface_renderer.rs index 1c7c0c8..4dfe4fb 100644 --- a/crates/wgpu_context/src/surface_renderer.rs +++ b/crates/wgpu_context/src/surface_renderer.rs @@ -1,7 +1,7 @@ use crate::{DeviceHandle, WgpuContextError, util::create_texture}; use wgpu::{ CommandEncoderDescriptor, CompositeAlphaMode, Device, PresentMode, Queue, Surface, - SurfaceConfiguration, SurfaceTexture, TextureFormat, TextureUsages, TextureView, + SurfaceConfiguration, SurfaceError, SurfaceTexture, TextureFormat, TextureUsages, TextureView, TextureViewDescriptor, util::TextureBlitter, }; @@ -86,7 +86,7 @@ pub struct SurfaceRenderer<'s> { pub surface: Surface<'s>, pub config: SurfaceConfiguration, - current_surface_texture: Option, + current_surface_texture: Option>, intermediate_texture: Option>, } @@ -189,30 +189,37 @@ impl<'s> SurfaceRenderer<'s> { .configure(&self.device_handle.device, &self.config); } - fn ensure_current_surface_texture(&mut self) { + pub fn ensure_current_surface_texture(&mut self) -> Result<(), SurfaceError> { if self.current_surface_texture.is_none() { - self.current_surface_texture = Some( - self.surface - .get_current_texture() - .expect("failed to get surface texture"), - ); + let tex = self.surface.get_current_texture(); + self.current_surface_texture = Some(tex); } + + self.current_surface_texture + .as_ref() + .unwrap() + .as_ref() + .map(|_| ()) + .map_err(|err| err.clone()) } /// Get a target texture view to render to. /// /// If there is an intermediate texture, this is a view of that intermediate texture, otherwise /// it is a view of the surface texture. - pub fn target_texture_view(&mut self) -> TextureView { + pub fn target_texture_view(&mut self) -> Result { match &self.intermediate_texture { - Some(intermediate_texture) => intermediate_texture.texture_view.clone(), + Some(intermediate_texture) => Ok(intermediate_texture.texture_view.clone()), None => { - self.ensure_current_surface_texture(); - self.current_surface_texture + self.ensure_current_surface_texture()?; + Ok(self + .current_surface_texture + .as_ref() + .unwrap() .as_ref() .unwrap() .texture - .create_view(&TextureViewDescriptor::default()) + .create_view(&TextureViewDescriptor::default())) } } } @@ -222,15 +229,17 @@ impl<'s> SurfaceRenderer<'s> { /// /// Prior to calling this, [`Self::target_texture_view`] must have been called and some /// rendering work must have been scheduled to the resulting view. - pub fn maybe_blit_and_present(&mut self) { - self.ensure_current_surface_texture(); - let surface_texture = self.current_surface_texture.take().unwrap(); + pub fn maybe_blit_and_present(&mut self) -> Result<(), SurfaceError> { + self.ensure_current_surface_texture()?; + let surface_texture = self.current_surface_texture.take().unwrap().unwrap(); if let Some(its) = &self.intermediate_texture { self.blit_from_intermediate_texture_to_surface(&surface_texture, its); } surface_texture.present(); + + Ok(()) } /// Blit from the intermediate texture to the surface texture From b0bdafa870ad2b7a1e863203c7322d609e8fc72f Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Tue, 17 Feb 2026 12:16:47 +0000 Subject: [PATCH 2/3] Reconfigure surface on Lost/Outdated error Signed-off-by: Nico Burns --- crates/wgpu_context/src/surface_renderer.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/wgpu_context/src/surface_renderer.rs b/crates/wgpu_context/src/surface_renderer.rs index 4dfe4fb..0a7849e 100644 --- a/crates/wgpu_context/src/surface_renderer.rs +++ b/crates/wgpu_context/src/surface_renderer.rs @@ -192,6 +192,11 @@ impl<'s> SurfaceRenderer<'s> { pub fn ensure_current_surface_texture(&mut self) -> Result<(), SurfaceError> { if self.current_surface_texture.is_none() { let tex = self.surface.get_current_texture(); + if let Err(SurfaceError::Lost | SurfaceError::Outdated) = &tex { + self.surface + .configure(&self.device_handle.device, &self.config); + } + self.current_surface_texture = Some(tex); } From f7d5b67c1f65f4e2f037216d3515e12bca8139e5 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Tue, 17 Feb 2026 12:44:02 +0000 Subject: [PATCH 3/3] Clear surface texture error before returning Signed-off-by: Nico Burns --- crates/anyrender_vello/src/window_renderer.rs | 5 ++++- crates/anyrender_vello_hybrid/src/window_renderer.rs | 5 ++++- crates/wgpu_context/src/surface_renderer.rs | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/anyrender_vello/src/window_renderer.rs b/crates/anyrender_vello/src/window_renderer.rs index aae3da7..f58a787 100644 --- a/crates/anyrender_vello/src/window_renderer.rs +++ b/crates/anyrender_vello/src/window_renderer.rs @@ -209,7 +209,10 @@ impl WindowRenderer for VelloWindowRenderer { match render_surface.ensure_current_surface_texture() { Ok(_) => {} - Err(SurfaceError::Timeout | SurfaceError::Lost | SurfaceError::Outdated) => return, + Err(SurfaceError::Timeout | SurfaceError::Lost | SurfaceError::Outdated) => { + render_surface.clear_surface_texture(); + return; + } Err(SurfaceError::OutOfMemory) => panic!("Out of memory"), Err(SurfaceError::Other) => panic!("Unknown error getting surface"), }; diff --git a/crates/anyrender_vello_hybrid/src/window_renderer.rs b/crates/anyrender_vello_hybrid/src/window_renderer.rs index 5ec218b..4e9b0c9 100644 --- a/crates/anyrender_vello_hybrid/src/window_renderer.rs +++ b/crates/anyrender_vello_hybrid/src/window_renderer.rs @@ -228,7 +228,10 @@ impl WindowRenderer for VelloHybridWindowRenderer { match render_surface.ensure_current_surface_texture() { Ok(_) => {} - Err(SurfaceError::Timeout | SurfaceError::Lost | SurfaceError::Outdated) => return, + Err(SurfaceError::Timeout | SurfaceError::Lost | SurfaceError::Outdated) => { + render_surface.clear_surface_texture(); + return; + } Err(SurfaceError::OutOfMemory) => panic!("Out of memory"), Err(SurfaceError::Other) => panic!("Unknown error getting surface"), }; diff --git a/crates/wgpu_context/src/surface_renderer.rs b/crates/wgpu_context/src/surface_renderer.rs index 0a7849e..659c0d8 100644 --- a/crates/wgpu_context/src/surface_renderer.rs +++ b/crates/wgpu_context/src/surface_renderer.rs @@ -189,6 +189,10 @@ impl<'s> SurfaceRenderer<'s> { .configure(&self.device_handle.device, &self.config); } + pub fn clear_surface_texture(&mut self) { + self.current_surface_texture = None; + } + pub fn ensure_current_surface_texture(&mut self) -> Result<(), SurfaceError> { if self.current_surface_texture.is_none() { let tex = self.surface.get_current_texture();