aboutsummaryrefslogtreecommitdiffstats
path: root/components/compositing
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2017-09-30 15:50:47 +0200
committerMartin Robinson <mrobinson@igalia.com>2017-10-17 23:33:13 +0200
commitb5d51dd2636935471447fc741ffbb95c62e37f94 (patch)
treed437a0a5114788c529692628729cdc3169ec4b2e /components/compositing
parent00e2a1c62a04ae337f9008dcea8e265edd2d3ef4 (diff)
downloadservo-b5d51dd2636935471447fc741ffbb95c62e37f94.tar.gz
servo-b5d51dd2636935471447fc741ffbb95c62e37f94.zip
Switch to using WebRender hit testing
This trades quite a bit of complicated code in Servo for few more messages and a significant performance improvement. In particular, WebRender can search the entire display list at once instead of ping-ponging down the pipeline tree. This allows us to send mouse events to the correct pipeline immediately.
Diffstat (limited to 'components/compositing')
-rw-r--r--components/compositing/Cargo.toml1
-rw-r--r--components/compositing/compositor.rs180
-rw-r--r--components/compositing/compositor_thread.rs4
-rw-r--r--components/compositing/lib.rs1
4 files changed, 102 insertions, 84 deletions
diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml
index 40ec9105448..56cdd8c1e4a 100644
--- a/components/compositing/Cargo.toml
+++ b/components/compositing/Cargo.toml
@@ -15,6 +15,7 @@ gfx_traits = {path = "../gfx_traits"}
gleam = "0.4"
image = "0.16"
ipc-channel = "0.9"
+libc = "0.2"
log = "0.3.5"
msg = {path = "../msg"}
net_traits = {path = "../net_traits"}
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index a652346c794..92c8aff4719 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -6,20 +6,20 @@ use CompositionPipeline;
use SendableFrameTree;
use compositor_thread::{CompositorProxy, CompositorReceiver};
use compositor_thread::{InitialCompositorState, Msg, RenderListener};
-use euclid::{Point2D, TypedPoint2D, TypedVector2D, ScaleFactor};
+use euclid::{TypedPoint2D, TypedVector2D, ScaleFactor};
use gfx_traits::Epoch;
use gleam::gl;
use image::{DynamicImage, ImageFormat, RgbImage};
use ipc_channel::ipc::{self, IpcSharedMemory};
+use libc::c_void;
use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId};
use net_traits::image::base::{Image, PixelFormat};
use nonzero::NonZero;
use profile_traits::time::{self, ProfilerCategory, profile};
-use script_traits::{AnimationState, AnimationTickType, ConstellationControlMsg};
-use script_traits::{ConstellationMsg, LayoutControlMsg, MouseButton};
-use script_traits::{MouseEventType, ScrollState};
-use script_traits::{TouchpadPressurePhase, TouchEventType, TouchId, WindowSizeData, WindowSizeType};
-use script_traits::CompositorEvent::{self, MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent};
+use script_traits::{AnimationState, AnimationTickType, ConstellationMsg, LayoutControlMsg};
+use script_traits::{MouseButton, MouseEventType, ScrollState, TouchEventType, TouchId};
+use script_traits::{TouchpadPressurePhase, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
+use script_traits::CompositorEvent::{MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent};
use servo_config::opts;
use servo_config::prefs::PREFS;
use servo_geometry::DeviceIndependentPixel;
@@ -29,12 +29,13 @@ use std::rc::Rc;
use std::sync::mpsc::Sender;
use std::time::{Duration, Instant};
use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
+use style_traits::cursor::Cursor;
use style_traits::viewport::ViewportConstraints;
use time::{precise_time_ns, precise_time_s};
use touch::{TouchHandler, TouchAction};
use webrender;
-use webrender_api::{self, ClipId, DeviceUintRect, DeviceUintSize, LayoutPoint, LayoutVector2D};
-use webrender_api::{ScrollEventPhase, ScrollLocation, ScrollClamping};
+use webrender_api::{self, DeviceUintRect, DeviceUintSize, HitTestFlags, HitTestResult};
+use webrender_api::{LayoutVector2D, ScrollEventPhase, ScrollLocation};
use windowing::{self, MouseWindowEvent, WebRenderDebugOption, WindowMethods};
#[derive(Debug, PartialEq)]
@@ -464,11 +465,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.send_viewport_rects();
}
- (Msg::ScrollFragmentPoint(scroll_root_id, point, _),
- ShutdownState::NotShuttingDown) => {
- self.scroll_fragment_to_point(scroll_root_id, point);
- }
-
(Msg::Recomposite(reason), ShutdownState::NotShuttingDown) => {
self.composition_request = CompositionRequest::CompositeNow(reason)
}
@@ -656,13 +652,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}
- fn scroll_fragment_to_point(&mut self, id: ClipId, point: Point2D<f32>) {
- self.webrender_api.scroll_node_with_id(self.webrender_document,
- LayoutPoint::from_untyped(&point),
- id,
- ScrollClamping::ToContentBounds);
- }
-
pub fn on_resize_window_event(&mut self, new_size: DeviceUintSize) {
debug!("compositor resizing to {:?}", new_size.to_untyped());
@@ -707,32 +696,47 @@ impl<Window: WindowMethods> IOCompositor<Window> {
MouseWindowEvent::MouseUp(_, p) => p,
};
- let root_pipeline_id = match self.get_root_pipeline_id() {
- Some(root_pipeline_id) => root_pipeline_id,
+ let results = self.hit_test_at_point(point);
+ let result = match results.items.first() {
+ Some(result) => result,
None => return,
};
- if let Some(pipeline) = self.pipeline(root_pipeline_id) {
- let dppx = self.page_zoom * self.hidpi_factor();
- let translated_point = (point / dppx).to_untyped();
- let event_to_send = match mouse_window_event {
- MouseWindowEvent::Click(button, _) => {
- MouseButtonEvent(MouseEventType::Click, button, translated_point)
- }
- MouseWindowEvent::MouseDown(button, _) => {
- MouseButtonEvent(MouseEventType::MouseDown, button, translated_point)
- }
- MouseWindowEvent::MouseUp(button, _) => {
- MouseButtonEvent(MouseEventType::MouseUp, button, translated_point)
- }
- };
- let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event_to_send);
- if let Err(e) = pipeline.script_chan.send(msg) {
- warn!("Sending control event to script failed ({}).", e);
+ let point = result.point_in_viewport.to_untyped();
+ let node_address = Some(UntrustedNodeAddress(result.tag.0 as *const c_void));
+ let event_to_send = match mouse_window_event {
+ MouseWindowEvent::Click(button, _) => {
+ MouseButtonEvent(MouseEventType::Click, button, point, node_address)
}
+ MouseWindowEvent::MouseDown(button, _) => {
+ MouseButtonEvent(MouseEventType::MouseDown, button, point, node_address)
+ }
+ MouseWindowEvent::MouseUp(button, _) => {
+ MouseButtonEvent(MouseEventType::MouseUp, button, point, node_address)
+ }
+ };
+
+ let pipeline_id = PipelineId::from_webrender(result.pipeline);
+ let msg = ConstellationMsg::ForwardEvent(pipeline_id, event_to_send);
+ if let Err(e) = self.constellation_chan.send(msg) {
+ warn!("Sending event to constellation failed ({}).", e);
}
}
+ fn hit_test_at_point(&self, point: TypedPoint2D<f32, DevicePixel>) -> HitTestResult {
+ let dppx = self.page_zoom * self.hidpi_factor();
+ let scaled_point = (point / dppx).to_untyped();
+
+ let world_cursor = webrender_api::WorldPoint::from_untyped(&scaled_point);
+ self.webrender_api.hit_test(
+ self.webrender_document,
+ None,
+ world_cursor,
+ HitTestFlags::empty()
+ )
+
+ }
+
pub fn on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<f32, DevicePixel>) {
if opts::get().convert_mouse_to_touch {
self.on_touch_move(TouchId(0), cursor);
@@ -751,26 +755,43 @@ impl<Window: WindowMethods> IOCompositor<Window> {
return;
}
- let dppx = self.page_zoom * self.hidpi_factor();
- let event_to_send = MouseMoveEvent(Some((cursor / dppx).to_untyped()));
- let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event_to_send);
- if let Some(pipeline) = self.pipeline(root_pipeline_id) {
- if let Err(e) = pipeline.script_chan.send(msg) {
- warn!("Sending mouse control event to script failed ({}).", e);
+ let results = self.hit_test_at_point(cursor);
+ if let Some(item) = results.items.first() {
+ let node_address = Some(UntrustedNodeAddress(item.tag.0 as *const c_void));
+ let event = MouseMoveEvent(Some(item.point_in_viewport.to_untyped()), node_address);
+ let pipeline_id = PipelineId::from_webrender(item.pipeline);
+ let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
+ if let Err(e) = self.constellation_chan.send(msg) {
+ warn!("Sending event to constellation failed ({}).", e);
+ }
+
+ if let Some(cursor) = Cursor::from_u8(item.tag.1).ok() {
+ let msg = ConstellationMsg::SetCursor(cursor);
+ if let Err(e) = self.constellation_chan.send(msg) {
+ warn!("Sending event to constellation failed ({}).", e);
+ }
}
}
}
- fn send_event_to_root_pipeline(&self, event: CompositorEvent) {
- let root_pipeline_id = match self.get_root_pipeline_id() {
- Some(root_pipeline_id) => root_pipeline_id,
- None => return,
- };
-
- if let Some(pipeline) = self.pipeline(root_pipeline_id) {
- let msg = ConstellationControlMsg::SendEvent(root_pipeline_id, event);
- if let Err(e) = pipeline.script_chan.send(msg) {
- warn!("Sending control event to script failed ({}).", e);
+ fn send_touch_event(
+ &self,
+ event_type: TouchEventType,
+ identifier: TouchId,
+ point: TypedPoint2D<f32, DevicePixel>)
+ {
+ let results = self.hit_test_at_point(point);
+ if let Some(item) = results.items.first() {
+ let event = TouchEvent(
+ event_type,
+ identifier,
+ item.point_in_viewport.to_untyped(),
+ Some(UntrustedNodeAddress(item.tag.0 as *const c_void)),
+ );
+ let pipeline_id = PipelineId::from_webrender(item.pipeline);
+ let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
+ if let Err(e) = self.constellation_chan.send(msg) {
+ warn!("Sending event to constellation failed ({}).", e);
}
}
}
@@ -789,11 +810,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fn on_touch_down(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
self.touch_handler.on_touch_down(identifier, point);
- let dppx = self.page_zoom * self.hidpi_factor();
- let translated_point = (point / dppx).to_untyped();
- self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Down,
- identifier,
- translated_point));
+ self.send_touch_event(TouchEventType::Down, identifier, point);
}
fn on_touch_move(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
@@ -821,22 +838,15 @@ impl<Window: WindowMethods> IOCompositor<Window> {
});
}
TouchAction::DispatchEvent => {
- let dppx = self.page_zoom * self.hidpi_factor();
- let translated_point = (point / dppx).to_untyped();
- self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Move,
- identifier,
- translated_point));
+ self.send_touch_event(TouchEventType::Move, identifier, point);
}
_ => {}
}
}
fn on_touch_up(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
- let dppx = self.page_zoom * self.hidpi_factor();
- let translated_point = (point / dppx).to_untyped();
- self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Up,
- identifier,
- translated_point));
+ self.send_touch_event(TouchEventType::Up, identifier, point);
+
if let TouchAction::Click = self.touch_handler.on_touch_up(identifier, point) {
self.simulate_mouse_click(point);
}
@@ -845,23 +855,31 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fn on_touch_cancel(&mut self, identifier: TouchId, point: TypedPoint2D<f32, DevicePixel>) {
// Send the event to script.
self.touch_handler.on_touch_cancel(identifier, point);
- let dppx = self.page_zoom * self.hidpi_factor();
- let translated_point = (point / dppx).to_untyped();
- self.send_event_to_root_pipeline(TouchEvent(TouchEventType::Cancel,
- identifier,
- translated_point));
+ self.send_touch_event(TouchEventType::Cancel, identifier, point);
}
pub fn on_touchpad_pressure_event(&self,
point: TypedPoint2D<f32, DevicePixel>,
pressure: f32,
phase: TouchpadPressurePhase) {
- if let Some(true) = PREFS.get("dom.forcetouch.enabled").as_boolean() {
- let dppx = self.page_zoom * self.hidpi_factor();
- let translated_point = (point / dppx).to_untyped();
- self.send_event_to_root_pipeline(TouchpadPressureEvent(translated_point,
- pressure,
- phase));
+ match PREFS.get("dom.forcetouch.enabled").as_boolean() {
+ Some(true) => {},
+ _ => return,
+ }
+
+ let results = self.hit_test_at_point(point);
+ if let Some(item) = results.items.first() {
+ let event = TouchpadPressureEvent(
+ item.point_in_viewport.to_untyped(),
+ pressure,
+ phase,
+ Some(UntrustedNodeAddress(item.tag.0 as *const c_void)),
+ );
+ let pipeline_id = PipelineId::from_webrender(item.pipeline);
+ let msg = ConstellationMsg::ForwardEvent(pipeline_id, event);
+ if let Err(e) = self.constellation_chan.send(msg) {
+ warn!("Sending event to constellation failed ({}).", e);
+ }
}
}
diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs
index 03a7b46c7c0..2fc1373da56 100644
--- a/components/compositing/compositor_thread.rs
+++ b/components/compositing/compositor_thread.rs
@@ -158,8 +158,6 @@ pub enum Msg {
/// (e.g. SetFrameTree) at the time that we send it an ExitMsg.
ShutdownComplete,
- /// Scroll a page in a window
- ScrollFragmentPoint(webrender_api::ClipId, Point2D<f32>, bool),
/// Alerts the compositor that the given pipeline has changed whether it is running animations.
ChangeRunningAnimationsState(PipelineId, AnimationState),
/// Replaces the current frame tree, typically called during main frame navigation.
@@ -195,6 +193,7 @@ pub enum Msg {
PendingPaintMetric(PipelineId, Epoch),
/// The load of a page has completed
LoadComplete(TopLevelBrowsingContextId),
+
}
impl Debug for Msg {
@@ -202,7 +201,6 @@ impl Debug for Msg {
match *self {
Msg::Exit => write!(f, "Exit"),
Msg::ShutdownComplete => write!(f, "ShutdownComplete"),
- Msg::ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
Msg::ChangeRunningAnimationsState(..) => write!(f, "ChangeRunningAnimationsState"),
Msg::SetFrameTree(..) => write!(f, "SetFrameTree"),
Msg::Recomposite(..) => write!(f, "Recomposite"),
diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs
index b2abdcbf94d..82702dfb734 100644
--- a/components/compositing/lib.rs
+++ b/components/compositing/lib.rs
@@ -9,6 +9,7 @@ extern crate gfx_traits;
extern crate gleam;
extern crate image;
extern crate ipc_channel;
+extern crate libc;
#[macro_use]
extern crate log;
extern crate msg;