Merge pull request #667 from BillyDM/wgpu_outdatedframe

Don't panic when wgpu swapchain frame is outdated
This commit is contained in:
Héctor Ramón 2021-08-05 14:44:32 +07:00 committed by GitHub
commit 45778ed598
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 180 additions and 109 deletions

View file

@ -172,8 +172,8 @@ 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( let mut encoder = device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { label: None }, &wgpu::CommandEncoderDescriptor { label: None },
); );
@ -222,6 +222,17 @@ pub fn main() {
local_pool.run_until_stalled(); local_pool.run_until_stalled();
} }
Err(error) => match error {
wgpu::SwapChainError::OutOfMemory => {
panic!("Swapchain error: {}. Rendering cannot continue.", error)
}
_ => {
// Try rendering again next frame.
window.request_redraw();
}
},
}
}
_ => {} _ => {}
} }
}) })

View file

@ -4,7 +4,7 @@ mod compositor;
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
mod gl_compositor; mod gl_compositor;
pub use compositor::Compositor; pub use compositor::{Compositor, SwapChainError};
#[cfg(feature = "opengl")] #[cfg(feature = "opengl")]
pub use gl_compositor::GLCompositor; pub use gl_compositor::GLCompositor;

View file

@ -1,6 +1,9 @@
use crate::{Color, Error, Viewport}; use crate::{Color, Error, Viewport};
use iced_native::mouse; use iced_native::mouse;
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
use thiserror::Error;
/// A graphics compositor that can draw to windows. /// A graphics compositor that can draw to windows.
pub trait Compositor: Sized { pub trait Compositor: Sized {
@ -52,5 +55,26 @@ 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, SwapChainError>;
}
/// Result of an unsuccessful call to [`Compositor::draw`].
#[derive(Clone, PartialEq, Eq, Debug, Error)]
pub enum SwapChainError {
/// A timeout was encountered while trying to acquire the next frame.
#[error(
"A timeout was encountered while trying to acquire the next frame"
)]
Timeout,
/// The underlying surface has changed, and therefore the swap chain must be updated.
#[error(
"The underlying surface has changed, and therefore the swap chain must be updated."
)]
Outdated,
/// The swap chain has been lost and needs to be recreated.
#[error("The swap chain has been lost and needs to be recreated")]
Lost,
/// There is no more memory left to allocate a new frame.
#[error("There is no more memory left to allocate a new frame")]
OutOfMemory,
} }

View file

@ -141,23 +141,27 @@ 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, iced_graphics::window::SwapChainError> {
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( let mut encoder = self.device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { &wgpu::CommandEncoderDescriptor {
label: Some("iced_wgpu encoder"), label: Some("iced_wgpu encoder"),
}, },
); );
let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let _ =
label: Some("iced_wgpu::window::Compositor render pass"), encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some(
"iced_wgpu::window::Compositor render pass",
),
color_attachments: &[wgpu::RenderPassColorAttachment { color_attachments: &[wgpu::RenderPassColorAttachment {
view: &frame.output.view, view: &frame.output.view,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Clear({ load: wgpu::LoadOp::Clear({
let [r, g, b, a] = background_color.into_linear(); let [r, g, b, a] =
background_color.into_linear();
wgpu::Color { wgpu::Color {
r: f64::from(r), r: f64::from(r),
@ -194,6 +198,22 @@ impl iced_graphics::window::Compositor for Compositor {
self.local_pool.run_until_stalled(); self.local_pool.run_until_stalled();
mouse_interaction Ok(mouse_interaction)
}
Err(error) => match error {
wgpu::SwapChainError::Timeout => {
Err(iced_graphics::window::SwapChainError::Timeout)
}
wgpu::SwapChainError::Outdated => {
Err(iced_graphics::window::SwapChainError::Outdated)
}
wgpu::SwapChainError::Lost => {
Err(iced_graphics::window::SwapChainError::Lost)
}
wgpu::SwapChainError::OutOfMemory => {
Err(iced_graphics::window::SwapChainError::OutOfMemory)
}
},
}
} }
} }

View file

@ -366,21 +366,23 @@ async fn run_instance<A, E, C>(
viewport_version = current_viewport_version; viewport_version = current_viewport_version;
} }
let new_mouse_interaction = compositor.draw( match 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(),
); ) {
Ok(new_mouse_interaction) => {
debug.render_finished(); debug.render_finished();
if new_mouse_interaction != mouse_interaction { if new_mouse_interaction != mouse_interaction {
window.set_cursor_icon(conversion::mouse_interaction( window.set_cursor_icon(
conversion::mouse_interaction(
new_mouse_interaction, new_mouse_interaction,
)); ),
);
mouse_interaction = new_mouse_interaction; mouse_interaction = new_mouse_interaction;
} }
@ -388,6 +390,20 @@ async fn run_instance<A, E, C>(
// TODO: Handle animations! // TODO: Handle animations!
// Maybe we can use `ControlFlow::WaitUntil` for this. // Maybe we can use `ControlFlow::WaitUntil` for this.
} }
Err(error) => match error {
// This is an unrecoverable error.
window::SwapChainError::OutOfMemory => {
panic!("{}", error);
}
_ => {
debug.render_finished();
// Try rendering again next frame.
window.request_redraw();
}
},
}
}
event::Event::WindowEvent { event::Event::WindowEvent {
event: event::WindowEvent::MenuEntryActivated(entry_id), event: event::WindowEvent::MenuEntryActivated(entry_id),
.. ..