@@ -34,6 +34,7 @@ use scene::{Scene, ScenePipeline, StackingContextHelpers};
34
34
use scene_builder:: DocumentResources ;
35
35
use spatial_node:: { SpatialNodeType , StickyFrameInfo } ;
36
36
use std:: { f32, mem} ;
37
+ use std:: collections:: vec_deque:: VecDeque ;
37
38
use tiling:: { CompositeOps } ;
38
39
use util:: { MaxRect , RectHelpers } ;
39
40
@@ -132,8 +133,8 @@ pub struct DisplayListFlattener<'a> {
132
133
/// A stack of stacking context properties.
133
134
sc_stack : Vec < FlattenedStackingContext > ,
134
135
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 > ,
137
138
138
139
/// The stack keeping track of the root clip chains associated with pipelines.
139
140
pipeline_clip_chain_stack : Vec < ClipChainId > ,
@@ -188,7 +189,7 @@ impl<'a> DisplayListFlattener<'a> {
188
189
output_pipelines,
189
190
id_to_index_mapper : ClipIdToIndexMapper :: default ( ) ,
190
191
hit_testing_runs : Vec :: new ( ) ,
191
- shadow_stack : Vec :: new ( ) ,
192
+ pending_shadow_items : VecDeque :: new ( ) ,
192
193
sc_stack : Vec :: new ( ) ,
193
194
pipeline_clip_chain_stack : vec ! [ ClipChainId :: NONE ] ,
194
195
prim_store : PrimitiveStore :: new ( ) ,
@@ -881,62 +882,37 @@ impl<'a> DisplayListFlattener<'a> {
881
882
clip_items : Vec < ClipItemKey > ,
882
883
container : PrimitiveContainer ,
883
884
) {
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 ( ) {
899
889
let clip_chain_id = self . build_clip_chain (
900
890
clip_items,
901
891
clip_and_scroll. spatial_node_index ,
902
892
clip_and_scroll. clip_chain_id ,
903
893
) ;
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,
908
896
clip_chain_id,
909
897
clip_and_scroll. spatial_node_index ,
910
- container. create_shadow ( shadow ) ,
898
+ container,
911
899
) ;
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) ;
916
906
}
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,
922
913
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 ,
930
914
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
+ } ) ) ;
940
916
}
941
917
}
942
918
@@ -1207,7 +1183,7 @@ impl<'a> DisplayListFlattener<'a> {
1207
1183
}
1208
1184
1209
1185
assert ! (
1210
- self . shadow_stack . is_empty( ) ,
1186
+ self . pending_shadow_items . is_empty( ) ,
1211
1187
"Found unpopped text shadows when popping stacking context!"
1212
1188
) ;
1213
1189
}
@@ -1400,69 +1376,156 @@ impl<'a> DisplayListFlattener<'a> {
1400
1376
shadow : Shadow ,
1401
1377
clip_and_scroll : ScrollNodeAndClipChain ,
1402
1378
) {
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
+
1403
1390
let pipeline_id = self . sc_stack . last ( ) . unwrap ( ) . pipeline_id ;
1404
1391
let max_clip = LayoutRect :: max_rect ( ) ;
1392
+ let mut items = mem:: replace ( & mut self . pending_shadow_items , VecDeque :: new ( ) ) ;
1405
1393
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
+ //
1427
1406
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
+ ) ;
1442
1446
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
+ ) ;
1454
1467
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
+ ) ;
1462
1475
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;
1466
1529
}
1467
1530
1468
1531
pub fn add_solid_rectangle (
@@ -2066,3 +2129,25 @@ struct FlattenedStackingContext {
2066
2129
participating_in_3d_context : bool ,
2067
2130
has_mix_blend_mode : bool ,
2068
2131
}
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
+ }
0 commit comments