mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
Auto merge of #6983 - glennw:tf2, r=pcwalton
Support transforms for display list optimization. Prevents clipping in #6643. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/6983) <!-- Reviewable:end -->
This commit is contained in:
commit
315c4f5ed7
7 changed files with 134 additions and 91 deletions
|
@ -17,7 +17,7 @@
|
||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
|
|
||||||
use display_list::optimizer::DisplayListOptimizer;
|
use display_list::optimizer::DisplayListOptimizer;
|
||||||
use paint_context::{PaintContext, ToAzureRect};
|
use paint_context::PaintContext;
|
||||||
use self::DisplayItem::*;
|
use self::DisplayItem::*;
|
||||||
use self::DisplayItemIterator::*;
|
use self::DisplayItemIterator::*;
|
||||||
use text::glyph::CharIndex;
|
use text::glyph::CharIndex;
|
||||||
|
@ -286,22 +286,15 @@ impl StackingContext {
|
||||||
pub fn draw_into_context(&self,
|
pub fn draw_into_context(&self,
|
||||||
display_list: &DisplayList,
|
display_list: &DisplayList,
|
||||||
paint_context: &mut PaintContext,
|
paint_context: &mut PaintContext,
|
||||||
tile_bounds: &Rect<AzFloat>,
|
|
||||||
transform: &Matrix4,
|
transform: &Matrix4,
|
||||||
clip_rect: Option<&Rect<Au>>) {
|
clip_rect: Option<&Rect<Au>>) {
|
||||||
// If a layer is being used, the transform for this layer
|
|
||||||
// will be handled by the compositor.
|
|
||||||
let transform = match self.layer {
|
|
||||||
Some(..) => *transform,
|
|
||||||
None => transform.mul(&self.transform),
|
|
||||||
};
|
|
||||||
let temporary_draw_target =
|
let temporary_draw_target =
|
||||||
paint_context.get_or_create_temporary_draw_target(&self.filters, self.blend_mode);
|
paint_context.get_or_create_temporary_draw_target(&self.filters, self.blend_mode);
|
||||||
{
|
{
|
||||||
let mut paint_subcontext = PaintContext {
|
let mut paint_subcontext = PaintContext {
|
||||||
draw_target: temporary_draw_target.clone(),
|
draw_target: temporary_draw_target.clone(),
|
||||||
font_context: &mut *paint_context.font_context,
|
font_context: &mut *paint_context.font_context,
|
||||||
page_rect: *tile_bounds,
|
page_rect: paint_context.page_rect,
|
||||||
screen_rect: paint_context.screen_rect,
|
screen_rect: paint_context.screen_rect,
|
||||||
clip_rect: clip_rect.map(|clip_rect| *clip_rect),
|
clip_rect: clip_rect.map(|clip_rect| *clip_rect),
|
||||||
transient_clip: None,
|
transient_clip: None,
|
||||||
|
@ -309,15 +302,17 @@ impl StackingContext {
|
||||||
};
|
};
|
||||||
|
|
||||||
if opts::get().dump_display_list_optimized {
|
if opts::get().dump_display_list_optimized {
|
||||||
println!("**** optimized display list. Tile bounds: {:?}", tile_bounds);
|
println!("**** optimized display list. Tile bounds: {:?}", paint_context.page_rect);
|
||||||
display_list.print_items("*".to_owned());
|
display_list.print_items("*".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort positioned children according to z-index.
|
// Sort positioned children according to z-index.
|
||||||
let mut positioned_children: SmallVec<[Arc<StackingContext>; 8]> = SmallVec::new();
|
let mut positioned_children: SmallVec<[Arc<StackingContext>; 8]> = SmallVec::new();
|
||||||
for kid in display_list.children.iter() {
|
for kid in display_list.children.iter() {
|
||||||
|
if kid.layer.is_none() {
|
||||||
positioned_children.push((*kid).clone());
|
positioned_children.push((*kid).clone());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
positioned_children.sort_by(|this, other| this.z_index.cmp(&other.z_index));
|
positioned_children.sort_by(|this, other| this.z_index.cmp(&other.z_index));
|
||||||
|
|
||||||
// Set up our clip rect and transform.
|
// Set up our clip rect and transform.
|
||||||
|
@ -338,7 +333,6 @@ impl StackingContext {
|
||||||
if positioned_kid.z_index >= 0 {
|
if positioned_kid.z_index >= 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if positioned_kid.layer.is_none() {
|
|
||||||
let new_transform =
|
let new_transform =
|
||||||
transform.translate(positioned_kid.bounds
|
transform.translate(positioned_kid.bounds
|
||||||
.origin
|
.origin
|
||||||
|
@ -349,15 +343,10 @@ impl StackingContext {
|
||||||
.y
|
.y
|
||||||
.to_nearest_px() as AzFloat,
|
.to_nearest_px() as AzFloat,
|
||||||
0.0);
|
0.0);
|
||||||
let new_tile_rect =
|
|
||||||
self.compute_tile_rect_for_child_stacking_context(tile_bounds,
|
|
||||||
&**positioned_kid);
|
|
||||||
positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext,
|
positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext,
|
||||||
&new_tile_rect,
|
|
||||||
&new_transform,
|
&new_transform,
|
||||||
Some(&positioned_kid.overflow))
|
Some(&positioned_kid.overflow))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4: Block backgrounds and borders.
|
// Step 4: Block backgrounds and borders.
|
||||||
for display_item in display_list.block_backgrounds_and_borders.iter() {
|
for display_item in display_list.block_backgrounds_and_borders.iter() {
|
||||||
|
@ -386,8 +375,6 @@ impl StackingContext {
|
||||||
if positioned_kid.z_index < 0 {
|
if positioned_kid.z_index < 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if positioned_kid.layer.is_none() {
|
|
||||||
let new_transform =
|
let new_transform =
|
||||||
transform.translate(positioned_kid.bounds
|
transform.translate(positioned_kid.bounds
|
||||||
.origin
|
.origin
|
||||||
|
@ -398,15 +385,10 @@ impl StackingContext {
|
||||||
.y
|
.y
|
||||||
.to_nearest_px() as AzFloat,
|
.to_nearest_px() as AzFloat,
|
||||||
0.0);
|
0.0);
|
||||||
let new_tile_rect =
|
|
||||||
self.compute_tile_rect_for_child_stacking_context(tile_bounds,
|
|
||||||
&**positioned_kid);
|
|
||||||
positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext,
|
positioned_kid.optimize_and_draw_into_context(&mut paint_subcontext,
|
||||||
&new_tile_rect,
|
|
||||||
&new_transform,
|
&new_transform,
|
||||||
Some(&positioned_kid.overflow))
|
Some(&positioned_kid.overflow))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Step 10: Outlines.
|
// Step 10: Outlines.
|
||||||
for display_item in display_list.outlines.iter() {
|
for display_item in display_list.outlines.iter() {
|
||||||
|
@ -428,62 +410,47 @@ impl StackingContext {
|
||||||
/// Optionally optimize and then draws the stacking context.
|
/// Optionally optimize and then draws the stacking context.
|
||||||
pub fn optimize_and_draw_into_context(&self,
|
pub fn optimize_and_draw_into_context(&self,
|
||||||
paint_context: &mut PaintContext,
|
paint_context: &mut PaintContext,
|
||||||
tile_bounds: &Rect<AzFloat>,
|
|
||||||
transform: &Matrix4,
|
transform: &Matrix4,
|
||||||
clip_rect: Option<&Rect<Au>>) {
|
clip_rect: Option<&Rect<Au>>) {
|
||||||
|
|
||||||
|
// If a layer is being used, the transform for this layer
|
||||||
|
// will be handled by the compositor.
|
||||||
|
let transform = match self.layer {
|
||||||
|
Some(..) => *transform,
|
||||||
|
None => transform.mul(&self.transform),
|
||||||
|
};
|
||||||
|
|
||||||
// TODO(gw): This is a hack to avoid running the DL optimizer
|
// TODO(gw): This is a hack to avoid running the DL optimizer
|
||||||
// on 3d transformed tiles. We should have a better solution
|
// on 3d transformed tiles. We should have a better solution
|
||||||
// than just disabling the opts here.
|
// than just disabling the opts here.
|
||||||
if paint_context.layer_kind == LayerKind::Layer3D {
|
if paint_context.layer_kind == LayerKind::Layer3D {
|
||||||
self.draw_into_context(&self.display_list,
|
self.draw_into_context(&self.display_list,
|
||||||
paint_context,
|
paint_context,
|
||||||
tile_bounds,
|
&transform,
|
||||||
transform,
|
|
||||||
clip_rect);
|
clip_rect);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// Invert the current transform, then use this to back transform
|
||||||
|
// the tile rect (placed at the origin) into the space of this
|
||||||
|
// stacking context.
|
||||||
|
let inverse_transform = transform.invert();
|
||||||
|
let inverse_transform_2d = Matrix2D::new(inverse_transform.m11, inverse_transform.m12,
|
||||||
|
inverse_transform.m21, inverse_transform.m22,
|
||||||
|
inverse_transform.m41, inverse_transform.m42);
|
||||||
|
|
||||||
|
let tile_rect = Rect::new(Point2D::zero(), paint_context.page_rect.size);
|
||||||
|
let tile_rect = inverse_transform_2d.transform_rect(&tile_rect);
|
||||||
|
|
||||||
// Optimize the display list to throw out out-of-bounds display items and so forth.
|
// Optimize the display list to throw out out-of-bounds display items and so forth.
|
||||||
let display_list =
|
let display_list = DisplayListOptimizer::new(&tile_rect).optimize(&*self.display_list);
|
||||||
DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list);
|
|
||||||
|
|
||||||
self.draw_into_context(&display_list,
|
self.draw_into_context(&display_list,
|
||||||
paint_context,
|
paint_context,
|
||||||
tile_bounds,
|
&transform,
|
||||||
transform,
|
|
||||||
clip_rect);
|
clip_rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translate the given tile rect into the coordinate system of a child stacking context.
|
|
||||||
fn compute_tile_rect_for_child_stacking_context(&self,
|
|
||||||
tile_bounds: &Rect<AzFloat>,
|
|
||||||
child_stacking_context: &StackingContext)
|
|
||||||
-> Rect<AzFloat> {
|
|
||||||
static ZERO_AZURE_RECT: Rect<f32> = Rect {
|
|
||||||
origin: Point2D {
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0,
|
|
||||||
},
|
|
||||||
size: Size2D {
|
|
||||||
width: 0.0,
|
|
||||||
height: 0.0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Translate the child's overflow region into our coordinate system.
|
|
||||||
let child_stacking_context_overflow =
|
|
||||||
child_stacking_context.overflow.translate(&child_stacking_context.bounds.origin)
|
|
||||||
.to_nearest_azure_rect();
|
|
||||||
|
|
||||||
// Intersect that with the current tile boundaries to find the tile boundaries that the
|
|
||||||
// child covers.
|
|
||||||
let tile_subrect = tile_bounds.intersection(&child_stacking_context_overflow)
|
|
||||||
.unwrap_or(ZERO_AZURE_RECT);
|
|
||||||
|
|
||||||
// Translate the resulting rect into the child's coordinate system.
|
|
||||||
tile_subrect.translate(&-child_stacking_context.bounds.to_nearest_azure_rect().origin)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Places all nodes containing the point of interest into `result`, topmost first. Respects
|
/// Places all nodes containing the point of interest into `result`, topmost first. Respects
|
||||||
/// the `pointer-events` CSS property If `topmost_only` is true, stops after placing one node
|
/// the `pointer-events` CSS property If `topmost_only` is true, stops after placing one node
|
||||||
/// into the list. `result` must be empty upon entry to this function.
|
/// into the list. `result` must be empty upon entry to this function.
|
||||||
|
|
|
@ -8,6 +8,7 @@ use display_list::{DisplayItem, DisplayList, StackingContext};
|
||||||
|
|
||||||
use std::collections::linked_list::LinkedList;
|
use std::collections::linked_list::LinkedList;
|
||||||
use euclid::rect::Rect;
|
use euclid::rect::Rect;
|
||||||
|
use euclid::{Matrix2D, Matrix4};
|
||||||
use util::geometry::{self, Au};
|
use util::geometry::{self, Au};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -62,10 +63,28 @@ impl DisplayListOptimizer {
|
||||||
stacking_contexts: I)
|
stacking_contexts: I)
|
||||||
where I: Iterator<Item=&'a Arc<StackingContext>> {
|
where I: Iterator<Item=&'a Arc<StackingContext>> {
|
||||||
for stacking_context in stacking_contexts {
|
for stacking_context in stacking_contexts {
|
||||||
let overflow = stacking_context.overflow.translate(&stacking_context.bounds.origin);
|
if stacking_context.layer.is_none() {
|
||||||
|
// Transform this stacking context to get it into the same space as
|
||||||
|
// the parent stacking context.
|
||||||
|
let origin_x = stacking_context.bounds.origin.x.to_f32_px();
|
||||||
|
let origin_y = stacking_context.bounds.origin.y.to_f32_px();
|
||||||
|
|
||||||
|
let transform = Matrix4::identity().translate(origin_x,
|
||||||
|
origin_y,
|
||||||
|
0.0)
|
||||||
|
.mul(&stacking_context.transform);
|
||||||
|
let transform_2d = Matrix2D::new(transform.m11, transform.m12,
|
||||||
|
transform.m21, transform.m22,
|
||||||
|
transform.m41, transform.m42);
|
||||||
|
|
||||||
|
let overflow = geometry::au_rect_to_f32_rect(stacking_context.overflow);
|
||||||
|
let overflow = transform_2d.transform_rect(&overflow);
|
||||||
|
let overflow = geometry::f32_rect_to_au_rect(overflow);
|
||||||
|
|
||||||
if self.visible_rect.intersects(&overflow) {
|
if self.visible_rect.intersects(&overflow) {
|
||||||
result_list.push_back((*stacking_context).clone())
|
result_list.push_back((*stacking_context).clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -602,7 +602,6 @@ impl WorkerThread {
|
||||||
self.time_profiler_sender.clone(),
|
self.time_profiler_sender.clone(),
|
||||||
|| {
|
|| {
|
||||||
stacking_context.optimize_and_draw_into_context(&mut paint_context,
|
stacking_context.optimize_and_draw_into_context(&mut paint_context,
|
||||||
&tile_bounds,
|
|
||||||
&matrix,
|
&matrix,
|
||||||
None);
|
None);
|
||||||
paint_context.draw_target.flush();
|
paint_context.draw_target.flush();
|
||||||
|
|
|
@ -290,3 +290,8 @@ pub fn f32_rect_to_au_rect(rect: Rect<f32>) -> Rect<Au> {
|
||||||
Size2D::new(Au::from_f32_px(rect.size.width), Au::from_f32_px(rect.size.height)))
|
Size2D::new(Au::from_f32_px(rect.size.width), Au::from_f32_px(rect.size.height)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A helper function to convert a rect of `Au` pixels to a rect of f32 units.
|
||||||
|
pub fn au_rect_to_f32_rect(rect: Rect<Au>) -> Rect<f32> {
|
||||||
|
Rect::new(Point2D::new(rect.origin.x.to_f32_px(), rect.origin.y.to_f32_px()),
|
||||||
|
Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()))
|
||||||
|
}
|
||||||
|
|
|
@ -320,6 +320,7 @@ flaky_cpu == linebreak_simple_a.html linebreak_simple_b.html
|
||||||
== text_transform_none_a.html text_transform_none_ref.html
|
== text_transform_none_a.html text_transform_none_ref.html
|
||||||
== text_transform_uppercase_a.html text_transform_uppercase_ref.html
|
== text_transform_uppercase_a.html text_transform_uppercase_ref.html
|
||||||
== transform_3d.html transform_3d_ref.html
|
== transform_3d.html transform_3d_ref.html
|
||||||
|
== transform_optimization.html transform_optimization_ref.html
|
||||||
== transform_simple_a.html transform_simple_ref.html
|
== transform_simple_a.html transform_simple_ref.html
|
||||||
== transform_stacking_context_a.html transform_stacking_context_ref.html
|
== transform_stacking_context_a.html transform_stacking_context_ref.html
|
||||||
== upper_id_attr.html upper_id_attr_ref.html
|
== upper_id_attr.html upper_id_attr_ref.html
|
||||||
|
|
29
tests/ref/transform_optimization.html
Normal file
29
tests/ref/transform_optimization.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#outer {
|
||||||
|
position: absolute;
|
||||||
|
background-color: transparent;
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
transform: translateX(300px);
|
||||||
|
}
|
||||||
|
#inner {
|
||||||
|
position: absolute;
|
||||||
|
width: 400px;
|
||||||
|
height: 100px;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="outer">
|
||||||
|
<div id="inner">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
23
tests/ref/transform_optimization_ref.html
Normal file
23
tests/ref/transform_optimization_ref.html
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#inner {
|
||||||
|
position: absolute;
|
||||||
|
left: 300px;
|
||||||
|
width: 400px;
|
||||||
|
height: 100px;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="outer">
|
||||||
|
<div id="inner">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue