aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/compositing/compositor.rs1
-rw-r--r--components/servo/lib.rs7
-rw-r--r--ports/servoshell/app.rs93
-rw-r--r--ports/servoshell/browser.rs4
4 files changed, 80 insertions, 25 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index 1ae3d4a4616..809d231d831 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -1540,6 +1540,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
rect: Option<Rect<f32, CSSPixel>>,
) -> Result<Option<Image>, UnableToComposite> {
if self.waiting_on_present {
+ debug!("tried to composite while waiting on present");
return Err(UnableToComposite::NotReadyToPaintImage(
NotReadyToPaint::WaitingOnConstellation,
));
diff --git a/components/servo/lib.rs b/components/servo/lib.rs
index baa59a06df8..e62a8e5f11b 100644
--- a/components/servo/lib.rs
+++ b/components/servo/lib.rs
@@ -62,7 +62,7 @@ use gaol::sandbox::{ChildSandbox, ChildSandboxMethods};
use gfx::font_cache_thread::FontCacheThread;
pub use gleam::gl;
use ipc_channel::ipc::{self, IpcSender};
-use log::{error, warn, Log, Metadata, Record};
+use log::{error, trace, warn, Log, Metadata, Record};
use media::{GLPlayerThreads, WindowGLContext};
pub use msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
use msg::constellation_msg::{PipelineNamespace, PipelineNamespaceId};
@@ -717,6 +717,7 @@ where
}
let mut need_resize = false;
for event in events {
+ trace!("servo <- embedder EmbedderEvent {:?}", event);
need_resize |= self.handle_window_event(event);
}
if self.compositor.shutdown_state != ShutdownState::FinishedShuttingDown {
@@ -764,6 +765,10 @@ where
pub fn present(&mut self) {
self.compositor.present();
}
+
+ pub fn recomposite(&mut self) {
+ self.compositor.composite();
+ }
}
fn create_embedder_channel(
diff --git a/ports/servoshell/app.rs b/ports/servoshell/app.rs
index 2152537a6b7..38463e41bc4 100644
--- a/ports/servoshell/app.rs
+++ b/ports/servoshell/app.rs
@@ -12,7 +12,7 @@ use crate::parser::get_default_url;
use crate::window_trait::WindowPortsMethods;
use crate::{headed_window, headless_window};
use gleam::gl;
-use log::warn;
+use log::{trace, warn};
use servo::compositing::windowing::EmbedderEvent;
use servo::config::opts;
use servo::servo_config::pref;
@@ -20,6 +20,7 @@ use servo::Servo;
use std::cell::{Cell, RefCell, RefMut};
use std::collections::HashMap;
use std::rc::Rc;
+use std::time::Instant;
use surfman::GLApi;
use webxr::glwindow::GlWindowDiscovery;
use winit::window::WindowId;
@@ -37,7 +38,8 @@ pub struct App {
/// Action to be taken by the caller of [`App::handle_events`].
enum PumpResult {
Shutdown,
- Present,
+ ReadyToPresent,
+ Resize,
}
impl App {
@@ -96,8 +98,28 @@ impl App {
window.set_toolbar_height(minibrowser.toolbar_height.get());
}
+ // Whether or not to recomposite during the next RedrawRequested event.
+ // Normally this is true, including for RedrawRequested events that come from the platform
+ // (e.g. X11 without picom or similar) when an offscreen or obscured window becomes visible.
+ // If we are calling request_redraw in response to the compositor having painted to this
+ // frame, set this to false, so we can avoid an unnecessary recomposite.
+ let mut need_recomposite = true;
+
+ // If we have a minibrowser, ask the compositor to notify us when a new frame
+ // is ready to present, so that we can paint the minibrowser then present.
+ let external_present = app.minibrowser.is_some();
+
+ let t_start = Instant::now();
+ let mut t = t_start;
let ev_waker = events_loop.create_event_loop_waker();
events_loop.run_forever(move |e, w, control_flow| {
+ let now = Instant::now();
+ match e {
+ // Uncomment to filter out logging of DeviceEvent, which can be very noisy.
+ // winit::event::Event::DeviceEvent { .. } => {},
+ _ => trace!("@{:?} (+{:?}) {:?}", now - t_start, now - t, e),
+ }
+ t = now;
match e {
winit::event::Event::NewEvents(winit::event::StartCause::Init) => {
let surfman = window.webrender_surfman();
@@ -135,10 +157,7 @@ impl App {
let servo_data = Servo::new(embedder, window.clone(), user_agent.clone());
let mut servo = servo_data.servo;
-
- // If we have a minibrowser, ask the compositor to notify us when a new frame
- // is ready to present, so that we can paint the minibrowser then present.
- servo.set_external_present(app.minibrowser.is_some());
+ servo.set_external_present(external_present);
servo.handle_events(vec![EmbedderEvent::NewBrowser(initial_url.to_owned(), servo_data.browser_id)]);
servo.setup_logging();
@@ -147,17 +166,26 @@ impl App {
app.servo = Some(servo);
}
- // TODO does windows still need this workaround?
- // https://github.com/emilk/egui/blob/9478e50d012c5138551c38cbee16b07bc1fcf283/crates/egui_glow/examples/pure_glow.rs#L203
- // winit::event::Event::RedrawEventsCleared => todo!(),
winit::event::Event::RedrawRequested(_) => {
+ // We need to redraw the window for some reason.
+ trace!("RedrawRequested");
+
+ // WARNING: do not defer painting or presenting to some later tick of the event
+ // loop or servoshell may become unresponsive! (servo#30312)
+ if need_recomposite {
+ trace!("need_recomposite");
+ app.servo.as_mut().unwrap().recomposite();
+ }
if let Some(mut minibrowser) = app.minibrowser() {
minibrowser.update(window.winit_window().unwrap(), "RedrawRequested");
-
- // Tell Servo to repaint, which will in turn allow us to repaint the
- // minibrowser and present a complete frame without partial updates.
- app.event_queue.borrow_mut().push(EmbedderEvent::Refresh);
+ minibrowser.paint(window.winit_window().unwrap());
}
+ if external_present {
+ app.servo.as_mut().unwrap().present();
+ }
+
+ // By default, the next RedrawRequested event will need to recomposite.
+ need_recomposite = true;
}
_ => {}
@@ -175,8 +203,8 @@ impl App {
if let winit::event::Event::WindowEvent { ref event, .. } = e {
let response = minibrowser.context.on_event(&event);
if response.repaint {
- // Request a redraw event that will in turn trigger a minibrowser update.
- // This allows us to coalesce minibrowser updates across multiple events.
+ // Request a winit redraw event, so we can recomposite, update and paint the
+ // minibrowser, and present the new frame.
window.winit_window().unwrap().request_redraw();
}
@@ -217,12 +245,33 @@ impl App {
minibrowser.context.destroy();
}
},
- Some(PumpResult::Present) => {
+ Some(PumpResult::ReadyToPresent) => {
+ // The compositor has painted to this frame.
+ trace!("PumpResult::ReadyToPresent");
+
+ // Request a winit redraw event, so we can paint the minibrowser and present.
+ window.winit_window().unwrap().request_redraw();
+
+ // We don’t need the compositor to paint to this frame during the redraw event.
+ // TODO(servo#30331) broken on macOS?
+ // need_recomposite = false;
+ },
+ Some(PumpResult::Resize) => {
+ // The window was resized.
+ trace!("PumpResult::Resize");
+
+ // Resizes are unusual in that we need to repaint synchronously.
+ // TODO(servo#30049) can we replace this with the simpler Servo::recomposite?
+ app.servo.as_mut().unwrap().repaint_synchronously();
+
if let Some(mut minibrowser) = app.minibrowser() {
+ minibrowser.update(window.winit_window().unwrap(), "PumpResult::Resize");
minibrowser.paint(window.winit_window().unwrap());
}
- app.servo.as_mut().unwrap().present();
- },
+ if external_present {
+ app.servo.as_mut().unwrap().present();
+ }
+ }
None => {},
}
});
@@ -328,11 +377,9 @@ impl App {
}
if need_resize {
- self.servo.as_mut().unwrap().repaint_synchronously();
- need_present = true;
- }
- if need_present {
- Some(PumpResult::Present)
+ Some(PumpResult::Resize)
+ } else if need_present {
+ Some(PumpResult::ReadyToPresent)
} else {
None
}
diff --git a/ports/servoshell/browser.rs b/ports/servoshell/browser.rs
index 57c5ae07c79..184cf031cc1 100644
--- a/ports/servoshell/browser.rs
+++ b/ports/servoshell/browser.rs
@@ -8,7 +8,7 @@ use crate::window_trait::{WindowPortsMethods, LINE_HEIGHT};
use arboard::Clipboard;
use euclid::{Point2D, Vector2D};
use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher};
-use log::{error, debug, warn, info};
+use log::{error, debug, trace, warn, info};
use servo::compositing::windowing::{WebRenderDebugOption, EmbedderEvent};
use servo::embedder_traits::{
ContextMenuResult, EmbedderMsg, FilterPattern, PermissionPrompt, PermissionRequest,
@@ -89,6 +89,7 @@ where
pub fn handle_window_events(&mut self, events: Vec<EmbedderEvent>) {
for event in events {
+ trace!("embedder <- window EmbedderEvent {:?}", event);
match event {
EmbedderEvent::Keyboard(key_event) => {
self.handle_key_from_window(key_event);
@@ -281,6 +282,7 @@ where
pub fn handle_servo_events(&mut self, events: Vec<(Option<BrowserId>, EmbedderMsg)>) -> bool {
let mut need_present = false;
for (browser_id, msg) in events {
+ trace!("embedder <- servo EmbedderMsg ({:?}, {:?})", browser_id.map(|x| format!("{}", x)), msg);
match msg {
EmbedderMsg::Status(_status) => {
// FIXME: surface this status string in the UI somehow