Merge pull request #667 from BillyDM/wgpu_outdatedframe
Don't panic when wgpu swapchain frame is outdated
This commit is contained in:
commit
45778ed598
5 changed files with 180 additions and 109 deletions
|
|
@ -172,55 +172,66 @@ 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::OutOfMemory => {
|
||||||
|
panic!("Swapchain error: {}. Rendering cannot continue.", error)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Try rendering again next frame.
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,59 +141,79 @@ 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(
|
||||||
|
&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"),
|
label: Some(
|
||||||
|
"iced_wgpu::window::Compositor render pass",
|
||||||
|
),
|
||||||
|
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||||
|
view: &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::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)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
}
|
||||||
|
|
||||||
let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: Some("iced_wgpu::window::Compositor render pass"),
|
|
||||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
|
||||||
view: &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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -366,27 +366,43 @@ 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 {
|
||||||
|
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!
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle animations!
|
|
||||||
// Maybe we can use `ControlFlow::WaitUntil` for this.
|
|
||||||
}
|
}
|
||||||
event::Event::WindowEvent {
|
event::Event::WindowEvent {
|
||||||
event: event::WindowEvent::MenuEntryActivated(entry_id),
|
event: event::WindowEvent::MenuEntryActivated(entry_id),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue