servo: Merge #17810 - Script paint worklets speculative evaluation (from asajeffrey:script-paint-worklets-speculative-evaluation); r=emilio

<!-- Please describe your changes on the following line: -->

This PR speculatively calls paint worklets during style, which increases the concurrency, since it increases the chance that the cache will have the right result in it when it comes to layout. The speculation is wasted effort if the size of the element has changed, but this is often not the case.

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #17486 and #17369.
- [X] These changes do not require tests because it's a performance improvement

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

Source-Repo: https://github.com/servo/servo
Source-Revision: cf5602e84f17d14b6a9abb77f1de6460db070921

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : ab914804642ce2d739a3a441c8691e8041e67958
This commit is contained in:
Alan Jeffrey 2017-07-31 13:13:26 -05:00
Родитель fafef4638c
Коммит 9f89278c5a
13 изменённых файлов: 279 добавлений и 52 удалений

2
servo/Cargo.lock сгенерированный
Просмотреть файл

@ -1520,6 +1520,7 @@ dependencies = [
"servo_geometry 0.0.1",
"servo_url 0.0.1",
"style 0.0.1",
"style_traits 0.0.1",
"webrender_api 0.48.0 (git+https://github.com/servo/webrender)",
]
@ -3087,6 +3088,7 @@ dependencies = [
"heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.19.0",
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"servo_atoms 0.0.1",
"webrender_api 0.48.0 (git+https://github.com/servo/webrender)",
]

Просмотреть файл

@ -4,7 +4,6 @@
//! Data needed by the layout thread.
use fnv::FnvHashMap;
use fnv::FnvHasher;
use gfx::display_list::{WebRenderImageInfo, OpaqueNode};
use gfx::font_cache_thread::FontCacheThread;
@ -25,8 +24,8 @@ use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::sync::{Arc, Mutex};
use std::thread;
use style::context::RegisteredSpeculativePainter;
use style::context::SharedStyleContext;
use style::properties::PropertyId;
thread_local!(static FONT_CONTEXT_KEY: RefCell<Option<FontContext>> = RefCell::new(None));
@ -73,7 +72,7 @@ pub struct LayoutContext<'a> {
BuildHasherDefault<FnvHasher>>>>,
/// Paint worklets
pub registered_painters: Arc<RwLock<FnvHashMap<Atom, RegisteredPainter>>>,
pub registered_painters: &'a RegisteredPainters,
/// A list of in-progress image loads to be shared with the script thread.
/// A None value means that this layout was not initiated by the script thread.
@ -179,9 +178,11 @@ impl<'a> LayoutContext<'a> {
}
}
/// A registered paint worklet.
pub struct RegisteredPainter {
pub name: Atom,
pub properties: FnvHashMap<Atom, PropertyId>,
pub painter: Arc<Painter>,
/// A registered painter
pub trait RegisteredPainter: RegisteredSpeculativePainter + Painter {}
/// A set of registered painters
pub trait RegisteredPainters: Sync {
/// Look up a painter
fn get(&self, name: &Atom) -> Option<&RegisteredPainter>;
}

Просмотреть файл

@ -1181,24 +1181,18 @@ impl FragmentDisplayListBuilding for Fragment {
.map(|argument| argument.to_css_string())
.collect();
// Get the painter, and the computed values for its properties.
// TODO: less copying.
let (properties, painter) = match state.layout_context.registered_painters.read().get(&name) {
Some(registered_painter) => (
registered_painter.properties
.iter()
let mut draw_result = match state.layout_context.registered_painters.get(&name) {
Some(painter) => {
debug!("Drawing a paint image {}({},{}).", name, size_in_px.width, size_in_px.height);
let properties = painter.properties().iter()
.filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
.map(|(name, id)| (name.clone(), style.computed_value_to_string(id)))
.collect(),
registered_painter.painter.clone()
),
.collect();
painter.draw_a_paint_image(size_in_px, device_pixel_ratio, properties, arguments)
},
None => return debug!("Worklet {} called before registration.", name),
};
// TODO: add a one-place cache to avoid drawing the paint image every time.
// https://github.com/servo/servo/issues/17369
debug!("Drawing a paint image {}({},{}).", name, size_in_px.width, size_in_px.height);
let mut draw_result = painter.draw_a_paint_image(size_in_px, device_pixel_ratio, properties, arguments);
let webrender_image = WebRenderImageInfo {
width: draw_result.width,
height: draw_result.height,

Просмотреть файл

@ -41,4 +41,5 @@ servo_config = {path = "../config"}
servo_geometry = {path = "../geometry"}
servo_url = {path = "../url"}
style = {path = "../style"}
style_traits = {path = "../style_traits"}
webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}

Просмотреть файл

@ -46,6 +46,7 @@ extern crate servo_config;
extern crate servo_geometry;
extern crate servo_url;
extern crate style;
extern crate style_traits;
extern crate webrender_api;
mod dom_wrapper;
@ -53,7 +54,7 @@ mod dom_wrapper;
use app_units::Au;
use dom_wrapper::{ServoLayoutElement, ServoLayoutDocument, ServoLayoutNode};
use dom_wrapper::drop_style_and_layout_data;
use euclid::{Point2D, Rect, Size2D, ScaleFactor};
use euclid::{Point2D, Rect, Size2D, ScaleFactor, TypedSize2D};
use fnv::FnvHashMap;
use gfx::display_list::{OpaqueNode, WebRenderImageInfo};
use gfx::font;
@ -67,6 +68,7 @@ use layout::animation;
use layout::construct::ConstructionResult;
use layout::context::LayoutContext;
use layout::context::RegisteredPainter;
use layout::context::RegisteredPainters;
use layout::context::heap_size_of_persistent_local_context;
use layout::display_list_builder::ToGfxColor;
use layout::flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
@ -99,6 +101,8 @@ use script_layout_interface::rpc::TextIndexResponse;
use script_layout_interface::wrapper_traits::LayoutNode;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
use script_traits::{ScrollState, UntrustedNodeAddress};
use script_traits::DrawAPaintImageResult;
use script_traits::Painter;
use selectors::Element;
use servo_arc::Arc as ServoArc;
use servo_atoms::Atom;
@ -122,6 +126,8 @@ use std::thread;
use style::animation::Animation;
use style::context::{QuirksMode, ReflowGoal, SharedStyleContext};
use style::context::{StyleSystemOptions, ThreadLocalStyleContextCreationInfo};
use style::context::RegisteredSpeculativePainter;
use style::context::RegisteredSpeculativePainters;
use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode};
use style::error_reporting::{NullReporter, RustLogReporter};
use style::invalidation::element::restyle_hints::RestyleHint;
@ -137,6 +143,9 @@ use style::thread_state;
use style::timer::Timer;
use style::traversal::{DomTraversal, TraversalDriver};
use style::traversal_flags::TraversalFlags;
use style_traits::CSSPixel;
use style_traits::DevicePixel;
use style_traits::SpeculativePainter;
/// Information needed by the layout thread.
pub struct LayoutThread {
@ -235,9 +244,9 @@ pub struct LayoutThread {
webrender_image_cache: Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder),
WebRenderImageInfo>>>,
/// The executor for paint worklets.
/// Will be None if the script thread hasn't added any paint worklet modules.
registered_painters: Arc<RwLock<FnvHashMap<Atom, RegisteredPainter>>>,
/// The executors for paint worklets.
registered_painters: RegisteredPaintersImpl,
/// Webrender interface.
webrender_api: webrender_api::RenderApi,
@ -520,7 +529,7 @@ impl LayoutThread {
constellation_chan: constellation_chan.clone(),
time_profiler_chan: time_profiler_chan,
mem_profiler_chan: mem_profiler_chan,
registered_painters: Arc::new(RwLock::new(FnvHashMap::default())),
registered_painters: RegisteredPaintersImpl(FnvHashMap::default()),
image_cache: image_cache.clone(),
font_cache_thread: font_cache_thread,
first_reflow: Cell::new(true),
@ -605,6 +614,7 @@ impl LayoutThread {
visited_styles_enabled: false,
running_animations: self.running_animations.clone(),
expired_animations: self.expired_animations.clone(),
registered_speculative_painters: &self.registered_painters,
local_context_creation_data: Mutex::new(thread_local_style_context_creation_data),
timer: self.timer.clone(),
quirks_mode: self.quirks_mode.unwrap(),
@ -616,7 +626,7 @@ impl LayoutThread {
webrender_image_cache: self.webrender_image_cache.clone(),
pending_images: if script_initiated_layout { Some(Mutex::new(vec![])) } else { None },
newly_transitioning_nodes: if script_initiated_layout { Some(Mutex::new(vec![])) } else { None },
registered_painters: self.registered_painters.clone(),
registered_painters: &self.registered_painters,
}
}
@ -738,13 +748,12 @@ impl LayoutThread {
.filter_map(|name| PropertyId::parse(&*name).ok().map(|id| (name.clone(), id)))
.filter(|&(_, ref id)| id.as_shorthand().is_err())
.collect();
let registered_painter = RegisteredPainter {
let registered_painter = RegisteredPainterImpl {
name: name.clone(),
properties: properties,
painter: painter,
};
self.registered_painters.write()
.insert(name, registered_painter);
self.registered_painters.0.insert(name, registered_painter);
},
Msg::PrepareToExit(response_chan) => {
self.prepare_to_exit(response_chan);
@ -1790,3 +1799,52 @@ lazy_static! {
}
};
}
struct RegisteredPainterImpl {
painter: Box<Painter>,
name: Atom,
properties: FnvHashMap<Atom, PropertyId>,
}
impl SpeculativePainter for RegisteredPainterImpl {
fn speculatively_draw_a_paint_image(&self, properties: Vec<(Atom, String)>, arguments: Vec<String>) {
self.painter.speculatively_draw_a_paint_image(properties, arguments);
}
}
impl RegisteredSpeculativePainter for RegisteredPainterImpl {
fn properties(&self) -> &FnvHashMap<Atom, PropertyId> {
&self.properties
}
fn name(&self) -> Atom {
self.name.clone()
}
}
impl Painter for RegisteredPainterImpl {
fn draw_a_paint_image(&self,
size: TypedSize2D<f32, CSSPixel>,
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
properties: Vec<(Atom, String)>,
arguments: Vec<String>)
-> DrawAPaintImageResult
{
self.painter.draw_a_paint_image(size, device_pixel_ratio, properties, arguments)
}
}
impl RegisteredPainter for RegisteredPainterImpl {}
struct RegisteredPaintersImpl(FnvHashMap<Atom, RegisteredPainterImpl>);
impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
fn get(&self, name: &Atom) -> Option<&RegisteredSpeculativePainter> {
self.0.get(&name).map(|painter| painter as &RegisteredSpeculativePainter)
}
}
impl RegisteredPainters for RegisteredPaintersImpl {
fn get(&self, name: &Atom) -> Option<&RegisteredPainter> {
self.0.get(&name).map(|painter| painter as &RegisteredPainter)
}
}

Просмотреть файл

@ -43,7 +43,7 @@ use dom::bindings::str::{DOMString, USVString};
use dom::bindings::utils::WindowProxyHandler;
use dom::document::PendingRestyle;
use encoding::types::EncodingRef;
use euclid::{Transform2D, Transform3D, Point2D, Vector2D, Rect, Size2D, ScaleFactor};
use euclid::{Transform2D, Transform3D, Point2D, Vector2D, Rect, TypedSize2D, ScaleFactor};
use euclid::Length as EuclidLength;
use html5ever::{Prefix, LocalName, Namespace, QualName};
use html5ever::buffer_queue::BufferQueue;
@ -75,6 +75,7 @@ use script_layout_interface::reporter::CSSErrorReporter;
use script_layout_interface::rpc::LayoutRPC;
use script_traits::{DocumentActivity, TimerEventId, TimerSource, TouchpadPressurePhase};
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use script_traits::DrawAPaintImageResult;
use selectors::matching::ElementSelectorFlags;
use serde::{Deserialize, Serialize};
use servo_arc::Arc as ServoArc;
@ -389,6 +390,7 @@ unsafe_no_jsmanaged_fields!(RelativePos);
unsafe_no_jsmanaged_fields!(OpaqueStyleAndLayoutData);
unsafe_no_jsmanaged_fields!(PathBuf);
unsafe_no_jsmanaged_fields!(CSSErrorReporter);
unsafe_no_jsmanaged_fields!(DrawAPaintImageResult);
unsafe_no_jsmanaged_fields!(WebGLBufferId);
unsafe_no_jsmanaged_fields!(WebGLFramebufferId);
unsafe_no_jsmanaged_fields!(WebGLProgramId);
@ -519,7 +521,14 @@ unsafe impl JSTraceable for Rect<f32> {
}
}
unsafe impl JSTraceable for Size2D<i32> {
unsafe impl<U> JSTraceable for TypedSize2D<i32, U> {
#[inline]
unsafe fn trace(&self, _trc: *mut JSTracer) {
// Do nothing
}
}
unsafe impl<U> JSTraceable for TypedSize2D<f32, U> {
#[inline]
unsafe fn trace(&self, _trc: *mut JSTracer) {
// Do nothing

Просмотреть файл

@ -63,6 +63,7 @@ use std::sync::mpsc;
use std::sync::mpsc::Sender;
use style_traits::CSSPixel;
use style_traits::DevicePixel;
use style_traits::SpeculativePainter;
/// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope
#[dom_struct]
@ -76,6 +77,18 @@ pub struct PaintWorkletGlobalScope {
paint_definitions: DOMRefCell<HashMap<Atom, Box<PaintDefinition>>>,
/// https://drafts.css-houdini.org/css-paint-api/#paint-class-instances
paint_class_instances: DOMRefCell<HashMap<Atom, Box<Heap<JSVal>>>>,
/// The most recent name the worklet was called with
cached_name: DOMRefCell<Atom>,
/// The most recent size the worklet was drawn at
cached_size: Cell<TypedSize2D<f32, CSSPixel>>,
/// The most recent device pixel ratio the worklet was drawn at
cached_device_pixel_ratio: Cell<ScaleFactor<f32, CSSPixel, DevicePixel>>,
/// The most recent properties the worklet was drawn at
cached_properties: DOMRefCell<Vec<(Atom, String)>>,
/// The most recent arguments the worklet was drawn at
cached_arguments: DOMRefCell<Vec<String>>,
/// The most recent result
cached_result: DOMRefCell<DrawAPaintImageResult>,
}
impl PaintWorkletGlobalScope {
@ -92,6 +105,18 @@ impl PaintWorkletGlobalScope {
image_cache: init.image_cache.clone(),
paint_definitions: Default::default(),
paint_class_instances: Default::default(),
cached_name: DOMRefCell::new(Atom::from("")),
cached_size: Cell::new(TypedSize2D::zero()),
cached_device_pixel_ratio: Cell::new(ScaleFactor::new(1.0)),
cached_properties: Default::default(),
cached_arguments: Default::default(),
cached_result: DOMRefCell::new(DrawAPaintImageResult {
width: 0,
height: 0,
format: PixelFormat::BGRA8,
image_key: None,
missing_image_urls: Vec::new(),
}),
};
unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) }
}
@ -102,21 +127,58 @@ impl PaintWorkletGlobalScope {
pub fn perform_a_worklet_task(&self, task: PaintWorkletTask) {
match task {
PaintWorkletTask::DrawAPaintImage(name, size_in_px, device_pixel_ratio, properties, arguments, sender) => {
let properties = StylePropertyMapReadOnly::from_iter(self.upcast(), properties);
let result = self.draw_a_paint_image(name, size_in_px, device_pixel_ratio, &*properties, arguments);
PaintWorkletTask::DrawAPaintImage(name, size, device_pixel_ratio, properties, arguments, sender) => {
let cache_hit = (&*self.cached_name.borrow() == &name) &&
(self.cached_size.get() == size) &&
(self.cached_device_pixel_ratio.get() == device_pixel_ratio) &&
(&*self.cached_properties.borrow() == &properties) &&
(&*self.cached_arguments.borrow() == &arguments);
let result = if cache_hit {
debug!("Cache hit on paint worklet {}!", name);
self.cached_result.borrow().clone()
} else {
debug!("Cache miss on paint worklet {}!", name);
let map = StylePropertyMapReadOnly::from_iter(self.upcast(), properties.iter().cloned());
let result = self.draw_a_paint_image(&name, size, device_pixel_ratio, &*map, &*arguments);
if (result.image_key.is_some()) && (result.missing_image_urls.is_empty()) {
*self.cached_name.borrow_mut() = name;
self.cached_size.set(size);
self.cached_device_pixel_ratio.set(device_pixel_ratio);
*self.cached_properties.borrow_mut() = properties;
*self.cached_arguments.borrow_mut() = arguments;
*self.cached_result.borrow_mut() = result.clone();
}
result
};
let _ = sender.send(result);
}
PaintWorkletTask::SpeculativelyDrawAPaintImage(name, properties, arguments) => {
let should_speculate = (&*self.cached_name.borrow() != &name) ||
(&*self.cached_properties.borrow() != &properties) ||
(&*self.cached_arguments.borrow() != &arguments);
if should_speculate {
let size = self.cached_size.get();
let device_pixel_ratio = self.cached_device_pixel_ratio.get();
let map = StylePropertyMapReadOnly::from_iter(self.upcast(), properties.iter().cloned());
let result = self.draw_a_paint_image(&name, size, device_pixel_ratio, &*map, &*arguments);
if (result.image_key.is_some()) && (result.missing_image_urls.is_empty()) {
*self.cached_name.borrow_mut() = name;
*self.cached_properties.borrow_mut() = properties;
*self.cached_arguments.borrow_mut() = arguments;
*self.cached_result.borrow_mut() = result;
}
}
}
}
}
/// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
fn draw_a_paint_image(&self,
name: Atom,
name: &Atom,
size_in_px: TypedSize2D<f32, CSSPixel>,
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
properties: &StylePropertyMapReadOnly,
arguments: Vec<String>)
arguments: &[String])
-> DrawAPaintImageResult
{
let size_in_dpx = size_in_px * device_pixel_ratio;
@ -131,12 +193,12 @@ impl PaintWorkletGlobalScope {
/// https://drafts.css-houdini.org/css-paint-api/#invoke-a-paint-callback
#[allow(unsafe_code)]
fn invoke_a_paint_callback(&self,
name: Atom,
name: &Atom,
size_in_px: TypedSize2D<f32, CSSPixel>,
size_in_dpx: TypedSize2D<u32, DevicePixel>,
device_pixel_ratio: ScaleFactor<f32, CSSPixel, DevicePixel>,
properties: &StylePropertyMapReadOnly,
mut arguments: Vec<String>)
arguments: &[String])
-> DrawAPaintImageResult
{
debug!("Invoking a paint callback {}({},{}) at {}.",
@ -149,7 +211,7 @@ impl PaintWorkletGlobalScope {
// Step 2.2-5.1.
rooted!(in(cx) let mut class_constructor = UndefinedValue());
rooted!(in(cx) let mut paint_function = UndefinedValue());
let rendering_context = match self.paint_definitions.borrow().get(&name) {
let rendering_context = match self.paint_definitions.borrow().get(name) {
None => {
// Step 2.2.
warn!("Drawing un-registered paint definition {}.", name);
@ -184,7 +246,7 @@ impl PaintWorkletGlobalScope {
if unsafe { JS_IsExceptionPending(cx) } {
debug!("Paint constructor threw an exception {}.", name);
unsafe { JS_ClearPendingException(cx); }
self.paint_definitions.borrow_mut().get_mut(&name)
self.paint_definitions.borrow_mut().get_mut(name)
.expect("Vanishing paint definition.")
.constructor_valid_flag.set(false);
return self.invalid_image(size_in_dpx, vec![]);
@ -206,7 +268,7 @@ impl PaintWorkletGlobalScope {
// TODO: Step 10
// Steps 11-12
debug!("Invoking paint function {}.", name);
rooted_vec!(let arguments_values <- arguments.drain(..)
rooted_vec!(let arguments_values <- arguments.iter().cloned()
.map(|argument| CSSStyleValue::new(self.upcast(), argument)));
let arguments_value_vec: Vec<JSVal> = arguments_values.iter()
.map(|argument| ObjectValue(argument.reflector().get_jsobject().get()))
@ -262,9 +324,22 @@ impl PaintWorkletGlobalScope {
}
}
fn painter(&self, name: Atom) -> Arc<Painter> {
fn painter(&self, name: Atom) -> Box<Painter> {
// Rather annoyingly we have to use a mutex here to make the painter Sync.
struct WorkletPainter(Atom, Mutex<WorkletExecutor>);
struct WorkletPainter {
name: Atom,
executor: Mutex<WorkletExecutor>,
}
impl SpeculativePainter for WorkletPainter {
fn speculatively_draw_a_paint_image(&self,
properties: Vec<(Atom, String)>,
arguments: Vec<String>) {
let name = self.name.clone();
let task = PaintWorkletTask::SpeculativelyDrawAPaintImage(name, properties, arguments);
self.executor.lock().expect("Locking a painter.")
.schedule_a_worklet_task(WorkletTask::Paint(task));
}
}
impl Painter for WorkletPainter {
fn draw_a_paint_image(&self,
size: TypedSize2D<f32, CSSPixel>,
@ -272,7 +347,7 @@ impl PaintWorkletGlobalScope {
properties: Vec<(Atom, String)>,
arguments: Vec<String>)
-> DrawAPaintImageResult {
let name = self.0.clone();
let name = self.name.clone();
let (sender, receiver) = mpsc::channel();
let task = PaintWorkletTask::DrawAPaintImage(name,
size,
@ -280,12 +355,15 @@ impl PaintWorkletGlobalScope {
properties,
arguments,
sender);
self.1.lock().expect("Locking a painter.")
self.executor.lock().expect("Locking a painter.")
.schedule_a_worklet_task(WorkletTask::Paint(task));
receiver.recv().expect("Worklet thread died?")
}
}
Arc::new(WorkletPainter(name, Mutex::new(self.worklet_global.executor())))
Box::new(WorkletPainter {
name: name,
executor: Mutex::new(self.worklet_global.executor()),
})
}
}
@ -380,7 +458,10 @@ pub enum PaintWorkletTask {
ScaleFactor<f32, CSSPixel, DevicePixel>,
Vec<(Atom, String)>,
Vec<String>,
Sender<DrawAPaintImageResult>)
Sender<DrawAPaintImageResult>),
SpeculativelyDrawAPaintImage(Atom,
Vec<(Atom, String)>,
Vec<String>),
}
/// A paint definition

Просмотреть файл

@ -89,7 +89,7 @@ pub enum Msg {
UpdateScrollStateFromScript(ScrollState),
/// Tells layout that script has added some paint worklet modules.
RegisterPaint(Atom, Vec<Atom>, Arc<Painter>),
RegisterPaint(Atom, Vec<Atom>, Box<Painter>),
/// Send to layout the precise time when the navigation started.
SetNavigationStart(f64),

Просмотреть файл

@ -66,6 +66,7 @@ use std::fmt;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, RecvTimeoutError};
use style_traits::CSSPixel;
use style_traits::SpeculativePainter;
use webdriver_msg::{LoadStatus, WebDriverScriptCommand};
use webrender_api::{ClipId, DevicePixel, ImageKey};
use webvr_traits::{WebVREvent, WebVRMsg};
@ -817,7 +818,7 @@ impl From<RecvTimeoutError> for PaintWorkletError {
}
/// Execute paint code in the worklet thread pool.
pub trait Painter: Sync + Send {
pub trait Painter: SpeculativePainter {
/// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
fn draw_a_paint_image(&self,
size: TypedSize2D<f32, CSSPixel>,
@ -829,7 +830,7 @@ pub trait Painter: Sync + Send {
/// The result of executing paint code: the image together with any image URLs that need to be loaded.
/// TODO: this should return a WR display list. https://github.com/servo/servo/issues/17497
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, Clone, HeapSizeOf)]
pub struct DrawAPaintImageResult {
/// The image height
pub width: u32,

Просмотреть файл

@ -18,10 +18,12 @@ use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")] use gecko_bindings::structs;
#[cfg(feature = "servo")] use parking_lot::RwLock;
use properties::ComputedValues;
#[cfg(feature = "servo")] use properties::PropertyId;
use rule_tree::StrongRuleNode;
use selector_parser::{EAGER_PSEUDO_COUNT, SnapshotMap};
use selectors::matching::ElementSelectorFlags;
use servo_arc::Arc;
#[cfg(feature = "servo")] use servo_atoms::Atom;
use shared_lock::StylesheetGuards;
use sharing::StyleSharingCandidateCache;
use std::fmt;
@ -30,6 +32,7 @@ use std::ops;
#[cfg(feature = "servo")] use std::sync::mpsc::Sender;
use style_traits::CSSPixel;
use style_traits::DevicePixel;
#[cfg(feature = "servo")] use style_traits::SpeculativePainter;
use stylist::Stylist;
use thread_state;
use time;
@ -151,6 +154,10 @@ pub struct SharedStyleContext<'a> {
#[cfg(feature = "servo")]
pub expired_animations: Arc<RwLock<FnvHashMap<OpaqueNode, Vec<Animation>>>>,
/// Paint worklets
#[cfg(feature = "servo")]
pub registered_speculative_painters: &'a RegisteredSpeculativePainters,
/// Data needed to create the thread-local style context from the shared one.
#[cfg(feature = "servo")]
pub local_context_creation_data: Mutex<ThreadLocalStyleContextCreationInfo>,
@ -677,3 +684,19 @@ pub enum ReflowGoal {
/// We're reflowing in order to satisfy a script query. No display list will be created.
ForScriptQuery,
}
/// A registered painter
#[cfg(feature = "servo")]
pub trait RegisteredSpeculativePainter: SpeculativePainter {
/// The name it was registered with
fn name(&self) -> Atom;
/// The properties it was registered with
fn properties(&self) -> &FnvHashMap<Atom, PropertyId>;
}
/// A set of registered painters
#[cfg(feature = "servo")]
pub trait RegisteredSpeculativePainters: Sync {
/// Look up a speculative painter
fn get(&self, name: &Atom) -> Option<&RegisteredSpeculativePainter>;
}

Просмотреть файл

@ -12,8 +12,11 @@ use matching::{ChildCascadeRequirement, MatchMethods};
use sharing::StyleSharingTarget;
use smallvec::SmallVec;
use style_resolver::StyleResolverForElement;
#[cfg(feature = "servo")] use style_traits::ToCss;
use stylist::RuleInclusion;
use traversal_flags::{TraversalFlags, self};
#[cfg(feature = "servo")] use values::Either;
#[cfg(feature = "servo")] use values::generics::image::Image;
/// A per-traversal-level chunk of data. This is sent down by the traversal, and
/// currently only holds the dom depth for the bloom filter.
@ -518,6 +521,11 @@ where
element);
clear_descendant_data(element)
}
// Inform any paint worklets of changed style, to speculatively
// evaluate the worklet code. In the case that the size hasn't changed,
// this will result in increased concurrency between script and layout.
notify_paint_worklet(context, data);
}
// Now that matching and cascading is done, clear the bits corresponding to
@ -715,6 +723,46 @@ where
)
}
#[cfg(feature = "servo")]
fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
where
E: TElement,
{
// We speculatively evaluate any paint worklets during styling.
// This allows us to run paint worklets in parallel with style and layout.
// Note that this is wasted effort if the size of the node has
// changed, but in may cases it won't have.
if let Some(ref values) = data.styles.primary {
for image in &values.get_background().background_image.0 {
let (name, arguments) = match *image {
Either::Second(Image::PaintWorklet(ref worklet)) => (&worklet.name, &worklet.arguments),
_ => continue,
};
let painter = match context.shared.registered_speculative_painters.get(name) {
Some(painter) => painter,
None => continue,
};
let properties = painter.properties().iter()
.filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
.map(|(name, id)| (name.clone(), values.computed_value_to_string(id)))
.collect();
let arguments = arguments.iter()
.map(|argument| argument.to_css_string())
.collect();
debug!("Notifying paint worklet {}.", painter.name());
painter.speculatively_draw_a_paint_image(properties, arguments);
}
}
}
#[cfg(feature = "gecko")]
fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)
where
E: TElement,
{
// The CSS paint API is Servo-only at the moment
}
fn note_children<E, D, F>(
context: &mut StyleContext<E>,
element: E,

Просмотреть файл

@ -10,7 +10,7 @@ name = "style_traits"
path = "lib.rs"
[features]
servo = ["heapsize", "heapsize_derive", "serde", "cssparser/heapsize", "cssparser/serde", "webrender_api"]
servo = ["heapsize", "heapsize_derive", "serde", "servo_atoms", "cssparser/heapsize", "cssparser/serde", "webrender_api"]
gecko = []
[dependencies]
@ -23,3 +23,4 @@ heapsize_derive = {version = "0.1", optional = true}
selectors = { path = "../selectors" }
serde = {version = "1.0", optional = true}
webrender_api = {git = "https://github.com/servo/webrender", optional = true}
servo_atoms = {path = "../atoms", optional = true}

Просмотреть файл

@ -22,11 +22,13 @@ extern crate euclid;
extern crate selectors;
#[cfg(feature = "servo")] #[macro_use] extern crate serde;
#[cfg(feature = "servo")] extern crate webrender_api;
#[cfg(feature = "servo")] extern crate servo_atoms;
#[cfg(feature = "servo")] pub use webrender_api::DevicePixel;
use cssparser::{CowRcStr, Token};
use selectors::parser::SelectorParseError;
#[cfg(feature = "servo")] use servo_atoms::Atom;
/// One hardware pixel.
///
@ -184,3 +186,9 @@ impl ParsingMode {
}
}
#[cfg(feature = "servo")]
/// Speculatively execute paint code in the worklet thread pool.
pub trait SpeculativePainter: Send + Sync {
/// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
fn speculatively_draw_a_paint_image(&self, properties: Vec<(Atom, String)>, arguments: Vec<String>);
}