Skip to content

Commit a6f0b51

Browse files
committed
Base the backface-visibility decision off the containing block, add Picture3DContext enum
1 parent 88cbe6c commit a6f0b51

File tree

7 files changed

+134
-58
lines changed

7 files changed

+134
-58
lines changed

webrender/src/batch.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use gpu_types::{ClipMaskInstance, SplitCompositeInstance};
1515
use gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
1616
use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
1717
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
18-
use picture::{PictureCompositeMode, PicturePrimitive, PictureSurface};
18+
use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureSurface};
1919
use plane_split::{BspSplitter, Clipper, Polygon, Splitter};
2020
use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, DeferredResolve};
2121
use prim_store::{EdgeAaSegmentMask, ImageSource};
@@ -653,7 +653,7 @@ impl AlphaBatchBuilder {
653653
// If this picture is participating in a 3D rendering context,
654654
// then don't add it to any batches here. Instead, create a polygon
655655
// for it and add it to the current plane splitter.
656-
if picture.is_in_3d_context {
656+
if let Picture3DContext::In { .. } = picture.context_3d {
657657
// Push into parent plane splitter.
658658
debug_assert!(picture.raster_config.is_some());
659659
let transform = transforms.get_world_transform(prim_metadata.spatial_node_index);

webrender/src/display_list_flattener.rs

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, S
1414
use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
1515
use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData};
1616
use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore};
17-
use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex};
17+
use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
1818
use euclid::vec2;
1919
use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
2020
use glyph_rasterizer::FontInstance;
@@ -23,7 +23,7 @@ use gpu_types::BrushFlags;
2323
use hit_test::{HitTestingItem, HitTestingRun};
2424
use image::simplify_repeated_primitive;
2525
use internal_types::{FastHashMap, FastHashSet};
26-
use picture::{PictureCompositeMode, PictureIdGenerator, PicturePrimitive};
26+
use picture::{Picture3DContext, PictureCompositeMode, PictureIdGenerator, PicturePrimitive};
2727
use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor};
2828
use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveOpacity};
2929
use prim_store::{BorderSource, BrushSegment, BrushSegmentVec, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
@@ -1015,11 +1015,42 @@ impl<'a> DisplayListFlattener<'a> {
10151015
composite_mode = Some(PictureCompositeMode::Blit);
10161016
}
10171017

1018+
// Find the spatial node index of the containing block, which
1019+
// defines the context of backface-visibility.
1020+
let last_leaf_prim_index = self.sc_stack.last().map(|sc| sc.leaf_prim_index);
1021+
// Note: the logic of obtaining these elements diverge upon the
1022+
// `establishes_3d_context` flag, but both try to find the parent 3D context,
1023+
// so we try to unity these code paths.
1024+
let (context_3d, parent_prim_index) = if participating_in_3d_context {
1025+
// If we're in a 3D context, we will parent the picture
1026+
// to the first stacking context we find that is a
1027+
// 3D rendering context container. This follows the spec
1028+
// by hoisting these items out into the same 3D context
1029+
// for plane splitting.
1030+
let parent = self.sc_stack
1031+
.iter()
1032+
.rfind(|sc| sc.establishes_3d_context);
1033+
let context_3d = Picture3DContext::In {
1034+
ancestor_index: match parent {
1035+
Some(sc) => self.prim_store.get_spatial_node_index(sc.leaf_prim_index),
1036+
None => ROOT_SPATIAL_NODE_INDEX,
1037+
},
1038+
};
1039+
let parent_prim_index = if establishes_3d_context {
1040+
last_leaf_prim_index
1041+
} else {
1042+
parent.map(|sc| sc.root_prim_index)
1043+
};
1044+
(context_3d, parent_prim_index)
1045+
} else {
1046+
(Picture3DContext::Out, last_leaf_prim_index)
1047+
};
1048+
10181049
// Add picture for this actual stacking context contents to render into.
10191050
let leaf_picture = PicturePrimitive::new_image(
10201051
self.picture_id_generator.next(),
10211052
composite_mode,
1022-
participating_in_3d_context,
1053+
context_3d,
10231054
pipeline_id,
10241055
frame_output_pipeline_id,
10251056
true,
@@ -1055,7 +1086,7 @@ impl<'a> DisplayListFlattener<'a> {
10551086
let mut filter_picture = PicturePrimitive::new_image(
10561087
self.picture_id_generator.next(),
10571088
Some(PictureCompositeMode::Filter(filter)),
1058-
false,
1089+
Picture3DContext::Out,
10591090
pipeline_id,
10601091
None,
10611092
true,
@@ -1085,7 +1116,7 @@ impl<'a> DisplayListFlattener<'a> {
10851116
let mut blend_picture = PicturePrimitive::new_image(
10861117
self.picture_id_generator.next(),
10871118
Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
1088-
false,
1119+
Picture3DContext::Out,
10891120
pipeline_id,
10901121
None,
10911122
true,
@@ -1118,7 +1149,7 @@ impl<'a> DisplayListFlattener<'a> {
11181149
let mut container_picture = PicturePrimitive::new_image(
11191150
self.picture_id_generator.next(),
11201151
None,
1121-
false,
1152+
Picture3DContext::Out,
11221153
pipeline_id,
11231154
None,
11241155
true,
@@ -1149,10 +1180,10 @@ impl<'a> DisplayListFlattener<'a> {
11491180
pipeline_id,
11501181
transform_style,
11511182
establishes_3d_context,
1152-
participating_in_3d_context,
11531183
leaf_prim_index,
11541184
root_prim_index: current_prim_index,
11551185
has_mix_blend_mode: composite_ops.mix_blend_mode.is_some(),
1186+
parent_prim_index,
11561187
};
11571188

11581189
self.sc_stack.push(sc);
@@ -1169,28 +1200,10 @@ impl<'a> DisplayListFlattener<'a> {
11691200
self.prim_store.optimize_picture_if_possible(prim_index);
11701201
}
11711202

1172-
if self.sc_stack.is_empty() {
1173-
// This must be the root stacking context
1174-
return;
1175-
}
1176-
1177-
let parent_prim_index = if !sc.establishes_3d_context && sc.participating_in_3d_context {
1178-
// If we're in a 3D context, we will parent the picture
1179-
// to the first stacking context we find that is a
1180-
// 3D rendering context container. This follows the spec
1181-
// by hoisting these items out into the same 3D context
1182-
// for plane splitting.
1183-
self.sc_stack
1184-
.iter()
1185-
.rev()
1186-
.find(|sc| sc.establishes_3d_context)
1187-
.map(|sc| sc.root_prim_index)
1188-
.unwrap()
1189-
} else {
1190-
self.sc_stack.last().unwrap().leaf_prim_index
1203+
let parent_pic = match sc.parent_prim_index {
1204+
Some(prim_index) => self.prim_store.get_pic_mut(prim_index),
1205+
None => return, // This must be the root stacking context
11911206
};
1192-
1193-
let parent_pic = self.prim_store.get_pic_mut(parent_prim_index);
11941207
parent_pic.add_primitive(sc.root_prim_index);
11951208

11961209
// If we have a mix-blend-mode, and we aren't the primary framebuffer,
@@ -1431,7 +1444,7 @@ impl<'a> DisplayListFlattener<'a> {
14311444
let shadow_pic = PicturePrimitive::new_image(
14321445
self.picture_id_generator.next(),
14331446
Some(PictureCompositeMode::Filter(blur_filter)),
1434-
false,
1447+
Picture3DContext::Out,
14351448
pipeline_id,
14361449
None,
14371450
is_passthrough,
@@ -2058,6 +2071,7 @@ struct FlattenedStackingContext {
20582071
/// If true, this stacking context establishes a new
20592072
/// 3d rendering context.
20602073
establishes_3d_context: bool,
2061-
participating_in_3d_context: bool,
20622074
has_mix_blend_mode: bool,
2075+
2076+
parent_prim_index: Option<PrimitiveIndex>,
20632077
}

webrender/src/frame_builder.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ pub struct FrameBuildingState<'a> {
8686
pub segment_builder: SegmentBuilder,
8787
}
8888

89+
#[derive(Debug)]
8990
pub struct PictureContext {
9091
pub pipeline_id: PipelineId,
9192
pub apply_local_clip_rect: bool,
@@ -105,6 +106,7 @@ pub struct PictureState {
105106
pub map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel>,
106107
pub map_pic_to_raster: SpaceMapper<PicturePixel, RasterPixel>,
107108
pub map_raster_to_world: SpaceMapper<RasterPixel, WorldPixel>,
109+
pub map_local_to_containing_block: SpaceMapper<LayoutPixel, LayoutPixel>,
108110
pub surface_spatial_node_index: SpatialNodeIndex,
109111
pub raster_spatial_node_index: SpatialNodeIndex,
110112
}

webrender/src/picture.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ pub struct PictureCacheKey {
154154
unclipped_size: DeviceIntSize,
155155
}
156156

157+
/// Enum value describing the place of a picture in a 3D context.
158+
#[derive(Debug)]
159+
pub enum Picture3DContext {
160+
/// The picture is not a part of 3D context sub-hierarchy.
161+
Out,
162+
/// The picture is a part of 3D context.
163+
In {
164+
/// The spatial node index of an "ancestor" element, i.e. one
165+
/// that establishes the transformed element’s containing block.
166+
///
167+
/// See CSS spec draft for more details:
168+
/// https://drafts.csswg.org/css-transforms-2/#accumulated-3d-transformation-matrix-computation
169+
ancestor_index: SpatialNodeIndex,
170+
}
171+
}
172+
157173
#[derive(Debug)]
158174
pub struct PicturePrimitive {
159175
// List of primitive runs that make up this picture.
@@ -182,8 +198,8 @@ pub struct PicturePrimitive {
182198
pub requested_raster_space: RasterSpace,
183199

184200
pub raster_config: Option<RasterConfig>,
185-
// If true, this picture is part of a 3D context.
186-
pub is_in_3d_context: bool,
201+
pub context_3d: Picture3DContext,
202+
187203
// If requested as a frame output (for rendering
188204
// pages to a texture), this is the pipeline this
189205
// picture is the root of.
@@ -217,7 +233,7 @@ impl PicturePrimitive {
217233
pub fn new_image(
218234
id: PictureId,
219235
requested_composite_mode: Option<PictureCompositeMode>,
220-
is_in_3d_context: bool,
236+
context_3d: Picture3DContext,
221237
pipeline_id: PipelineId,
222238
frame_output_pipeline_id: Option<PipelineId>,
223239
apply_local_clip_rect: bool,
@@ -229,7 +245,7 @@ impl PicturePrimitive {
229245
secondary_render_task_id: None,
230246
requested_composite_mode,
231247
raster_config: None,
232-
is_in_3d_context,
248+
context_3d,
233249
frame_output_pipeline_id,
234250
extra_gpu_data_handle: GpuCacheHandle::new(),
235251
apply_local_clip_rect,
@@ -318,6 +334,15 @@ impl PicturePrimitive {
318334
frame_context,
319335
);
320336

337+
let containing_node_index = match self.context_3d {
338+
Picture3DContext::Out => surface_spatial_node_index,
339+
Picture3DContext::In { ancestor_index } => ancestor_index,
340+
};
341+
let map_local_to_containing_block = SpaceMapper::new(
342+
containing_node_index,
343+
LayoutRect::zero(), // bounds aren't going to be used for this mapping
344+
);
345+
321346
self.raster_config = actual_composite_mode.map(|composite_mode| {
322347
RasterConfig {
323348
composite_mode,
@@ -337,6 +362,7 @@ impl PicturePrimitive {
337362
map_pic_to_world,
338363
map_pic_to_raster,
339364
map_raster_to_world,
365+
map_local_to_containing_block,
340366
};
341367

342368
// Disallow subpixel AA if an intermediate surface is needed.

webrender/src/prim_store.rs

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHand
2929
use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
3030
use resource_cache::{ImageProperties, ImageRequest, ResourceCache};
3131
use scene::SceneProperties;
32-
use std::{cmp, fmt, mem, usize};
32+
use std::{cmp, fmt, mem, ops, usize};
3333
use util::{ScaleOffset, MatrixHelpers, pack_as_float, project_rect, raster_rect_to_device_pixels};
3434
use smallvec::SmallVec;
3535

@@ -82,6 +82,16 @@ pub enum VisibleFace {
8282
Back,
8383
}
8484

85+
impl ops::Not for VisibleFace {
86+
type Output = Self;
87+
fn not(self) -> Self {
88+
match self {
89+
VisibleFace::Front => VisibleFace::Back,
90+
VisibleFace::Back => VisibleFace::Front,
91+
}
92+
}
93+
}
94+
8595
#[derive(Debug)]
8696
pub enum CoordinateSpaceMapping<F, T> {
8797
Local,
@@ -1413,6 +1423,10 @@ impl PrimitiveStore {
14131423
self.primitives[index.0].as_pic_mut()
14141424
}
14151425

1426+
pub fn get_spatial_node_index(&self, index: PrimitiveIndex) -> SpatialNodeIndex {
1427+
self.primitives[index.0].metadata.spatial_node_index
1428+
}
1429+
14161430
pub fn add_primitive(
14171431
&mut self,
14181432
local_rect: &LayoutRect,
@@ -1883,31 +1897,14 @@ impl PrimitiveStore {
18831897
(prim.metadata.spatial_node_index, prim.metadata.is_backface_visible)
18841898
};
18851899

1886-
let spatial_node = &frame_context
1887-
.clip_scroll_tree
1888-
.spatial_nodes[spatial_node_index.0];
1889-
1890-
// TODO(gw): Although constructing these is cheap, they are often
1891-
// the same for many consecutive primitives, so it may
1892-
// be worth caching the most recent context.
1893-
let prim_context = PrimitiveContext::new(
1894-
spatial_node,
1895-
spatial_node_index,
1896-
);
1897-
1898-
// Mark whether this picture contains any complex coordinate
1899-
// systems, due to either the scroll node or the clip-chain.
1900-
pic_state.has_non_root_coord_system |=
1901-
spatial_node.coordinate_system_id != CoordinateSystemId::root();
1902-
1903-
pic_state.map_local_to_pic.set_target_spatial_node(
1900+
pic_state.map_local_to_containing_block.set_target_spatial_node(
19041901
spatial_node_index,
19051902
frame_context.clip_scroll_tree,
19061903
);
19071904

19081905
// Do some basic checks first, that can early out
19091906
// without even knowing the local rect.
1910-
match pic_state.map_local_to_pic.visible_face() {
1907+
match pic_state.map_local_to_containing_block.visible_face() {
19111908
VisibleFace::Back if !is_backface_visible => {
19121909
if cfg!(debug_assertions) && is_chased {
19131910
println!("\tculled for not having visible back faces");
@@ -1917,7 +1914,17 @@ impl PrimitiveStore {
19171914
_ => {}
19181915
}
19191916

1917+
if !is_backface_visible {
1918+
println!("\tprim {:?} with property {} is not culled for visible face {:?}, transform {:?}",
1919+
prim_index, is_backface_visible,
1920+
pic_state.map_local_to_containing_block.visible_face(),
1921+
pic_state.map_local_to_containing_block);
1922+
println!("\tpicture context {:?}", pic_context);
1923+
}
19201924

1925+
let spatial_node = &frame_context
1926+
.clip_scroll_tree
1927+
.spatial_nodes[spatial_node_index.0];
19211928

19221929
if !spatial_node.invertible {
19231930
if cfg!(debug_assertions) && is_chased {
@@ -1926,14 +1933,22 @@ impl PrimitiveStore {
19261933
continue;
19271934
}
19281935

1936+
pic_state.map_local_to_pic.set_target_spatial_node(
1937+
spatial_node_index,
1938+
frame_context.clip_scroll_tree,
1939+
);
1940+
19291941
// Mark whether this picture contains any complex coordinate
19301942
// systems, due to either the scroll node or the clip-chain.
19311943
pic_state.has_non_root_coord_system |=
19321944
spatial_node.coordinate_system_id != CoordinateSystemId::root();
19331945

1934-
pic_state.map_local_to_pic.set_target_spatial_node(
1946+
// TODO(gw): Although constructing these is cheap, they are often
1947+
// the same for many consecutive primitives, so it may
1948+
// be worth caching the most recent context.
1949+
let prim_context = PrimitiveContext::new(
1950+
spatial_node,
19351951
spatial_node_index,
1936-
frame_context.clip_scroll_tree,
19371952
);
19381953

19391954
if self.prepare_prim_for_render(
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# In this test, the leaf green rectangle has only its back visible
2+
# due to rotate-x(180) transformation,
3+
# and "backface-visible = false", so it's completely hidden.
4+
---
5+
root:
6+
items:
7+
- type: rect
8+
color: red
9+
bounds: 0 0 1024 768
10+
- type: stacking-context
11+
bounds: 0 0 1024 768
12+
transform: rotate-x(180)
13+
transform-style: preserve-3d
14+
items:
15+
- type: rect
16+
bounds: 0 0 1024 768
17+
color: green
18+
backface-visible: false

wrench/reftests/backface/reftest.list

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
== backface-leaf.yaml backface-ref.yaml
2+
== backface-hidden.yaml backface-ref.yaml
23
== backface-sc.yaml backface-ref.yaml
34
== backface-picture.yaml backface-picture-ref.yaml
45
== backface-double-flip.yaml blank.yaml

0 commit comments

Comments
 (0)