aboutsummaryrefslogtreecommitdiff
path: root/stockton-skeleton/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'stockton-skeleton/src/lib.rs')
-rw-r--r--stockton-skeleton/src/lib.rs70
1 files changed, 45 insertions, 25 deletions
diff --git a/stockton-skeleton/src/lib.rs b/stockton-skeleton/src/lib.rs
index b514531..6260753 100644
--- a/stockton-skeleton/src/lib.rs
+++ b/stockton-skeleton/src/lib.rs
@@ -18,6 +18,8 @@ pub mod texture;
pub mod types;
pub mod utils;
+use std::mem::ManuallyDrop;
+
use context::RenderingContext;
use draw_passes::{DrawPass, IntoDrawPass, Singular};
@@ -30,7 +32,7 @@ use winit::window::Window;
/// Also takes ownership of the window and channels window events to be processed outside winit's event loop.
pub struct Renderer<DP> {
/// All the vulkan stuff
- context: RenderingContext,
+ context: ManuallyDrop<RenderingContext>,
/// The draw pass we're using
draw_pass: DP,
@@ -50,25 +52,53 @@ impl<DP: DrawPass<Singular>> Renderer<DP> {
.init(session, &mut context)
.context("Error initialising draw pass")?;
- Ok(Renderer { context, draw_pass })
+ Ok(Renderer {
+ context: ManuallyDrop::new(context),
+ draw_pass,
+ })
}
/// Render a single frame of the given session.
- pub fn render(&mut self, session: &Session) -> Result<()> {
- // Try to draw
- if self
- .context
- .draw_next_frame(session, &mut self.draw_pass)
- .is_err()
- {
- // Probably the surface changed
- self.handle_surface_change(session)?;
-
- // If it fails twice, then error
- self.context.draw_next_frame(session, &mut self.draw_pass)?;
+ /// If this returns an error, the whole renderer is dead, hence it takes ownership to ensure it can't be called in that case.
+ pub fn render(mut self, session: &Session) -> Result<Renderer<DP>> {
+ // Safety: If this fails at any point, the ManuallyDrop won't be touched again, as Renderer will be dropped.
+ // Hence, we can always take from the ManuallyDrop
+ unsafe {
+ match ManuallyDrop::take(&mut self.context)
+ .draw_next_frame(session, &mut self.draw_pass)
+ {
+ Ok(c) => {
+ self.context = ManuallyDrop::new(c);
+ Ok(self)
+ }
+ Err((_e, c)) => {
+ // TODO: Try to detect if the error is actually surface related.
+ let c = c.attempt_recovery()?;
+ match c.draw_next_frame(session, &mut self.draw_pass) {
+ Ok(c) => {
+ self.context = ManuallyDrop::new(c);
+ Ok(self)
+ }
+ Err((e, _c)) => Err(e),
+ }
+ }
+ }
+ }
+ }
+
+ /// Recreate the surface, and other derived components.
+ /// This should be called when the window is resized.
+ pub fn recreate_surface(mut self, session: &Session) -> Result<Renderer<DP>> {
+ // Safety: If this fails at any point, the ManuallyDrop won't be touched again, as Renderer will be dropped.
+ // Hence, we can always take from the ManuallyDrop
+ unsafe {
+ let ctx = ManuallyDrop::take(&mut self.context).recreate_surface()?;
+ self.context = ManuallyDrop::new(ctx);
}
+ self.draw_pass
+ .handle_surface_change(session, &mut self.context)?;
- Ok(())
+ Ok(self)
}
pub fn get_aspect_ratio(&self) -> f32 {
@@ -76,16 +106,6 @@ impl<DP: DrawPass<Singular>> Renderer<DP> {
e.width as f32 / e.height as f32
}
- pub fn handle_surface_change(&mut self, session: &Session) -> Result<()> {
- unsafe {
- self.context.handle_surface_change()?;
- self.draw_pass
- .handle_surface_change(session, &mut self.context)?;
- }
-
- Ok(())
- }
-
/// Get a reference to the renderer's context.
pub fn context(&self) -> &RenderingContext {
&self.context