@@ -11,7 +11,9 @@ use std::rc::{Rc, Weak};
1111
1212use super :: { ItemGraphicsCache , TextureCache } ;
1313use crate :: event_loop:: WinitWindow ;
14+ use crate :: glcontext:: OpenGLContext ;
1415use const_field_offset:: FieldOffsets ;
16+ use corelib:: api:: { GraphicsAPI , RenderingNotifier , RenderingState , SetRenderingNotifierError } ;
1517use corelib:: component:: ComponentRc ;
1618use corelib:: graphics:: * ;
1719use corelib:: input:: KeyboardModifiers ;
@@ -38,6 +40,8 @@ pub struct GLWindow {
3840
3941 fps_counter : Option < Rc < FPSCounter > > ,
4042
43+ rendering_notifier : RefCell < Option < Box < dyn RenderingNotifier > > > ,
44+
4145 #[ cfg( target_arch = "wasm32" ) ]
4246 canvas_id : String ,
4347}
@@ -61,16 +65,17 @@ impl GLWindow {
6165 graphics_cache : Default :: default ( ) ,
6266 texture_cache : Default :: default ( ) ,
6367 fps_counter : FPSCounter :: new ( ) ,
68+ rendering_notifier : Default :: default ( ) ,
6469 #[ cfg( target_arch = "wasm32" ) ]
6570 canvas_id,
6671 } )
6772 }
6873
69- fn with_current_context < T > ( & self , cb : impl FnOnce ( ) -> T ) -> T {
74+ fn with_current_context < T > ( & self , cb : impl FnOnce ( & OpenGLContext ) -> T ) -> Option < T > {
7075 match & * self . map_state . borrow ( ) {
71- GraphicsWindowBackendState :: Unmapped => cb ( ) ,
76+ GraphicsWindowBackendState :: Unmapped => None ,
7277 GraphicsWindowBackendState :: Mapped ( window) => {
73- window. opengl_context . with_current_context ( cb)
78+ Some ( window. opengl_context . with_current_context ( cb) )
7479 }
7580 }
7681 }
@@ -108,6 +113,38 @@ impl GLWindow {
108113 pub fn default_font_properties ( & self ) -> FontRequest {
109114 self . self_weak . upgrade ( ) . unwrap ( ) . default_font_properties ( )
110115 }
116+
117+ fn release_graphics_resources ( & self ) {
118+ // Release GL textures and other GPU bound resources.
119+ self . with_current_context ( |context| {
120+ self . graphics_cache . borrow_mut ( ) . clear ( ) ;
121+ self . texture_cache . borrow_mut ( ) . clear ( ) ;
122+
123+ self . invoke_rendering_notifier ( RenderingState :: RenderingTeardown , context) ;
124+ } ) ;
125+ }
126+
127+ /// Invoke any registered rendering notifiers about the state the backend renderer is currently in.
128+ fn invoke_rendering_notifier ( & self , state : RenderingState , opengl_context : & OpenGLContext ) {
129+ if let Some ( callback) = self . rendering_notifier . borrow_mut ( ) . as_mut ( ) {
130+ #[ cfg( not( target_arch = "wasm32" ) ) ]
131+ let api = GraphicsAPI :: NativeOpenGL {
132+ get_proc_address : & |name| opengl_context. get_proc_address ( name) ,
133+ } ;
134+ #[ cfg( target_arch = "wasm32" ) ]
135+ let canvas_element_id = opengl_context. html_canvas_element ( ) . id ( ) ;
136+ #[ cfg( target_arch = "wasm32" ) ]
137+ let api = GraphicsAPI :: WebGL {
138+ canvas_element_id : canvas_element_id. as_str ( ) ,
139+ context_type : "webgl" ,
140+ } ;
141+ callback. notify ( state, & api)
142+ }
143+ }
144+
145+ fn has_rendering_notifier ( & self ) -> bool {
146+ self . rendering_notifier . borrow ( ) . is_some ( )
147+ }
111148}
112149
113150impl WinitWindow for GLWindow {
@@ -127,7 +164,7 @@ impl WinitWindow for GLWindow {
127164 fn draw ( self : Rc < Self > ) {
128165 let runtime_window = self . self_weak . upgrade ( ) . unwrap ( ) ;
129166 let scale_factor = runtime_window. scale_factor ( ) ;
130- runtime_window. draw_contents ( |components| {
167+ runtime_window. clone ( ) . draw_contents ( |components| {
131168 let window = match self . borrow_mapped_window ( ) {
132169 Some ( window) => window,
133170 None => return , // caller bug, doesn't make sense to call draw() when not mapped
@@ -151,6 +188,18 @@ impl WinitWindow for GLWindow {
151188 size. height ,
152189 crate :: to_femtovg_color ( & window. clear_color ) ,
153190 ) ;
191+ // For the BeforeRendering rendering notifier callback it's important that this happens *after* clearing
192+ // the back buffer, in order to allow the callback to provide its own rendering of the background.
193+ // femtovg's clear_rect() will merely schedule a clear call, so flush right away to make it immediate.
194+ if self . has_rendering_notifier ( ) {
195+ canvas. flush ( ) ;
196+ canvas. set_size ( size. width , size. height , 1.0 ) ;
197+
198+ self . invoke_rendering_notifier (
199+ RenderingState :: BeforeRendering ,
200+ & window. opengl_context ,
201+ ) ;
202+ }
154203 }
155204
156205 let mut renderer = crate :: GLItemRenderer {
@@ -184,6 +233,8 @@ impl WinitWindow for GLWindow {
184233
185234 drop ( renderer) ;
186235
236+ self . invoke_rendering_notifier ( RenderingState :: AfterRendering , & window. opengl_context ) ;
237+
187238 window. opengl_context . swap_buffers ( ) ;
188239 window. opengl_context . make_not_current ( ) ;
189240 } ) ;
@@ -250,14 +301,28 @@ impl PlatformWindow for GLWindow {
250301 } )
251302 . peekable ( ) ;
252303 if cache_entries_to_clear. peek ( ) . is_some ( ) {
253- self . with_current_context ( || {
304+ self . with_current_context ( |_ | {
254305 cache_entries_to_clear. for_each ( drop) ;
255306 } ) ;
256307 }
257308 }
258309 }
259310 }
260311
312+ /// This function is called through the public API to register a callback that the backend needs to invoke during
313+ /// different phases of rendering.
314+ fn set_rendering_notifier (
315+ & self ,
316+ callback : Box < dyn RenderingNotifier > ,
317+ ) -> std:: result:: Result < ( ) , SetRenderingNotifierError > {
318+ let mut notifier = self . rendering_notifier . borrow_mut ( ) ;
319+ if notifier. replace ( callback) . is_some ( ) {
320+ Err ( SetRenderingNotifierError :: AlreadySet )
321+ } else {
322+ Ok ( ( ) )
323+ }
324+ }
325+
261326 fn show_popup ( & self , popup : & ComponentRc , position : Point ) {
262327 let runtime_window = self . self_weak . upgrade ( ) . unwrap ( ) ;
263328 let size = runtime_window. set_active_popup ( PopupWindow {
@@ -383,6 +448,8 @@ impl PlatformWindow for GLWindow {
383448 )
384449 . unwrap ( ) ;
385450
451+ self . invoke_rendering_notifier ( RenderingState :: RenderingSetup , & opengl_context) ;
452+
386453 opengl_context. make_not_current ( ) ;
387454
388455 let canvas = Rc :: new ( RefCell :: new ( canvas) ) ;
@@ -443,10 +510,7 @@ impl PlatformWindow for GLWindow {
443510
444511 fn hide ( self : Rc < Self > ) {
445512 // Release GL textures and other GPU bound resources.
446- self . with_current_context ( || {
447- self . graphics_cache . borrow_mut ( ) . clear ( ) ;
448- self . texture_cache . borrow_mut ( ) . clear ( ) ;
449- } ) ;
513+ self . release_graphics_resources ( ) ;
450514
451515 self . map_state . replace ( GraphicsWindowBackendState :: Unmapped ) ;
452516 /* FIXME:
@@ -621,6 +685,12 @@ impl PlatformWindow for GLWindow {
621685 }
622686}
623687
688+ impl Drop for GLWindow {
689+ fn drop ( & mut self ) {
690+ self . release_graphics_resources ( ) ;
691+ }
692+ }
693+
624694struct MappedWindow {
625695 canvas : Option < CanvasRc > ,
626696 opengl_context : crate :: OpenGLContext ,
@@ -632,7 +702,7 @@ impl Drop for MappedWindow {
632702 fn drop ( & mut self ) {
633703 if let Some ( canvas) = self . canvas . take ( ) . map ( |canvas| Rc :: try_unwrap ( canvas) . ok ( ) ) {
634704 // The canvas must be destructed with a GL context current, in order to clean up correctly
635- self . opengl_context . with_current_context ( || {
705+ self . opengl_context . with_current_context ( |_ | {
636706 drop ( canvas) ;
637707 } ) ;
638708 } else {
0 commit comments