Skip to content

Commit 0691c2d

Browse files
authored
Handle transient WGPU SurfaceErrors without panicking (#46)
* Handle WGPU SurfaceErrors better Signed-off-by: Nico Burns <nico@nicoburns.com> * Reconfigure surface on Lost/Outdated error Signed-off-by: Nico Burns <nico@nicoburns.com> * Clear surface texture error before returning Signed-off-by: Nico Burns <nico@nicoburns.com> --------- Signed-off-by: Nico Burns <nico@nicoburns.com>
1 parent e78e467 commit 0691c2d

3 files changed

Lines changed: 67 additions & 21 deletions

File tree

crates/anyrender_vello/src/window_renderer.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use vello::{
1010
AaConfig, AaSupport, RenderParams, Renderer as VelloRenderer, RendererOptions,
1111
Scene as VelloScene,
1212
};
13-
use wgpu::{Features, Limits, PresentMode, TextureFormat, TextureUsages};
13+
use wgpu::{Features, Limits, PresentMode, SurfaceError, TextureFormat, TextureUsages};
1414
use wgpu_context::{
1515
DeviceHandle, SurfaceRenderer, SurfaceRendererConfiguration, TextureConfiguration, WGPUContext,
1616
};
@@ -207,7 +207,19 @@ impl WindowRenderer for VelloWindowRenderer {
207207
});
208208
timer.record_time("cmd");
209209

210-
let texture_view = render_surface.target_texture_view();
210+
match render_surface.ensure_current_surface_texture() {
211+
Ok(_) => {}
212+
Err(SurfaceError::Timeout | SurfaceError::Lost | SurfaceError::Outdated) => {
213+
render_surface.clear_surface_texture();
214+
return;
215+
}
216+
Err(SurfaceError::OutOfMemory) => panic!("Out of memory"),
217+
Err(SurfaceError::Other) => panic!("Unknown error getting surface"),
218+
};
219+
220+
let texture_view = render_surface
221+
.target_texture_view()
222+
.expect("handled errorss from ensure_current_surface_texture above");
211223
state
212224
.renderer
213225
.render_to_texture(
@@ -227,7 +239,9 @@ impl WindowRenderer for VelloWindowRenderer {
227239

228240
drop(texture_view);
229241

230-
render_surface.maybe_blit_and_present();
242+
render_surface
243+
.maybe_blit_and_present()
244+
.expect("handled errorss from ensure_current_surface_texture above");
231245
timer.record_time("present");
232246

233247
render_surface

crates/anyrender_vello_hybrid/src/window_renderer.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use vello_hybrid::{
1010
RenderSettings, RenderSize, RenderTargetConfig, Renderer as VelloHybridRenderer,
1111
Scene as VelloHybridScene,
1212
};
13-
use wgpu::{CommandEncoderDescriptor, Features, Limits, PresentMode, TextureFormat};
13+
use wgpu::{CommandEncoderDescriptor, Features, Limits, PresentMode, SurfaceError, TextureFormat};
1414
use wgpu_context::{DeviceHandle, SurfaceRenderer, SurfaceRendererConfiguration, WGPUContext};
1515

1616
use crate::{VelloHybridScenePainter, scene::ImageManager};
@@ -226,7 +226,19 @@ impl WindowRenderer for VelloHybridWindowRenderer {
226226
});
227227
timer.record_time("cmd");
228228

229-
let texture_view = render_surface.target_texture_view();
229+
match render_surface.ensure_current_surface_texture() {
230+
Ok(_) => {}
231+
Err(SurfaceError::Timeout | SurfaceError::Lost | SurfaceError::Outdated) => {
232+
render_surface.clear_surface_texture();
233+
return;
234+
}
235+
Err(SurfaceError::OutOfMemory) => panic!("Out of memory"),
236+
Err(SurfaceError::Other) => panic!("Unknown error getting surface"),
237+
};
238+
239+
let texture_view = render_surface
240+
.target_texture_view()
241+
.expect("handled errorss from ensure_current_surface_texture above");
230242

231243
state
232244
.renderer
@@ -247,7 +259,9 @@ impl WindowRenderer for VelloHybridWindowRenderer {
247259

248260
drop(texture_view);
249261

250-
render_surface.maybe_blit_and_present();
262+
render_surface
263+
.maybe_blit_and_present()
264+
.expect("handled errorss from ensure_current_surface_texture above");
251265
timer.record_time("present");
252266

253267
render_surface

crates/wgpu_context/src/surface_renderer.rs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{DeviceHandle, WgpuContextError, util::create_texture};
22
use wgpu::{
33
CommandEncoderDescriptor, CompositeAlphaMode, Device, PresentMode, Queue, Surface,
4-
SurfaceConfiguration, SurfaceTexture, TextureFormat, TextureUsages, TextureView,
4+
SurfaceConfiguration, SurfaceError, SurfaceTexture, TextureFormat, TextureUsages, TextureView,
55
TextureViewDescriptor, util::TextureBlitter,
66
};
77

@@ -86,7 +86,7 @@ pub struct SurfaceRenderer<'s> {
8686
pub surface: Surface<'s>,
8787
pub config: SurfaceConfiguration,
8888

89-
current_surface_texture: Option<SurfaceTexture>,
89+
current_surface_texture: Option<Result<SurfaceTexture, SurfaceError>>,
9090
intermediate_texture: Option<Box<IntermediateTextureStuff>>,
9191
}
9292

@@ -189,30 +189,46 @@ impl<'s> SurfaceRenderer<'s> {
189189
.configure(&self.device_handle.device, &self.config);
190190
}
191191

192-
fn ensure_current_surface_texture(&mut self) {
192+
pub fn clear_surface_texture(&mut self) {
193+
self.current_surface_texture = None;
194+
}
195+
196+
pub fn ensure_current_surface_texture(&mut self) -> Result<(), SurfaceError> {
193197
if self.current_surface_texture.is_none() {
194-
self.current_surface_texture = Some(
198+
let tex = self.surface.get_current_texture();
199+
if let Err(SurfaceError::Lost | SurfaceError::Outdated) = &tex {
195200
self.surface
196-
.get_current_texture()
197-
.expect("failed to get surface texture"),
198-
);
201+
.configure(&self.device_handle.device, &self.config);
202+
}
203+
204+
self.current_surface_texture = Some(tex);
199205
}
206+
207+
self.current_surface_texture
208+
.as_ref()
209+
.unwrap()
210+
.as_ref()
211+
.map(|_| ())
212+
.map_err(|err| err.clone())
200213
}
201214

202215
/// Get a target texture view to render to.
203216
///
204217
/// If there is an intermediate texture, this is a view of that intermediate texture, otherwise
205218
/// it is a view of the surface texture.
206-
pub fn target_texture_view(&mut self) -> TextureView {
219+
pub fn target_texture_view(&mut self) -> Result<TextureView, SurfaceError> {
207220
match &self.intermediate_texture {
208-
Some(intermediate_texture) => intermediate_texture.texture_view.clone(),
221+
Some(intermediate_texture) => Ok(intermediate_texture.texture_view.clone()),
209222
None => {
210-
self.ensure_current_surface_texture();
211-
self.current_surface_texture
223+
self.ensure_current_surface_texture()?;
224+
Ok(self
225+
.current_surface_texture
226+
.as_ref()
227+
.unwrap()
212228
.as_ref()
213229
.unwrap()
214230
.texture
215-
.create_view(&TextureViewDescriptor::default())
231+
.create_view(&TextureViewDescriptor::default()))
216232
}
217233
}
218234
}
@@ -222,15 +238,17 @@ impl<'s> SurfaceRenderer<'s> {
222238
///
223239
/// Prior to calling this, [`Self::target_texture_view`] must have been called and some
224240
/// rendering work must have been scheduled to the resulting view.
225-
pub fn maybe_blit_and_present(&mut self) {
226-
self.ensure_current_surface_texture();
227-
let surface_texture = self.current_surface_texture.take().unwrap();
241+
pub fn maybe_blit_and_present(&mut self) -> Result<(), SurfaceError> {
242+
self.ensure_current_surface_texture()?;
243+
let surface_texture = self.current_surface_texture.take().unwrap().unwrap();
228244

229245
if let Some(its) = &self.intermediate_texture {
230246
self.blit_from_intermediate_texture_to_surface(&surface_texture, its);
231247
}
232248

233249
surface_texture.present();
250+
251+
Ok(())
234252
}
235253

236254
/// Blit from the intermediate texture to the surface texture

0 commit comments

Comments
 (0)