Skip to content

Commit 45778ed

Browse files
authored
Merge pull request #667 from BillyDM/wgpu_outdatedframe
Don't panic when wgpu swapchain frame is outdated
2 parents 63bdbf8 + 3e03a42 commit 45778ed

File tree

5 files changed

+181
-110
lines changed

5 files changed

+181
-110
lines changed

examples/integration/src/main.rs

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -172,55 +172,66 @@ pub fn main() {
172172
resized = false;
173173
}
174174

175-
let frame = swap_chain.get_current_frame().expect("Next frame");
175+
match swap_chain.get_current_frame() {
176+
Ok(frame) => {
177+
let mut encoder = device.create_command_encoder(
178+
&wgpu::CommandEncoderDescriptor { label: None },
179+
);
176180

177-
let mut encoder = device.create_command_encoder(
178-
&wgpu::CommandEncoderDescriptor { label: None },
179-
);
181+
let program = state.program();
182+
183+
{
184+
// We clear the frame
185+
let mut render_pass = scene.clear(
186+
&frame.output.view,
187+
&mut encoder,
188+
program.background_color(),
189+
);
190+
191+
// Draw the scene
192+
scene.draw(&mut render_pass);
193+
}
194+
195+
// And then iced on top
196+
let mouse_interaction = renderer.backend_mut().draw(
197+
&mut device,
198+
&mut staging_belt,
199+
&mut encoder,
200+
&frame.output.view,
201+
&viewport,
202+
state.primitive(),
203+
&debug.overlay(),
204+
);
180205

181-
let program = state.program();
206+
// Then we submit the work
207+
staging_belt.finish();
208+
queue.submit(Some(encoder.finish()));
182209

183-
{
184-
// We clear the frame
185-
let mut render_pass = scene.clear(
186-
&frame.output.view,
187-
&mut encoder,
188-
program.background_color(),
189-
);
210+
// Update the mouse cursor
211+
window.set_cursor_icon(
212+
iced_winit::conversion::mouse_interaction(
213+
mouse_interaction,
214+
),
215+
);
190216

191-
// Draw the scene
192-
scene.draw(&mut render_pass);
193-
}
217+
// And recall staging buffers
218+
local_pool
219+
.spawner()
220+
.spawn(staging_belt.recall())
221+
.expect("Recall staging buffers");
194222

195-
// And then iced on top
196-
let mouse_interaction = renderer.backend_mut().draw(
197-
&mut device,
198-
&mut staging_belt,
199-
&mut encoder,
200-
&frame.output.view,
201-
&viewport,
202-
state.primitive(),
203-
&debug.overlay(),
204-
);
205-
206-
// Then we submit the work
207-
staging_belt.finish();
208-
queue.submit(Some(encoder.finish()));
209-
210-
// Update the mouse cursor
211-
window.set_cursor_icon(
212-
iced_winit::conversion::mouse_interaction(
213-
mouse_interaction,
214-
),
215-
);
216-
217-
// And recall staging buffers
218-
local_pool
219-
.spawner()
220-
.spawn(staging_belt.recall())
221-
.expect("Recall staging buffers");
222-
223-
local_pool.run_until_stalled();
223+
local_pool.run_until_stalled();
224+
}
225+
Err(error) => match error {
226+
wgpu::SwapChainError::OutOfMemory => {
227+
panic!("Swapchain error: {}. Rendering cannot continue.", error)
228+
}
229+
_ => {
230+
// Try rendering again next frame.
231+
window.request_redraw();
232+
}
233+
},
234+
}
224235
}
225236
_ => {}
226237
}

graphics/src/window.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ mod compositor;
44
#[cfg(feature = "opengl")]
55
mod gl_compositor;
66

7-
pub use compositor::Compositor;
7+
pub use compositor::{Compositor, SwapChainError};
88

99
#[cfg(feature = "opengl")]
1010
pub use gl_compositor::GLCompositor;

graphics/src/window/compositor.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use crate::{Color, Error, Viewport};
2+
23
use iced_native::mouse;
4+
35
use raw_window_handle::HasRawWindowHandle;
6+
use thiserror::Error;
47

58
/// A graphics compositor that can draw to windows.
69
pub trait Compositor: Sized {
@@ -52,5 +55,26 @@ pub trait Compositor: Sized {
5255
background_color: Color,
5356
output: &<Self::Renderer as iced_native::Renderer>::Output,
5457
overlay: &[T],
55-
) -> mouse::Interaction;
58+
) -> Result<mouse::Interaction, SwapChainError>;
59+
}
60+
61+
/// Result of an unsuccessful call to [`Compositor::draw`].
62+
#[derive(Clone, PartialEq, Eq, Debug, Error)]
63+
pub enum SwapChainError {
64+
/// A timeout was encountered while trying to acquire the next frame.
65+
#[error(
66+
"A timeout was encountered while trying to acquire the next frame"
67+
)]
68+
Timeout,
69+
/// The underlying surface has changed, and therefore the swap chain must be updated.
70+
#[error(
71+
"The underlying surface has changed, and therefore the swap chain must be updated."
72+
)]
73+
Outdated,
74+
/// The swap chain has been lost and needs to be recreated.
75+
#[error("The swap chain has been lost and needs to be recreated")]
76+
Lost,
77+
/// There is no more memory left to allocate a new frame.
78+
#[error("There is no more memory left to allocate a new frame")]
79+
OutOfMemory,
5680
}

wgpu/src/window/compositor.rs

Lines changed: 73 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -141,59 +141,79 @@ impl iced_graphics::window::Compositor for Compositor {
141141
background_color: Color,
142142
output: &<Self::Renderer as iced_native::Renderer>::Output,
143143
overlay: &[T],
144-
) -> mouse::Interaction {
145-
let frame = swap_chain.get_current_frame().expect("Next frame");
146-
147-
let mut encoder = self.device.create_command_encoder(
148-
&wgpu::CommandEncoderDescriptor {
149-
label: Some("iced_wgpu encoder"),
144+
) -> Result<mouse::Interaction, iced_graphics::window::SwapChainError> {
145+
match swap_chain.get_current_frame() {
146+
Ok(frame) => {
147+
let mut encoder = self.device.create_command_encoder(
148+
&wgpu::CommandEncoderDescriptor {
149+
label: Some("iced_wgpu encoder"),
150+
},
151+
);
152+
153+
let _ =
154+
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
155+
label: Some(
156+
"iced_wgpu::window::Compositor render pass",
157+
),
158+
color_attachments: &[wgpu::RenderPassColorAttachment {
159+
view: &frame.output.view,
160+
resolve_target: None,
161+
ops: wgpu::Operations {
162+
load: wgpu::LoadOp::Clear({
163+
let [r, g, b, a] =
164+
background_color.into_linear();
165+
166+
wgpu::Color {
167+
r: f64::from(r),
168+
g: f64::from(g),
169+
b: f64::from(b),
170+
a: f64::from(a),
171+
}
172+
}),
173+
store: true,
174+
},
175+
}],
176+
depth_stencil_attachment: None,
177+
});
178+
179+
let mouse_interaction = renderer.backend_mut().draw(
180+
&mut self.device,
181+
&mut self.staging_belt,
182+
&mut encoder,
183+
&frame.output.view,
184+
viewport,
185+
output,
186+
overlay,
187+
);
188+
189+
// Submit work
190+
self.staging_belt.finish();
191+
self.queue.submit(Some(encoder.finish()));
192+
193+
// Recall staging buffers
194+
self.local_pool
195+
.spawner()
196+
.spawn(self.staging_belt.recall())
197+
.expect("Recall staging belt");
198+
199+
self.local_pool.run_until_stalled();
200+
201+
Ok(mouse_interaction)
202+
}
203+
Err(error) => match error {
204+
wgpu::SwapChainError::Timeout => {
205+
Err(iced_graphics::window::SwapChainError::Timeout)
206+
}
207+
wgpu::SwapChainError::Outdated => {
208+
Err(iced_graphics::window::SwapChainError::Outdated)
209+
}
210+
wgpu::SwapChainError::Lost => {
211+
Err(iced_graphics::window::SwapChainError::Lost)
212+
}
213+
wgpu::SwapChainError::OutOfMemory => {
214+
Err(iced_graphics::window::SwapChainError::OutOfMemory)
215+
}
150216
},
151-
);
152-
153-
let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
154-
label: Some("iced_wgpu::window::Compositor render pass"),
155-
color_attachments: &[wgpu::RenderPassColorAttachment {
156-
view: &frame.output.view,
157-
resolve_target: None,
158-
ops: wgpu::Operations {
159-
load: wgpu::LoadOp::Clear({
160-
let [r, g, b, a] = background_color.into_linear();
161-
162-
wgpu::Color {
163-
r: f64::from(r),
164-
g: f64::from(g),
165-
b: f64::from(b),
166-
a: f64::from(a),
167-
}
168-
}),
169-
store: true,
170-
},
171-
}],
172-
depth_stencil_attachment: None,
173-
});
174-
175-
let mouse_interaction = renderer.backend_mut().draw(
176-
&mut self.device,
177-
&mut self.staging_belt,
178-
&mut encoder,
179-
&frame.output.view,
180-
viewport,
181-
output,
182-
overlay,
183-
);
184-
185-
// Submit work
186-
self.staging_belt.finish();
187-
self.queue.submit(Some(encoder.finish()));
188-
189-
// Recall staging buffers
190-
self.local_pool
191-
.spawner()
192-
.spawn(self.staging_belt.recall())
193-
.expect("Recall staging belt");
194-
195-
self.local_pool.run_until_stalled();
196-
197-
mouse_interaction
217+
}
198218
}
199219
}

winit/src/application.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -366,27 +366,43 @@ async fn run_instance<A, E, C>(
366366
viewport_version = current_viewport_version;
367367
}
368368

369-
let new_mouse_interaction = compositor.draw(
369+
match compositor.draw(
370370
&mut renderer,
371371
&mut swap_chain,
372372
state.viewport(),
373373
state.background_color(),
374374
&primitive,
375375
&debug.overlay(),
376-
);
376+
) {
377+
Ok(new_mouse_interaction) => {
378+
debug.render_finished();
377379

378-
debug.render_finished();
380+
if new_mouse_interaction != mouse_interaction {
381+
window.set_cursor_icon(
382+
conversion::mouse_interaction(
383+
new_mouse_interaction,
384+
),
385+
);
379386

380-
if new_mouse_interaction != mouse_interaction {
381-
window.set_cursor_icon(conversion::mouse_interaction(
382-
new_mouse_interaction,
383-
));
387+
mouse_interaction = new_mouse_interaction;
388+
}
384389

385-
mouse_interaction = new_mouse_interaction;
390+
// TODO: Handle animations!
391+
// Maybe we can use `ControlFlow::WaitUntil` for this.
392+
}
393+
Err(error) => match error {
394+
// This is an unrecoverable error.
395+
window::SwapChainError::OutOfMemory => {
396+
panic!("{}", error);
397+
}
398+
_ => {
399+
debug.render_finished();
400+
401+
// Try rendering again next frame.
402+
window.request_redraw();
403+
}
404+
},
386405
}
387-
388-
// TODO: Handle animations!
389-
// Maybe we can use `ControlFlow::WaitUntil` for this.
390406
}
391407
event::Event::WindowEvent {
392408
event: event::WindowEvent::MenuEntryActivated(entry_id),

0 commit comments

Comments
 (0)