servo: Merge #7891 - Properly size synthesized layers (from mrobinson:display-list-paint-layer); r=pcwalton

Layers that are composed of several stacking contexts that need to be
rendered on top of other layered content need synthesized layers.
Previously, these layers were placed into a stacking context that was
the same size as their parent. This patch creates a new type of
PaintLayer which simply holds a display list. The layer is sized to the
bounds of the DisplayList and its children are positioned relative to
the parent stacking context.

This will also be useful in the future, when items outside of
StackingContexts are given their own layer for render order purposes.

Source-Repo: https://github.com/servo/servo
Source-Revision: 6d52bdf4ffd5479780df4a3fb0ff07bd7378720e
This commit is contained in:
Martin Robinson 2015-10-09 17:00:21 -06:00
Родитель 87d5049829
Коммит eb37118a1e
2 изменённых файлов: 227 добавлений и 111 удалений

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

@ -26,7 +26,7 @@ use libc::uintptr_t;
use msg::compositor_msg::{LayerId, LayerKind, ScrollPolicy, SubpageLayerInfo};
use net_traits::image::base::Image;
use paint_context::PaintContext;
use paint_task::PaintLayer;
use paint_task::{PaintLayerContents, PaintLayer};
use self::DisplayItem::*;
use self::DisplayItemIterator::*;
use smallvec::SmallVec;
@ -254,21 +254,38 @@ impl DisplayList {
print_display_list_section(print_tree, &self.outlines, "Outlines");
for stacking_context in &self.children {
stacking_context.print_with_tree(print_tree);
if !self.children.is_empty() {
print_tree.new_level("Stacking Contexts".to_owned());
for stacking_context in &self.children {
stacking_context.print_with_tree(print_tree);
}
print_tree.end_level();
}
for paint_layer in &self.layered_children {
paint_layer.stacking_context.print_with_tree(print_tree);
if !self.layered_children.is_empty() {
print_tree.new_level("Layers".to_owned());
for paint_layer in &self.layered_children {
match paint_layer.contents {
PaintLayerContents::StackingContext(ref stacking_context) =>
stacking_context.print_with_tree(print_tree),
PaintLayerContents::DisplayList(ref display_list) => {
print_tree.new_level(format!("DisplayList Layer with bounds {:?}:",
display_list.calculate_bounding_rect()));
display_list.print_with_tree(print_tree);
print_tree.end_level();
}
}
}
print_tree.end_level();
}
}
/// Draws the DisplayList in stacking context order according to the steps in CSS 2.1 § E.2.
fn draw_into_context(&self,
draw_target: &DrawTarget,
paint_context: &mut PaintContext,
transform: &Matrix4,
clip_rect: Option<&Rect<Au>>) {
pub fn draw_into_context(&self,
draw_target: &DrawTarget,
paint_context: &mut PaintContext,
transform: &Matrix4,
clip_rect: Option<&Rect<Au>>) {
let mut paint_subcontext = PaintContext {
draw_target: draw_target.clone(),
font_context: &mut *paint_context.font_context,
@ -424,9 +441,15 @@ impl DisplayList {
}
}
// Layers are positioned on top of this layer should get a shot at the hit test first.
// Layers that are positioned on top of this layer should get a shot at the hit test first.
for layer in self.layered_children.iter().rev() {
layer.stacking_context.hit_test(point, result, topmost_only);
match layer.contents {
PaintLayerContents::StackingContext(ref stacking_context) =>
stacking_context.hit_test(point, result, topmost_only),
PaintLayerContents::DisplayList(ref display_list) =>
display_list.hit_test(point, result, topmost_only),
}
if topmost_only && !result.is_empty() {
return
}
@ -486,6 +509,51 @@ impl DisplayList {
self.background_and_borders.iter().rev())
}
/// Returns the PaintLayer in the given DisplayList with a specific layer ID.
pub fn find_layer_with_layer_id(&self, layer_id: LayerId) -> Option<Arc<PaintLayer>> {
for kid in &self.layered_children {
if let Some(paint_layer) = PaintLayer::find_layer_with_layer_id(&kid, layer_id) {
return Some(paint_layer);
}
}
for kid in &self.children {
if let Some(paint_layer) = kid.display_list.find_layer_with_layer_id(layer_id) {
return Some(paint_layer);
}
}
None
}
/// Calculate the union of all the bounds of all of the items in this display list.
/// This is an expensive operation, so it shouldn't be done unless absolutely necessary
/// and, if possible, the result should be cached.
pub fn calculate_bounding_rect(&self) -> Rect<Au> {
fn union_all_items(list: &LinkedList<DisplayItem>, mut bounds: Rect<Au>) -> Rect<Au> {
for item in list {
bounds = bounds.union(&item.base().bounds);
}
bounds
};
let mut bounds = Rect::zero();
bounds = union_all_items(&self.background_and_borders, bounds);
bounds = union_all_items(&self.block_backgrounds_and_borders, bounds);
bounds = union_all_items(&self.floats, bounds);
bounds = union_all_items(&self.content, bounds);
bounds = union_all_items(&self.positioned_content, bounds);
bounds = union_all_items(&self.outlines, bounds);
for stacking_context in &self.children {
bounds = bounds.union(&Rect::new(
stacking_context.overflow.origin + stacking_context.bounds.origin,
stacking_context.overflow.size));
}
bounds
}
}
#[derive(HeapSizeOf, Deserialize, Serialize)]
@ -687,7 +755,7 @@ impl StackingContext {
}
struct StackingContextLayerCreator {
display_list_for_next_layer: Option<Box<DisplayList>>,
display_list_for_next_layer: Option<DisplayList>,
next_layer_info: Option<LayerInfo>,
}
@ -728,12 +796,8 @@ impl StackingContextLayerCreator {
fn finish_building_current_layer(&mut self, stacking_context: &mut StackingContext) {
if let Some(display_list) = self.display_list_for_next_layer.take() {
let layer_info = self.next_layer_info.take().unwrap();
let child_stacking_context =
Arc::new(stacking_context.create_layered_child(layer_info.clone(), display_list));
stacking_context.display_list.layered_children.push_back(
Arc::new(PaintLayer::new(layer_info,
color::transparent(),
child_stacking_context)));
Arc::new(PaintLayer::new_with_display_list(layer_info, display_list)));
}
}
@ -768,7 +832,7 @@ impl StackingContextLayerCreator {
}
if self.display_list_for_next_layer.is_none() {
self.display_list_for_next_layer = Some(box DisplayList::new());
self.display_list_for_next_layer = Some(DisplayList::new());
}
if let Some(ref mut display_list) = self.display_list_for_next_layer {
display_list.children.push_back(stacking_context);
@ -776,25 +840,6 @@ impl StackingContextLayerCreator {
}
}
/// Returns the stacking context in the given tree of stacking contexts with a specific layer ID.
pub fn find_layer_with_layer_id(this: &Arc<StackingContext>,
layer_id: LayerId)
-> Option<Arc<PaintLayer>> {
for kid in &this.display_list.layered_children {
if let Some(paint_layer) = PaintLayer::find_layer_with_layer_id(&kid, layer_id) {
return Some(paint_layer);
}
}
for kid in &this.display_list.children {
if let Some(paint_layer) = find_layer_with_layer_id(kid, layer_id) {
return Some(paint_layer);
}
}
None
}
/// One drawing command in the list.
#[derive(Clone, Deserialize, HeapSizeOf, Serialize)]
pub enum DisplayItem {

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

@ -8,13 +8,14 @@ use app_units::Au;
use azure::AzFloat;
use azure::azure_hl::{BackendType, Color, DrawTarget, SurfaceFormat};
use canvas_traits::CanvasMsg;
use display_list::{self, LayerInfo, StackingContext};
use display_list::{DisplayList, LayerInfo, StackingContext};
use euclid::Matrix4;
use euclid::point::Point2D;
use euclid::rect::Rect;
use euclid::size::Size2D;
use font_cache_task::FontCacheTask;
use font_context::FontContext;
use gfx_traits::color;
use ipc_channel::ipc::IpcSender;
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
use layers::platform::surface::{NativeDisplay, NativeSurface};
@ -40,6 +41,12 @@ use util::task::spawn_named;
use util::task::spawn_named_with_send_on_failure;
use util::task_state;
#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
pub enum PaintLayerContents {
StackingContext(Arc<StackingContext>),
DisplayList(Arc<DisplayList>),
}
/// Information about a hardware graphics layer that layout sends to the painting task.
#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
pub struct PaintLayer {
@ -47,8 +54,10 @@ pub struct PaintLayer {
pub id: LayerId,
/// The color of the background in this layer. Used for unpainted content.
pub background_color: Color,
/// The stacking context that represents the content of this layer.
pub stacking_context: Arc<StackingContext>,
/// The content of this layer, which is either a stacking context or a display list.
pub contents: PaintLayerContents,
/// The layer's boundaries in the parent layer's coordinate system.
pub bounds: Rect<Au>,
/// The scrolling policy of this layer.
pub scroll_policy: ScrollPolicy,
/// The subpage that this layer represents, if there is one.
@ -56,29 +65,33 @@ pub struct PaintLayer {
}
impl PaintLayer {
/// Creates a new `PaintLayer`.
pub fn new(layer_info: LayerInfo,
background_color: Color,
stacking_context: Arc<StackingContext>)
-> PaintLayer {
PaintLayer {
id: layer_info.layer_id,
background_color: background_color,
stacking_context: stacking_context,
scroll_policy: layer_info.scroll_policy,
subpage_layer_info: layer_info.subpage_layer_info,
}
}
/// Creates a new `PaintLayer` with a stacking context.
pub fn new_with_stacking_context(layer_info: LayerInfo,
stacking_context: Arc<StackingContext>,
background_color: Color)
-> PaintLayer {
PaintLayer {
let bounds = Rect::new(stacking_context.bounds.origin + stacking_context.overflow.origin,
stacking_context.overflow.size);
PaintLayer {
id: layer_info.layer_id,
background_color: background_color,
stacking_context: stacking_context,
contents: PaintLayerContents::StackingContext(stacking_context),
bounds: bounds,
scroll_policy: layer_info.scroll_policy,
subpage_layer_info: layer_info.subpage_layer_info,
}
}
/// Creates a new `PaintLayer` with a display list.
pub fn new_with_display_list(layer_info: LayerInfo,
display_list: DisplayList)
-> PaintLayer {
let bounds = display_list.calculate_bounding_rect();
PaintLayer {
id: layer_info.layer_id,
background_color: color::transparent(),
contents: PaintLayerContents::DisplayList(Arc::new(display_list)),
bounds: bounds,
scroll_policy: layer_info.scroll_policy,
subpage_layer_info: layer_info.subpage_layer_info,
}
@ -91,7 +104,80 @@ impl PaintLayer {
return Some(this.clone());
}
display_list::find_layer_with_layer_id(&this.stacking_context, layer_id)
match this.contents {
PaintLayerContents::StackingContext(ref stacking_context) =>
stacking_context.display_list.find_layer_with_layer_id(layer_id),
PaintLayerContents::DisplayList(ref display_list) =>
display_list.find_layer_with_layer_id(layer_id),
}
}
fn build_layer_properties(&self,
parent_origin: &Point2D<Au>,
transform: &Matrix4,
perspective: &Matrix4,
parent_id: Option<LayerId>)
-> LayerProperties {
let layer_boundaries = Rect::new(
Point2D::new((parent_origin.x + self.bounds.min_x()).to_nearest_px() as f32,
(parent_origin.y + self.bounds.min_y()).to_nearest_px() as f32),
Size2D::new(self.bounds.size.width.to_nearest_px() as f32,
self.bounds.size.height.to_nearest_px() as f32));
let (transform,
perspective,
establishes_3d_context,
scrolls_overflow_area) = match self.contents {
PaintLayerContents::StackingContext(ref stacking_context) => {
(transform.mul(&stacking_context.transform),
perspective.mul(&stacking_context.perspective),
stacking_context.establishes_3d_context,
stacking_context.scrolls_overflow_area)
},
PaintLayerContents::DisplayList(_) => {
(*transform, *perspective, false, false)
}
};
LayerProperties {
id: self.id,
parent_id: parent_id,
rect: layer_boundaries,
background_color: self.background_color,
scroll_policy: self.scroll_policy,
transform: transform,
perspective: perspective,
establishes_3d_context: establishes_3d_context,
scrolls_overflow_area: scrolls_overflow_area,
subpage_layer_info: self.subpage_layer_info,
}
}
// The origin for child layers might be somewhere other than the layer origin,
// since layer boundaries are expanded to include overflow.
pub fn origin_for_child_layers(&self) -> Point2D<Au> {
match self.contents {
PaintLayerContents::StackingContext(ref stacking_context) =>
-stacking_context.overflow.origin,
PaintLayerContents::DisplayList(_) => Point2D::zero(),
}
}
pub fn display_list_origin(&self) -> Point2D<f32> {
// The layer's bounds start at the overflow origin, but display items are
// positioned relative to the stacking context counds, so we need to
// offset by the overflow rect (which will be in the coordinate system of
// the stacking context bounds).
match self.contents {
PaintLayerContents::StackingContext(ref stacking_context) => {
Point2D::new(stacking_context.overflow.origin.x.to_f32_px(),
stacking_context.overflow.origin.y.to_f32_px())
},
PaintLayerContents::DisplayList(_) => {
Point2D::new(self.bounds.origin.x.to_f32_px(), self.bounds.origin.y.to_f32_px())
}
}
}
}
@ -372,59 +458,37 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
fn build_from_paint_layer(properties: &mut Vec<LayerProperties>,
paint_layer: &Arc<PaintLayer>,
page_position: &Point2D<Au>,
parent_origin: &Point2D<Au>,
transform: &Matrix4,
perspective: &Matrix4,
parent_id: Option<LayerId>) {
let transform = transform.mul(&paint_layer.stacking_context.transform);
let perspective = perspective.mul(&paint_layer.stacking_context.perspective);
let overflow_size = Size2D::new(
paint_layer.stacking_context.overflow.size.width.to_nearest_px() as f32,
paint_layer.stacking_context.overflow.size.height.to_nearest_px() as f32);
properties.push(paint_layer.build_layer_properties(parent_origin,
transform,
perspective,
parent_id));
// Layers start at the top left of their overflow rect, as far as the info
// we give to the compositor is concerned.
let overflow_relative_page_position = *page_position +
paint_layer.stacking_context.bounds.origin +
paint_layer.stacking_context.overflow.origin;
let layer_position = Rect::new(
Point2D::new(overflow_relative_page_position.x.to_nearest_px() as f32,
overflow_relative_page_position.y.to_nearest_px() as f32),
overflow_size);
properties.push(LayerProperties {
id: paint_layer.id,
parent_id: parent_id,
rect: layer_position,
background_color: paint_layer.background_color,
scroll_policy: paint_layer.scroll_policy,
transform: transform,
perspective: perspective,
establishes_3d_context: paint_layer.stacking_context.establishes_3d_context,
scrolls_overflow_area: paint_layer.stacking_context.scrolls_overflow_area,
subpage_layer_info: paint_layer.subpage_layer_info,
});
// When there is a new layer, the transforms and origin are handled by the compositor,
// so the new transform and perspective matrices are just the identity.
continue_walking_stacking_context(properties,
&paint_layer.stacking_context,
&-paint_layer.stacking_context.overflow.origin,
&Matrix4::identity(),
&Matrix4::identity(),
Some(paint_layer.id));
if let PaintLayerContents::StackingContext(ref context) = paint_layer.contents {
// When there is a new layer, the transforms and origin are handled by the compositor,
// so the new transform and perspective matrices are just the identity.
continue_walking_stacking_context(properties,
&context,
&paint_layer.origin_for_child_layers(),
&Matrix4::identity(),
&Matrix4::identity(),
Some(paint_layer.id));
}
}
fn build_from_stacking_context(properties: &mut Vec<LayerProperties>,
stacking_context: &Arc<StackingContext>,
page_position: &Point2D<Au>,
parent_origin: &Point2D<Au>,
transform: &Matrix4,
perspective: &Matrix4,
parent_id: Option<LayerId>) {
continue_walking_stacking_context(properties,
stacking_context,
&(stacking_context.bounds.origin + *page_position),
&(stacking_context.bounds.origin + *parent_origin),
&transform.mul(&stacking_context.transform),
&perspective.mul(&stacking_context.perspective),
parent_id);
@ -432,14 +496,14 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
fn continue_walking_stacking_context(properties: &mut Vec<LayerProperties>,
stacking_context: &Arc<StackingContext>,
page_position: &Point2D<Au>,
parent_origin: &Point2D<Au>,
transform: &Matrix4,
perspective: &Matrix4,
parent_id: Option<LayerId>) {
for kid in stacking_context.display_list.children.iter() {
build_from_stacking_context(properties,
&kid,
&page_position,
&parent_origin,
&transform,
&perspective,
parent_id)
@ -448,7 +512,7 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
for kid in stacking_context.display_list.layered_children.iter() {
build_from_paint_layer(properties,
&kid,
&page_position,
&parent_origin,
&transform,
&perspective,
parent_id)
@ -605,7 +669,6 @@ impl WorkerThread {
scale: f32,
layer_kind: LayerKind)
-> Box<LayerBuffer> {
let stacking_context = &paint_layer.stacking_context;
let size = Size2D::new(tile.screen_rect.size.width as i32,
tile.screen_rect.size.height as i32);
let mut buffer = self.create_layer_buffer(&mut tile, scale);
@ -623,15 +686,10 @@ impl WorkerThread {
layer_kind: layer_kind,
};
// Apply a translation to start at the boundaries of the stacking context, since the
// layer's origin starts at its overflow rect's origin.
let tile_bounds = tile.page_rect.translate(
&Point2D::new(stacking_context.overflow.origin.x.to_f32_px(),
stacking_context.overflow.origin.y.to_f32_px()));
// Apply the translation to paint the tile we want.
let matrix = Matrix4::identity();
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat, 1.0);
let tile_bounds = tile.page_rect.translate(&paint_layer.display_list_origin());
let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat,
-tile_bounds.origin.y as AzFloat,
0.0);
@ -644,11 +702,24 @@ impl WorkerThread {
None,
self.time_profiler_sender.clone(),
|| {
stacking_context.optimize_and_draw_into_context(&mut paint_context,
&matrix,
None);
match paint_layer.contents {
PaintLayerContents::StackingContext(ref stacking_context) => {
stacking_context.optimize_and_draw_into_context(&mut paint_context,
&matrix,
None);
}
PaintLayerContents::DisplayList(ref display_list) => {
paint_context.remove_transient_clip_if_applicable();
let draw_target = paint_context.draw_target.clone();
display_list.draw_into_context(&draw_target,
&mut paint_context,
&matrix,
None);
}
}
paint_context.draw_target.flush();
});
});
if opts::get().show_debug_parallel_paint {
// Overlay a transparent solid color to identify the thread that