Bug 1490282. Update webrender to commit 02f14d0f333ef125d1abff7b1146039a0ba75f43

This commit is contained in:
Jeff Muizelaar 2018-09-12 00:09:43 -04:00
parent 8a61079eb7
commit 0b8add99da
10 changed files with 329 additions and 310 deletions

View File

@ -61,9 +61,10 @@ varying vec2 vPos;
#define BORDER_STYLE_INSET 8
#define BORDER_STYLE_OUTSET 9
#define CLIP_NONE 0
#define CLIP_DASH 1
#define CLIP_DOT 2
#define CLIP_NONE 0
#define CLIP_DASH_CORNER 1
#define CLIP_DASH_EDGE 2
#define CLIP_DOT 3
#ifdef WR_VERTEX_SHADER
@ -356,7 +357,21 @@ void main(void) {
d = distance(vClipParams1.xy, vPos) - vClipParams1.z;
break;
}
case CLIP_DASH: {
case CLIP_DASH_EDGE: {
bool is_vertical = vClipParams1.x == 0.;
float half_dash = is_vertical ? vClipParams1.y : vClipParams1.x;
// We want to draw something like:
// +---+---+---+---+
// |xxx| | |xxx|
// +---+---+---+---+
float pos = is_vertical ? vPos.y : vPos.x;
bool in_dash = pos < half_dash || pos > 3.0 * half_dash;
if (!in_dash) {
d = 1.;
}
break;
}
case CLIP_DASH_CORNER: {
// Get SDF for the two line/tangent clip lines,
// do SDF subtract to get clip distance.
float d0 = distance_to_line(vClipParams1.xy,

View File

@ -235,16 +235,19 @@ impl BorderSideHelpers for BorderSide {
/// The kind of border corner clip.
#[repr(C)]
#[derive(Copy, Debug, Clone, PartialEq)]
pub enum BorderCornerClipKind {
Dash = 1,
Dot = 2,
pub enum BorderClipKind {
DashCorner = 1,
DashEdge = 2,
Dot = 3,
}
/// The source data for a border corner clip mask.
#[derive(Debug, Clone)]
pub struct BorderCornerClipSource {
pub max_clip_count: usize,
kind: BorderCornerClipKind,
struct BorderCornerClipSource {
// FIXME(emilio): the `max_clip_count` name makes no sense for dashed
// borders now that it represents half-dashes.
max_clip_count: usize,
kind: BorderClipKind,
widths: DeviceSize,
radius: DeviceSize,
ellipse: Ellipse<DevicePixel>,
@ -254,7 +257,7 @@ impl BorderCornerClipSource {
pub fn new(
corner_radius: DeviceSize,
widths: DeviceSize,
kind: BorderCornerClipKind,
kind: BorderClipKind,
) -> BorderCornerClipSource {
// Work out a dash length (and therefore dash count)
// based on the width of the border edges. The "correct"
@ -265,24 +268,21 @@ impl BorderCornerClipSource {
// uses for dash length.
let (ellipse, max_clip_count) = match kind {
BorderCornerClipKind::Dash => {
BorderClipKind::DashEdge => unreachable!("not for corners"),
BorderClipKind::DashCorner => {
let ellipse = Ellipse::new(corner_radius);
// The desired dash length is ~3x the border width.
let average_border_width = 0.5 * (widths.width + widths.height);
let desired_dash_arc_length = average_border_width * 3.0;
// Get the ideal number of dashes for that arc length.
// This is scaled by 0.5 since there is an on/off length
// for each dash.
let desired_count = 0.5 * ellipse.total_arc_length / desired_dash_arc_length;
let (_half_dash, num_half_dashes) =
compute_half_dash(average_border_width, ellipse.total_arc_length);
// Round that up to the nearest integer, so that the dash length
// doesn't exceed the ratio above. Add one extra dash to cover
// the last half-dash of the arc.
(ellipse, desired_count.ceil() as usize)
(ellipse, num_half_dashes as usize)
}
BorderCornerClipKind::Dot => {
BorderClipKind::Dot => {
let mut corner_radius = corner_radius;
if corner_radius.width < (widths.width / 2.0) {
corner_radius.width = 0.0;
@ -334,6 +334,10 @@ impl BorderCornerClipSource {
pub fn write(self, segment: BorderSegment) -> Vec<[f32; 8]> {
let mut dot_dash_data = Vec::new();
if self.max_clip_count == 0 {
return dot_dash_data;
}
let outer_scale = match segment {
BorderSegment::TopLeft => DeviceVector2D::new(0.0, 0.0),
BorderSegment::TopRight => DeviceVector2D::new(1.0, 0.0),
@ -353,29 +357,32 @@ impl BorderCornerClipSource {
let max_clip_count = self.max_clip_count.min(MAX_DASH_COUNT);
match self.kind {
BorderCornerClipKind::Dash => {
// Get the correct dash arc length.
let dash_arc_length =
0.5 * self.ellipse.total_arc_length / max_clip_count as f32;
// Start the first dash at one quarter the length of a single dash
// along the arc line. This is arbitrary but looks reasonable in
// most cases. We need to spend some time working on a more
// sophisticated dash placement algorithm that takes into account
// the offset of the dashes along edge segments.
let mut current_arc_length = 0.25 * dash_arc_length;
dot_dash_data.reserve(max_clip_count);
for _ in 0 .. max_clip_count {
let arc_length0 = current_arc_length;
current_arc_length += dash_arc_length;
BorderClipKind::DashEdge => unreachable!("not for corners"),
BorderClipKind::DashCorner => {
// Get the correct half-dash arc length.
let half_dash_arc_length =
self.ellipse.total_arc_length / max_clip_count as f32;
let dash_length = 2. * half_dash_arc_length;
let arc_length1 = current_arc_length;
current_arc_length += dash_arc_length;
let mut current_length = 0.;
dot_dash_data.reserve(max_clip_count / 4 + 1);
for i in 0 .. (max_clip_count / 4 + 1) {
let arc_length0 = current_length;
current_length += if i == 0 {
half_dash_arc_length
} else {
dash_length
};
let arc_length1 = current_length;
current_length += dash_length;
let alpha = self.ellipse.find_angle_for_arc_length(arc_length0);
let beta = self.ellipse.find_angle_for_arc_length(arc_length1);
let beta = self.ellipse.find_angle_for_arc_length(arc_length1);
let (point0, tangent0) = self.ellipse.get_point_and_tangent(alpha);
let (point1, tangent1) = self.ellipse.get_point_and_tangent(beta);
let (point0, tangent0) = self.ellipse.get_point_and_tangent(alpha);
let (point1, tangent1) = self.ellipse.get_point_and_tangent(beta);
let point0 = DevicePoint::new(
outer.x + clip_sign.x * (self.radius.width - point0.x),
@ -409,14 +416,14 @@ impl BorderCornerClipSource {
]);
}
}
BorderCornerClipKind::Dot if max_clip_count == 1 => {
BorderClipKind::Dot if max_clip_count == 1 => {
let dot_diameter = lerp(self.widths.width, self.widths.height, 0.5);
dot_dash_data.push([
self.widths.width / 2.0, self.widths.height / 2.0, 0.5 * dot_diameter, 0.,
0., 0., 0., 0.,
]);
}
BorderCornerClipKind::Dot => {
BorderClipKind::Dot => {
let mut forward_dots = Vec::with_capacity(max_clip_count / 2 + 1);
let mut back_dots = Vec::with_capacity(max_clip_count / 2 + 1);
let mut leftover_arc_length = 0.0;
@ -539,13 +546,14 @@ pub struct BorderRenderTaskInfo {
pub size: DeviceIntSize,
}
// Information needed to place and draw a border edge.
/// Information needed to place and draw a border edge.
#[derive(Debug)]
struct EdgeInfo {
// Offset in local space to place the edge from origin.
/// Offset in local space to place the edge from origin.
local_offset: f32,
// Size of the edge in local space.
/// Size of the edge in local space.
local_size: f32,
// Size in device pixels needed in the render task.
/// Size in device pixels needed in the render task.
device_size: f32,
}
@ -563,6 +571,30 @@ impl EdgeInfo {
}
}
// Given a side width and the available space, compute the half-dash (half of
// the 'on' segment) and the count of them for a given segment.
fn compute_half_dash(side_width: f32, total_size: f32) -> (f32, u32) {
let half_dash = side_width * 1.5;
let num_half_dashes = (total_size / half_dash).ceil() as u32;
if num_half_dashes == 0 {
return (0., 0);
}
// TODO(emilio): Gecko has some other heuristics here to start with a full
// dash when the border side is zero, for example. We might consider those
// in the future.
let num_half_dashes = if num_half_dashes % 4 != 0 {
num_half_dashes + 4 - num_half_dashes % 4
} else {
num_half_dashes
};
let half_dash = total_size / num_half_dashes as f32;
(half_dash, num_half_dashes)
}
// Get the needed size in device pixels for an edge,
// based on the border style of that edge. This is used
// to determine how big the render task should be.
@ -579,14 +611,11 @@ fn get_edge_info(
match style {
BorderStyle::Dashed => {
let dash_size = 3.0 * side_width;
let approx_dash_count = (avail_size - dash_size) / dash_size;
let dash_count = 1.0 + 2.0 * (approx_dash_count / 2.0).floor();
let used_size = dash_count * dash_size;
let extra_space = avail_size - used_size;
let device_size = 2.0 * dash_size * scale;
let offset = (extra_space * 0.5).round();
EdgeInfo::new(offset, used_size, device_size)
// Basically, two times the dash size.
let (half_dash, _num_half_dashes) =
compute_half_dash(side_width, avail_size);
let device_size = (2.0 * 2.0 * half_dash * scale).round();
EdgeInfo::new(0., avail_size, device_size)
}
BorderStyle::Dotted => {
let dot_and_space_size = 2.0 * side_width;
@ -960,7 +989,7 @@ fn add_brush_segment(
brush_segments.push(
BrushSegment::new(
image_rect,
true,
/* may_need_clip_mask = */ true,
edge_flags,
[
task_rect.origin.x,
@ -1014,8 +1043,8 @@ fn add_segment(
}
let clip_kind = match style0 {
BorderStyle::Dashed => Some(BorderCornerClipKind::Dash),
BorderStyle::Dotted => Some(BorderCornerClipKind::Dot),
BorderStyle::Dashed => Some(BorderClipKind::DashCorner),
BorderStyle::Dotted => Some(BorderClipKind::Dot),
_ => None,
};
@ -1031,12 +1060,16 @@ fn add_segment(
// so that we don't allocate a Vec here.
let clip_list = clip_source.write(segment);
for params in clip_list {
instances.push(BorderInstance {
flags: base_flags | ((clip_kind as i32) << 24),
clip_params: params,
..base_instance
});
if clip_list.is_empty() {
instances.push(base_instance);
} else {
for params in clip_list {
instances.push(BorderInstance {
flags: base_flags | ((clip_kind as i32) << 24),
clip_params: params,
..base_instance
});
}
}
}
None => {
@ -1053,32 +1086,19 @@ fn add_segment(
match style0 {
BorderStyle::Dashed => {
let rect = if is_vertical {
let half_dash_size = task_rect.size.height * 0.5;
let y0 = task_rect.origin.y;
let y1 = y0 + half_dash_size.round();
DeviceRect::from_floats(
task_rect.origin.x,
y0,
task_rect.origin.x + task_rect.size.width,
y1,
)
let (x, y) = if is_vertical {
let half_dash_size = task_rect.size.height * 0.25;
(0., half_dash_size)
} else {
let half_dash_size = task_rect.size.width * 0.5;
let x0 = task_rect.origin.x;
let x1 = x0 + half_dash_size.round();
DeviceRect::from_floats(
x0,
task_rect.origin.y,
x1,
task_rect.origin.y + task_rect.size.height,
)
let half_dash_size = task_rect.size.width * 0.25;
(half_dash_size, 0.)
};
instances.push(BorderInstance {
local_rect: rect,
flags: base_flags | ((BorderClipKind::DashEdge as i32) << 24),
clip_params: [
x, y, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
],
..base_instance
});
}
@ -1094,7 +1114,7 @@ fn add_segment(
};
instances.push(BorderInstance {
flags: base_flags | ((BorderCornerClipKind::Dot as i32) << 24),
flags: base_flags | ((BorderClipKind::Dot as i32) << 24),
clip_params: [
x, y, r, 0.0, 0.0, 0.0, 0.0, 0.0,
],

View File

@ -328,6 +328,9 @@ pub struct ClipChainInstance {
pub local_clip_rect: LayoutRect,
pub has_non_root_coord_system: bool,
pub has_non_local_clips: bool,
// If true, this clip chain requires allocation
// of a clip mask.
pub needs_mask: bool,
// Combined clip rect in picture space (may
// be more conservative that local_clip_rect).
pub pic_clip_rect: PictureRect,
@ -524,6 +527,7 @@ impl ClipStore {
let first_clip_node_index = self.clip_node_indices.len() as u32;
let mut has_non_root_coord_system = false;
let mut has_non_local_clips = false;
let mut needs_mask = false;
// For each potential clip node
for node_info in self.clip_node_info.drain(..) {
@ -580,6 +584,26 @@ impl ClipStore {
}
};
// As a special case, a partial accept of a clip rect that is
// in the same coordinate system as the primitive doesn't need
// a clip mask. Instead, it can be handled by the primitive
// vertex shader as part of the local clip rect. This is an
// important optimization for reducing the number of clip
// masks that are allocated on common pages.
needs_mask |= match node.item {
ClipItem::Rectangle(_, ClipMode::ClipOut) |
ClipItem::RoundedRectangle(..) |
ClipItem::Image(..) |
ClipItem::BoxShadow(..) |
ClipItem::LineDecoration(..) => {
true
}
ClipItem::Rectangle(_, ClipMode::Clip) => {
!flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM)
}
};
// Store this in the index buffer for this clip chain instance.
self.clip_node_indices
.push(ClipNodeInstance::new(node_info.node_index, flags));
@ -602,6 +626,7 @@ impl ClipStore {
has_non_local_clips,
local_clip_rect,
pic_clip_rect,
needs_mask,
})
}
}

View File

@ -107,9 +107,6 @@ pub struct DisplayListFlattener<'a> {
/// A stack of stacking context properties.
sc_stack: Vec<FlattenedStackingContext>,
/// A stack of the current pictures.
picture_stack: Vec<PrimitiveIndex>,
/// A stack of the currently active shadows
shadow_stack: Vec<(Shadow, PrimitiveIndex)>,
@ -164,7 +161,6 @@ impl<'a> DisplayListFlattener<'a> {
id_to_index_mapper: ClipIdToIndexMapper::default(),
hit_testing_runs: recycle_vec(old_builder.hit_testing_runs),
scrollbar_prims: recycle_vec(old_builder.scrollbar_prims),
picture_stack: Vec::new(),
shadow_stack: Vec::new(),
sc_stack: Vec::new(),
next_picture_id: old_builder.next_picture_id,
@ -181,7 +177,7 @@ impl<'a> DisplayListFlattener<'a> {
flattener.setup_viewport_offset(view.inner_rect, view.accumulated_scale_factor());
flattener.flatten_root(root_pipeline, &root_pipeline.viewport_size);
debug_assert!(flattener.picture_stack.is_empty());
debug_assert!(flattener.sc_stack.is_empty());
new_scene.root_pipeline_id = Some(root_pipeline_id);
new_scene.pipeline_epochs = scene.pipeline_epochs.clone();
@ -818,7 +814,7 @@ impl<'a> DisplayListFlattener<'a> {
prim_index: PrimitiveIndex,
) {
// Add primitive to the top-most Picture on the stack.
let pic_prim_index = *self.picture_stack.last().unwrap();
let pic_prim_index = self.sc_stack.last().unwrap().leaf_prim_index;
let pic = self.prim_store.get_pic_mut(pic_prim_index);
pic.add_primitive(prim_index);
}
@ -922,51 +918,6 @@ impl<'a> DisplayListFlattener<'a> {
// to correctly handle some CSS cases (see #1957).
let max_clip = LayoutRect::max_rect();
// If there is no root picture, create one for the main framebuffer.
if self.sc_stack.is_empty() {
// Should be no pictures at all if the stack is empty...
debug_assert!(self.prim_store.primitives.is_empty());
debug_assert_eq!(transform_style, TransformStyle::Flat);
// This picture stores primitive runs for items on the
// main framebuffer.
let picture = PicturePrimitive::new_image(
self.get_next_picture_id(),
None,
false,
pipeline_id,
None,
true,
);
let prim_index = self.prim_store.add_primitive(
&LayoutRect::zero(),
&max_clip,
true,
ClipChainId::NONE,
spatial_node_index,
None,
PrimitiveContainer::Brush(BrushPrimitive::new_picture(picture)),
);
self.picture_stack.push(prim_index);
} else if composite_ops.mix_blend_mode.is_some() && self.sc_stack.len() > 2 {
// If we have a mix-blend-mode, and we aren't the primary framebuffer,
// the stacking context needs to be isolated to blend correctly as per
// the CSS spec.
// TODO(gw): The way we detect not being the primary framebuffer (len > 2)
// is hacky and depends on how we create a root stacking context
// during flattening.
let parent_prim_index = *self.picture_stack.last().unwrap();
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
// If not already isolated for some other reason,
// make this picture as isolated.
if parent_pic.requested_composite_mode.is_none() {
parent_pic.requested_composite_mode = Some(PictureCompositeMode::Blit);
}
}
// Get the transform-style of the parent stacking context,
// which determines if we *might* need to draw this on
// an intermediate surface for plane splitting purposes.
@ -992,126 +943,13 @@ impl<'a> DisplayListFlattener<'a> {
participating_in_3d_context &&
parent_transform_style == TransformStyle::Flat;
let rendering_context_3d_prim_index = if establishes_3d_context {
// If establishing a 3d context, we need to add a picture
// that will be the container for all the planes and any
// un-transformed content.
let picture = PicturePrimitive::new_image(
self.get_next_picture_id(),
None,
false,
pipeline_id,
None,
true,
);
let prim = BrushPrimitive::new_picture(picture);
let prim_index = self.prim_store.add_primitive(
&LayoutRect::zero(),
&max_clip,
true,
clip_chain_id,
spatial_node_index,
None,
PrimitiveContainer::Brush(prim),
);
let parent_prim_index = *self.picture_stack.last().unwrap();
let pic = self.prim_store.get_pic_mut(parent_prim_index);
pic.add_primitive(prim_index);
self.picture_stack.push(prim_index);
Some(prim_index)
} else {
None
};
let mut parent_prim_index = if !establishes_3d_context && participating_in_3d_context {
// If we're in a 3D context, we will parent the picture
// to the first stacking context we find that is a
// 3D rendering context container. This follows the spec
// by hoisting these items out into the same 3D context
// for plane splitting.
self.sc_stack
.iter()
.rev()
.find(|sc| sc.rendering_context_3d_prim_index.is_some())
.map(|sc| sc.rendering_context_3d_prim_index.unwrap())
.unwrap()
} else {
*self.picture_stack.last().unwrap()
};
// Same for mix-blend-mode.
if let Some(mix_blend_mode) = composite_ops.mix_blend_mode {
let picture = PicturePrimitive::new_image(
self.get_next_picture_id(),
Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
false,
pipeline_id,
None,
true,
);
let src_prim = BrushPrimitive::new_picture(picture);
let src_prim_index = self.prim_store.add_primitive(
&LayoutRect::zero(),
&max_clip,
true,
clip_chain_id,
spatial_node_index,
None,
PrimitiveContainer::Brush(src_prim),
);
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
parent_prim_index = src_prim_index;
parent_pic.add_primitive(src_prim_index);
self.picture_stack.push(src_prim_index);
}
// For each filter, create a new image with that composite mode.
for filter in composite_ops.filters.iter().rev() {
let picture = PicturePrimitive::new_image(
self.get_next_picture_id(),
Some(PictureCompositeMode::Filter(*filter)),
false,
pipeline_id,
None,
true,
);
let src_prim = BrushPrimitive::new_picture(picture);
let src_prim_index = self.prim_store.add_primitive(
&LayoutRect::zero(),
&max_clip,
true,
clip_chain_id,
spatial_node_index,
None,
PrimitiveContainer::Brush(src_prim),
);
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
parent_prim_index = src_prim_index;
parent_pic.add_primitive(src_prim_index);
self.picture_stack.push(src_prim_index);
}
// By default, this picture will be collapsed into
// the owning target.
let mut composite_mode = None;
let mut frame_output_pipeline_id = None;
// If this stacking context if the root of a pipeline, and the caller
// If this stacking context is the root of a pipeline, and the caller
// has requested it as an output frame, create a render task to isolate it.
let mut frame_output_pipeline_id = None;
if is_pipeline_root && self.output_pipelines.contains(&pipeline_id) {
composite_mode = Some(PictureCompositeMode::Blit);
frame_output_pipeline_id = Some(pipeline_id);
@ -1133,7 +971,7 @@ impl<'a> DisplayListFlattener<'a> {
}
// Add picture for this actual stacking context contents to render into.
let picture = PicturePrimitive::new_image(
let leaf_picture = PicturePrimitive::new_image(
self.get_next_picture_id(),
composite_mode,
participating_in_3d_context,
@ -1143,34 +981,112 @@ impl<'a> DisplayListFlattener<'a> {
);
// Create a brush primitive that draws this picture.
let sc_prim = BrushPrimitive::new_picture(picture);
let leaf_prim = BrushPrimitive::new_picture(leaf_picture);
// Add the brush to the parent picture.
let sc_prim_index = self.prim_store.add_primitive(
let leaf_prim_index = self.prim_store.add_primitive(
&LayoutRect::zero(),
&max_clip,
true,
clip_chain_id,
spatial_node_index,
None,
PrimitiveContainer::Brush(sc_prim),
PrimitiveContainer::Brush(leaf_prim),
);
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
parent_pic.add_primitive(sc_prim_index);
// Create a chain of pictures based on presence of filters,
// mix-blend-mode and/or 3d rendering context containers.
let mut current_prim_index = leaf_prim_index;
// Add this as the top-most picture for primitives to be added to.
self.picture_stack.push(sc_prim_index);
// For each filter, create a new image with that composite mode.
for filter in &composite_ops.filters {
let mut filter_picture = PicturePrimitive::new_image(
self.get_next_picture_id(),
Some(PictureCompositeMode::Filter(*filter)),
false,
pipeline_id,
None,
true,
);
filter_picture.add_primitive(current_prim_index);
let filter_prim = BrushPrimitive::new_picture(filter_picture);
current_prim_index = self.prim_store.add_primitive(
&LayoutRect::zero(),
&max_clip,
true,
clip_chain_id,
spatial_node_index,
None,
PrimitiveContainer::Brush(filter_prim),
);
}
// Same for mix-blend-mode.
if let Some(mix_blend_mode) = composite_ops.mix_blend_mode {
let mut blend_picture = PicturePrimitive::new_image(
self.get_next_picture_id(),
Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
false,
pipeline_id,
None,
true,
);
blend_picture.add_primitive(current_prim_index);
let blend_prim = BrushPrimitive::new_picture(blend_picture);
current_prim_index = self.prim_store.add_primitive(
&LayoutRect::zero(),
&max_clip,
true,
clip_chain_id,
spatial_node_index,
None,
PrimitiveContainer::Brush(blend_prim),
);
}
if establishes_3d_context {
// If establishing a 3d context, we need to add a picture
// that will be the container for all the planes and any
// un-transformed content.
let mut container_picture = PicturePrimitive::new_image(
self.get_next_picture_id(),
None,
false,
pipeline_id,
None,
true,
);
container_picture.add_primitive(current_prim_index);
let container_prim = BrushPrimitive::new_picture(container_picture);
current_prim_index = self.prim_store.add_primitive(
&LayoutRect::zero(),
&max_clip,
true,
clip_chain_id,
spatial_node_index,
None,
PrimitiveContainer::Brush(container_prim),
);
}
// Push the SC onto the stack, so we know how to handle things in
// pop_stacking_context.
let sc = FlattenedStackingContext {
composite_ops,
is_backface_visible,
pipeline_id,
transform_style,
rendering_context_3d_prim_index,
establishes_3d_context,
participating_in_3d_context,
leaf_prim_index,
root_prim_index: current_prim_index,
glyph_raster_space,
has_mix_blend_mode: composite_ops.mix_blend_mode.is_some(),
};
self.sc_stack.push(sc);
@ -1179,30 +1095,47 @@ impl<'a> DisplayListFlattener<'a> {
pub fn pop_stacking_context(&mut self) {
let sc = self.sc_stack.pop().unwrap();
// Always pop at least the main picture for this stacking context.
let mut pop_count = 1;
// Remove the picture for any filter/mix-blend-mode effects.
pop_count += sc.composite_ops.count();
// Remove the 3d context container if created
if sc.rendering_context_3d_prim_index.is_some() {
pop_count += 1;
}
for _ in 0 .. pop_count {
let prim_index = self
.picture_stack
.pop()
.expect("bug: mismatched picture stack");
// Run the optimize pass on each picture in the chain,
// to see if we can collapse opacity and avoid drawing
// to an off-screen surface.
for i in sc.leaf_prim_index.0 .. sc.root_prim_index.0 + 1 {
let prim_index = PrimitiveIndex(i);
self.prim_store.optimize_picture_if_possible(prim_index);
}
// By the time the stacking context stack is empty, we should
// also have cleared the picture stack.
if self.sc_stack.is_empty() {
self.picture_stack.pop().expect("bug: picture stack invalid");
debug_assert!(self.picture_stack.is_empty());
// This must be the root stacking context
return;
}
let parent_prim_index = if !sc.establishes_3d_context && sc.participating_in_3d_context {
// If we're in a 3D context, we will parent the picture
// to the first stacking context we find that is a
// 3D rendering context container. This follows the spec
// by hoisting these items out into the same 3D context
// for plane splitting.
self.sc_stack
.iter()
.rev()
.find(|sc| sc.establishes_3d_context)
.map(|sc| sc.root_prim_index)
.unwrap()
} else {
self.sc_stack.last().unwrap().leaf_prim_index
};
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
parent_pic.add_primitive(sc.root_prim_index);
// If we have a mix-blend-mode, and we aren't the primary framebuffer,
// the stacking context needs to be isolated to blend correctly as per
// the CSS spec.
// If not already isolated for some other reason,
// make this picture as isolated.
if sc.has_mix_blend_mode &&
self.sc_stack.len() > 2 &&
parent_pic.requested_composite_mode.is_none() {
parent_pic.requested_composite_mode = Some(PictureCompositeMode::Blit);
}
assert!(
@ -2006,9 +1939,6 @@ struct FlattenedStackingContext {
/// Pipeline this stacking context belongs to.
pipeline_id: PipelineId,
/// Filters / mix-blend-mode effects
composite_ops: CompositeOps,
/// If true, visible when backface is visible.
is_backface_visible: bool,
@ -2019,10 +1949,14 @@ struct FlattenedStackingContext {
/// CSS transform-style property.
transform_style: TransformStyle,
/// If Some(..), this stacking context establishes a new
/// 3d rendering context, and the value is the picture
// index of the 3d context container.
rendering_context_3d_prim_index: Option<PrimitiveIndex>,
root_prim_index: PrimitiveIndex,
leaf_prim_index: PrimitiveIndex,
/// If true, this stacking context establishes a new
/// 3d rendering context.
establishes_3d_context: bool,
participating_in_3d_context: bool,
has_mix_blend_mode: bool,
}
#[derive(Debug)]

View File

@ -2245,7 +2245,7 @@ impl Primitive {
match segment_clip_chain {
Some(segment_clip_chain) => {
if segment_clip_chain.clips_range.count == 0 ||
if !segment_clip_chain.needs_mask ||
(!segment.may_need_clip_mask && !segment_clip_chain.has_non_local_clips) {
segment.clip_task_id = BrushSegmentTaskId::Opaque;
continue;
@ -2794,7 +2794,7 @@ impl Primitive {
return;
}
if clip_chain.clips_range.count > 0 {
if clip_chain.needs_mask {
if let Some((device_rect, _, _)) = get_raster_rects(
clip_chain.pic_clip_rect,
&pic_state.map_pic_to_raster,

View File

@ -753,7 +753,31 @@ impl RenderBackend {
window_size: doc.view.window_size,
};
tx.send(captured).unwrap();
// notify the active recorder
if let Some(ref mut r) = self.recorder {
let pipeline_id = doc.scene.root_pipeline_id.unwrap();
let epoch = doc.scene.pipeline_epochs[&pipeline_id];
let pipeline = &doc.scene.pipelines[&pipeline_id];
let scene_msg = SceneMsg::SetDisplayList {
list_descriptor: pipeline.display_list.descriptor().clone(),
epoch,
pipeline_id,
background: pipeline.background_color,
viewport_size: pipeline.viewport_size,
content_size: pipeline.content_size,
preserve_frame_state: false,
};
let txn = TransactionMsg::scene_message(scene_msg);
r.write_msg(*frame_counter, &ApiMsg::UpdateDocument(*id, txn));
r.write_payload(*frame_counter, &Payload::construct_data(
epoch,
pipeline_id,
pipeline.display_list.data(),
));
}
}
// Note: we can't pass `LoadCapture` here since it needs to arrive
// before the `PublishDocument` messages sent by `load_capture`.
return true;

View File

@ -2048,10 +2048,7 @@ impl Renderer {
);
for alpha_batch_container in &target.alpha_batch_containers {
for batch in alpha_batch_container
.opaque_batches
.iter()
.rev() {
for batch in alpha_batch_container.opaque_batches.iter().rev() {
debug_target.add(
debug_server::BatchKind::Opaque,
batch.key.kind.debug_name(),
@ -2059,8 +2056,7 @@ impl Renderer {
);
}
for batch in &alpha_batch_container
.alpha_batches {
for batch in &alpha_batch_container.alpha_batches {
debug_target.add(
debug_server::BatchKind::Alpha,
batch.key.kind.debug_name(),

View File

@ -382,7 +382,7 @@ impl TransactionMsg {
}
// TODO: We only need this for a few RenderApi methods which we should remove.
fn frame_message(msg: FrameMsg) -> Self {
pub fn frame_message(msg: FrameMsg) -> Self {
TransactionMsg {
scene_ops: Vec::new(),
frame_ops: vec![msg],
@ -393,7 +393,7 @@ impl TransactionMsg {
}
}
fn scene_message(msg: SceneMsg) -> Self {
pub fn scene_message(msg: SceneMsg) -> Self {
TransactionMsg {
scene_ops: vec![msg],
frame_ops: Vec::new(),

View File

@ -22,23 +22,28 @@ pub struct Payload {
}
impl Payload {
/// Convert the payload to a raw byte vector, in order for it to be
/// efficiently shared via shmem, for example.
/// This is a helper static method working on a slice.
pub fn construct_data(epoch: Epoch, pipeline_id: PipelineId, dl_data: &[u8]) -> Vec<u8> {
let mut data = Vec::with_capacity(
mem::size_of::<u32>() + 2 * mem::size_of::<u32>() + mem::size_of::<u64>() + dl_data.len(),
);
data.write_u32::<LittleEndian>(epoch.0).unwrap();
data.write_u32::<LittleEndian>(pipeline_id.0).unwrap();
data.write_u32::<LittleEndian>(pipeline_id.1).unwrap();
data.write_u64::<LittleEndian>(dl_data.len() as u64)
.unwrap();
data.extend_from_slice(dl_data);
data
}
/// Convert the payload to a raw byte vector, in order for it to be
/// efficiently shared via shmem, for example.
///
/// TODO(emilio, #1049): Consider moving the IPC boundary to the
/// constellation in Servo and remove this complexity from WR.
pub fn to_data(&self) -> Vec<u8> {
let mut data = Vec::with_capacity(
mem::size_of::<u32>() + 2 * mem::size_of::<u32>() + mem::size_of::<u64>() +
self.display_list_data.len(),
);
data.write_u32::<LittleEndian>(self.epoch.0).unwrap();
data.write_u32::<LittleEndian>(self.pipeline_id.0).unwrap();
data.write_u32::<LittleEndian>(self.pipeline_id.1).unwrap();
data.write_u64::<LittleEndian>(self.display_list_data.len() as u64)
.unwrap();
data.extend_from_slice(&self.display_list_data);
data
Self::construct_data(self.epoch, self.pipeline_id, &self.display_list_data)
}
/// Deserializes the given payload from a raw byte vector.

View File

@ -1 +1 @@
04d63e7d73b9661d9eb934a0933c8f9751a9a3db
02f14d0f333ef125d1abff7b1146039a0ba75f43