layout: Rewrite clipping to be per-display-item instead of having

a separate `ClipDisplayItem`.

We push down clipping areas during absolute position calculation. This
makes display items into a flat list, improving cache locality. It
dramatically simplifies the code all around.

Because we need to push down clip rects even for absolutely-positioned
children of non-absolutely-positioned flows, this patch alters the
parallel traversal to compute absolute positions for
absolutely-positioned children at the same time it computes absolute
positions for other children. This doesn't seem to break anything either
in theory (since the overall order remains correct) or in practice. It
simplifies the parallel traversal code quite a bit.

See the relevant Gecko bug:
https://bugzilla.mozilla.org/show_bug.cgi?id=615734
This commit is contained in:
Patrick Walton 2014-10-10 15:13:12 -07:00
parent f350879574
commit bffaad118e
12 changed files with 312 additions and 487 deletions

View file

@ -196,93 +196,35 @@ impl StackingContext {
};
for item in list.into_iter() {
match item {
ClipDisplayItemClass(box ClipDisplayItem {
base: base,
children: sublist
}) => {
let sub_stacking_context = StackingContext::new(sublist);
stacking_context.merge_with_clip(sub_stacking_context, &base.bounds, base.node)
match item.base().level {
BackgroundAndBordersStackingLevel => {
stacking_context.background_and_borders.push(item)
}
item => {
match item.base().level {
BackgroundAndBordersStackingLevel => {
stacking_context.background_and_borders.push(item)
}
BlockBackgroundsAndBordersStackingLevel => {
stacking_context.block_backgrounds_and_borders.push(item)
}
FloatStackingLevel => stacking_context.floats.push(item),
ContentStackingLevel => stacking_context.content.push(item),
PositionedDescendantStackingLevel(z_index) => {
match stacking_context.positioned_descendants
.iter_mut()
.find(|& &(z, _)| z_index == z) {
Some(&(_, ref mut my_list)) => {
my_list.push(item);
continue
}
None => {}
}
let mut new_list = DisplayList::new();
new_list.list.push(item);
stacking_context.positioned_descendants.push((z_index, new_list))
BlockBackgroundsAndBordersStackingLevel => {
stacking_context.block_backgrounds_and_borders.push(item)
}
FloatStackingLevel => stacking_context.floats.push(item),
ContentStackingLevel => stacking_context.content.push(item),
PositionedDescendantStackingLevel(z_index) => {
match stacking_context.positioned_descendants
.iter_mut()
.find(|& &(z, _)| z_index == z) {
Some(&(_, ref mut my_list)) => {
my_list.push(item);
continue
}
None => {}
}
let mut new_list = DisplayList::new();
new_list.list.push(item);
stacking_context.positioned_descendants.push((z_index, new_list))
}
}
}
stacking_context
}
/// Merges another stacking context into this one, with the given clipping rectangle and DOM
/// node that supplies it.
fn merge_with_clip(&mut self,
other: StackingContext,
clip_rect: &Rect<Au>,
clipping_dom_node: OpaqueNode) {
let StackingContext {
background_and_borders,
block_backgrounds_and_borders,
floats,
content,
positioned_descendants: positioned_descendants
} = other;
let push = |destination: &mut DisplayList, source: DisplayList, level| {
if !source.is_empty() {
let base = BaseDisplayItem::new(*clip_rect, clipping_dom_node, level);
destination.push(ClipDisplayItemClass(box ClipDisplayItem::new(base, source)))
}
};
push(&mut self.background_and_borders,
background_and_borders,
BackgroundAndBordersStackingLevel);
push(&mut self.block_backgrounds_and_borders,
block_backgrounds_and_borders,
BlockBackgroundsAndBordersStackingLevel);
push(&mut self.floats, floats, FloatStackingLevel);
push(&mut self.content, content, ContentStackingLevel);
for (z_index, list) in positioned_descendants.into_iter() {
match self.positioned_descendants
.iter_mut()
.find(|& &(existing_z_index, _)| z_index == existing_z_index) {
Some(&(_, ref mut existing_list)) => {
push(existing_list, list, PositionedDescendantStackingLevel(z_index));
continue
}
None => {}
}
let mut new_list = DisplayList::new();
push(&mut new_list, list, PositionedDescendantStackingLevel(z_index));
self.positioned_descendants.push((z_index, new_list));
}
}
}
/// Which level to place backgrounds and borders in.
@ -342,11 +284,13 @@ impl DisplayList {
/// Draws the display list into the given render context. The display list must be flattened
/// first for correct painting.
pub fn draw_into_context(&self, render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>) {
pub fn draw_into_context(&self,
render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>,
current_clip_rect: &Rect<Au>) {
debug!("Beginning display list.");
for item in self.list.iter() {
item.draw_into_context(render_context, current_transform)
item.draw_into_context(render_context, current_transform, current_clip_rect)
}
debug!("Ending display list.");
}
@ -356,12 +300,6 @@ impl DisplayList {
ParentDisplayItemIterator(self.list.iter())
}
/// Returns true if this list is empty and false otherwise.
#[inline]
fn is_empty(&self) -> bool {
self.list.is_empty()
}
/// Flattens a display list into a display list with a single stacking level according to the
/// steps in CSS 2.1 § E.2.
///
@ -421,10 +359,6 @@ impl DisplayList {
fn set_stacking_level(&mut self, new_level: StackingLevel) {
for item in self.list.iter_mut() {
item.mut_base().level = new_level;
match item.mut_sublist() {
None => {}
Some(sublist) => sublist.set_stacking_level(new_level),
}
}
}
}
@ -437,7 +371,6 @@ pub enum DisplayItem {
ImageDisplayItemClass(Box<ImageDisplayItem>),
BorderDisplayItemClass(Box<BorderDisplayItem>),
LineDisplayItemClass(Box<LineDisplayItem>),
ClipDisplayItemClass(Box<ClipDisplayItem>),
/// A pseudo-display item that exists only so that queries like `ContentBoxQuery` and
/// `ContentBoxesQuery` can be answered.
@ -450,9 +383,7 @@ pub enum DisplayItem {
/// Information common to all display items.
#[deriving(Clone)]
pub struct BaseDisplayItem {
/// The boundaries of the display item.
///
/// TODO: Which coordinate system should this use?
/// The boundaries of the display item, in layer coordinates.
pub bounds: Rect<Au>,
/// The originating DOM node.
@ -460,14 +391,22 @@ pub struct BaseDisplayItem {
/// The stacking level in which this display item lives.
pub level: StackingLevel,
/// The rectangle to clip to.
///
/// TODO(pcwalton): Eventually, to handle `border-radius`, this will (at least) need to grow
/// the ability to describe rounded rectangles.
pub clip_rect: Rect<Au>,
}
impl BaseDisplayItem {
pub fn new(bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel) -> BaseDisplayItem {
pub fn new(bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel, clip_rect: Rect<Au>)
-> BaseDisplayItem {
BaseDisplayItem {
bounds: bounds,
node: node,
level: level,
clip_rect: clip_rect,
}
}
}
@ -544,25 +483,6 @@ pub struct LineDisplayItem {
pub style: border_style::T
}
/// Clips a list of child display items to this display item's boundaries.
#[deriving(Clone)]
pub struct ClipDisplayItem {
/// The base information.
pub base: BaseDisplayItem,
/// The child nodes.
pub children: DisplayList,
}
impl ClipDisplayItem {
pub fn new(base: BaseDisplayItem, children: DisplayList) -> ClipDisplayItem {
ClipDisplayItem {
base: base,
children: children,
}
}
}
pub enum DisplayItemIterator<'a> {
EmptyDisplayItemIterator,
ParentDisplayItemIterator(dlist::Items<'a,DisplayItem>),
@ -580,24 +500,24 @@ impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
impl DisplayItem {
/// Renders this display item into the given render context.
fn draw_into_context(&self, render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>) {
fn draw_into_context(&self,
render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>,
current_clip_rect: &Rect<Au>) {
// This should have been flattened to the content stacking level first.
assert!(self.base().level == ContentStackingLevel);
let clip_rect = &self.base().clip_rect;
let need_to_clip = current_clip_rect != clip_rect;
if need_to_clip {
render_context.draw_push_clip(clip_rect);
}
match *self {
SolidColorDisplayItemClass(ref solid_color) => {
render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color)
}
ClipDisplayItemClass(ref clip) => {
render_context.draw_push_clip(&clip.base.bounds);
for item in clip.children.iter() {
(*item).draw_into_context(render_context, current_transform);
}
render_context.draw_pop_clip();
}
TextDisplayItemClass(ref text) => {
debug!("Drawing text at {}.", text.base.bounds);
@ -688,6 +608,10 @@ impl DisplayItem {
PseudoDisplayItemClass(_) => {}
}
if need_to_clip {
render_context.draw_pop_clip();
}
}
pub fn base<'a>(&'a self) -> &'a BaseDisplayItem {
@ -697,7 +621,6 @@ impl DisplayItem {
ImageDisplayItemClass(ref image_item) => &image_item.base,
BorderDisplayItemClass(ref border) => &border.base,
LineDisplayItemClass(ref line) => &line.base,
ClipDisplayItemClass(ref clip) => &clip.base,
PseudoDisplayItemClass(ref base) => &**base,
}
}
@ -709,7 +632,6 @@ impl DisplayItem {
ImageDisplayItemClass(ref mut image_item) => &mut image_item.base,
BorderDisplayItemClass(ref mut border) => &mut border.base,
LineDisplayItemClass(ref mut line) => &mut line.base,
ClipDisplayItemClass(ref mut clip) => &mut clip.base,
PseudoDisplayItemClass(ref mut base) => &mut **base,
}
}
@ -718,40 +640,12 @@ impl DisplayItem {
self.base().bounds
}
pub fn children<'a>(&'a self) -> DisplayItemIterator<'a> {
match *self {
ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.children.list.iter()),
SolidColorDisplayItemClass(..) |
TextDisplayItemClass(..) |
ImageDisplayItemClass(..) |
BorderDisplayItemClass(..) |
LineDisplayItemClass(..) |
PseudoDisplayItemClass(..) => EmptyDisplayItemIterator,
}
}
/// Returns a mutable reference to the sublist contained within this display list item, if any.
fn mut_sublist<'a>(&'a mut self) -> Option<&'a mut DisplayList> {
match *self {
ClipDisplayItemClass(ref mut clip) => Some(&mut clip.children),
SolidColorDisplayItemClass(..) |
TextDisplayItemClass(..) |
ImageDisplayItemClass(..) |
BorderDisplayItemClass(..) |
LineDisplayItemClass(..) |
PseudoDisplayItemClass(..) => None,
}
}
pub fn debug_with_level(&self, level: uint) {
let mut indent = String::new();
for _ in range(0, level) {
indent.push_str("| ")
}
debug!("{}+ {}", indent, self);
for child in self.children() {
child.debug_with_level(level + 1);
}
}
}
@ -764,7 +658,6 @@ impl fmt::Show for DisplayItem {
ImageDisplayItemClass(_) => "Image",
BorderDisplayItemClass(_) => "Border",
LineDisplayItemClass(_) => "Line",
ClipDisplayItemClass(_) => "Clip",
PseudoDisplayItemClass(_) => "Pseudo",
},
self.base().bounds,