diff options
-rw-r--r-- | examples/integration/src/main.rs | 99 | ||||
-rw-r--r-- | graphics/src/window.rs | 2 | ||||
-rw-r--r-- | graphics/src/window/compositor.rs | 26 | ||||
-rw-r--r-- | wgpu/src/window/compositor.rs | 126 | ||||
-rw-r--r-- | winit/src/application.rs | 38 |
5 files changed, 181 insertions, 110 deletions
diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index b4f580a4..6f319466 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -172,55 +172,66 @@ pub fn main() { 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( - &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 + scene.draw(&mut render_pass); + } + + // 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(), + ); - let program = state.program(); + // Then we submit the work + staging_belt.finish(); + queue.submit(Some(encoder.finish())); - { - // We clear the frame - let mut render_pass = scene.clear( - &frame.output.view, - &mut encoder, - program.background_color(), - ); + // Update the mouse cursor + window.set_cursor_icon( + iced_winit::conversion::mouse_interaction( + mouse_interaction, + ), + ); - // Draw the scene - scene.draw(&mut render_pass); - } + // And recall staging buffers + local_pool + .spawner() + .spawn(staging_belt.recall()) + .expect("Recall staging buffers"); - // 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(); + 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(); + } + }, + } } _ => {} } diff --git a/graphics/src/window.rs b/graphics/src/window.rs index 3e74db5f..365ddfbc 100644 --- a/graphics/src/window.rs +++ b/graphics/src/window.rs @@ -4,7 +4,7 @@ mod compositor; #[cfg(feature = "opengl")] mod gl_compositor; -pub use compositor::Compositor; +pub use compositor::{Compositor, SwapChainError}; #[cfg(feature = "opengl")] pub use gl_compositor::GLCompositor; diff --git a/graphics/src/window/compositor.rs b/graphics/src/window/compositor.rs index 7d5d789b..de2a6990 100644 --- a/graphics/src/window/compositor.rs +++ b/graphics/src/window/compositor.rs @@ -1,6 +1,9 @@ use crate::{Color, Error, Viewport}; + use iced_native::mouse; + use raw_window_handle::HasRawWindowHandle; +use thiserror::Error; /// A graphics compositor that can draw to windows. pub trait Compositor: Sized { @@ -52,5 +55,26 @@ pub trait Compositor: Sized { background_color: Color, output: &<Self::Renderer as iced_native::Renderer>::Output, 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, } diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index 68ebf234..b60efd25 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -141,59 +141,79 @@ impl iced_graphics::window::Compositor for Compositor { background_color: Color, output: &<Self::Renderer as iced_native::Renderer>::Output, overlay: &[T], - ) -> mouse::Interaction { - let frame = swap_chain.get_current_frame().expect("Next frame"); - - let mut encoder = self.device.create_command_encoder( - &wgpu::CommandEncoderDescriptor { - label: Some("iced_wgpu encoder"), + ) -> Result<mouse::Interaction, iced_graphics::window::SwapChainError> { + match swap_chain.get_current_frame() { + Ok(frame) => { + let mut encoder = self.device.create_command_encoder( + &wgpu::CommandEncoderDescriptor { + label: Some("iced_wgpu encoder"), + }, + ); + + 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(); + + 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 + } } } diff --git a/winit/src/application.rs b/winit/src/application.rs index 5d1aabf9..1d32a5f3 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -366,27 +366,43 @@ async fn run_instance<A, E, C>( viewport_version = current_viewport_version; } - let new_mouse_interaction = compositor.draw( + match compositor.draw( &mut renderer, &mut swap_chain, state.viewport(), state.background_color(), &primitive, &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 { - window.set_cursor_icon(conversion::mouse_interaction( - new_mouse_interaction, - )); + 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::MenuEntryActivated(entry_id), |