Make Restyle tracking more granular.

The primary idea of this patch is to ditch the rigid enum of Previous/Current
styles, and replace it with a series of indicators for the various types of
work that needs to be performed (expanding snapshots, rematching, recascading,
and damage processing). This loses us a little bit of sanity checking (since
the up-to-date-ness of our style is no longer baked into the type system), but
gives us a lot more flexibility that we'll need going forward (especially when
we separate matching from cascading). We also eliminate get_styling_mode in
favor of a method on the traversal.

This patch does a few other things as ridealongs:
* Temporarily eliminates the handling for transfering ownership of styles to the
  frame. We'll need this again at some point, but for now it's causing too much
  complexity for a half-implemented feature.
* Ditches TRestyleDamage, which is no longer necessary post-crate-merge, and is
  a constant source of compilation failures from either needing to be imported
  or being unnecessarily imported (which varies between gecko and servo).
* Expands Snapshots for the traversal root, which was missing before.
* Fixes up the skip_root stuff to avoid visiting the skipped root.
* Unifies parallel traversal and avoids spawning for a single work item.
* Adds an explicit pre_traverse step do any pre-processing and determine whether
  we need to traverse at all.

MozReview-Commit-ID: IKhLAkAigXE
This commit is contained in:
Bobby Holley 2016-11-30 19:36:08 -08:00
parent 4cb3404c09
commit 80460cc549
27 changed files with 502 additions and 474 deletions

View file

@ -9,15 +9,16 @@
use dom::{OpaqueNode, TElement, TNode, UnsafeNode};
use rayon;
use std::sync::atomic::Ordering;
use traversal::{DomTraversalContext, PerLevelTraversalData};
use traversal::{DomTraversalContext, PerLevelTraversalData, PreTraverseToken};
use traversal::{STYLE_SHARING_CACHE_HITS, STYLE_SHARING_CACHE_MISSES};
use util::opts;
pub const CHUNK_SIZE: usize = 64;
pub fn traverse_dom<N, C>(root: N,
pub fn traverse_dom<N, C>(root: N::ConcreteElement,
known_root_dom_depth: Option<usize>,
shared_context: &C::SharedContext,
token: PreTraverseToken,
queue: &rayon::ThreadPool)
where N: TNode,
C: DomTraversalContext<N>
@ -27,15 +28,26 @@ pub fn traverse_dom<N, C>(root: N,
STYLE_SHARING_CACHE_MISSES.store(0, Ordering::SeqCst);
}
let nodes = vec![root.to_unsafe()].into_boxed_slice();
let data = PerLevelTraversalData {
current_dom_depth: known_root_dom_depth,
// Handle root skipping. We don't currently support it in conjunction with
// bottom-up traversal. If we did, we'd need to put it on the context to make
// it available to the bottom-up phase.
debug_assert!(!token.should_skip_root() || !C::needs_postorder_traversal());
let (nodes, depth) = if token.should_skip_root() {
let mut children = vec![];
C::traverse_children(root, |kid| children.push(kid.to_unsafe()));
(children, known_root_dom_depth.map(|x| x + 1))
} else {
(vec![root.as_node().to_unsafe()], known_root_dom_depth)
};
let root = root.opaque();
let data = PerLevelTraversalData {
current_dom_depth: depth,
};
let root = root.as_node().opaque();
queue.install(|| {
rayon::scope(|scope| {
let nodes = nodes;
top_down_dom::<N, C>(&nodes, root, data, scope, shared_context);
traverse_nodes::<_, C>(nodes, root, data, scope, shared_context);
});
});
@ -79,7 +91,7 @@ fn top_down_dom<'a, 'scope, N, C>(unsafe_nodes: &'a [UnsafeNode],
// Reset the count of children if we need to do a bottom-up traversal
// after the top up.
if context.needs_postorder_traversal() {
if C::needs_postorder_traversal() {
if children_to_process == 0 {
// If there were no more children, start walking back up.
bottom_up_dom::<N, C>(root, *unsafe_node, shared_context)
@ -99,7 +111,30 @@ fn top_down_dom<'a, 'scope, N, C>(unsafe_nodes: &'a [UnsafeNode],
*depth += 1;
}
for chunk in discovered_child_nodes.chunks(CHUNK_SIZE) {
traverse_nodes::<_, C>(discovered_child_nodes, root, data, scope, shared_context);
}
fn traverse_nodes<'a, 'scope, N, C>(nodes: Vec<UnsafeNode>, root: OpaqueNode,
data: PerLevelTraversalData,
scope: &'a rayon::Scope<'scope>,
shared_context: &'scope C::SharedContext)
where N: TNode,
C: DomTraversalContext<N>,
{
if nodes.is_empty() {
return;
}
// Optimization: traverse directly and avoid a heap-allocating spawn() call if
// we're only pushing one work unit.
if nodes.len() <= CHUNK_SIZE {
let nodes = nodes.into_boxed_slice();
top_down_dom::<N, C>(&nodes, root, data, scope, shared_context);
return;
}
// General case.
for chunk in nodes.chunks(CHUNK_SIZE) {
let nodes = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
let data = data.clone();
scope.spawn(move |scope| {