Skip to content

Commit 2ce3b01

Browse files
committed
Bug 1918181 - Render filters in picture snapshots. r=gw
Before this patch, most filters aren't baked into the snapshots because we capture the render task that is used as input for the filter. This patch handles filters by nesting the stacking context into another one, snapshotting the parent to capture the result of the filter. Differential Revision: https://phabricator.services.mozilla.com/D233494
1 parent c0c612d commit 2ce3b01

File tree

1 file changed

+52
-2
lines changed

1 file changed

+52
-2
lines changed

webrender/src/scene_building.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,12 @@ pub struct SceneBuilder<'a> {
543543

544544
/// Used to build a ClipTree from the clip-chains, clips and state during scene building.
545545
clip_tree_builder: ClipTreeBuilder,
546+
547+
/// Some primitives need to nest two stacking contexts instead of one
548+
/// (see push_stacking_context). We keep track of the extra stacking context info
549+
/// here and set a boolean on the inner stacking context info to remember to
550+
/// pop from this stack (see StackingContextInfo::needs_extra_stacking_context)
551+
extra_stacking_context_stack: Vec<StackingContextInfo>,
546552
}
547553

548554
impl<'a> SceneBuilder<'a> {
@@ -602,6 +608,7 @@ impl<'a> SceneBuilder<'a> {
602608
pipeline_instance_ids: FastHashMap::default(),
603609
surfaces: mem::take(&mut recycler.surfaces),
604610
clip_tree_builder: recycler.clip_tree_builder.take().unwrap_or_else(|| ClipTreeBuilder::new()),
611+
extra_stacking_context_stack: Vec::new(),
605612
};
606613

607614
// Reset
@@ -2150,17 +2157,50 @@ impl<'a> SceneBuilder<'a> {
21502157
/// Push a new stacking context. Returns context that must be passed to pop_stacking_context().
21512158
fn push_stacking_context(
21522159
&mut self,
2153-
composite_ops: CompositeOps,
2160+
mut composite_ops: CompositeOps,
21542161
transform_style: TransformStyle,
21552162
prim_flags: PrimitiveFlags,
21562163
spatial_node_index: SpatialNodeIndex,
2157-
clip_chain_id: Option<api::ClipChainId>,
2164+
mut clip_chain_id: Option<api::ClipChainId>,
21582165
requested_raster_space: RasterSpace,
21592166
flags: StackingContextFlags,
21602167
subregion_offset: LayoutVector2D,
21612168
) -> StackingContextInfo {
21622169
profile_scope!("push_stacking_context");
21632170

2171+
// Filters have to be baked into the snapshot. Most filters are applied
2172+
// when rendering the picture into its parent, so if the stacking context
2173+
// needs to be snapshotted, we nest it into an extra stacking context and
2174+
// capture the outer stacking context into which the filter is drawn.
2175+
// Note: blur filters don't actually need an extra stacking context
2176+
// since the blur is baked into a render task instead of being applied
2177+
// when compositing the picture into its parent. This case is fairly rare
2178+
// so we pay the cost of the extra render pass for now.
2179+
let needs_extra_stacking_context = composite_ops.snapshot.is_some()
2180+
&& composite_ops.has_valid_filters();
2181+
2182+
if needs_extra_stacking_context {
2183+
let snapshot = mem::take(&mut composite_ops.snapshot);
2184+
let mut info = self.push_stacking_context(
2185+
CompositeOps {
2186+
filters: Vec::new(),
2187+
filter_datas: Vec::new(),
2188+
filter_primitives: Vec::new(),
2189+
mix_blend_mode: None,
2190+
snapshot,
2191+
},
2192+
TransformStyle::Flat,
2193+
prim_flags,
2194+
spatial_node_index,
2195+
clip_chain_id.take(),
2196+
requested_raster_space,
2197+
flags,
2198+
LayoutVector2D::zero(),
2199+
);
2200+
info.pop_stacking_context = true;
2201+
self.extra_stacking_context_stack.push(info);
2202+
}
2203+
21642204
let clip_node_id = match clip_chain_id {
21652205
Some(id) => {
21662206
self.clip_tree_builder.build_clip_set(id)
@@ -2342,6 +2382,7 @@ impl<'a> SceneBuilder<'a> {
23422382
pop_stacking_context: false,
23432383
pop_containing_block: false,
23442384
set_tile_cache_barrier,
2385+
needs_extra_stacking_context,
23452386
};
23462387

23472388
// If this is not 3d, then it establishes an ancestor root for child 3d contexts.
@@ -2699,6 +2740,11 @@ impl<'a> SceneBuilder<'a> {
26992740
self.pending_shadow_items.is_empty(),
27002741
"Found unpopped shadows when popping stacking context!"
27012742
);
2743+
2744+
if info.needs_extra_stacking_context {
2745+
let inner_info = self.extra_stacking_context_stack.pop().unwrap();
2746+
self.pop_stacking_context(inner_info);
2747+
}
27022748
}
27032749

27042750
pub fn push_reference_frame(
@@ -4498,6 +4544,10 @@ struct StackingContextInfo {
44984544
pop_stacking_context: bool,
44994545
/// If true, set a tile cache barrier when popping the stacking context.
45004546
set_tile_cache_barrier: bool,
4547+
/// If true, this stacking context was nested into two pushes instead of
4548+
/// one, and requires an extra pop to compensate. The info to pop is stored
4549+
/// at the top of `extra_stacking_context_stack`.
4550+
needs_extra_stacking_context: bool,
45014551
}
45024552

45034553
/// Properties of a stacking context that are maintained

0 commit comments

Comments
 (0)