mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
style: Don't loop over all the set of dependencies always.
The dependency count is not at all minor, and this way we avoid looping through all of them in the common cases, mainly either changing state, or attributes.
This commit is contained in:
parent
b8d725d207
commit
3e80f482ba
1 changed files with 94 additions and 22 deletions
|
@ -5,6 +5,8 @@
|
||||||
//! Restyle hints: an optimization to avoid unnecessarily matching selectors.
|
//! Restyle hints: an optimization to avoid unnecessarily matching selectors.
|
||||||
|
|
||||||
use element_state::*;
|
use element_state::*;
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
use heapsize::HeapSizeOf;
|
||||||
use selector_impl::{ElementExt, TheSelectorImpl, NonTSPseudoClass, AttrValue};
|
use selector_impl::{ElementExt, TheSelectorImpl, NonTSPseudoClass, AttrValue};
|
||||||
use selectors::matching::StyleRelations;
|
use selectors::matching::StyleRelations;
|
||||||
use selectors::matching::matches_complex_selector;
|
use selectors::matching::matches_complex_selector;
|
||||||
|
@ -33,6 +35,11 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
impl HeapSizeOf for RestyleHint {
|
||||||
|
fn heap_size_of_children(&self) -> usize { 0 }
|
||||||
|
}
|
||||||
|
|
||||||
/// In order to compute restyle hints, we perform a selector match against a
|
/// In order to compute restyle hints, we perform a selector match against a
|
||||||
/// list of partial selectors whose rightmost simple selector may be sensitive
|
/// list of partial selectors whose rightmost simple selector may be sensitive
|
||||||
/// to the thing being changed. We do this matching twice, once for the element
|
/// to the thing being changed. We do this matching twice, once for the element
|
||||||
|
@ -334,23 +341,50 @@ impl Sensitivities {
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
struct Dependency {
|
struct Dependency {
|
||||||
selector: Arc<ComplexSelector<TheSelectorImpl>>,
|
selector: Arc<ComplexSelector<TheSelectorImpl>>,
|
||||||
combinator: Option<Combinator>,
|
hint: RestyleHint,
|
||||||
sensitivities: Sensitivities,
|
sensitivities: Sensitivities,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A set of dependencies for a given stylist.
|
||||||
|
///
|
||||||
|
/// Note that there are measurable perf wins from storing them separately
|
||||||
|
/// depending on what kind of change they affect, and its also not a big deal to
|
||||||
|
/// do it, since the dependencies are per-document.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct DependencySet {
|
pub struct DependencySet {
|
||||||
deps: Vec<Dependency>,
|
/// Dependencies only affected by state.
|
||||||
|
state_deps: Vec<Dependency>,
|
||||||
|
/// Dependencies only affected by attributes.
|
||||||
|
attr_deps: Vec<Dependency>,
|
||||||
|
/// Dependencies affected by both.
|
||||||
|
common_deps: Vec<Dependency>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DependencySet {
|
impl DependencySet {
|
||||||
|
fn add_dependency(&mut self, dep: Dependency) {
|
||||||
|
let affects_attrs = dep.sensitivities.attrs;
|
||||||
|
let affects_states = !dep.sensitivities.states.is_empty();
|
||||||
|
|
||||||
|
if affects_attrs && affects_states {
|
||||||
|
self.common_deps.push(dep)
|
||||||
|
} else if affects_attrs {
|
||||||
|
self.attr_deps.push(dep)
|
||||||
|
} else {
|
||||||
|
self.state_deps.push(dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
DependencySet { deps: Vec::new() }
|
DependencySet {
|
||||||
|
state_deps: vec![],
|
||||||
|
attr_deps: vec![],
|
||||||
|
common_deps: vec![],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.deps.len()
|
self.common_deps.len() + self.attr_deps.len() + self.state_deps.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn note_selector(&mut self, selector: &Arc<ComplexSelector<TheSelectorImpl>>) {
|
pub fn note_selector(&mut self, selector: &Arc<ComplexSelector<TheSelectorImpl>>) {
|
||||||
|
@ -365,9 +399,9 @@ impl DependencySet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !sensitivities.is_empty() {
|
if !sensitivities.is_empty() {
|
||||||
self.deps.push(Dependency {
|
self.add_dependency(Dependency {
|
||||||
selector: cur.clone(),
|
selector: cur.clone(),
|
||||||
combinator: combinator,
|
hint: combinator_to_restyle_hint(combinator),
|
||||||
sensitivities: sensitivities,
|
sensitivities: sensitivities,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -383,11 +417,11 @@ impl DependencySet {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.deps.clear();
|
self.common_deps.clear();
|
||||||
|
self.attr_deps.clear();
|
||||||
|
self.state_deps.clear();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl DependencySet {
|
|
||||||
pub fn compute_hint<E>(&self, el: &E,
|
pub fn compute_hint<E>(&self, el: &E,
|
||||||
snapshot: &E::Snapshot,
|
snapshot: &E::Snapshot,
|
||||||
current_state: ElementState)
|
current_state: ElementState)
|
||||||
|
@ -395,26 +429,64 @@ impl DependencySet {
|
||||||
where E: ElementExt + Clone
|
where E: ElementExt + Clone
|
||||||
{
|
{
|
||||||
debug!("About to calculate restyle hint for element. Deps: {}",
|
debug!("About to calculate restyle hint for element. Deps: {}",
|
||||||
self.deps.len());
|
self.len());
|
||||||
|
|
||||||
let state_changes = snapshot.state().map_or_else(ElementState::empty, |old_state| current_state ^ old_state);
|
let state_changes = snapshot.state()
|
||||||
|
.map_or_else(ElementState::empty, |old_state| current_state ^ old_state);
|
||||||
let attrs_changed = snapshot.has_attrs();
|
let attrs_changed = snapshot.has_attrs();
|
||||||
|
|
||||||
|
if state_changes.is_empty() && !attrs_changed {
|
||||||
|
return RestyleHint::empty();
|
||||||
|
}
|
||||||
|
|
||||||
let mut hint = RestyleHint::empty();
|
let mut hint = RestyleHint::empty();
|
||||||
for dep in &self.deps {
|
let snapshot = ElementWrapper::new_with_snapshot(el.clone(), snapshot);
|
||||||
if state_changes.intersects(dep.sensitivities.states) || (attrs_changed && dep.sensitivities.attrs) {
|
|
||||||
let old_el: ElementWrapper<E> = ElementWrapper::new_with_snapshot(el.clone(), snapshot);
|
Self::compute_partial_hint(&self.common_deps, el, &snapshot,
|
||||||
let matched_then =
|
&state_changes, attrs_changed, &mut hint);
|
||||||
matches_complex_selector(&*dep.selector, &old_el, None, &mut StyleRelations::empty());
|
|
||||||
let matches_now =
|
if !state_changes.is_empty() {
|
||||||
matches_complex_selector(&*dep.selector, el, None, &mut StyleRelations::empty());
|
Self::compute_partial_hint(&self.state_deps, el, &snapshot,
|
||||||
if matched_then != matches_now {
|
&state_changes, attrs_changed, &mut hint);
|
||||||
hint.insert(combinator_to_restyle_hint(dep.combinator));
|
|
||||||
if hint.is_all() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if attrs_changed {
|
||||||
|
Self::compute_partial_hint(&self.attr_deps, el, &snapshot,
|
||||||
|
&state_changes, attrs_changed, &mut hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
hint
|
hint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_partial_hint<E>(deps: &[Dependency],
|
||||||
|
element: &E,
|
||||||
|
snapshot: &ElementWrapper<E>,
|
||||||
|
state_changes: &ElementState,
|
||||||
|
attrs_changed: bool,
|
||||||
|
hint: &mut RestyleHint)
|
||||||
|
where E: ElementExt
|
||||||
|
{
|
||||||
|
if hint.is_all() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for dep in deps {
|
||||||
|
debug_assert!(state_changes.intersects(dep.sensitivities.states) ||
|
||||||
|
attrs_changed && dep.sensitivities.attrs,
|
||||||
|
"Testing a completely ineffective dependency?");
|
||||||
|
if !hint.intersects(dep.hint) {
|
||||||
|
let matched_then =
|
||||||
|
matches_complex_selector(&dep.selector, snapshot, None,
|
||||||
|
&mut StyleRelations::empty());
|
||||||
|
let matches_now =
|
||||||
|
matches_complex_selector(&dep.selector, element, None,
|
||||||
|
&mut StyleRelations::empty());
|
||||||
|
if matched_then != matches_now {
|
||||||
|
hint.insert(dep.hint);
|
||||||
|
}
|
||||||
|
if hint.is_all() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue