don't panic when swapchain frame is outdated

This commit is contained in:
Billy Messenger 2020-12-16 10:03:51 -06:00
parent a42b3c6998
commit 4e391013c8
4 changed files with 140 additions and 108 deletions

View file

@ -168,55 +168,64 @@ pub fn main() {
resized = false; resized = false;
} }
let frame = swap_chain.get_current_frame().expect("Next frame"); match swap_chain.get_current_frame() {
Ok(frame) => {
let mut encoder = device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { label: None },
);
let mut encoder = device.create_command_encoder( let program = state.program();
&wgpu::CommandEncoderDescriptor { label: None },
);
let program = state.program(); {
// We clear the frame
let mut render_pass = scene.clear(
&frame.output.view,
&mut encoder,
program.background_color(),
);
{ // Draw the scene
// We clear the frame scene.draw(&mut render_pass);
let mut render_pass = scene.clear( }
&frame.output.view,
&mut encoder,
program.background_color(),
);
// Draw the scene // And then iced on top
scene.draw(&mut render_pass); let mouse_interaction = renderer.backend_mut().draw(
&mut device,
&mut staging_belt,
&mut encoder,
&frame.output.view,
&viewport,
state.primitive(),
&debug.overlay(),
);
// Then we submit the work
staging_belt.finish();
queue.submit(Some(encoder.finish()));
// Update the mouse cursor
window.set_cursor_icon(
iced_winit::conversion::mouse_interaction(
mouse_interaction,
),
);
// And recall staging buffers
local_pool
.spawner()
.spawn(staging_belt.recall())
.expect("Recall staging buffers");
local_pool.run_until_stalled();
}
Err(error) => match error {
wgpu::SwapChainError::Outdated => {
// Try rendering again next frame.
window.request_redraw();
}
_ => panic!("Swapchain error: {:?}", error),
},
} }
// And then iced on top
let mouse_interaction = renderer.backend_mut().draw(
&mut device,
&mut staging_belt,
&mut encoder,
&frame.output.view,
&viewport,
state.primitive(),
&debug.overlay(),
);
// Then we submit the work
staging_belt.finish();
queue.submit(Some(encoder.finish()));
// Update the mouse cursor
window.set_cursor_icon(
iced_winit::conversion::mouse_interaction(
mouse_interaction,
),
);
// And recall staging buffers
local_pool
.spawner()
.spawn(staging_belt.recall())
.expect("Recall staging buffers");
local_pool.run_until_stalled();
} }
_ => {} _ => {}
} }

View file

@ -40,6 +40,9 @@ pub trait Compositor: Sized {
/// Draws the output primitives to the next frame of the given [`SwapChain`]. /// Draws the output primitives to the next frame of the given [`SwapChain`].
/// ///
/// This will return an error if drawing could not be completed on this frame.
/// If an error occurs, try calling this again on the next frame.
///
/// [`SwapChain`]: Self::SwapChain /// [`SwapChain`]: Self::SwapChain
fn draw<T: AsRef<str>>( fn draw<T: AsRef<str>>(
&mut self, &mut self,
@ -49,5 +52,5 @@ pub trait Compositor: Sized {
background_color: Color, background_color: Color,
output: &<Self::Renderer as iced_native::Renderer>::Output, output: &<Self::Renderer as iced_native::Renderer>::Output,
overlay: &[T], overlay: &[T],
) -> mouse::Interaction; ) -> Result<mouse::Interaction, ()>;
} }

View file

@ -100,7 +100,7 @@ impl iced_graphics::window::Compositor for Compositor {
width: u32, width: u32,
height: u32, height: u32,
) -> Self::SwapChain { ) -> Self::SwapChain {
self.device.create_swap_chain( let swap_chain = self.device.create_swap_chain(
surface, surface,
&wgpu::SwapChainDescriptor { &wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
@ -109,7 +109,9 @@ impl iced_graphics::window::Compositor for Compositor {
width, width,
height, height,
}, },
) );
swap_chain
} }
fn draw<T: AsRef<str>>( fn draw<T: AsRef<str>>(
@ -120,58 +122,71 @@ impl iced_graphics::window::Compositor for Compositor {
background_color: Color, background_color: Color,
output: &<Self::Renderer as iced_native::Renderer>::Output, output: &<Self::Renderer as iced_native::Renderer>::Output,
overlay: &[T], overlay: &[T],
) -> mouse::Interaction { ) -> Result<mouse::Interaction, ()> {
let frame = swap_chain.get_current_frame().expect("Next frame"); match swap_chain.get_current_frame() {
Ok(frame) => {
let mut encoder = self.device.create_command_encoder(
&wgpu::CommandEncoderDescriptor {
label: Some("iced_wgpu encoder"),
},
);
let mut encoder = self.device.create_command_encoder( let _ =
&wgpu::CommandEncoderDescriptor { encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("iced_wgpu encoder"), color_attachments: &[
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.output.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear({
let [r, g, b, a] =
background_color.into_linear();
wgpu::Color {
r: f64::from(r),
g: f64::from(g),
b: f64::from(b),
a: f64::from(a),
}
}),
store: true,
},
},
],
depth_stencil_attachment: None,
});
let mouse_interaction = renderer.backend_mut().draw(
&mut self.device,
&mut self.staging_belt,
&mut encoder,
&frame.output.view,
viewport,
output,
overlay,
);
// Submit work
self.staging_belt.finish();
self.queue.submit(Some(encoder.finish()));
// Recall staging buffers
self.local_pool
.spawner()
.spawn(self.staging_belt.recall())
.expect("Recall staging belt");
self.local_pool.run_until_stalled();
Ok(mouse_interaction)
}
Err(error) => match error {
wgpu::SwapChainError::Outdated => {
// Try again next frame.
Err(())
}
_ => panic!("Swapchain error: {:?}", error),
}, },
); }
let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.output.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear({
let [r, g, b, a] = background_color.into_linear();
wgpu::Color {
r: f64::from(r),
g: f64::from(g),
b: f64::from(b),
a: f64::from(a),
}
}),
store: true,
},
}],
depth_stencil_attachment: None,
});
let mouse_interaction = renderer.backend_mut().draw(
&mut self.device,
&mut self.staging_belt,
&mut encoder,
&frame.output.view,
viewport,
output,
overlay,
);
// Submit work
self.staging_belt.finish();
self.queue.submit(Some(encoder.finish()));
// Recall staging buffers
self.local_pool
.spawner()
.spawn(self.staging_belt.recall())
.expect("Recall staging belt");
self.local_pool.run_until_stalled();
mouse_interaction
} }
} }

View file

@ -311,27 +311,32 @@ async fn run_instance<A, E, C>(
viewport_version = current_viewport_version; viewport_version = current_viewport_version;
} }
let new_mouse_interaction = compositor.draw( if let Ok(new_mouse_interaction) = compositor.draw(
&mut renderer, &mut renderer,
&mut swap_chain, &mut swap_chain,
state.viewport(), state.viewport(),
state.background_color(), state.background_color(),
&primitive, &primitive,
&debug.overlay(), &debug.overlay(),
); ) {
debug.render_finished();
debug.render_finished(); if new_mouse_interaction != mouse_interaction {
window.set_cursor_icon(conversion::mouse_interaction(
new_mouse_interaction,
));
if new_mouse_interaction != mouse_interaction { mouse_interaction = new_mouse_interaction;
window.set_cursor_icon(conversion::mouse_interaction( }
new_mouse_interaction,
));
mouse_interaction = new_mouse_interaction;
}
// TODO: Handle animations! // TODO: Handle animations!
// Maybe we can use `ControlFlow::WaitUntil` for this. // Maybe we can use `ControlFlow::WaitUntil` for this.
} else {
debug.render_finished();
// Rendering could not complete, try again next frame.
window.request_redraw();
}
} }
event::Event::WindowEvent { event::Event::WindowEvent {
event: window_event, event: window_event,