diff --git a/src/window/canvas.rs b/src/window/canvas.rs index a7782934a..442579303 100644 --- a/src/window/canvas.rs +++ b/src/window/canvas.rs @@ -70,7 +70,7 @@ impl Canvas { } /// Run the platform-specific render loop. - pub fn render_loop(data: impl RenderLoopClosure) { + pub fn render_loop(data: impl FnMut(f64) -> bool + 'static) { CanvasImpl::render_loop(data) } @@ -148,13 +148,6 @@ impl Canvas { } } -/// Note: the closure must have static lifetime because of the constraints imposed by wasm-bindgen: -/// https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen/closure/struct.Closure.html -pub trait RenderLoopClosure: 'static { - /// Call the closure - fn call(&mut self, x: f64) -> bool; -} - pub(crate) trait AbstractCanvas { fn open( title: &str, @@ -164,7 +157,7 @@ pub(crate) trait AbstractCanvas { window_setup: Option, out_events: Sender, ) -> Self; - fn render_loop(data: impl RenderLoopClosure); + fn render_loop(data: impl FnMut(f64) -> bool + 'static); fn poll_events(&mut self); fn swap_buffers(&mut self); fn size(&self) -> (u32, u32); diff --git a/src/window/gl_canvas.rs b/src/window/gl_canvas.rs index 44af52acc..f7760e570 100644 --- a/src/window/gl_canvas.rs +++ b/src/window/gl_canvas.rs @@ -2,7 +2,7 @@ use std::sync::mpsc::Sender; use crate::context::Context; use crate::event::{Action, Key, Modifiers, MouseButton, TouchAction, WindowEvent}; -use crate::window::canvas::{CanvasSetup, NumSamples, RenderLoopClosure}; +use crate::window::canvas::{CanvasSetup, NumSamples}; use crate::window::AbstractCanvas; use glutin::{ self, @@ -97,9 +97,9 @@ impl AbstractCanvas for GLCanvas { } } - fn render_loop(mut callback: impl RenderLoopClosure) { + fn render_loop(mut callback: impl FnMut(f64) -> bool + 'static) { loop { - if !callback.call(0.0) { + if !callback(0.0) { break; } // XXX: timestamp } diff --git a/src/window/mod.rs b/src/window/mod.rs index 028a2569c..8c9c29c10 100644 --- a/src/window/mod.rs +++ b/src/window/mod.rs @@ -10,7 +10,7 @@ mod window; mod window_cache; pub(crate) use canvas::AbstractCanvas; -pub use canvas::{Canvas, CanvasSetup, NumSamples, RenderLoopClosure}; +pub use canvas::{Canvas, CanvasSetup, NumSamples}; #[cfg(not(target_arch = "wasm32"))] pub use gl_canvas::GLCanvas; pub use state::State; diff --git a/src/window/webgl_canvas.rs b/src/window/webgl_canvas.rs index 35ac55906..620e73e4d 100644 --- a/src/window/webgl_canvas.rs +++ b/src/window/webgl_canvas.rs @@ -7,7 +7,7 @@ use std::sync::mpsc::Sender; use crate::context::Context; use crate::event::{Action, Key, Modifiers, MouseButton, TouchAction, WindowEvent}; -use crate::window::{AbstractCanvas, CanvasSetup, RenderLoopClosure}; +use crate::window::{AbstractCanvas, CanvasSetup}; use image::{GenericImage, Pixel}; use wasm_bindgen::closure::Closure; use wasm_bindgen::{JsCast, JsValue}; @@ -424,13 +424,13 @@ impl AbstractCanvas for WebGLCanvas { } } - fn render_loop(mut callback: impl RenderLoopClosure) { + fn render_loop(mut callback: impl FnMut(f64) -> bool + 'static) { // See https://rustwasm.github.io/docs/wasm-bindgen/examples/request-animation-frame.html if let Some(window) = web_sys::window() { let f = Rc::new(RefCell::new(None)); let g: Rc>>> = f.clone(); *g.borrow_mut() = Some(Closure::wrap(Box::new(move || { - if callback.call(0.0) { + if callback(0.0) { let _ = window.request_animation_frame( f.borrow().as_ref().unwrap().as_ref().unchecked_ref(), ); diff --git a/src/window/window.rs b/src/window/window.rs index c6bff03a5..92fdc2694 100644 --- a/src/window/window.rs +++ b/src/window/window.rs @@ -27,7 +27,7 @@ use crate::resource::{ use crate::scene::{PlanarSceneNode, SceneNode}; use crate::text::{Font, TextRenderer}; use crate::window::canvas::CanvasSetup; -use crate::window::{Canvas, RenderLoopClosure, State}; +use crate::window::{Canvas, State}; use image::imageops; use image::{GenericImage, Pixel}; use image::{ImageBuffer, Rgb}; @@ -59,26 +59,6 @@ impl ConrodContext { } } -// Note: this struct, and the RenderLoopClosure trait it implements, were created solely to control -// the drop order of its members. -// Since the Window contains the OpenGL context, it must be dropped _after_ the state, and there's -// no way to control that in a plain old closure. -struct RenderLoopClosureImpl bool> { - pub state: S, - pub window: Window, - pub closure: F, -} - -impl RenderLoopClosure for RenderLoopClosureImpl -where - S: State, - F: Fn(f64, &mut Window, &mut S) -> bool + 'static, -{ - fn call(&mut self, x: f64) -> bool { - (self.closure)(x, &mut self.window, &mut self.state) - } -} - /// Structure representing a window and a 3D scene. /// /// This is the main interface with the 3d engine. @@ -943,11 +923,27 @@ impl Window { /// Runs the render and event loop until the window is closed. pub fn render_loop(self, state: S) { - Canvas::render_loop(RenderLoopClosureImpl { - window: self, + // We have to be really careful here about drop order. + // + // The State may contain various OpenGL objects (for example, shaders), + // that, when dropped, make calls to the OpenGL context. + // Since the Window contains the OpenGL context, we must not drop it + // until we are done dropping the state. + // + // Since we can't directly control the drop order of fields in a closure, + // we instead put the relevant objects into a struct, for which the + // drop order _is_ controllable (top-to-bottom). + struct DropControl { + state: S, + window: Window, + } + + let mut dc = DropControl { state, - closure: |_, window, state| window.do_render_with_state(state), - }); + window: self, + }; + + Canvas::render_loop(move |_| dc.window.do_render_with_state(&mut dc.state)); } /// Render one frame using the specified state.