From 956ff54a5520f2858514e2a5be13ac31d0b6cdb9 Mon Sep 17 00:00:00 2001 From: rustbasic <127506429+rustbasic@users.noreply.github.com> Date: Sat, 29 Jun 2024 11:31:58 +0900 Subject: [PATCH] Update glow_integration.rs --- crates/eframe/src/native/glow_integration.rs | 337 +++++++++++-------- 1 file changed, 193 insertions(+), 144 deletions(-) diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 13576bdbd2d..6d270b2ccb6 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -142,7 +142,6 @@ impl GlowWinitApp { } } - #[allow(unsafe_code)] fn create_glutin_windowed_context( egui_ctx: &egui::Context, event_loop: &EventLoopWindowTarget, @@ -161,21 +160,23 @@ impl GlowWinitApp { ) .with_visible(false); // Start hidden until we render the first frame to fix white flash on startup (https://github.com/emilk/egui/pull/3631) - let mut glutin_window_context = unsafe { - GlutinWindowContext::new(egui_ctx, winit_window_builder, native_options, event_loop)? - }; + let mut glutin_window_context = + GlutinWindowContext::new(egui_ctx, winit_window_builder, native_options, event_loop)?; // Creates the window - must come before we create our glow context glutin_window_context.initialize_window(ViewportId::ROOT, event_loop)?; { let viewport = &glutin_window_context.viewports[&ViewportId::ROOT]; - let window = viewport.window.as_ref().unwrap(); // Can't fail - we just called `initialize_all_viewports` - epi_integration::apply_window_settings(window, window_settings); + // let window = viewport.window.as_ref().unwrap(); // Can't fail - we just called `initialize_all_viewports` + if let Some(window) = viewport.window.as_ref() { + epi_integration::apply_window_settings(window, window_settings); + }; } + crate::profile_scope!("glow::Context::from_loader_function"); + #[allow(unsafe_code)] let gl = unsafe { - crate::profile_scope!("glow::Context::from_loader_function"); Arc::new(glow::Context::from_loader_function(|s| { let s = std::ffi::CString::new(s) .expect("failed to construct C string from string for gl proc address"); @@ -251,8 +252,10 @@ impl GlowWinitApp { .egui_ctx .set_request_repaint_callback(move |info| { log::trace!("request_repaint_callback: {info:?}"); - let when = Instant::now() + info.delay; + // let when = Instant::now() + info.delay; + let when = Instant::now(); let frame_nr = info.current_frame_nr; + event_loop_proxy .lock() .send_event(UserEvent::RequestRepaint { @@ -266,6 +269,18 @@ impl GlowWinitApp { #[cfg(feature = "accesskit")] { + let event_loop_proxy = self.repaint_proxy.lock().clone(); + if let Some(viewport) = glutin.viewports.get_mut(&ViewportId::ROOT) { + if let Viewport { + window: Some(window), + egui_winit: Some(egui_winit), + .. + } = viewport + { + integration.init_accesskit(egui_winit, window, event_loop_proxy); + } + }; + /* let event_loop_proxy = self.repaint_proxy.lock().clone(); let viewport = glutin.viewports.get_mut(&ViewportId::ROOT).unwrap(); // we always have a root if let Viewport { @@ -276,6 +291,7 @@ impl GlowWinitApp { { integration.init_accesskit(egui_winit, window, event_loop_proxy); } + */ } let theme = system_theme.unwrap_or(self.native_options.default_theme); @@ -332,6 +348,18 @@ impl GlowWinitApp { // SAFETY: the event loop lives longer than // the Rc:s we just upgraded above. #[allow(unsafe_code)] + if let Some(event_loop) = unsafe { event_loop.as_ref() } { + render_immediate_viewport( + event_loop, + egui_ctx, + &glutin, + &painter, + beginning, + immediate_viewport, + ); + }; + /* + #[allow(unsafe_code)] let event_loop = unsafe { event_loop.as_ref().unwrap() }; render_immediate_viewport( @@ -342,6 +370,7 @@ impl GlowWinitApp { beginning, immediate_viewport, ); + */ } else { log::warn!("render_sync_callback called after window closed"); } @@ -462,7 +491,7 @@ impl WinitApp for GlowWinitApp { } if let Some(window) = viewport.window.as_ref() { - EventResult::RepaintNext(window.id()) + EventResult::RepaintNow(window.id()) } else { EventResult::Wait } @@ -495,6 +524,7 @@ impl WinitApp for GlowWinitApp { EventResult::Wait } } + _ => EventResult::Wait, }) } @@ -508,7 +538,7 @@ impl GlowWinitRunning { ) -> EventResult { crate::profile_function!(); - let Some(viewport_id) = self + let Some(mut viewport_id) = self .glutin .borrow() .viewport_from_window @@ -524,24 +554,25 @@ impl GlowWinitRunning { let mut frame_timer = crate::stopwatch::Stopwatch::new(); frame_timer.start(); - { - let glutin = self.glutin.borrow(); - let viewport = &glutin.viewports[&viewport_id]; - let is_immediate = viewport.viewport_ui_cb.is_none(); - if is_immediate && viewport_id != ViewportId::ROOT { - // This will only happen if this is an immediate viewport. - // That means that the viewport cannot be rendered by itself and needs his parent to be rendered. - if let Some(parent_viewport) = glutin.viewports.get(&viewport.ids.parent) { - if let Some(window) = parent_viewport.window.as_ref() { - return EventResult::RepaintNext(window.id()); - } + let (raw_input, viewport_ui_cb) = { + crate::profile_scope!("Prepare"); + + let mut glutin = self.glutin.borrow_mut(); + let original_viewport = &glutin.viewports[&viewport_id]; + + if original_viewport.class == ViewportClass::Immediate { + let Some(parent_viewport) = glutin.viewports.get(&original_viewport.ids.parent) + else { + return EventResult::Wait; + }; + + if parent_viewport.class == ViewportClass::Deferred { + viewport_id = parent_viewport.ids.this; + } else { + viewport_id = ViewportId::ROOT; } - return EventResult::Wait; } - } - let (raw_input, viewport_ui_cb) = { - let mut glutin = self.glutin.borrow_mut(); let egui_ctx = glutin.egui_ctx.clone(); let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else { return EventResult::Wait; @@ -569,6 +600,7 @@ impl GlowWinitRunning { (raw_input, viewport_ui_cb) }; + /* let clear_color = self .app .clear_color(&self.integration.egui_ctx.style().visuals); @@ -586,25 +618,26 @@ impl GlowWinitRunning { .. } = &mut *glutin; let viewport = &viewports[&viewport_id]; - let Some(window) = viewport.window.as_ref() else { - return EventResult::Wait; - }; let Some(gl_surface) = viewport.gl_surface.as_ref() else { return EventResult::Wait; }; - let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); - { frame_timer.pause(); change_gl_context(current_gl_context, gl_surface); frame_timer.resume(); } + let Some(window) = viewport.window.as_ref() else { + return EventResult::Wait; + }; + let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); + self.painter .borrow() .clear(screen_size_in_pixels, clear_color); } + */ // ------------------------------------------------------------ // The update function, which could call immediate viewports, @@ -646,13 +679,15 @@ impl GlowWinitRunning { let Some(viewport) = viewports.get_mut(&viewport_id) else { return EventResult::Wait; }; - viewport.info.events.clear(); // they should have been processed - let window = viewport.window.clone().unwrap(); - let gl_surface = viewport.gl_surface.as_ref().unwrap(); - let egui_winit = viewport.egui_winit.as_mut().unwrap(); - egui_winit.handle_platform_output(&window, platform_output); + let (Some(egui_winit), Some(window), Some(gl_surface)) = ( + viewport.egui_winit.as_mut(), + &viewport.window.clone(), + &viewport.gl_surface, + ) else { + return EventResult::Wait; + }; let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point); @@ -664,10 +699,14 @@ impl GlowWinitRunning { } let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); + let clear_color = app.clear_color(&integration.egui_ctx.style().visuals); + /* if !clear_before_update { painter.clear(screen_size_in_pixels, clear_color); } + */ + painter.clear(screen_size_in_pixels, clear_color); painter.paint_and_update_textures( screen_size_in_pixels, @@ -709,18 +748,18 @@ impl GlowWinitRunning { } } - integration.post_rendering(&window); + integration.post_rendering(window); } { + let Some(current_gl_context) = current_gl_context.as_ref() else { + return EventResult::Wait; + }; + // vsync - don't count as frame-time: frame_timer.pause(); crate::profile_scope!("swap_buffers"); - if let Err(err) = gl_surface.swap_buffers( - current_gl_context - .as_ref() - .expect("failed to get current context to swap buffers"), - ) { + if let Err(err) = gl_surface.swap_buffers(current_gl_context) { log::error!("swap_buffers failed: {err}"); } frame_timer.resume(); @@ -734,24 +773,29 @@ impl GlowWinitRunning { } } + egui_winit.handle_platform_output(&window, platform_output); + glutin.handle_viewport_output(event_loop, &integration.egui_ctx, &viewport_output); integration.report_frame_time(frame_timer.total_time_sec()); // don't count auto-save time as part of regular frame time - integration.maybe_autosave(app.as_mut(), Some(&window)); + integration.maybe_autosave(app.as_mut(), Some(window)); - if window.is_minimized() == Some(true) { - // On Mac, a minimized Window uses up all CPU: - // https://github.com/emilk/egui/issues/325 - crate::profile_scope!("minimized_sleep"); - std::thread::sleep(std::time::Duration::from_millis(10)); + let is_windows = cfg!(target_os = "windows"); + if !is_windows { + let is_minimized = window.is_minimized().unwrap_or(false); + if is_minimized { + // On Mac, a minimized Window uses up all CPU: + // https://github.com/emilk/egui/issues/325 + crate::profile_scope!("minimized_sleep"); + std::thread::sleep(std::time::Duration::from_millis(10)); + } } - if integration.should_close() { - EventResult::Exit - } else { - EventResult::Wait - } + // When we press the Close button on the Title Bar, `WindowEvent::CloseRequested` occurs, + // Select whether to use `ViewportCommand::CancelClose` or `ViewportCommand::Close`. + // If `ViewportCommand::Close` is sent, it is processed here after being handled in `process_viewport_command()`. + glutin.handle_viewport_close(window_id, viewport_id) } fn on_window_event( @@ -761,63 +805,47 @@ impl GlowWinitRunning { ) -> EventResult { crate::profile_function!(egui_winit::short_window_event_description(event)); - let mut glutin = self.glutin.borrow_mut(); - let viewport_id = glutin.viewport_from_window.get(&window_id).copied(); - - // On Windows, if a window is resized by the user, it should repaint synchronously, inside the - // event handler. - // - // If this is not done, the compositor will assume that the window does not want to redraw, - // and continue ahead. - // - // In eframe's case, that causes the window to rapidly flicker, as it struggles to deliver - // new frames to the compositor in time. - // - // The flickering is technically glutin or glow's fault, but we should be responding properly - // to resizes anyway, as doing so avoids dropping frames. - // - // See: https://github.com/emilk/egui/issues/903 - let mut repaint_asap = false; + let mut glutin_mut = self.glutin.borrow_mut(); + let viewport_id = glutin_mut.viewport_from_window.get(&window_id).copied(); match event { winit::event::WindowEvent::Focused(new_focused) => { - glutin.focused_viewport = new_focused.then(|| viewport_id).flatten(); + glutin_mut.focused_viewport = new_focused.then(|| viewport_id).flatten(); } winit::event::WindowEvent::Resized(physical_size) => { - // Resize with 0 width and height is used by winit to signal a minimize event on Windows. - // See: https://github.com/rust-windowing/winit/issues/208 - // This solves an issue where the app would panic when minimizing on Windows. - if 0 < physical_size.width && 0 < physical_size.height { - if let Some(viewport_id) = viewport_id { - repaint_asap = true; - glutin.resize(viewport_id, *physical_size); - } + if let Some(viewport_id) = viewport_id { + glutin_mut.resize(viewport_id, *physical_size); + return EventResult::RepaintNext(window_id); } } winit::event::WindowEvent::CloseRequested => { - if viewport_id == Some(ViewportId::ROOT) && self.integration.should_close() { - log::debug!( - "Received WindowEvent::CloseRequested for main viewport - shutting down." - ); - return EventResult::Exit; - } - log::debug!("Received WindowEvent::CloseRequested for viewport {viewport_id:?}"); if let Some(viewport_id) = viewport_id { - if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) { + if let Some(viewport) = glutin_mut.viewports.get_mut(&viewport_id) { // Tell viewport it should close: + viewport.info.close_requested_on(); viewport.info.events.push(egui::ViewportEvent::Close); // We may need to repaint both us and our parent to close the window, // and perhaps twice (once to notice the close-event, once again to enforce it). // `request_repaint_of` does a double-repaint though: self.integration.egui_ctx.request_repaint_of(viewport_id); - self.integration - .egui_ctx - .request_repaint_of(viewport.ids.parent); + if viewport_id != ViewportId::ROOT { + self.integration + .egui_ctx + .request_repaint_of(viewport.ids.parent); + if viewport.ids.parent != ViewportId::ROOT { + self.integration + .egui_ctx + .request_repaint_of(ViewportId::ROOT); + } + } + + // If `close_cancelable` is `false`, `ViewportCommand::CancelClose` is not possible, it is processed here. + return glutin_mut.handle_viewport_close(window_id, viewport_id); } } } @@ -825,16 +853,12 @@ impl GlowWinitRunning { _ => {} } - if self.integration.should_close() { - return EventResult::Exit; - } - let mut event_response = egui_winit::EventResponse { consumed: false, repaint: false, }; if let Some(viewport_id) = viewport_id { - if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) { + if let Some(viewport) = glutin_mut.viewports.get_mut(&viewport_id) { if let (Some(window), Some(egui_winit)) = (&viewport.window, &mut viewport.egui_winit) { @@ -848,11 +872,7 @@ impl GlowWinitRunning { } if event_response.repaint { - if repaint_asap { - EventResult::RepaintNow(window_id) - } else { - EventResult::RepaintNext(window_id) - } + EventResult::RepaintNow(window_id) } else { EventResult::Wait } @@ -879,22 +899,23 @@ fn change_gl_context( } } - let not_current = { - crate::profile_scope!("make_not_current"); - current_gl_context - .take() - .unwrap() - .make_not_current() - .unwrap() + let Some(p_current_gl_context) = current_gl_context.take() else { + return; + }; + + crate::profile_scope!("make_not_current"); + let Ok(not_current) = p_current_gl_context.make_not_current() else { + return; }; crate::profile_scope!("make_current"); - *current_gl_context = Some(not_current.make_current(gl_surface).unwrap()); + if let Ok(current) = not_current.make_current(gl_surface) { + *current_gl_context = Some(current); + } } impl GlutinWindowContext { - #[allow(unsafe_code)] - unsafe fn new( + fn new( egui_ctx: &egui::Context, viewport_builder: ViewportBuilder, native_options: &NativeOptions, @@ -991,24 +1012,28 @@ impl GlutinWindowContext { // create gl context. if core context cannot be created, try gl es context as fallback. let context_attributes = glutin::context::ContextAttributesBuilder::new().build(glutin_raw_window_handle); - let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new() - .with_context_api(glutin::context::ContextApi::Gles(None)) - .build(glutin_raw_window_handle); + crate::profile_scope!("create_context"); + #[allow(unsafe_code)] let gl_context_result = unsafe { - crate::profile_scope!("create_context"); gl_config .display() .create_context(&gl_config, &context_attributes) }; let gl_context = match gl_context_result { - Ok(it) => it, + Ok(not_current_context) => not_current_context, Err(err) => { log::warn!("Failed to create context using default context attributes {context_attributes:?} due to error: {err}"); + + let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new() + .with_context_api(glutin::context::ContextApi::Gles(None)) + .build(glutin_raw_window_handle); log::debug!( "Retrying with fallback context attributes: {fallback_context_attributes:?}" ); + + #[allow(unsafe_code)] unsafe { gl_config .display() @@ -1083,7 +1108,6 @@ impl GlutinWindowContext { } /// Create a surface, window, and winit integration for the viewport, if missing. - #[allow(unsafe_code)] pub(crate) fn initialize_window( &mut self, viewport_id: ViewportId, @@ -1091,10 +1115,12 @@ impl GlutinWindowContext { ) -> Result { crate::profile_function!(); - let viewport = self - .viewports - .get_mut(&viewport_id) - .expect("viewport doesn't exist"); + let Some(viewport) = self.viewports.get_mut(&viewport_id) else { + log::debug!("viewport {viewport_id:?} doesn't exist"); + return Ok(()); + }; + viewport.info.this = viewport_id; + viewport.info.parent = Some(self.egui_ctx.parent_viewport_id_of(viewport_id)); let window = if let Some(window) = &mut viewport.window { window @@ -1117,6 +1143,7 @@ impl GlutinWindowContext { &viewport.builder, ); + viewport.info.transparent = viewport.builder.transparent; egui_winit::update_viewport_info(&mut viewport.info, &self.egui_ctx, &window, true); viewport.window.insert(Arc::new(window)) }; @@ -1146,6 +1173,7 @@ impl GlutinWindowContext { }; log::trace!("creating surface with attributes: {surface_attributes:?}"); + #[allow(unsafe_code)] let gl_surface = unsafe { self.gl_config .display() @@ -1158,11 +1186,13 @@ impl GlutinWindowContext { if let Some(not_current_context) = self.not_current_gl_context.take() { not_current_context } else { - self.current_gl_context - .take() - .unwrap() - .make_not_current() - .unwrap() + let Some(current_gl) = self.current_gl_context.take() else { + return Ok(()); + }; + let Ok(not_current) = current_gl.make_not_current() else { + return Ok(()); + }; + not_current }; let current_gl_context = not_current_gl_context.make_current(&gl_surface)?; @@ -1223,13 +1253,11 @@ impl GlutinWindowContext { if let Some(viewport) = self.viewports.get(&viewport_id) { if let Some(gl_surface) = &viewport.gl_surface { change_gl_context(&mut self.current_gl_context, gl_surface); - gl_surface.resize( - self.current_gl_context - .as_ref() - .expect("failed to get current context to resize surface"), - width_px, - height_px, - ); + let Some(current_gl_context) = self.current_gl_context.as_ref() else { + return; + }; + + gl_surface.resize(current_gl_context, width_px, height_px); } } } @@ -1309,6 +1337,32 @@ impl GlutinWindowContext { self.remove_viewports_not_in(viewport_output); } + + pub(crate) fn handle_viewport_close( + &mut self, + window_id: WindowId, + viewport_id: ViewportId, + ) -> EventResult { + if let Some(viewport) = self.viewports.get_mut(&viewport_id) { + if viewport.info.should_close() { + if viewport_id == ViewportId::ROOT { + log::debug!( + "Received WindowEvent::CloseRequested for main viewport - shutting down." + ); + } + + if viewport_id == ViewportId::ROOT { + return EventResult::Exit(window_id); + } else { + let physical_size = winit::dpi::PhysicalSize::new(0, 0); + self.resize(viewport_id, physical_size); + return EventResult::ViewportExit(window_id); + } + } + } + + EventResult::Wait + } } fn initialize_or_update_viewport( @@ -1345,7 +1399,8 @@ fn initialize_or_update_viewport( }) } - std::collections::hash_map::Entry::Occupied(mut entry) => { + std::collections::hash_map::Entry::Occupied(entry) => { + /* // Patch an existing viewport: let viewport = entry.get_mut(); @@ -1366,7 +1421,7 @@ fn initialize_or_update_viewport( } viewport.deferred_commands.append(&mut delta_commands); - + */ entry.into_mut() } } @@ -1472,19 +1527,9 @@ fn render_immediate_viewport( return; }; - let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); - change_gl_context(current_gl_context, gl_surface); - let current_gl_context = current_gl_context.as_ref().unwrap(); - - if !gl_surface.is_current(current_gl_context) { - log::error!( - "egui::show_viewport_immediate: viewport {:?} ({:?}) was not created on main thread.", - viewport.ids.this, - viewport.builder.title - ); - } + let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); egui_glow::painter::clear( painter.borrow().gl(), @@ -1500,6 +1545,10 @@ fn render_immediate_viewport( ); { + let Some(current_gl_context) = current_gl_context.as_ref() else { + return; + }; + crate::profile_scope!("swap_buffers"); if let Err(err) = gl_surface.swap_buffers(current_gl_context) { log::error!("swap_buffers failed: {err}");