Skip to content

Commit 84bb9f0

Browse files
committed
fix(renderer): correctly handle lost/outdated surface errors
Previously, minimizing and then restoring the window would cause a `SurfaceError::Lost` panic. This was because the render loop attempted to rebuild render targets without first reconfiguring the underlying `wgpu::Surface`, which becomes invalid after being lost. This commit fixes the issue by: 1. Introducing a new `WgpuApp::resize_surface()` method that properly reconfigures the surface with the device and then rebuilds all dependent render targets (pass targets, MSAA textures, etc.). 2. Updating the main render loop in `renderer.rs` to call this new method upon receiving a `SurfaceError::Lost` or `SurfaceError::Outdated`, ensuring a full recovery. 3. Refactoring the existing `resize_if_needed` logic to use the new centralized `resize_surface` method for consistency.
1 parent bc0ede9 commit 84bb9f0

File tree

2 files changed

+53
-37
lines changed

2 files changed

+53
-37
lines changed

tessera-ui/src/renderer.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -723,10 +723,22 @@ Fps: {:.2}
723723
}
724724

725725
debug!("Rendering draw commands...");
726-
// Forward commands to WgpuApp::render which accepts the same iterator type
727-
args.app.render(commands).unwrap_or_else(|e| {
728-
error!("Render error: {e}");
729-
});
726+
if let Err(e) = args.app.render(commands) {
727+
match e {
728+
wgpu::SurfaceError::Outdated | wgpu::SurfaceError::Lost => {
729+
debug!("Surface outdated/lost, resizing...");
730+
args.app.resize_surface();
731+
}
732+
wgpu::SurfaceError::Timeout => warn!("Surface timeout. Frame will be dropped."),
733+
wgpu::SurfaceError::OutOfMemory => {
734+
error!("Surface out of memory. Panicking.");
735+
panic!("Surface out of memory");
736+
}
737+
_ => {
738+
error!("Surface error: {e}. Attempting to continue.");
739+
}
740+
}
741+
}
730742
let render_cost = render_timer.elapsed();
731743
debug!("Rendered to surface in {render_cost:?}");
732744
render_cost

tessera-ui/src/renderer/app.rs

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -365,48 +365,52 @@ impl WgpuApp {
365365
self.size
366366
}
367367

368-
pub(crate) fn resize_pass_targets_if_needed(&mut self) {
369-
if self.size_changed {
370-
self.pass_a.texture.destroy();
371-
self.pass_b.texture.destroy();
372-
self.compute_target_a.texture.destroy();
373-
self.compute_target_b.texture.destroy();
374-
375-
self.pass_a = Self::create_pass_target(&self.gpu, &self.config, "A");
376-
self.pass_b = Self::create_pass_target(&self.gpu, &self.config, "B");
377-
self.compute_target_a = Self::create_compute_pass_target(
378-
&self.gpu,
379-
&self.config,
380-
TextureFormat::Rgba8Unorm,
381-
"Compute A",
382-
);
383-
self.compute_target_b = Self::create_compute_pass_target(
384-
&self.gpu,
385-
&self.config,
386-
TextureFormat::Rgba8Unorm,
387-
"Compute B",
388-
);
368+
pub(crate) fn resize_surface(&mut self) {
369+
if self.size.width > 0 && self.size.height > 0 {
370+
self.config.width = self.size.width;
371+
self.config.height = self.size.height;
372+
self.surface.configure(&self.gpu, &self.config);
373+
self.rebuild_pass_targets();
374+
}
375+
}
389376

390-
if self.sample_count > 1 {
391-
if let Some(t) = self.msaa_texture.take() {
392-
t.destroy();
393-
}
394-
let (msaa_texture, msaa_view) =
395-
Self::make_msaa_resources(&self.gpu, self.sample_count, &self.config);
396-
self.msaa_texture = msaa_texture;
397-
self.msaa_view = msaa_view;
377+
pub(crate) fn rebuild_pass_targets(&mut self) {
378+
self.pass_a.texture.destroy();
379+
self.pass_b.texture.destroy();
380+
self.compute_target_a.texture.destroy();
381+
self.compute_target_b.texture.destroy();
382+
383+
self.pass_a = Self::create_pass_target(&self.gpu, &self.config, "A");
384+
self.pass_b = Self::create_pass_target(&self.gpu, &self.config, "B");
385+
self.compute_target_a = Self::create_compute_pass_target(
386+
&self.gpu,
387+
&self.config,
388+
TextureFormat::Rgba8Unorm,
389+
"Compute A",
390+
);
391+
self.compute_target_b = Self::create_compute_pass_target(
392+
&self.gpu,
393+
&self.config,
394+
TextureFormat::Rgba8Unorm,
395+
"Compute B",
396+
);
397+
398+
if self.sample_count > 1 {
399+
if let Some(t) = self.msaa_texture.take() {
400+
t.destroy();
398401
}
402+
let (msaa_texture, msaa_view) =
403+
Self::make_msaa_resources(&self.gpu, self.sample_count, &self.config);
404+
self.msaa_texture = msaa_texture;
405+
self.msaa_view = msaa_view;
399406
}
400407
}
401408

402409
/// Resize the surface if needed.
403410
pub(crate) fn resize_if_needed(&mut self) -> bool {
404411
let result = self.size_changed;
405412
if self.size_changed {
406-
self.config.width = self.size.width;
407-
self.config.height = self.size.height;
408-
self.resize_pass_targets_if_needed();
409-
self.surface.configure(&self.gpu, &self.config);
413+
self.resize_surface();
410414
self.size_changed = false;
411415
}
412416
result

0 commit comments

Comments
 (0)