mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 07:15:46 +00:00
Bug 1490282. Update webrender to commit 02f14d0f333ef125d1abff7b1146039a0ba75f43
This commit is contained in:
parent
8a61079eb7
commit
0b8add99da
@ -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,
|
||||
|
@ -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,
|
||||
],
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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.
|
||||
|
@ -1 +1 @@
|
||||
04d63e7d73b9661d9eb934a0933c8f9751a9a3db
|
||||
02f14d0f333ef125d1abff7b1146039a0ba75f43
|
||||
|
Loading…
Reference in New Issue
Block a user