Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c81a684

Browse files
committedOct 4, 2018
Refactor shadow flattening to do work in pop_all_shadows.
This simplifies how shadow contexts are handled, by deferring the work of adding shadows and primitives until pop_all_shadows. Longer explanation below: Instead of adding a picture primitive, and then adding primitives to it, the picture API will be modified to take an immutable list of primitives during construction. This will be part of the changes to support interning of primitives for picture caching. This patch allows all the primitives for a shadow to be created before the shadow picture itself is added / created, which is required to work with these planned changes. Additionally, we can now detect if the shadow ended up having no primitives in it, and skip adding the picture in this case. This is probably a small win in some cases.
1 parent 3c3f9a4 commit c81a684

File tree

2 files changed

+195
-105
lines changed

2 files changed

+195
-105
lines changed
 

‎webrender/src/display_list_flattener.rs

Lines changed: 190 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use scene::{Scene, ScenePipeline, StackingContextHelpers};
3434
use scene_builder::DocumentResources;
3535
use spatial_node::{SpatialNodeType, StickyFrameInfo};
3636
use std::{f32, mem};
37+
use std::collections::vec_deque::VecDeque;
3738
use tiling::{CompositeOps};
3839
use util::{MaxRect, RectHelpers};
3940

@@ -132,8 +133,8 @@ pub struct DisplayListFlattener<'a> {
132133
/// A stack of stacking context properties.
133134
sc_stack: Vec<FlattenedStackingContext>,
134135

135-
/// A stack of the currently active shadows
136-
shadow_stack: Vec<(Shadow, PrimitiveIndex)>,
136+
/// Maintains state for any currently active shadows
137+
pending_shadow_items: VecDeque<ShadowItem>,
137138

138139
/// The stack keeping track of the root clip chains associated with pipelines.
139140
pipeline_clip_chain_stack: Vec<ClipChainId>,
@@ -188,7 +189,7 @@ impl<'a> DisplayListFlattener<'a> {
188189
output_pipelines,
189190
id_to_index_mapper: ClipIdToIndexMapper::default(),
190191
hit_testing_runs: Vec::new(),
191-
shadow_stack: Vec::new(),
192+
pending_shadow_items: VecDeque::new(),
192193
sc_stack: Vec::new(),
193194
pipeline_clip_chain_stack: vec![ClipChainId::NONE],
194195
prim_store: PrimitiveStore::new(),
@@ -881,62 +882,37 @@ impl<'a> DisplayListFlattener<'a> {
881882
clip_items: Vec<ClipItemKey>,
882883
container: PrimitiveContainer,
883884
) {
884-
if !self.shadow_stack.is_empty() {
885-
// TODO(gw): Restructure this so we don't need to move the shadow
886-
// stack out (borrowck due to create_primitive below).
887-
let shadow_stack = mem::replace(&mut self.shadow_stack, Vec::new());
888-
for &(ref shadow, shadow_pic_prim_index) in &shadow_stack {
889-
// Offset the local rect and clip rect by the shadow offset.
890-
let mut info = info.clone();
891-
info.rect = info.rect.translate(&shadow.offset);
892-
info.clip_rect = info.clip_rect.translate(&shadow.offset);
893-
894-
// Offset any local clip sources by the shadow offset.
895-
let clip_items: Vec<ClipItemKey> = clip_items
896-
.iter()
897-
.map(|cs| cs.offset(&shadow.offset))
898-
.collect();
885+
// If a shadow context is not active, then add the primitive
886+
// directly to the parent picture.
887+
if self.pending_shadow_items.is_empty() {
888+
if container.is_visible() {
899889
let clip_chain_id = self.build_clip_chain(
900890
clip_items,
901891
clip_and_scroll.spatial_node_index,
902892
clip_and_scroll.clip_chain_id,
903893
);
904-
905-
// Construct and add a primitive for the given shadow.
906-
let shadow_prim_index = self.create_primitive(
907-
&info,
894+
let prim_index = self.create_primitive(
895+
info,
908896
clip_chain_id,
909897
clip_and_scroll.spatial_node_index,
910-
container.create_shadow(shadow),
898+
container,
911899
);
912-
913-
// Add the new primitive to the shadow picture.
914-
let shadow_pic = self.prim_store.get_pic_mut(shadow_pic_prim_index);
915-
shadow_pic.add_primitive(shadow_prim_index);
900+
if cfg!(debug_assertions) && ChasePrimitive::LocalRect(info.rect) == self.config.chase_primitive {
901+
println!("Chasing {:?} by local rect", prim_index);
902+
self.prim_store.chase_id = Some(prim_index);
903+
}
904+
self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
905+
self.add_primitive_to_draw_list(prim_index);
916906
}
917-
self.shadow_stack = shadow_stack;
918-
}
919-
920-
if container.is_visible() {
921-
let clip_chain_id = self.build_clip_chain(
907+
} else {
908+
// There is an active shadow context. Store as a pending primitive
909+
// for processing during pop_all_shadows.
910+
self.pending_shadow_items.push_back(ShadowItem::Primitive(PendingPrimitive {
911+
clip_and_scroll,
912+
info: *info,
922913
clip_items,
923-
clip_and_scroll.spatial_node_index,
924-
clip_and_scroll.clip_chain_id,
925-
);
926-
let prim_index = self.create_primitive(
927-
info,
928-
clip_chain_id,
929-
clip_and_scroll.spatial_node_index,
930914
container,
931-
);
932-
if cfg!(debug_assertions) && ChasePrimitive::LocalRect(info.rect) == self.config.chase_primitive {
933-
println!("Chasing {:?} by local rect", prim_index);
934-
self.prim_store.chase_id = Some(prim_index);
935-
}
936-
self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
937-
self.add_primitive_to_draw_list(
938-
prim_index,
939-
);
915+
}));
940916
}
941917
}
942918

@@ -1207,7 +1183,7 @@ impl<'a> DisplayListFlattener<'a> {
12071183
}
12081184

12091185
assert!(
1210-
self.shadow_stack.is_empty(),
1186+
self.pending_shadow_items.is_empty(),
12111187
"Found unpopped text shadows when popping stacking context!"
12121188
);
12131189
}
@@ -1400,69 +1376,156 @@ impl<'a> DisplayListFlattener<'a> {
14001376
shadow: Shadow,
14011377
clip_and_scroll: ScrollNodeAndClipChain,
14021378
) {
1379+
// Store this shadow in the pending list, for processing
1380+
// during pop_all_shadows.
1381+
self.pending_shadow_items.push_back(ShadowItem::Shadow(PendingShadow {
1382+
shadow,
1383+
clip_and_scroll,
1384+
}));
1385+
}
1386+
1387+
pub fn pop_all_shadows(&mut self) {
1388+
assert!(!self.pending_shadow_items.is_empty(), "popped shadows, but none were present");
1389+
14031390
let pipeline_id = self.sc_stack.last().unwrap().pipeline_id;
14041391
let max_clip = LayoutRect::max_rect();
1392+
let mut items = mem::replace(&mut self.pending_shadow_items, VecDeque::new());
14051393

1406-
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
1407-
// "the image that would be generated by applying to the shadow a
1408-
// Gaussian blur with a standard deviation equal to half the blur radius."
1409-
let std_deviation = shadow.blur_radius * 0.5;
1410-
1411-
// If the shadow has no blur, any elements will get directly rendered
1412-
// into the parent picture surface, instead of allocating and drawing
1413-
// into an intermediate surface. In this case, we will need to apply
1414-
// the local clip rect to primitives.
1415-
let is_passthrough = shadow.blur_radius == 0.0;
1416-
1417-
// shadows always rasterize in local space.
1418-
// TODO(gw): expose API for clients to specify a raster scale
1419-
let raster_space = if is_passthrough {
1420-
let parent_pic_prim_index = self.sc_stack.last().unwrap().leaf_prim_index;
1421-
self.prim_store
1422-
.get_pic(parent_pic_prim_index)
1423-
.requested_raster_space
1424-
} else {
1425-
RasterSpace::Local(1.0)
1426-
};
1394+
//
1395+
// The pending_shadow_items queue contains a list of shadows and primitives
1396+
// that were pushed during the active shadow context. To process these, we:
1397+
//
1398+
// Iterate the list, popping an item from the front each iteration.
1399+
//
1400+
// If the item is a shadow:
1401+
// - Create a shadow picture primitive.
1402+
// - Add *any* primitives that remain in the item list to this shadow.
1403+
// If the item is a primitive:
1404+
// - Add that primitive as a normal item (if alpha > 0)
1405+
//
14271406

1428-
// Create a picture that the shadow primitives will be added to. If the
1429-
// blur radius is 0, the code in Picture::prepare_for_render will
1430-
// detect this and mark the picture to be drawn directly into the
1431-
// parent picture, which avoids an intermediate surface and blur.
1432-
let blur_filter = FilterOp::Blur(std_deviation).sanitize();
1433-
let shadow_pic = PicturePrimitive::new_image(
1434-
self.picture_id_generator.next(),
1435-
Some(PictureCompositeMode::Filter(blur_filter)),
1436-
false,
1437-
pipeline_id,
1438-
None,
1439-
is_passthrough,
1440-
raster_space,
1441-
);
1407+
while let Some(item) = items.pop_front() {
1408+
match item {
1409+
ShadowItem::Shadow(pending_shadow) => {
1410+
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
1411+
// "the image that would be generated by applying to the shadow a
1412+
// Gaussian blur with a standard deviation equal to half the blur radius."
1413+
let std_deviation = pending_shadow.shadow.blur_radius * 0.5;
1414+
1415+
// If the shadow has no blur, any elements will get directly rendered
1416+
// into the parent picture surface, instead of allocating and drawing
1417+
// into an intermediate surface. In this case, we will need to apply
1418+
// the local clip rect to primitives.
1419+
let is_passthrough = pending_shadow.shadow.blur_radius == 0.0;
1420+
1421+
// shadows always rasterize in local space.
1422+
// TODO(gw): expose API for clients to specify a raster scale
1423+
let raster_space = if is_passthrough {
1424+
let parent_pic_prim_index = self.sc_stack.last().unwrap().leaf_prim_index;
1425+
self.prim_store
1426+
.get_pic(parent_pic_prim_index)
1427+
.requested_raster_space
1428+
} else {
1429+
RasterSpace::Local(1.0)
1430+
};
1431+
1432+
// Create a picture that the shadow primitives will be added to. If the
1433+
// blur radius is 0, the code in Picture::prepare_for_render will
1434+
// detect this and mark the picture to be drawn directly into the
1435+
// parent picture, which avoids an intermediate surface and blur.
1436+
let blur_filter = FilterOp::Blur(std_deviation).sanitize();
1437+
let mut shadow_pic = PicturePrimitive::new_image(
1438+
self.picture_id_generator.next(),
1439+
Some(PictureCompositeMode::Filter(blur_filter)),
1440+
false,
1441+
pipeline_id,
1442+
None,
1443+
is_passthrough,
1444+
raster_space,
1445+
);
14421446

1443-
// Create the primitive to draw the shadow picture into the scene.
1444-
let shadow_prim = BrushPrimitive::new_picture(shadow_pic);
1445-
let shadow_prim_index = self.prim_store.add_primitive(
1446-
&LayoutRect::zero(),
1447-
&max_clip,
1448-
true,
1449-
clip_and_scroll.clip_chain_id,
1450-
clip_and_scroll.spatial_node_index,
1451-
None,
1452-
PrimitiveContainer::Brush(shadow_prim),
1453-
);
1447+
// Add any primitives that come after this shadow in the item
1448+
// list to this shadow.
1449+
for item in &items {
1450+
if let ShadowItem::Primitive(ref pending_primitive) = item {
1451+
// Offset the local rect and clip rect by the shadow offset.
1452+
let mut info = pending_primitive.info.clone();
1453+
info.rect = info.rect.translate(&pending_shadow.shadow.offset);
1454+
info.clip_rect = info.clip_rect.translate(&pending_shadow.shadow.offset);
1455+
1456+
// Offset any local clip sources by the shadow offset.
1457+
let clip_items: Vec<ClipItemKey> = pending_primitive
1458+
.clip_items
1459+
.iter()
1460+
.map(|cs| cs.offset(&pending_shadow.shadow.offset))
1461+
.collect();
1462+
let clip_chain_id = self.build_clip_chain(
1463+
clip_items,
1464+
pending_primitive.clip_and_scroll.spatial_node_index,
1465+
pending_primitive.clip_and_scroll.clip_chain_id,
1466+
);
14541467

1455-
// Add the shadow primitive. This must be done before pushing this
1456-
// picture on to the shadow stack, to avoid infinite recursion!
1457-
self.add_primitive_to_draw_list(
1458-
shadow_prim_index,
1459-
);
1460-
self.shadow_stack.push((shadow, shadow_prim_index));
1461-
}
1468+
// Construct and add a primitive for the given shadow.
1469+
let shadow_prim_index = self.create_primitive(
1470+
&info,
1471+
clip_chain_id,
1472+
pending_primitive.clip_and_scroll.spatial_node_index,
1473+
pending_primitive.container.create_shadow(&pending_shadow.shadow),
1474+
);
14621475

1463-
pub fn pop_all_shadows(&mut self) {
1464-
assert!(self.shadow_stack.len() > 0, "popped shadows, but none were present");
1465-
self.shadow_stack.clear();
1476+
// Add the new primitive to the shadow picture.
1477+
shadow_pic.add_primitive(shadow_prim_index);
1478+
}
1479+
}
1480+
1481+
// No point in adding a shadow here if there were no primitives
1482+
// added to the shadow.
1483+
if !shadow_pic.is_empty() {
1484+
// Create the primitive to draw the shadow picture into the scene.
1485+
let shadow_prim = BrushPrimitive::new_picture(shadow_pic);
1486+
let shadow_prim_index = self.prim_store.add_primitive(
1487+
&LayoutRect::zero(),
1488+
&max_clip,
1489+
true,
1490+
pending_shadow.clip_and_scroll.clip_chain_id,
1491+
pending_shadow.clip_and_scroll.spatial_node_index,
1492+
None,
1493+
PrimitiveContainer::Brush(shadow_prim),
1494+
);
1495+
1496+
// Add the shadow primitive. This must be done before pushing this
1497+
// picture on to the shadow stack, to avoid infinite recursion!
1498+
self.add_primitive_to_draw_list(shadow_prim_index);
1499+
}
1500+
}
1501+
ShadowItem::Primitive(pending_primitive) => {
1502+
// For a normal primitive, if it has alpha > 0, then we add this
1503+
// as a normal primitive to the parent picture.
1504+
if pending_primitive.container.is_visible() {
1505+
let clip_chain_id = self.build_clip_chain(
1506+
pending_primitive.clip_items,
1507+
pending_primitive.clip_and_scroll.spatial_node_index,
1508+
pending_primitive.clip_and_scroll.clip_chain_id,
1509+
);
1510+
let prim_index = self.create_primitive(
1511+
&pending_primitive.info,
1512+
clip_chain_id,
1513+
pending_primitive.clip_and_scroll.spatial_node_index,
1514+
pending_primitive.container,
1515+
);
1516+
if cfg!(debug_assertions) && ChasePrimitive::LocalRect(pending_primitive.info.rect) == self.config.chase_primitive {
1517+
println!("Chasing {:?} by local rect", prim_index);
1518+
self.prim_store.chase_id = Some(prim_index);
1519+
}
1520+
self.add_primitive_to_hit_testing_list(&pending_primitive.info, pending_primitive.clip_and_scroll);
1521+
self.add_primitive_to_draw_list(prim_index);
1522+
}
1523+
}
1524+
}
1525+
}
1526+
1527+
debug_assert!(items.is_empty());
1528+
self.pending_shadow_items = items;
14661529
}
14671530

14681531
pub fn add_solid_rectangle(
@@ -2066,3 +2129,25 @@ struct FlattenedStackingContext {
20662129
participating_in_3d_context: bool,
20672130
has_mix_blend_mode: bool,
20682131
}
2132+
2133+
/// A primitive that is added while a shadow context is
2134+
/// active is stored as a pending primitive and only
2135+
/// added to pictures during pop_all_shadows.
2136+
struct PendingPrimitive {
2137+
clip_and_scroll: ScrollNodeAndClipChain,
2138+
info: LayoutPrimitiveInfo,
2139+
clip_items: Vec<ClipItemKey>,
2140+
container: PrimitiveContainer,
2141+
}
2142+
2143+
/// As shadows are pushed, they are stored as pending
2144+
/// shadows, and handled at once during pop_all_shadows.
2145+
struct PendingShadow {
2146+
shadow: Shadow,
2147+
clip_and_scroll: ScrollNodeAndClipChain,
2148+
}
2149+
2150+
enum ShadowItem {
2151+
Shadow(PendingShadow),
2152+
Primitive(PendingPrimitive),
2153+
}

‎webrender/src/picture.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,11 @@ impl PicturePrimitive {
378378
Some((context, state, instances))
379379
}
380380

381+
/// Return true if this picture doesn't contain any primitives.
382+
pub fn is_empty(&self) -> bool {
383+
self.prim_instances.is_empty()
384+
}
385+
381386
pub fn add_primitive(
382387
&mut self,
383388
prim_index: PrimitiveIndex,

0 commit comments

Comments
 (0)
Please sign in to comment.