1use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayList, BuiltDisplayListIter, PrimitiveFlags, SnapshotInfo};
39use api::{ClipId, ColorF, CommonItemProperties, ComplexClipRegion, ComponentTransferFuncType, RasterSpace};
40use api::{DebugFlags, DisplayItem, DisplayItemRef, ExtendMode, ExternalScrollId, FilterData};
41use api::{FilterOp, FilterPrimitive, FontInstanceKey, FontSize, GlyphInstance, GlyphOptions, GradientStop};
42use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, ColorDepth, QualitySettings};
43use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId, MixBlendMode, StackingContextFlags};
44use api::{PropertyBinding, ReferenceFrameKind, ScrollFrameDescriptor};
45use api::{APZScrollGeneration, HasScrollLinkedEffect, Shadow, SpatialId, StickyFrameDescriptor, ImageMask, ItemTag};
46use api::{ClipMode, PrimitiveKeyKind, TransformStyle, YuvColorSpace, ColorRange, YuvData, TempFilterData};
47use api::{ReferenceTransformBinding, Rotation, FillRule, SpatialTreeItem, ReferenceFrameDescriptor};
48use api::{FilterOpGraphPictureBufferId, SVGFE_GRAPH_MAX};
49use api::channel::{unbounded_channel, Receiver, Sender};
50use api::units::*;
51use crate::image_tiling::simplify_repeated_primitive;
52use crate::box_shadow::BLUR_SAMPLE_SCALE;
53use crate::clip::{ClipIntern, ClipItemKey, ClipItemKeyKind, ClipStore};
54use crate::clip::{ClipInternData, ClipNodeId, ClipLeafId};
55use crate::clip::{PolygonDataHandle, ClipTreeBuilder};
56use crate::gpu_types::BlurEdgeMode;
57use crate::segment::EdgeAaSegmentMask;
58use crate::spatial_tree::{SceneSpatialTree, SpatialNodeContainer, SpatialNodeIndex, get_external_scroll_offset};
59use crate::frame_builder::FrameBuilderConfig;
60use glyph_rasterizer::{FontInstance, SharedFontResources};
61use crate::hit_test::HitTestingScene;
62use crate::intern::Interner;
63use crate::internal_types::{FastHashMap, LayoutPrimitiveInfo, Filter, FilterGraphNode, FilterGraphOp, FilterGraphPictureReference, PlaneSplitterIndex, PipelineInstanceId};
64use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive};
65use crate::picture::{BlitReason, OrderedPictureChild, PrimitiveList, SurfaceInfo, PictureFlags};
66use crate::picture_graph::PictureGraph;
67use crate::prim_store::{PrimitiveInstance, PrimitiveStoreStats};
68use crate::prim_store::{PrimitiveInstanceKind, NinePatchDescriptor, PrimitiveStore};
69use crate::prim_store::{InternablePrimitive, PictureIndex};
70use crate::prim_store::PolygonKey;
71use crate::prim_store::backdrop::{BackdropCapture, BackdropRender};
72use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
73use crate::prim_store::gradient::{
74 GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams, ConicGradient,
75 ConicGradientParams, optimize_radial_gradient, apply_gradient_local_clip,
76 optimize_linear_gradient, self,
77};
78use crate::prim_store::image::{Image, YuvImage};
79use crate::prim_store::line_dec::{LineDecoration, LineDecorationCacheKey, get_line_decoration_size};
80use crate::prim_store::picture::{Picture, PictureCompositeKey, PictureKey};
81use crate::prim_store::text_run::TextRun;
82use crate::render_backend::SceneView;
83use crate::resource_cache::ImageRequest;
84use crate::scene::{BuiltScene, Scene, ScenePipeline, SceneStats, StackingContextHelpers};
85use crate::scene_builder_thread::Interners;
86use crate::space::SpaceSnapper;
87use crate::spatial_node::{
88 ReferenceFrameInfo, StickyFrameInfo, ScrollFrameKind, SpatialNodeUid, SpatialNodeType
89};
90use crate::tile_cache::TileCacheBuilder;
91use euclid::approxeq::ApproxEq;
92use std::{f32, mem, usize};
93use std::collections::vec_deque::VecDeque;
94use std::sync::Arc;
95use crate::util::{VecHelper, MaxRect};
96use crate::filterdata::{SFilterDataComponent, SFilterData, SFilterDataKey};
97use log::Level;
98
99pub struct ScrollOffsetMapper {
102 pub current_spatial_node: SpatialNodeIndex,
103 pub current_offset: LayoutVector2D,
104}
105
106impl ScrollOffsetMapper {
107 fn new() -> Self {
108 ScrollOffsetMapper {
109 current_spatial_node: SpatialNodeIndex::INVALID,
110 current_offset: LayoutVector2D::zero(),
111 }
112 }
113
114 fn external_scroll_offset(
118 &mut self,
119 spatial_node_index: SpatialNodeIndex,
120 spatial_tree: &SceneSpatialTree,
121 ) -> LayoutVector2D {
122 if spatial_node_index != self.current_spatial_node {
123 self.current_spatial_node = spatial_node_index;
124 self.current_offset = get_external_scroll_offset(spatial_tree, spatial_node_index);
125 }
126
127 self.current_offset
128 }
129}
130
131#[derive(Default)]
135pub struct NodeIdToIndexMapper {
136 spatial_node_map: FastHashMap<SpatialId, SpatialNodeIndex>,
137}
138
139impl NodeIdToIndexMapper {
140 fn add_spatial_node(&mut self, id: SpatialId, index: SpatialNodeIndex) {
141 let _old_value = self.spatial_node_map.insert(id, index);
142 assert!(_old_value.is_none());
143 }
144
145 fn get_spatial_node_index(&self, id: SpatialId) -> SpatialNodeIndex {
146 self.spatial_node_map[&id]
147 }
148}
149
150#[derive(Debug, Clone, Default)]
151pub struct CompositeOps {
152 pub filters: Vec<Filter>,
154 pub filter_datas: Vec<FilterData>,
155 pub filter_primitives: Vec<FilterPrimitive>,
156 pub snapshot: Option<SnapshotInfo>,
157
158 pub mix_blend_mode: Option<MixBlendMode>,
160}
161
162impl CompositeOps {
163 pub fn new(
164 filters: Vec<Filter>,
165 filter_datas: Vec<FilterData>,
166 filter_primitives: Vec<FilterPrimitive>,
167 mix_blend_mode: Option<MixBlendMode>,
168 snapshot: Option<SnapshotInfo>,
169 ) -> Self {
170 CompositeOps {
171 filters,
172 filter_datas,
173 filter_primitives,
174 mix_blend_mode,
175 snapshot,
176 }
177 }
178
179 pub fn is_empty(&self) -> bool {
180 self.filters.is_empty() &&
181 self.filter_primitives.is_empty() &&
182 self.mix_blend_mode.is_none() &&
183 self.snapshot.is_none()
184 }
185
186 fn has_valid_filters(&self) -> bool {
189 let mut current_filter_data_index = 0;
191 for filter in &self.filters {
192 match filter {
193 Filter::ComponentTransfer => {
194 let filter_data =
195 &self.filter_datas[current_filter_data_index];
196 let filter_data = filter_data.sanitize();
197 current_filter_data_index = current_filter_data_index + 1;
198 if filter_data.is_identity() {
199 continue
200 } else {
201 return true;
202 }
203 }
204 Filter::SVGGraphNode(..) => {return true;}
205 _ => {
206 if filter.is_noop() {
207 continue;
208 } else {
209 return true;
210 }
211 }
212 }
213 }
214
215 if !self.filter_primitives.is_empty() {
216 return true;
217 }
218
219 false
220 }
221}
222
223enum PictureSource {
226 PrimitiveList {
227 prim_list: PrimitiveList,
228 },
229 WrappedPicture {
230 instance: PrimitiveInstance,
231 },
232}
233
234struct PictureChainBuilder {
237 current: PictureSource,
239
240 spatial_node_index: SpatialNodeIndex,
242 flags: PrimitiveFlags,
244 raster_space: RasterSpace,
246 set_resolve_target: bool,
248 establishes_sub_graph: bool,
250}
251
252impl PictureChainBuilder {
253 fn from_prim_list(
255 prim_list: PrimitiveList,
256 flags: PrimitiveFlags,
257 spatial_node_index: SpatialNodeIndex,
258 raster_space: RasterSpace,
259 is_sub_graph: bool,
260 ) -> Self {
261 PictureChainBuilder {
262 current: PictureSource::PrimitiveList {
263 prim_list,
264 },
265 spatial_node_index,
266 flags,
267 raster_space,
268 establishes_sub_graph: is_sub_graph,
269 set_resolve_target: is_sub_graph,
270 }
271 }
272
273 fn from_instance(
275 instance: PrimitiveInstance,
276 flags: PrimitiveFlags,
277 spatial_node_index: SpatialNodeIndex,
278 raster_space: RasterSpace,
279 ) -> Self {
280 PictureChainBuilder {
281 current: PictureSource::WrappedPicture {
282 instance,
283 },
284 flags,
285 spatial_node_index,
286 raster_space,
287 establishes_sub_graph: false,
288 set_resolve_target: false,
289 }
290 }
291
292 #[must_use]
294 fn add_picture(
295 self,
296 composite_mode: PictureCompositeMode,
297 clip_node_id: ClipNodeId,
298 context_3d: Picture3DContext<OrderedPictureChild>,
299 interners: &mut Interners,
300 prim_store: &mut PrimitiveStore,
301 prim_instances: &mut Vec<PrimitiveInstance>,
302 clip_tree_builder: &mut ClipTreeBuilder,
303 ) -> PictureChainBuilder {
304 let prim_list = match self.current {
305 PictureSource::PrimitiveList { prim_list } => {
306 prim_list
307 }
308 PictureSource::WrappedPicture { instance } => {
309 let mut prim_list = PrimitiveList::empty();
310
311 prim_list.add_prim(
312 instance,
313 LayoutRect::zero(),
314 self.spatial_node_index,
315 self.flags,
316 prim_instances,
317 clip_tree_builder,
318 );
319
320 prim_list
321 }
322 };
323
324 let flags = if self.set_resolve_target {
325 PictureFlags::IS_RESOLVE_TARGET
326 } else {
327 PictureFlags::empty()
328 };
329
330 let pic_index = PictureIndex(prim_store.pictures
331 .alloc()
332 .init(PicturePrimitive::new_image(
333 Some(composite_mode.clone()),
334 context_3d,
335 self.flags,
336 prim_list,
337 self.spatial_node_index,
338 self.raster_space,
339 flags,
340 None,
341 ))
342 );
343
344 let instance = create_prim_instance(
345 pic_index,
346 Some(composite_mode).into(),
347 self.raster_space,
348 clip_node_id,
349 interners,
350 clip_tree_builder,
351 );
352
353 PictureChainBuilder {
354 current: PictureSource::WrappedPicture {
355 instance,
356 },
357 spatial_node_index: self.spatial_node_index,
358 flags: self.flags,
359 raster_space: self.raster_space,
360 set_resolve_target: false,
362 establishes_sub_graph: self.establishes_sub_graph,
363 }
364 }
365
366 fn finalize(
368 self,
369 clip_node_id: ClipNodeId,
370 interners: &mut Interners,
371 prim_store: &mut PrimitiveStore,
372 clip_tree_builder: &mut ClipTreeBuilder,
373 snapshot: Option<SnapshotInfo>,
374 ) -> PrimitiveInstance {
375 let mut flags = PictureFlags::empty();
376 if self.establishes_sub_graph {
377 flags |= PictureFlags::IS_SUB_GRAPH;
378 }
379
380 match self.current {
381 PictureSource::WrappedPicture { instance } => {
382 let pic_index = instance.kind.as_pic();
383 let picture = &mut prim_store.pictures[pic_index.0];
384 picture.flags |= flags;
385 picture.snapshot = snapshot;
386
387 instance
388 }
389 PictureSource::PrimitiveList { prim_list } => {
390 if self.set_resolve_target {
391 flags |= PictureFlags::IS_RESOLVE_TARGET;
392 }
393
394 let composite_mode = snapshot.map(|_| PictureCompositeMode::Blit(BlitReason::SNAPSHOT));
401
402 let pic_index = PictureIndex(prim_store.pictures
403 .alloc()
404 .init(PicturePrimitive::new_image(
405 composite_mode,
406 Picture3DContext::Out,
407 self.flags,
408 prim_list,
409 self.spatial_node_index,
410 self.raster_space,
411 flags,
412 snapshot,
413 ))
414 );
415
416 create_prim_instance(
417 pic_index,
418 None.into(),
419 self.raster_space,
420 clip_node_id,
421 interners,
422 clip_tree_builder,
423 )
424 }
425 }
426 }
427
428 #[allow(dead_code)]
430 fn has_picture(&self) -> bool {
431 match self.current {
432 PictureSource::WrappedPicture { .. } => true,
433 PictureSource::PrimitiveList { .. } => false,
434 }
435 }
436}
437
438bitflags! {
439 #[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
441 pub struct SliceFlags : u8 {
442 const IS_SCROLLBAR = 1;
444 const IS_ATOMIC = 2;
446 }
447}
448
449pub struct SceneBuilder<'a> {
453 scene: &'a Scene,
455
456 fonts: SharedFontResources,
458
459 id_to_index_mapper_stack: Vec<NodeIdToIndexMapper>,
462
463 sc_stack: Vec<FlattenedStackingContext>,
465
466 containing_block_stack: Vec<SpatialNodeIndex>,
468
469 raster_space_stack: Vec<RasterSpace>,
471
472 pending_shadow_items: VecDeque<ShadowItem>,
474
475 pub spatial_tree: &'a mut SceneSpatialTree,
477
478 pub prim_store: PrimitiveStore,
480
481 pub hit_testing_scene: HitTestingScene,
483
484 pub clip_store: ClipStore,
486
487 pub config: FrameBuilderConfig,
490
491 pub interners: &'a mut Interners,
493
494 external_scroll_mapper: ScrollOffsetMapper,
496
497 iframe_size: Vec<LayoutSize>,
500
501 root_iframe_clip: Option<ClipId>,
503
504 quality_settings: QualitySettings,
506
507 tile_cache_builder: TileCacheBuilder,
509
510 snap_to_device: SpaceSnapper,
516
517 picture_graph: PictureGraph,
522
523 snapshot_pictures: Vec<PictureIndex>,
526
527 next_plane_splitter_index: usize,
535
536 prim_instances: Vec<PrimitiveInstance>,
540
541 pipeline_instance_ids: FastHashMap<PipelineId, u32>,
544
545 surfaces: Vec<SurfaceInfo>,
549
550 clip_tree_builder: ClipTreeBuilder,
552
553 extra_stacking_context_stack: Vec<StackingContextInfo>,
558}
559
560impl<'a> SceneBuilder<'a> {
561 pub fn build(
562 scene: &Scene,
563 root_pipeline: Option<PipelineId>,
564 fonts: SharedFontResources,
565 view: &SceneView,
566 frame_builder_config: &FrameBuilderConfig,
567 interners: &mut Interners,
568 spatial_tree: &mut SceneSpatialTree,
569 recycler: &mut SceneRecycler,
570 stats: &SceneStats,
571 debug_flags: DebugFlags,
572 ) -> BuiltScene {
573 profile_scope!("build_scene");
574
575 let root_pipeline_id = root_pipeline.or(scene.root_pipeline_id).unwrap();
577 let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
578 let root_reference_frame_index = spatial_tree.root_reference_frame_index();
579
580 let snap_to_device = SpaceSnapper::new(
582 root_reference_frame_index,
583 RasterPixelScale::new(1.0),
584 );
585
586 let mut builder = SceneBuilder {
587 scene,
588 spatial_tree,
589 fonts,
590 config: *frame_builder_config,
591 id_to_index_mapper_stack: mem::take(&mut recycler.id_to_index_mapper_stack),
592 hit_testing_scene: recycler.hit_testing_scene.take().unwrap_or_else(|| HitTestingScene::new(&stats.hit_test_stats)),
593 pending_shadow_items: mem::take(&mut recycler.pending_shadow_items),
594 sc_stack: mem::take(&mut recycler.sc_stack),
595 containing_block_stack: mem::take(&mut recycler.containing_block_stack),
596 raster_space_stack: mem::take(&mut recycler.raster_space_stack),
597 prim_store: mem::take(&mut recycler.prim_store),
598 clip_store: mem::take(&mut recycler.clip_store),
599 interners,
600 external_scroll_mapper: ScrollOffsetMapper::new(),
601 iframe_size: mem::take(&mut recycler.iframe_size),
602 root_iframe_clip: None,
603 quality_settings: view.quality_settings,
604 tile_cache_builder: TileCacheBuilder::new(
605 root_reference_frame_index,
606 frame_builder_config.background_color,
607 debug_flags,
608 ),
609 snap_to_device,
610 picture_graph: mem::take(&mut recycler.picture_graph),
611 snapshot_pictures: Vec::new(),
613 next_plane_splitter_index: 0,
614 prim_instances: mem::take(&mut recycler.prim_instances),
615 pipeline_instance_ids: FastHashMap::default(),
616 surfaces: mem::take(&mut recycler.surfaces),
617 clip_tree_builder: recycler.clip_tree_builder.take().unwrap_or_else(|| ClipTreeBuilder::new()),
618 extra_stacking_context_stack: Vec::new(),
619 };
620
621 builder.hit_testing_scene.reset();
623 builder.prim_store.reset();
624 builder.clip_store.reset();
625 builder.picture_graph.reset();
626 builder.prim_instances.clear();
627 builder.surfaces.clear();
628 builder.sc_stack.clear();
629 builder.containing_block_stack.clear();
630 builder.id_to_index_mapper_stack.clear();
631 builder.pending_shadow_items.clear();
632 builder.iframe_size.clear();
633
634 builder.raster_space_stack.clear();
635 builder.raster_space_stack.push(RasterSpace::Screen);
636
637 builder.clip_tree_builder.begin();
638
639 builder.build_all(
640 root_pipeline_id,
641 &root_pipeline,
642 );
643
644 let (tile_cache_config, tile_cache_pictures) = builder.tile_cache_builder.build(
646 &builder.config,
647 &mut builder.prim_store,
648 &builder.spatial_tree,
649 &builder.prim_instances,
650 &mut builder.clip_tree_builder,
651 &builder.interners,
652 );
653
654 for pic_index in &builder.snapshot_pictures {
655 builder.picture_graph.add_root(*pic_index);
656 }
657
658 for pic_index in &tile_cache_pictures {
660 builder.picture_graph.add_root(*pic_index);
661 SceneBuilder::finalize_picture(
662 *pic_index,
663 None,
664 &mut builder.prim_store.pictures,
665 None,
666 &builder.clip_tree_builder,
667 &builder.prim_instances,
668 &builder.interners.clip,
669 );
670 }
671
672 let clip_tree = builder.clip_tree_builder.finalize();
673
674 recycler.clip_tree_builder = Some(builder.clip_tree_builder);
675 recycler.sc_stack = builder.sc_stack;
676 recycler.id_to_index_mapper_stack = builder.id_to_index_mapper_stack;
677 recycler.containing_block_stack = builder.containing_block_stack;
678 recycler.raster_space_stack = builder.raster_space_stack;
679 recycler.pending_shadow_items = builder.pending_shadow_items;
680 recycler.iframe_size = builder.iframe_size;
681
682 BuiltScene {
683 has_root_pipeline: scene.has_root_pipeline(),
684 pipeline_epochs: scene.pipeline_epochs.clone(),
685 output_rect: view.device_rect.size().into(),
686 hit_testing_scene: Arc::new(builder.hit_testing_scene),
687 prim_store: builder.prim_store,
688 clip_store: builder.clip_store,
689 config: builder.config,
690 tile_cache_config,
691 snapshot_pictures: builder.snapshot_pictures,
692 tile_cache_pictures,
693 picture_graph: builder.picture_graph,
694 num_plane_splitters: builder.next_plane_splitter_index,
695 prim_instances: builder.prim_instances,
696 surfaces: builder.surfaces,
697 clip_tree,
698 recycler_tx: Some(recycler.tx.clone()),
699 }
700 }
701
702 fn finalize_picture(
710 pic_index: PictureIndex,
711 prim_index: Option<usize>,
712 pictures: &mut [PicturePrimitive],
713 parent_spatial_node_index: Option<SpatialNodeIndex>,
714 clip_tree_builder: &ClipTreeBuilder,
715 prim_instances: &[PrimitiveInstance],
716 clip_interner: &Interner<ClipIntern>,
717 ) {
718 let (mut prim_list, spatial_node_index) = {
721 let pic = &mut pictures[pic_index.0];
722 assert_ne!(pic.spatial_node_index, SpatialNodeIndex::UNKNOWN);
723
724 if pic.flags.contains(PictureFlags::IS_RESOLVE_TARGET) {
725 pic.flags |= PictureFlags::DISABLE_SNAPPING;
726 }
727
728 let spatial_node_index = match pic.composite_mode {
730 Some(_) => pic.spatial_node_index,
731 None => parent_spatial_node_index.expect("bug: no parent"),
732 };
733
734 (
735 mem::replace(&mut pic.prim_list, PrimitiveList::empty()),
736 spatial_node_index,
737 )
738 };
739
740 for cluster in &mut prim_list.clusters {
742 if cluster.spatial_node_index == SpatialNodeIndex::UNKNOWN {
743 cluster.spatial_node_index = spatial_node_index;
744 }
745 }
746
747 let mut shared_clip_node_id = None;
756
757 let is_snapshot = pictures[pic_index.0].snapshot.is_some();
762
763 if is_snapshot {
764 if let Some(idx) = prim_index {
775 let clip_node = clip_tree_builder.get_leaf(prim_instances[idx].clip_leaf_id).node_id;
776 shared_clip_node_id = clip_tree_builder.get_parent(clip_node);
777 }
778 } else {
779 for cluster in &prim_list.clusters {
780 for prim_instance in &prim_instances[cluster.prim_range()] {
781 let leaf = clip_tree_builder.get_leaf(prim_instance.clip_leaf_id);
782
783 shared_clip_node_id = match shared_clip_node_id {
784 Some(current) => {
785 Some(clip_tree_builder.find_lowest_common_ancestor(
786 current,
787 leaf.node_id,
788 ))
789 }
790 None => Some(leaf.node_id)
791 };
792 }
793 }
794 }
795
796 let lca_tree_node = shared_clip_node_id
797 .and_then(|node_id| (node_id != ClipNodeId::NONE).then_some(node_id))
798 .map(|node_id| clip_tree_builder.get_node(node_id));
799 let lca_node = lca_tree_node
800 .map(|tree_node| &clip_interner[tree_node.handle]);
801 let pic_node_id = prim_index
802 .map(|prim_index| clip_tree_builder.get_leaf(prim_instances[prim_index].clip_leaf_id).node_id)
803 .and_then(|node_id| (node_id != ClipNodeId::NONE).then_some(node_id));
804 let pic_node = pic_node_id
805 .map(|node_id| clip_tree_builder.get_node(node_id))
806 .map(|tree_node| &clip_interner[tree_node.handle]);
807
808 let has_blur = match &pictures[pic_index.0].composite_mode {
814 Some(PictureCompositeMode::Filter(Filter::Blur { .. })) => true,
815 Some(PictureCompositeMode::Filter(Filter::DropShadows { .. })) => true,
816 Some(PictureCompositeMode::SvgFilter( .. )) => true,
817 Some(PictureCompositeMode::SVGFEGraph( .. )) => true,
818 _ => false,
819 };
820
821 let direct_parent = lca_tree_node
827 .zip(pic_node_id)
828 .map(|(lca_tree_node, pic_node_id)| lca_tree_node.parent == pic_node_id)
829 .unwrap_or(false);
830
831 let should_set_clip_root = is_snapshot || lca_node.zip(pic_node).map_or(false, |(lca_node, pic_node)| {
832 lca_node.key == pic_node.key && !has_blur && direct_parent
841 });
842
843 if should_set_clip_root {
844 pictures[pic_index.0].clip_root = shared_clip_node_id;
845 }
846
847 for cluster in &prim_list.clusters {
849 for prim_instance_index in cluster.prim_range() {
850 if let PrimitiveInstanceKind::Picture { pic_index: child_pic_index, .. } = prim_instances[prim_instance_index].kind {
851 let child_pic = &mut pictures[child_pic_index.0];
852
853 if child_pic.spatial_node_index == SpatialNodeIndex::UNKNOWN {
854 child_pic.spatial_node_index = spatial_node_index;
855 }
856
857 SceneBuilder::finalize_picture(
859 child_pic_index,
860 Some(prim_instance_index),
861 pictures,
862 Some(spatial_node_index),
863 clip_tree_builder,
864 prim_instances,
865 clip_interner,
866 );
867
868 if pictures[child_pic_index.0].flags.contains(PictureFlags::DISABLE_SNAPPING) {
869 pictures[pic_index.0].flags |= PictureFlags::DISABLE_SNAPPING;
870 }
871 }
872 }
873 }
874
875 pictures[pic_index.0].prim_list = prim_list;
877 }
878
879 fn current_external_scroll_offset(
881 &mut self,
882 spatial_node_index: SpatialNodeIndex,
883 ) -> LayoutVector2D {
884 self.external_scroll_mapper
886 .external_scroll_offset(
887 spatial_node_index,
888 self.spatial_tree,
889 )
890 }
891
892 fn build_spatial_tree_for_display_list(
893 &mut self,
894 dl: &BuiltDisplayList,
895 pipeline_id: PipelineId,
896 instance_id: PipelineInstanceId,
897 ) {
898 dl.iter_spatial_tree(|item| {
899 match item {
900 SpatialTreeItem::ScrollFrame(descriptor) => {
901 let parent_space = self.get_space(descriptor.parent_space);
902 self.build_scroll_frame(
903 descriptor,
904 parent_space,
905 pipeline_id,
906 instance_id,
907 );
908 }
909 SpatialTreeItem::ReferenceFrame(descriptor) => {
910 let parent_space = self.get_space(descriptor.parent_spatial_id);
911 self.build_reference_frame(
912 descriptor,
913 parent_space,
914 pipeline_id,
915 instance_id,
916 );
917 }
918 SpatialTreeItem::StickyFrame(descriptor) => {
919 let parent_space = self.get_space(descriptor.parent_spatial_id);
920 self.build_sticky_frame(
921 descriptor,
922 parent_space,
923 instance_id,
924 );
925 }
926 SpatialTreeItem::Invalid => {
927 unreachable!();
928 }
929 }
930 });
931 }
932
933 fn build_all(
934 &mut self,
935 root_pipeline_id: PipelineId,
936 root_pipeline: &ScenePipeline,
937 ) {
938 enum ContextKind<'a> {
939 Root,
940 StackingContext {
941 sc_info: StackingContextInfo,
942 },
943 ReferenceFrame,
944 Iframe {
945 parent_traversal: BuiltDisplayListIter<'a>,
946 }
947 }
948 struct BuildContext<'a> {
949 pipeline_id: PipelineId,
950 kind: ContextKind<'a>,
951 }
952
953 self.id_to_index_mapper_stack.push(NodeIdToIndexMapper::default());
954
955 let instance_id = self.get_next_instance_id_for_pipeline(root_pipeline_id);
956
957 self.push_root(
958 root_pipeline_id,
959 instance_id,
960 );
961 self.build_spatial_tree_for_display_list(
962 &root_pipeline.display_list.display_list,
963 root_pipeline_id,
964 instance_id,
965 );
966
967 let mut stack = vec![BuildContext {
968 pipeline_id: root_pipeline_id,
969 kind: ContextKind::Root,
970 }];
971 let mut traversal = root_pipeline.display_list.iter();
972
973 'outer: while let Some(bc) = stack.pop() {
974 loop {
975 let item = match traversal.next() {
976 Some(item) => item,
977 None => break,
978 };
979
980 match item.item() {
981 DisplayItem::PushStackingContext(ref info) => {
982 profile_scope!("build_stacking_context");
983 let spatial_node_index = self.get_space(info.spatial_id);
984 let mut subtraversal = item.sub_iter();
985 if subtraversal.current_stacking_context_empty() && item.filters().is_empty() {
991 subtraversal.skip_current_stacking_context();
992 traversal = subtraversal;
993 continue;
994 }
995
996 let snapshot = info.snapshot.map(|snapshot| {
997 SnapshotInfo {
1001 area: snapshot.area.translate(info.origin.to_vector()),
1002 .. snapshot
1003 }
1004 });
1005
1006 let composition_operations = CompositeOps::new(
1007 filter_ops_for_compositing(item.filters()),
1008 filter_datas_for_compositing(item.filter_datas()),
1009 filter_primitives_for_compositing(item.filter_primitives()),
1010 info.stacking_context.mix_blend_mode_for_compositing(),
1011 snapshot,
1012 );
1013
1014 let sc_info = self.push_stacking_context(
1015 composition_operations,
1016 info.stacking_context.transform_style,
1017 info.prim_flags,
1018 spatial_node_index,
1019 info.stacking_context.clip_chain_id,
1020 info.stacking_context.raster_space,
1021 info.stacking_context.flags,
1022 info.ref_frame_offset + info.origin.to_vector(),
1023 );
1024
1025 let new_context = BuildContext {
1026 pipeline_id: bc.pipeline_id,
1027 kind: ContextKind::StackingContext {
1028 sc_info,
1029 },
1030 };
1031 stack.push(bc);
1032 stack.push(new_context);
1033
1034 subtraversal.merge_debug_stats_from(&mut traversal);
1035 traversal = subtraversal;
1036 continue 'outer;
1037 }
1038 DisplayItem::PushReferenceFrame(..) => {
1039 profile_scope!("build_reference_frame");
1040 let mut subtraversal = item.sub_iter();
1041
1042 let new_context = BuildContext {
1043 pipeline_id: bc.pipeline_id,
1044 kind: ContextKind::ReferenceFrame,
1045 };
1046 stack.push(bc);
1047 stack.push(new_context);
1048
1049 subtraversal.merge_debug_stats_from(&mut traversal);
1050 traversal = subtraversal;
1051 continue 'outer;
1052 }
1053 DisplayItem::PopReferenceFrame |
1054 DisplayItem::PopStackingContext => break,
1055 DisplayItem::Iframe(ref info) => {
1056 profile_scope!("iframe");
1057
1058 let space = self.get_space(info.space_and_clip.spatial_id);
1059 let subtraversal = match self.push_iframe(info, space) {
1060 Some(pair) => pair,
1061 None => continue,
1062 };
1063
1064 let new_context = BuildContext {
1065 pipeline_id: info.pipeline_id,
1066 kind: ContextKind::Iframe {
1067 parent_traversal: mem::replace(&mut traversal, subtraversal),
1068 },
1069 };
1070 stack.push(bc);
1071 stack.push(new_context);
1072 continue 'outer;
1073 }
1074 _ => {
1075 self.build_item(item);
1076 }
1077 };
1078 }
1079
1080 match bc.kind {
1081 ContextKind::Root => {}
1082 ContextKind::StackingContext { sc_info } => {
1083 self.pop_stacking_context(sc_info);
1084 }
1085 ContextKind::ReferenceFrame => {
1086 }
1087 ContextKind::Iframe { parent_traversal } => {
1088 self.iframe_size.pop();
1089 self.clip_tree_builder.pop_clip();
1090 self.clip_tree_builder.pop_clip();
1091
1092 if self.iframe_size.is_empty() {
1093 assert!(self.root_iframe_clip.is_some());
1094 self.root_iframe_clip = None;
1095 self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
1096 }
1097
1098 self.id_to_index_mapper_stack.pop().unwrap();
1099
1100 traversal = parent_traversal;
1101 }
1102 }
1103
1104 if cfg!(feature = "display_list_stats") {
1106 let stats = traversal.debug_stats();
1107 let total_bytes: usize = stats.iter().map(|(_, stats)| stats.num_bytes).sum();
1108 debug!("item, total count, total bytes, % of DL bytes, bytes per item");
1109 for (label, stats) in stats {
1110 debug!("{}, {}, {}kb, {}%, {}",
1111 label,
1112 stats.total_count,
1113 stats.num_bytes / 1000,
1114 ((stats.num_bytes as f32 / total_bytes.max(1) as f32) * 100.0) as usize,
1115 stats.num_bytes / stats.total_count.max(1));
1116 }
1117 debug!("");
1118 }
1119 }
1120
1121 debug_assert!(self.sc_stack.is_empty());
1122
1123 self.id_to_index_mapper_stack.pop().unwrap();
1124 assert!(self.id_to_index_mapper_stack.is_empty());
1125 }
1126
1127 fn build_sticky_frame(
1128 &mut self,
1129 info: &StickyFrameDescriptor,
1130 parent_node_index: SpatialNodeIndex,
1131 instance_id: PipelineInstanceId,
1132 ) {
1133 let external_scroll_offset = self.current_external_scroll_offset(parent_node_index);
1134
1135 let sticky_frame_info = StickyFrameInfo::new(
1136 info.bounds.translate(external_scroll_offset),
1137 info.margins,
1138 info.vertical_offset_bounds,
1139 info.horizontal_offset_bounds,
1140 info.previously_applied_offset,
1141 info.transform,
1142 );
1143
1144 let index = self.spatial_tree.add_sticky_frame(
1145 parent_node_index,
1146 sticky_frame_info,
1147 info.id.pipeline_id(),
1148 info.key,
1149 instance_id,
1150 );
1151 self.id_to_index_mapper_stack.last_mut().unwrap().add_spatial_node(info.id, index);
1152 }
1153
1154 fn build_reference_frame(
1155 &mut self,
1156 info: &ReferenceFrameDescriptor,
1157 parent_space: SpatialNodeIndex,
1158 pipeline_id: PipelineId,
1159 instance_id: PipelineInstanceId,
1160 ) {
1161 let transform = match info.reference_frame.transform {
1162 ReferenceTransformBinding::Static { binding } => binding,
1163 ReferenceTransformBinding::Computed { scale_from, vertical_flip, rotation } => {
1164 let content_size = &self.iframe_size.last().unwrap();
1165
1166 let mut transform = if let Some(scale_from) = scale_from {
1167 match rotation {
1171 Rotation::Degree0 |
1172 Rotation::Degree180 => {
1173 LayoutTransform::scale(
1174 content_size.width / scale_from.width,
1175 content_size.height / scale_from.height,
1176 1.0
1177 )
1178 },
1179 Rotation::Degree90 |
1180 Rotation::Degree270 => {
1181 LayoutTransform::scale(
1182 content_size.height / scale_from.width,
1183 content_size.width / scale_from.height,
1184 1.0
1185 )
1186
1187 }
1188 }
1189 } else {
1190 LayoutTransform::identity()
1191 };
1192
1193 if vertical_flip {
1194 let content_size = &self.iframe_size.last().unwrap();
1195 let content_height = match rotation {
1196 Rotation::Degree0 | Rotation::Degree180 => content_size.height,
1197 Rotation::Degree90 | Rotation::Degree270 => content_size.width,
1198 };
1199 transform = transform
1200 .then_translate(LayoutVector3D::new(0.0, content_height, 0.0))
1201 .pre_scale(1.0, -1.0, 1.0);
1202 }
1203
1204 let rotate = rotation.to_matrix(**content_size);
1205 let transform = transform.then(&rotate);
1206
1207 PropertyBinding::Value(transform)
1208 },
1209 };
1210
1211 let external_scroll_offset = self.current_external_scroll_offset(parent_space);
1212
1213 self.push_reference_frame(
1214 info.reference_frame.id,
1215 parent_space,
1216 pipeline_id,
1217 info.reference_frame.transform_style,
1218 transform,
1219 info.reference_frame.kind,
1220 (info.origin + external_scroll_offset).to_vector(),
1221 SpatialNodeUid::external(info.reference_frame.key, pipeline_id, instance_id),
1222 );
1223 }
1224
1225 fn build_scroll_frame(
1226 &mut self,
1227 info: &ScrollFrameDescriptor,
1228 parent_node_index: SpatialNodeIndex,
1229 pipeline_id: PipelineId,
1230 instance_id: PipelineInstanceId,
1231 ) {
1232 let content_size = info.content_rect.size();
1236 let external_scroll_offset = self.current_external_scroll_offset(parent_node_index);
1237
1238 self.add_scroll_frame(
1239 info.scroll_frame_id,
1240 parent_node_index,
1241 info.external_id,
1242 pipeline_id,
1243 &info.frame_rect.translate(external_scroll_offset),
1244 &content_size,
1245 ScrollFrameKind::Explicit,
1246 info.external_scroll_offset,
1247 info.scroll_offset_generation,
1248 info.has_scroll_linked_effect,
1249 SpatialNodeUid::external(info.key, pipeline_id, instance_id),
1250 );
1251 }
1252
1253 fn get_next_instance_id_for_pipeline(
1255 &mut self,
1256 pipeline_id: PipelineId,
1257 ) -> PipelineInstanceId {
1258 let next_instance = self.pipeline_instance_ids
1259 .entry(pipeline_id)
1260 .or_insert(0);
1261
1262 let instance_id = PipelineInstanceId::new(*next_instance);
1263 *next_instance += 1;
1264
1265 instance_id
1266 }
1267
1268 fn push_iframe(
1269 &mut self,
1270 info: &IframeDisplayItem,
1271 spatial_node_index: SpatialNodeIndex,
1272 ) -> Option<BuiltDisplayListIter<'a>> {
1273 let iframe_pipeline_id = info.pipeline_id;
1274 let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
1275 Some(pipeline) => pipeline,
1276 None => {
1277 debug_assert!(info.ignore_missing_pipeline);
1278 return None
1279 },
1280 };
1281
1282 self.clip_tree_builder.push_clip_chain(Some(info.space_and_clip.clip_chain_id), false, false);
1283
1284 self.add_rect_clip_node(
1286 ClipId::root(iframe_pipeline_id),
1287 info.space_and_clip.spatial_id,
1288 &info.clip_rect,
1289 );
1290
1291 self.clip_tree_builder.push_clip_id(ClipId::root(iframe_pipeline_id));
1292
1293 let instance_id = self.get_next_instance_id_for_pipeline(iframe_pipeline_id);
1294
1295 self.id_to_index_mapper_stack.push(NodeIdToIndexMapper::default());
1296
1297 let bounds = self.normalize_scroll_offset_and_snap_rect(
1298 &info.bounds,
1299 spatial_node_index,
1300 );
1301
1302 let spatial_node_index = self.push_reference_frame(
1303 SpatialId::root_reference_frame(iframe_pipeline_id),
1304 spatial_node_index,
1305 iframe_pipeline_id,
1306 TransformStyle::Flat,
1307 PropertyBinding::Value(LayoutTransform::identity()),
1308 ReferenceFrameKind::Transform {
1309 is_2d_scale_translation: true,
1310 should_snap: true,
1311 paired_with_perspective: false,
1312 },
1313 bounds.min.to_vector(),
1314 SpatialNodeUid::root_reference_frame(iframe_pipeline_id, instance_id),
1315 );
1316
1317 let iframe_rect = LayoutRect::from_size(bounds.size());
1318 let is_root_pipeline = self.iframe_size.is_empty();
1319
1320 self.add_scroll_frame(
1321 SpatialId::root_scroll_node(iframe_pipeline_id),
1322 spatial_node_index,
1323 ExternalScrollId(0, iframe_pipeline_id),
1324 iframe_pipeline_id,
1325 &iframe_rect,
1326 &bounds.size(),
1327 ScrollFrameKind::PipelineRoot {
1328 is_root_pipeline,
1329 },
1330 LayoutVector2D::zero(),
1331 APZScrollGeneration::default(),
1332 HasScrollLinkedEffect::No,
1333 SpatialNodeUid::root_scroll_frame(iframe_pipeline_id, instance_id),
1334 );
1335
1336 if self.iframe_size.is_empty() {
1339 assert!(self.root_iframe_clip.is_none());
1340 self.root_iframe_clip = Some(ClipId::root(iframe_pipeline_id));
1341 self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
1342 }
1343 self.iframe_size.push(bounds.size());
1344
1345 self.build_spatial_tree_for_display_list(
1346 &pipeline.display_list.display_list,
1347 iframe_pipeline_id,
1348 instance_id,
1349 );
1350
1351 Some(pipeline.display_list.iter())
1352 }
1353
1354 fn get_space(
1355 &self,
1356 spatial_id: SpatialId,
1357 ) -> SpatialNodeIndex {
1358 self.id_to_index_mapper_stack.last().unwrap().get_spatial_node_index(spatial_id)
1359 }
1360
1361 fn get_clip_node(
1362 &mut self,
1363 clip_chain_id: api::ClipChainId,
1364 ) -> ClipNodeId {
1365 self.clip_tree_builder.build_clip_set(
1366 clip_chain_id,
1367 )
1368 }
1369
1370 fn process_common_properties(
1371 &mut self,
1372 common: &CommonItemProperties,
1373 bounds: Option<LayoutRect>,
1374 ) -> (LayoutPrimitiveInfo, LayoutRect, SpatialNodeIndex, ClipNodeId) {
1375 let spatial_node_index = self.get_space(common.spatial_id);
1376
1377 let mut clip_rect = common.clip_rect;
1379 let mut prim_rect = bounds.unwrap_or(clip_rect);
1380 let unsnapped_rect = self.normalize_rect_scroll_offset(&prim_rect, spatial_node_index);
1381
1382 if common.flags.contains(PrimitiveFlags::ANTIALISED) {
1387 prim_rect = self.normalize_rect_scroll_offset(&prim_rect, spatial_node_index);
1388 clip_rect = self.normalize_rect_scroll_offset(&clip_rect, spatial_node_index);
1389 } else {
1390 clip_rect = self.normalize_scroll_offset_and_snap_rect(
1391 &clip_rect,
1392 spatial_node_index,
1393 );
1394
1395 prim_rect = self.normalize_scroll_offset_and_snap_rect(
1396 &prim_rect,
1397 spatial_node_index,
1398 );
1399 }
1400
1401 let clip_node_id = self.get_clip_node(
1402 common.clip_chain_id,
1403 );
1404
1405 let layout = LayoutPrimitiveInfo {
1406 rect: prim_rect,
1407 clip_rect,
1408 flags: common.flags,
1409 };
1410
1411 (layout, unsnapped_rect, spatial_node_index, clip_node_id)
1412 }
1413
1414 fn process_common_properties_with_bounds(
1415 &mut self,
1416 common: &CommonItemProperties,
1417 bounds: LayoutRect,
1418 ) -> (LayoutPrimitiveInfo, LayoutRect, SpatialNodeIndex, ClipNodeId) {
1419 self.process_common_properties(
1420 common,
1421 Some(bounds),
1422 )
1423 }
1424
1425 fn normalize_rect_scroll_offset(
1429 &mut self,
1430 rect: &LayoutRect,
1431 spatial_node_index: SpatialNodeIndex,
1432 ) -> LayoutRect {
1433 let current_offset = self.current_external_scroll_offset(spatial_node_index);
1434
1435 rect.translate(current_offset)
1436 }
1437
1438 fn normalize_scroll_offset_and_snap_rect(
1442 &mut self,
1443 rect: &LayoutRect,
1444 target_spatial_node: SpatialNodeIndex,
1445 ) -> LayoutRect {
1446 let rect = self.normalize_rect_scroll_offset(rect, target_spatial_node);
1447
1448 self.snap_to_device.set_target_spatial_node(
1449 target_spatial_node,
1450 self.spatial_tree,
1451 );
1452 self.snap_to_device.snap_rect(&rect)
1453 }
1454
1455 fn build_item<'b>(
1456 &'b mut self,
1457 item: DisplayItemRef,
1458 ) {
1459 match *item.item() {
1460 DisplayItem::Image(ref info) => {
1461 profile_scope!("image");
1462
1463 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1464 &info.common,
1465 info.bounds,
1466 );
1467
1468 self.add_image(
1469 spatial_node_index,
1470 clip_node_id,
1471 &layout,
1472 layout.rect.size(),
1473 LayoutSize::zero(),
1474 info.image_key,
1475 info.image_rendering,
1476 info.alpha_type,
1477 info.color,
1478 );
1479 }
1480 DisplayItem::RepeatingImage(ref info) => {
1481 profile_scope!("repeating_image");
1482
1483 let (layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1484 &info.common,
1485 info.bounds,
1486 );
1487
1488 let stretch_size = process_repeat_size(
1489 &layout.rect,
1490 &unsnapped_rect,
1491 info.stretch_size,
1492 );
1493
1494 self.add_image(
1495 spatial_node_index,
1496 clip_node_id,
1497 &layout,
1498 stretch_size,
1499 info.tile_spacing,
1500 info.image_key,
1501 info.image_rendering,
1502 info.alpha_type,
1503 info.color,
1504 );
1505 }
1506 DisplayItem::YuvImage(ref info) => {
1507 profile_scope!("yuv_image");
1508
1509 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1510 &info.common,
1511 info.bounds,
1512 );
1513
1514 self.add_yuv_image(
1515 spatial_node_index,
1516 clip_node_id,
1517 &layout,
1518 info.yuv_data,
1519 info.color_depth,
1520 info.color_space,
1521 info.color_range,
1522 info.image_rendering,
1523 );
1524 }
1525 DisplayItem::Text(ref info) => {
1526 profile_scope!("text");
1527
1528 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1535 &info.common,
1536 info.bounds,
1537 );
1538
1539 self.add_text(
1540 spatial_node_index,
1541 clip_node_id,
1542 &layout,
1543 &info.font_key,
1544 &info.color,
1545 item.glyphs(),
1546 info.glyph_options,
1547 info.ref_frame_offset,
1548 );
1549 }
1550 DisplayItem::Rectangle(ref info) => {
1551 profile_scope!("rect");
1552
1553 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1554 &info.common,
1555 info.bounds,
1556 );
1557
1558 self.add_primitive(
1559 spatial_node_index,
1560 clip_node_id,
1561 &layout,
1562 Vec::new(),
1563 PrimitiveKeyKind::Rectangle {
1564 color: info.color.into(),
1565 },
1566 );
1567
1568 if info.common.flags.contains(PrimitiveFlags::CHECKERBOARD_BACKGROUND) {
1569 self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
1570 }
1571 }
1572 DisplayItem::HitTest(ref info) => {
1573 profile_scope!("hit_test");
1574
1575 let spatial_node_index = self.get_space(info.spatial_id);
1576
1577 let rect = self.normalize_scroll_offset_and_snap_rect(
1578 &info.rect,
1579 spatial_node_index,
1580 );
1581
1582 let layout = LayoutPrimitiveInfo {
1583 rect,
1584 clip_rect: rect,
1585 flags: info.flags,
1586 };
1587
1588 let spatial_node = self.spatial_tree.get_node_info(spatial_node_index);
1589 let anim_id: u64 = match spatial_node.node_type {
1590 SpatialNodeType::ReferenceFrame(ReferenceFrameInfo {
1591 source_transform: PropertyBinding::Binding(key, _),
1592 ..
1593 }) => key.clone().into(),
1594 SpatialNodeType::StickyFrame(StickyFrameInfo {
1595 transform: Some(PropertyBinding::Binding(key, _)),
1596 ..
1597 }) => key.clone().into(),
1598 _ => 0,
1599 };
1600
1601 let clip_node_id = self.get_clip_node(info.clip_chain_id);
1602
1603 self.add_primitive_to_hit_testing_list(
1604 &layout,
1605 spatial_node_index,
1606 clip_node_id,
1607 info.tag,
1608 anim_id,
1609 );
1610 }
1611 DisplayItem::ClearRectangle(ref info) => {
1612 profile_scope!("clear");
1613
1614 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1615 &info.common,
1616 info.bounds,
1617 );
1618
1619 self.add_clear_rectangle(
1620 spatial_node_index,
1621 clip_node_id,
1622 &layout,
1623 );
1624 }
1625 DisplayItem::Line(ref info) => {
1626 profile_scope!("line");
1627
1628 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1629 &info.common,
1630 info.area,
1631 );
1632
1633 self.add_line(
1634 spatial_node_index,
1635 clip_node_id,
1636 &layout,
1637 info.wavy_line_thickness,
1638 info.orientation,
1639 info.color,
1640 info.style,
1641 );
1642 }
1643 DisplayItem::Gradient(ref info) => {
1644 profile_scope!("gradient");
1645
1646 if !info.gradient.is_valid() {
1647 return;
1648 }
1649
1650 let (mut layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1651 &info.common,
1652 info.bounds,
1653 );
1654
1655 let mut tile_size = process_repeat_size(
1656 &layout.rect,
1657 &unsnapped_rect,
1658 info.tile_size,
1659 );
1660
1661 let mut stops = read_gradient_stops(item.gradient_stops());
1662 let mut start = info.gradient.start_point;
1663 let mut end = info.gradient.end_point;
1664 let flags = layout.flags;
1665
1666 let optimized = optimize_linear_gradient(
1667 &mut layout.rect,
1668 &mut tile_size,
1669 info.tile_spacing,
1670 &layout.clip_rect,
1671 &mut start,
1672 &mut end,
1673 info.gradient.extend_mode,
1674 &mut stops,
1675 self.config.enable_dithering,
1676 &mut |rect, start, end, stops, edge_aa_mask| {
1677 let layout = LayoutPrimitiveInfo { rect: *rect, clip_rect: *rect, flags };
1678 if let Some(prim_key_kind) = self.create_linear_gradient_prim(
1679 &layout,
1680 start,
1681 end,
1682 stops.to_vec(),
1683 ExtendMode::Clamp,
1684 rect.size(),
1685 LayoutSize::zero(),
1686 None,
1687 edge_aa_mask,
1688 ) {
1689 self.add_nonshadowable_primitive(
1690 spatial_node_index,
1691 clip_node_id,
1692 &layout,
1693 Vec::new(),
1694 prim_key_kind,
1695 );
1696 }
1697 }
1698 );
1699
1700 if !optimized && !tile_size.ceil().is_empty() {
1701 if let Some(prim_key_kind) = self.create_linear_gradient_prim(
1702 &layout,
1703 start,
1704 end,
1705 stops,
1706 info.gradient.extend_mode,
1707 tile_size,
1708 info.tile_spacing,
1709 None,
1710 EdgeAaSegmentMask::all(),
1711 ) {
1712 self.add_nonshadowable_primitive(
1713 spatial_node_index,
1714 clip_node_id,
1715 &layout,
1716 Vec::new(),
1717 prim_key_kind,
1718 );
1719 }
1720 }
1721 }
1722 DisplayItem::RadialGradient(ref info) => {
1723 profile_scope!("radial");
1724
1725 if !info.gradient.is_valid() {
1726 return;
1727 }
1728
1729 let (mut layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1730 &info.common,
1731 info.bounds,
1732 );
1733
1734 let mut center = info.gradient.center;
1735
1736 let stops = read_gradient_stops(item.gradient_stops());
1737
1738 let mut tile_size = process_repeat_size(
1739 &layout.rect,
1740 &unsnapped_rect,
1741 info.tile_size,
1742 );
1743
1744 let mut prim_rect = layout.rect;
1745 let mut tile_spacing = info.tile_spacing;
1746 optimize_radial_gradient(
1747 &mut prim_rect,
1748 &mut tile_size,
1749 &mut center,
1750 &mut tile_spacing,
1751 &layout.clip_rect,
1752 info.gradient.radius,
1753 info.gradient.end_offset,
1754 info.gradient.extend_mode,
1755 &stops,
1756 &mut |solid_rect, color| {
1757 self.add_nonshadowable_primitive(
1758 spatial_node_index,
1759 clip_node_id,
1760 &LayoutPrimitiveInfo {
1761 rect: *solid_rect,
1762 .. layout
1763 },
1764 Vec::new(),
1765 PrimitiveKeyKind::Rectangle { color: PropertyBinding::Value(color) },
1766 );
1767 }
1768 );
1769
1770 simplify_repeated_primitive(&tile_size, &mut tile_spacing, &mut prim_rect);
1775
1776 if !tile_size.ceil().is_empty() {
1777 layout.rect = prim_rect;
1778 let prim_key_kind = self.create_radial_gradient_prim(
1779 &layout,
1780 center,
1781 info.gradient.start_offset * info.gradient.radius.width,
1782 info.gradient.end_offset * info.gradient.radius.width,
1783 info.gradient.radius.width / info.gradient.radius.height,
1784 stops,
1785 info.gradient.extend_mode,
1786 tile_size,
1787 tile_spacing,
1788 None,
1789 );
1790
1791 self.add_nonshadowable_primitive(
1792 spatial_node_index,
1793 clip_node_id,
1794 &layout,
1795 Vec::new(),
1796 prim_key_kind,
1797 );
1798 }
1799 }
1800 DisplayItem::ConicGradient(ref info) => {
1801 profile_scope!("conic");
1802
1803 if !info.gradient.is_valid() {
1804 return;
1805 }
1806
1807 let (mut layout, unsnapped_rect, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1808 &info.common,
1809 info.bounds,
1810 );
1811
1812 let tile_size = process_repeat_size(
1813 &layout.rect,
1814 &unsnapped_rect,
1815 info.tile_size,
1816 );
1817
1818 let offset = apply_gradient_local_clip(
1819 &mut layout.rect,
1820 &tile_size,
1821 &info.tile_spacing,
1822 &layout.clip_rect,
1823 );
1824 let center = info.gradient.center + offset;
1825
1826 if !tile_size.ceil().is_empty() {
1827 let prim_key_kind = self.create_conic_gradient_prim(
1828 &layout,
1829 center,
1830 info.gradient.angle,
1831 info.gradient.start_offset,
1832 info.gradient.end_offset,
1833 item.gradient_stops(),
1834 info.gradient.extend_mode,
1835 tile_size,
1836 info.tile_spacing,
1837 None,
1838 );
1839
1840 self.add_nonshadowable_primitive(
1841 spatial_node_index,
1842 clip_node_id,
1843 &layout,
1844 Vec::new(),
1845 prim_key_kind,
1846 );
1847 }
1848 }
1849 DisplayItem::BoxShadow(ref info) => {
1850 profile_scope!("box_shadow");
1851
1852 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1853 &info.common,
1854 info.box_bounds,
1855 );
1856
1857 self.add_box_shadow(
1858 spatial_node_index,
1859 clip_node_id,
1860 &layout,
1861 &info.offset,
1862 info.color,
1863 info.blur_radius,
1864 info.spread_radius,
1865 info.border_radius,
1866 info.clip_mode,
1867 self.spatial_tree.is_root_coord_system(spatial_node_index),
1868 );
1869 }
1870 DisplayItem::Border(ref info) => {
1871 profile_scope!("border");
1872
1873 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties_with_bounds(
1874 &info.common,
1875 info.bounds,
1876 );
1877
1878 self.add_border(
1879 spatial_node_index,
1880 clip_node_id,
1881 &layout,
1882 info,
1883 item.gradient_stops(),
1884 );
1885 }
1886 DisplayItem::ImageMaskClip(ref info) => {
1887 profile_scope!("image_clip");
1888
1889 self.add_image_mask_clip_node(
1890 info.id,
1891 info.spatial_id,
1892 &info.image_mask,
1893 info.fill_rule,
1894 item.points(),
1895 );
1896 }
1897 DisplayItem::RoundedRectClip(ref info) => {
1898 profile_scope!("rounded_clip");
1899
1900 self.add_rounded_rect_clip_node(
1901 info.id,
1902 info.spatial_id,
1903 &info.clip,
1904 );
1905 }
1906 DisplayItem::RectClip(ref info) => {
1907 profile_scope!("rect_clip");
1908
1909 self.add_rect_clip_node(
1910 info.id,
1911 info.spatial_id,
1912 &info.clip_rect,
1913 );
1914 }
1915 DisplayItem::ClipChain(ref info) => {
1916 profile_scope!("clip_chain");
1917
1918 self.clip_tree_builder.define_clip_chain(
1919 info.id,
1920 info.parent,
1921 item.clip_chain_items().into_iter(),
1922 );
1923 },
1924 DisplayItem::BackdropFilter(ref info) => {
1925 profile_scope!("backdrop");
1926
1927 let (layout, _, spatial_node_index, clip_node_id) = self.process_common_properties(
1928 &info.common,
1929 None,
1930 );
1931
1932 let filters = filter_ops_for_compositing(item.filters());
1933 let filter_datas = filter_datas_for_compositing(item.filter_datas());
1934 let filter_primitives = filter_primitives_for_compositing(item.filter_primitives());
1935
1936 self.add_backdrop_filter(
1937 spatial_node_index,
1938 clip_node_id,
1939 &layout,
1940 filters,
1941 filter_datas,
1942 filter_primitives,
1943 );
1944 }
1945
1946 DisplayItem::SetGradientStops |
1948 DisplayItem::SetFilterOps |
1949 DisplayItem::SetFilterData |
1950 DisplayItem::SetFilterPrimitives |
1951 DisplayItem::SetPoints => {}
1952
1953 DisplayItem::PushStackingContext(..) |
1955 DisplayItem::PushReferenceFrame(..) |
1956 DisplayItem::PopReferenceFrame |
1957 DisplayItem::PopStackingContext |
1958 DisplayItem::Iframe(_) => {
1959 unreachable!("Handled in `build_all`")
1960 }
1961
1962 DisplayItem::ReuseItems(key) |
1963 DisplayItem::RetainedItems(key) => {
1964 unreachable!("Iterator logic error: {:?}", key);
1965 }
1966
1967 DisplayItem::PushShadow(info) => {
1968 profile_scope!("push_shadow");
1969
1970 let spatial_node_index = self.get_space(info.space_and_clip.spatial_id);
1971
1972 self.push_shadow(
1973 info.shadow,
1974 spatial_node_index,
1975 info.space_and_clip.clip_chain_id,
1976 info.should_inflate,
1977 );
1978 }
1979 DisplayItem::PopAllShadows => {
1980 profile_scope!("pop_all_shadows");
1981
1982 self.pop_all_shadows();
1983 }
1984 DisplayItem::DebugMarker(..) => {}
1985 }
1986 }
1987
1988 fn create_primitive<P>(
1994 &mut self,
1995 info: &LayoutPrimitiveInfo,
1996 clip_leaf_id: ClipLeafId,
1997 prim: P,
1998 ) -> PrimitiveInstance
1999 where
2000 P: InternablePrimitive,
2001 Interners: AsMut<Interner<P>>,
2002 {
2003 let prim_key = prim.into_key(info);
2005
2006 let interner = self.interners.as_mut();
2007 let prim_data_handle = interner
2008 .intern(&prim_key, || ());
2009
2010 let instance_kind = P::make_instance_kind(
2011 prim_key,
2012 prim_data_handle,
2013 &mut self.prim_store,
2014 );
2015
2016 PrimitiveInstance::new(
2017 instance_kind,
2018 clip_leaf_id,
2019 )
2020 }
2021
2022 fn add_primitive_to_hit_testing_list(
2023 &mut self,
2024 info: &LayoutPrimitiveInfo,
2025 spatial_node_index: SpatialNodeIndex,
2026 clip_node_id: ClipNodeId,
2027 tag: ItemTag,
2028 anim_id: u64,
2029 ) {
2030 self.hit_testing_scene.add_item(
2031 tag,
2032 anim_id,
2033 info,
2034 spatial_node_index,
2035 clip_node_id,
2036 &self.clip_tree_builder,
2037 self.interners,
2038 );
2039 }
2040
2041 pub fn add_primitive_to_draw_list(
2043 &mut self,
2044 prim_instance: PrimitiveInstance,
2045 prim_rect: LayoutRect,
2046 spatial_node_index: SpatialNodeIndex,
2047 flags: PrimitiveFlags,
2048 ) {
2049 match self.sc_stack.last_mut() {
2055 Some(stacking_context) => {
2056 stacking_context.prim_list.add_prim(
2057 prim_instance,
2058 prim_rect,
2059 spatial_node_index,
2060 flags,
2061 &mut self.prim_instances,
2062 &self.clip_tree_builder,
2063 );
2064 }
2065 None => {
2066 self.tile_cache_builder.add_prim(
2067 prim_instance,
2068 prim_rect,
2069 spatial_node_index,
2070 flags,
2071 self.spatial_tree,
2072 self.interners,
2073 &self.quality_settings,
2074 &mut self.prim_instances,
2075 &self.clip_tree_builder,
2076 );
2077 }
2078 }
2079 }
2080
2081 pub fn add_nonshadowable_primitive<P>(
2084 &mut self,
2085 spatial_node_index: SpatialNodeIndex,
2086 clip_node_id: ClipNodeId,
2087 info: &LayoutPrimitiveInfo,
2088 clip_items: Vec<ClipItemKey>,
2089 prim: P,
2090 )
2091 where
2092 P: InternablePrimitive + IsVisible,
2093 Interners: AsMut<Interner<P>>,
2094 {
2095 if prim.is_visible() {
2096 let clip_leaf_id = self.clip_tree_builder.build_for_prim(
2097 clip_node_id,
2098 info,
2099 &clip_items,
2100 &mut self.interners,
2101 );
2102
2103 self.add_prim_to_draw_list(
2104 info,
2105 spatial_node_index,
2106 clip_leaf_id,
2107 prim,
2108 );
2109 }
2110 }
2111
2112 pub fn add_primitive<P>(
2113 &mut self,
2114 spatial_node_index: SpatialNodeIndex,
2115 clip_node_id: ClipNodeId,
2116 info: &LayoutPrimitiveInfo,
2117 clip_items: Vec<ClipItemKey>,
2118 prim: P,
2119 )
2120 where
2121 P: InternablePrimitive + IsVisible,
2122 Interners: AsMut<Interner<P>>,
2123 ShadowItem: From<PendingPrimitive<P>>
2124 {
2125 if self.pending_shadow_items.is_empty() {
2128 self.add_nonshadowable_primitive(
2129 spatial_node_index,
2130 clip_node_id,
2131 info,
2132 clip_items,
2133 prim,
2134 );
2135 } else {
2136 debug_assert!(clip_items.is_empty(), "No per-prim clips expected for shadowed primitives");
2137
2138 self.pending_shadow_items.push_back(PendingPrimitive {
2141 spatial_node_index,
2142 clip_node_id,
2143 info: *info,
2144 prim,
2145 }.into());
2146 }
2147 }
2148
2149 fn add_prim_to_draw_list<P>(
2150 &mut self,
2151 info: &LayoutPrimitiveInfo,
2152 spatial_node_index: SpatialNodeIndex,
2153 clip_leaf_id: ClipLeafId,
2154 prim: P,
2155 )
2156 where
2157 P: InternablePrimitive,
2158 Interners: AsMut<Interner<P>>,
2159 {
2160 let prim_instance = self.create_primitive(
2161 info,
2162 clip_leaf_id,
2163 prim,
2164 );
2165 self.add_primitive_to_draw_list(
2166 prim_instance,
2167 info.rect,
2168 spatial_node_index,
2169 info.flags,
2170 );
2171 }
2172
2173 fn make_current_slice_atomic_if_required(&mut self) {
2174 let has_non_wrapping_sc = self.sc_stack
2175 .iter()
2176 .position(|sc| {
2177 !sc.flags.contains(StackingContextFlags::WRAPS_BACKDROP_FILTER)
2178 })
2179 .is_some();
2180
2181 if has_non_wrapping_sc {
2182 return;
2183 }
2184
2185 assert!(self.pending_shadow_items.is_empty());
2187 self.tile_cache_builder.make_current_slice_atomic();
2188 }
2189
2190 fn add_tile_cache_barrier_if_needed(
2193 &mut self,
2194 slice_flags: SliceFlags,
2195 ) {
2196 if self.sc_stack.is_empty() {
2197 assert!(self.pending_shadow_items.is_empty());
2199
2200 self.tile_cache_builder.add_tile_cache_barrier(
2201 slice_flags,
2202 self.root_iframe_clip,
2203 );
2204 }
2205 }
2206
2207 fn push_stacking_context(
2209 &mut self,
2210 mut composite_ops: CompositeOps,
2211 transform_style: TransformStyle,
2212 prim_flags: PrimitiveFlags,
2213 spatial_node_index: SpatialNodeIndex,
2214 clip_chain_id: Option<api::ClipChainId>,
2215 requested_raster_space: RasterSpace,
2216 flags: StackingContextFlags,
2217 subregion_offset: LayoutVector2D,
2218 ) -> StackingContextInfo {
2219 profile_scope!("push_stacking_context");
2220
2221 let needs_extra_stacking_context = composite_ops.snapshot.is_some()
2230 && composite_ops.has_valid_filters();
2231
2232 if needs_extra_stacking_context {
2233 let snapshot = mem::take(&mut composite_ops.snapshot);
2234 let mut info = self.push_stacking_context(
2235 CompositeOps {
2236 filters: Vec::new(),
2237 filter_datas: Vec::new(),
2238 filter_primitives: Vec::new(),
2239 mix_blend_mode: None,
2240 snapshot,
2241 },
2242 TransformStyle::Flat,
2243 prim_flags,
2244 spatial_node_index,
2245 clip_chain_id,
2246 requested_raster_space,
2247 flags,
2248 LayoutVector2D::zero(),
2249 );
2250 info.pop_stacking_context = true;
2251 self.extra_stacking_context_stack.push(info);
2252 }
2253
2254 let clip_node_id = match clip_chain_id {
2255 Some(id) => {
2256 self.clip_tree_builder.build_clip_set(id)
2257 }
2258 None => {
2259 self.clip_tree_builder.build_clip_set(api::ClipChainId::INVALID)
2260 }
2261 };
2262
2263 self.clip_tree_builder.push_clip_chain(
2264 clip_chain_id,
2265 !composite_ops.is_empty(),
2266 composite_ops.snapshot.is_some(),
2267 );
2268
2269 let new_space = match (self.raster_space_stack.last(), requested_raster_space) {
2270 (None, _) => requested_raster_space,
2272 (Some(parent_space), RasterSpace::Screen) => *parent_space,
2274 (Some(RasterSpace::Screen), space) => space,
2276 (Some(RasterSpace::Local(parent_scale)), RasterSpace::Local(scale)) => RasterSpace::Local(parent_scale.max(scale)),
2278 };
2279 self.raster_space_stack.push(new_space);
2280
2281 let (parent_is_3d, extra_3d_instance, plane_splitter_index) = match self.sc_stack.last_mut() {
2285 Some(ref mut sc) if sc.is_3d() => {
2286 let (flat_items_context_3d, plane_splitter_index) = match sc.context_3d {
2287 Picture3DContext::In { ancestor_index, plane_splitter_index, .. } => {
2288 (
2289 Picture3DContext::In {
2290 root_data: None,
2291 ancestor_index,
2292 plane_splitter_index,
2293 },
2294 plane_splitter_index,
2295 )
2296 }
2297 Picture3DContext::Out => panic!("Unexpected out of 3D context"),
2298 };
2299 let extra_instance = sc.cut_item_sequence(
2302 &mut self.prim_store,
2303 &mut self.interners,
2304 Some(PictureCompositeMode::Blit(BlitReason::PRESERVE3D)),
2305 flat_items_context_3d,
2306 &mut self.clip_tree_builder,
2307 );
2308 let extra_instance = extra_instance.map(|(_, instance)| {
2309 ExtendedPrimitiveInstance {
2310 instance,
2311 spatial_node_index: sc.spatial_node_index,
2312 flags: sc.prim_flags,
2313 }
2314 });
2315 (true, extra_instance, Some(plane_splitter_index))
2316 },
2317 _ => (false, None, None),
2318 };
2319
2320 if let Some(instance) = extra_3d_instance {
2321 self.add_primitive_instance_to_3d_root(instance);
2322 }
2323
2324 let participating_in_3d_context =
2330 composite_ops.is_empty() &&
2331 (parent_is_3d || transform_style == TransformStyle::Preserve3D);
2332
2333 let context_3d = if participating_in_3d_context {
2334 let ancestor_index = self.containing_block_stack
2337 .last()
2338 .cloned()
2339 .unwrap_or(self.spatial_tree.root_reference_frame_index());
2340
2341 let plane_splitter_index = plane_splitter_index.unwrap_or_else(|| {
2342 let index = self.next_plane_splitter_index;
2343 self.next_plane_splitter_index += 1;
2344 PlaneSplitterIndex(index)
2345 });
2346
2347 Picture3DContext::In {
2348 root_data: if parent_is_3d {
2349 None
2350 } else {
2351 Some(Vec::new())
2352 },
2353 plane_splitter_index,
2354 ancestor_index,
2355 }
2356 } else {
2357 Picture3DContext::Out
2358 };
2359
2360 let mut blit_reason = BlitReason::empty();
2365
2366 if flags.contains(StackingContextFlags::FORCED_ISOLATION) {
2369 blit_reason = BlitReason::FORCED_ISOLATION;
2370 }
2371
2372 if composite_ops.snapshot.is_some() {
2374 blit_reason = BlitReason::SNAPSHOT;
2375 }
2376
2377 if let Some(clip_chain_id) = clip_chain_id {
2380 if self.clip_tree_builder.clip_chain_has_complex_clips(clip_chain_id, &self.interners) {
2381 blit_reason |= BlitReason::CLIP;
2382 }
2383 }
2384
2385 let mut is_redundant = FlattenedStackingContext::is_redundant(
2388 &context_3d,
2389 &composite_ops,
2390 blit_reason,
2391 self.sc_stack.last(),
2392 prim_flags,
2393 );
2394
2395 if flags.contains(StackingContextFlags::IS_BLEND_CONTAINER) {
2401 if !self.sc_stack.is_empty() {
2403 blit_reason |= BlitReason::BLEND_MODE;
2406 is_redundant = false;
2407 } else {
2408 if self.tile_cache_builder.is_current_slice_empty() &&
2412 self.spatial_tree.is_root_coord_system(spatial_node_index) &&
2413 !self.clip_tree_builder.clip_node_has_complex_clips(clip_node_id, &self.interners)
2414 {
2415 self.add_tile_cache_barrier_if_needed(SliceFlags::IS_ATOMIC);
2416 self.tile_cache_builder.make_current_slice_atomic();
2417 } else {
2418 blit_reason |= BlitReason::BLEND_MODE;
2422 is_redundant = false;
2423 }
2424 }
2425 }
2426
2427 let set_tile_cache_barrier = prim_flags.contains(PrimitiveFlags::IS_SCROLLBAR_CONTAINER);
2430
2431 if set_tile_cache_barrier {
2432 self.add_tile_cache_barrier_if_needed(SliceFlags::IS_SCROLLBAR);
2433 }
2434
2435 let mut sc_info = StackingContextInfo {
2436 pop_stacking_context: false,
2437 pop_containing_block: false,
2438 set_tile_cache_barrier,
2439 needs_extra_stacking_context,
2440 };
2441
2442 if !participating_in_3d_context {
2444 sc_info.pop_containing_block = true;
2445 self.containing_block_stack.push(spatial_node_index);
2446 }
2447
2448 if !is_redundant {
2450 sc_info.pop_stacking_context = true;
2451
2452 self.sc_stack.push(FlattenedStackingContext {
2455 prim_list: PrimitiveList::empty(),
2456 prim_flags,
2457 spatial_node_index,
2458 clip_node_id,
2459 composite_ops,
2460 blit_reason,
2461 transform_style,
2462 context_3d,
2463 flags,
2464 raster_space: new_space,
2465 subregion_offset,
2466 });
2467 }
2468
2469 sc_info
2470 }
2471
2472 fn pop_stacking_context(
2473 &mut self,
2474 info: StackingContextInfo,
2475 ) {
2476 profile_scope!("pop_stacking_context");
2477
2478 self.clip_tree_builder.pop_clip();
2479
2480 self.raster_space_stack.pop().unwrap();
2482
2483 if info.pop_containing_block {
2485 self.containing_block_stack.pop().unwrap();
2486 }
2487
2488 if info.set_tile_cache_barrier {
2489 self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
2490 }
2491
2492 if !info.pop_stacking_context {
2494 return;
2495 }
2496
2497 let stacking_context = self.sc_stack.pop().unwrap();
2498
2499 let mut source = match stacking_context.context_3d {
2500 Picture3DContext::In { ancestor_index, plane_splitter_index, .. } => {
2508 let composite_mode = Some(
2509 PictureCompositeMode::Blit(BlitReason::PRESERVE3D | stacking_context.blit_reason)
2510 );
2511
2512 let pic_index = PictureIndex(self.prim_store.pictures
2514 .alloc()
2515 .init(PicturePrimitive::new_image(
2516 composite_mode.clone(),
2517 Picture3DContext::In { root_data: None, ancestor_index, plane_splitter_index },
2518 stacking_context.prim_flags,
2519 stacking_context.prim_list,
2520 stacking_context.spatial_node_index,
2521 stacking_context.raster_space,
2522 PictureFlags::empty(),
2523 None,
2524 ))
2525 );
2526
2527 let instance = create_prim_instance(
2528 pic_index,
2529 composite_mode.into(),
2530 stacking_context.raster_space,
2531 stacking_context.clip_node_id,
2532 &mut self.interners,
2533 &mut self.clip_tree_builder,
2534 );
2535
2536 PictureChainBuilder::from_instance(
2537 instance,
2538 stacking_context.prim_flags,
2539 stacking_context.spatial_node_index,
2540 stacking_context.raster_space,
2541 )
2542 }
2543 Picture3DContext::Out => {
2544 if stacking_context.blit_reason.is_empty() {
2545 PictureChainBuilder::from_prim_list(
2546 stacking_context.prim_list,
2547 stacking_context.prim_flags,
2548 stacking_context.spatial_node_index,
2549 stacking_context.raster_space,
2550 false,
2551 )
2552 } else {
2553 let composite_mode = Some(
2554 PictureCompositeMode::Blit(stacking_context.blit_reason)
2555 );
2556
2557 let pic_index = PictureIndex(self.prim_store.pictures
2559 .alloc()
2560 .init(PicturePrimitive::new_image(
2561 composite_mode.clone(),
2562 Picture3DContext::Out,
2563 stacking_context.prim_flags,
2564 stacking_context.prim_list,
2565 stacking_context.spatial_node_index,
2566 stacking_context.raster_space,
2567 PictureFlags::empty(),
2568 None,
2569 ))
2570 );
2571
2572 let instance = create_prim_instance(
2573 pic_index,
2574 composite_mode.into(),
2575 stacking_context.raster_space,
2576 stacking_context.clip_node_id,
2577 &mut self.interners,
2578 &mut self.clip_tree_builder,
2579 );
2580
2581 PictureChainBuilder::from_instance(
2582 instance,
2583 stacking_context.prim_flags,
2584 stacking_context.spatial_node_index,
2585 stacking_context.raster_space,
2586 )
2587 }
2588 }
2589 };
2590
2591 if let Picture3DContext::In { root_data: Some(mut prims), ancestor_index, plane_splitter_index } = stacking_context.context_3d {
2595 let instance = source.finalize(
2596 ClipNodeId::NONE,
2597 &mut self.interners,
2598 &mut self.prim_store,
2599 &mut self.clip_tree_builder,
2600 None,
2601 );
2602
2603 prims.push(ExtendedPrimitiveInstance {
2604 instance,
2605 spatial_node_index: stacking_context.spatial_node_index,
2606 flags: stacking_context.prim_flags,
2607 });
2608
2609 let mut prim_list = PrimitiveList::empty();
2610
2611 let mut needs_3d_context = false;
2624
2625 for ext_prim in prims.drain(..) {
2626 if !self.spatial_tree.is_root_coord_system(ext_prim.spatial_node_index) {
2631 needs_3d_context = true;
2632 }
2633
2634 prim_list.add_prim(
2635 ext_prim.instance,
2636 LayoutRect::zero(),
2637 ext_prim.spatial_node_index,
2638 ext_prim.flags,
2639 &mut self.prim_instances,
2640 &self.clip_tree_builder,
2641 );
2642 }
2643
2644 let context_3d = if needs_3d_context {
2645 Picture3DContext::In {
2646 root_data: Some(Vec::new()),
2647 ancestor_index,
2648 plane_splitter_index,
2649 }
2650 } else {
2651 for child_pic_index in &prim_list.child_pictures {
2655 let child_pic = &mut self.prim_store.pictures[child_pic_index.0];
2656 let needs_surface = child_pic.snapshot.is_some();
2657 if !needs_surface {
2658 child_pic.composite_mode = None;
2659 }
2660 child_pic.context_3d = Picture3DContext::Out;
2661 }
2662
2663 Picture3DContext::Out
2664 };
2665
2666 let pic_index = PictureIndex(self.prim_store.pictures
2668 .alloc()
2669 .init(PicturePrimitive::new_image(
2670 None,
2671 context_3d,
2672 stacking_context.prim_flags,
2673 prim_list,
2674 stacking_context.spatial_node_index,
2675 stacking_context.raster_space,
2676 PictureFlags::empty(),
2677 None,
2678 ))
2679 );
2680
2681 let instance = create_prim_instance(
2682 pic_index,
2683 PictureCompositeKey::Identity,
2684 stacking_context.raster_space,
2685 stacking_context.clip_node_id,
2686 &mut self.interners,
2687 &mut self.clip_tree_builder,
2688 );
2689
2690 source = PictureChainBuilder::from_instance(
2691 instance,
2692 stacking_context.prim_flags,
2693 stacking_context.spatial_node_index,
2694 stacking_context.raster_space,
2695 );
2696 }
2697
2698 let has_filters = stacking_context.composite_ops.has_valid_filters();
2699
2700 let spatial_node_context_offset =
2701 stacking_context.subregion_offset +
2702 self.current_external_scroll_offset(stacking_context.spatial_node_index);
2703 source = self.wrap_prim_with_filters(
2704 source,
2705 stacking_context.clip_node_id,
2706 stacking_context.composite_ops.filters,
2707 stacking_context.composite_ops.filter_primitives,
2708 stacking_context.composite_ops.filter_datas,
2709 false,
2710 spatial_node_context_offset,
2711 );
2712
2713 if let Some(mix_blend_mode) = stacking_context.composite_ops.mix_blend_mode {
2726 let composite_mode = PictureCompositeMode::MixBlend(mix_blend_mode);
2727
2728 source = source.add_picture(
2729 composite_mode,
2730 stacking_context.clip_node_id,
2731 Picture3DContext::Out,
2732 &mut self.interners,
2733 &mut self.prim_store,
2734 &mut self.prim_instances,
2735 &mut self.clip_tree_builder,
2736 );
2737 }
2738
2739 let cur_instance = source.finalize(
2742 stacking_context.clip_node_id,
2743 &mut self.interners,
2744 &mut self.prim_store,
2745 &mut self.clip_tree_builder,
2746 stacking_context.composite_ops.snapshot,
2747 );
2748
2749 if stacking_context.composite_ops.snapshot.is_some() {
2750 let pic_index = cur_instance.kind.as_pic();
2751 self.snapshot_pictures.push(pic_index);
2752 }
2753
2754 let trailing_children_instance = match self.sc_stack.last_mut() {
2757 Some(ref parent_sc) if !has_filters && parent_sc.is_3d() => {
2759 Some(cur_instance)
2760 }
2761 Some(ref mut parent_sc) => {
2763 parent_sc.prim_list.add_prim(
2764 cur_instance,
2765 LayoutRect::zero(),
2766 stacking_context.spatial_node_index,
2767 stacking_context.prim_flags,
2768 &mut self.prim_instances,
2769 &self.clip_tree_builder,
2770 );
2771 None
2772 }
2773 None => {
2775 self.add_primitive_to_draw_list(
2776 cur_instance,
2777 LayoutRect::zero(),
2778 stacking_context.spatial_node_index,
2779 stacking_context.prim_flags,
2780 );
2781
2782 None
2783 }
2784 };
2785
2786 if let Some(instance) = trailing_children_instance {
2789 self.add_primitive_instance_to_3d_root(ExtendedPrimitiveInstance {
2790 instance,
2791 spatial_node_index: stacking_context.spatial_node_index,
2792 flags: stacking_context.prim_flags,
2793 });
2794 }
2795
2796 assert!(
2797 self.pending_shadow_items.is_empty(),
2798 "Found unpopped shadows when popping stacking context!"
2799 );
2800
2801 if info.needs_extra_stacking_context {
2802 let inner_info = self.extra_stacking_context_stack.pop().unwrap();
2803 self.pop_stacking_context(inner_info);
2804 }
2805 }
2806
2807 pub fn push_reference_frame(
2808 &mut self,
2809 reference_frame_id: SpatialId,
2810 parent_index: SpatialNodeIndex,
2811 pipeline_id: PipelineId,
2812 transform_style: TransformStyle,
2813 source_transform: PropertyBinding<LayoutTransform>,
2814 kind: ReferenceFrameKind,
2815 origin_in_parent_reference_frame: LayoutVector2D,
2816 uid: SpatialNodeUid,
2817 ) -> SpatialNodeIndex {
2818 let index = self.spatial_tree.add_reference_frame(
2819 parent_index,
2820 transform_style,
2821 source_transform,
2822 kind,
2823 origin_in_parent_reference_frame,
2824 pipeline_id,
2825 uid,
2826 );
2827 self.id_to_index_mapper_stack.last_mut().unwrap().add_spatial_node(reference_frame_id, index);
2828
2829 index
2830 }
2831
2832 fn push_root(
2833 &mut self,
2834 pipeline_id: PipelineId,
2835 instance: PipelineInstanceId,
2836 ) {
2837 let spatial_node_index = self.push_reference_frame(
2838 SpatialId::root_reference_frame(pipeline_id),
2839 self.spatial_tree.root_reference_frame_index(),
2840 pipeline_id,
2841 TransformStyle::Flat,
2842 PropertyBinding::Value(LayoutTransform::identity()),
2843 ReferenceFrameKind::Transform {
2844 is_2d_scale_translation: true,
2845 should_snap: true,
2846 paired_with_perspective: false,
2847 },
2848 LayoutVector2D::zero(),
2849 SpatialNodeUid::root_reference_frame(pipeline_id, instance),
2850 );
2851
2852 let viewport_rect = LayoutRect::max_rect();
2853
2854 self.add_scroll_frame(
2855 SpatialId::root_scroll_node(pipeline_id),
2856 spatial_node_index,
2857 ExternalScrollId(0, pipeline_id),
2858 pipeline_id,
2859 &viewport_rect,
2860 &viewport_rect.size(),
2861 ScrollFrameKind::PipelineRoot {
2862 is_root_pipeline: true,
2863 },
2864 LayoutVector2D::zero(),
2865 APZScrollGeneration::default(),
2866 HasScrollLinkedEffect::No,
2867 SpatialNodeUid::root_scroll_frame(pipeline_id, instance),
2868 );
2869 }
2870
2871 fn add_image_mask_clip_node(
2872 &mut self,
2873 new_node_id: ClipId,
2874 spatial_id: SpatialId,
2875 image_mask: &ImageMask,
2876 fill_rule: FillRule,
2877 points_range: ItemRange<LayoutPoint>,
2878 ) {
2879 let spatial_node_index = self.get_space(spatial_id);
2880
2881 let snapped_mask_rect = self.normalize_scroll_offset_and_snap_rect(
2882 &image_mask.rect,
2883 spatial_node_index,
2884 );
2885
2886 let points: Vec<LayoutPoint> = points_range.iter().collect();
2887
2888 let mut polygon_handle: Option<PolygonDataHandle> = None;
2890 if points.len() > 0 {
2891 let item = PolygonKey::new(&points, fill_rule);
2892
2893 let handle = self
2894 .interners
2895 .polygon
2896 .intern(&item, || item);
2897 polygon_handle = Some(handle);
2898 }
2899
2900 let item = ClipItemKey {
2901 kind: ClipItemKeyKind::image_mask(image_mask, snapped_mask_rect, polygon_handle),
2902 spatial_node_index,
2903 };
2904
2905 let handle = self
2906 .interners
2907 .clip
2908 .intern(&item, || {
2909 ClipInternData {
2910 key: item,
2911 }
2912 });
2913
2914 self.clip_tree_builder.define_image_mask_clip(
2915 new_node_id,
2916 handle,
2917 );
2918 }
2919
2920 fn add_rect_clip_node(
2922 &mut self,
2923 new_node_id: ClipId,
2924 spatial_id: SpatialId,
2925 clip_rect: &LayoutRect,
2926 ) {
2927 let spatial_node_index = self.get_space(spatial_id);
2928
2929 let snapped_clip_rect = self.normalize_scroll_offset_and_snap_rect(
2930 clip_rect,
2931 spatial_node_index,
2932 );
2933
2934 let item = ClipItemKey {
2935 kind: ClipItemKeyKind::rectangle(snapped_clip_rect, ClipMode::Clip),
2936 spatial_node_index,
2937 };
2938 let handle = self
2939 .interners
2940 .clip
2941 .intern(&item, || {
2942 ClipInternData {
2943 key: item,
2944 }
2945 });
2946
2947 self.clip_tree_builder.define_rect_clip(
2948 new_node_id,
2949 handle,
2950 );
2951 }
2952
2953 fn add_rounded_rect_clip_node(
2954 &mut self,
2955 new_node_id: ClipId,
2956 spatial_id: SpatialId,
2957 clip: &ComplexClipRegion,
2958 ) {
2959 let spatial_node_index = self.get_space(spatial_id);
2960
2961 let snapped_region_rect = self.normalize_scroll_offset_and_snap_rect(
2962 &clip.rect,
2963 spatial_node_index,
2964 );
2965
2966 let item = ClipItemKey {
2967 kind: ClipItemKeyKind::rounded_rect(
2968 snapped_region_rect,
2969 clip.radii,
2970 clip.mode,
2971 ),
2972 spatial_node_index,
2973 };
2974
2975 let handle = self
2976 .interners
2977 .clip
2978 .intern(&item, || {
2979 ClipInternData {
2980 key: item,
2981 }
2982 });
2983
2984 self.clip_tree_builder.define_rounded_rect_clip(
2985 new_node_id,
2986 handle,
2987 );
2988 }
2989
2990 pub fn add_scroll_frame(
2991 &mut self,
2992 new_node_id: SpatialId,
2993 parent_node_index: SpatialNodeIndex,
2994 external_id: ExternalScrollId,
2995 pipeline_id: PipelineId,
2996 frame_rect: &LayoutRect,
2997 content_size: &LayoutSize,
2998 frame_kind: ScrollFrameKind,
2999 external_scroll_offset: LayoutVector2D,
3000 scroll_offset_generation: APZScrollGeneration,
3001 has_scroll_linked_effect: HasScrollLinkedEffect,
3002 uid: SpatialNodeUid,
3003 ) -> SpatialNodeIndex {
3004 let node_index = self.spatial_tree.add_scroll_frame(
3005 parent_node_index,
3006 external_id,
3007 pipeline_id,
3008 frame_rect,
3009 content_size,
3010 frame_kind,
3011 external_scroll_offset,
3012 scroll_offset_generation,
3013 has_scroll_linked_effect,
3014 uid,
3015 );
3016 self.id_to_index_mapper_stack.last_mut().unwrap().add_spatial_node(new_node_id, node_index);
3017 node_index
3018 }
3019
3020 pub fn push_shadow(
3021 &mut self,
3022 shadow: Shadow,
3023 spatial_node_index: SpatialNodeIndex,
3024 clip_chain_id: api::ClipChainId,
3025 should_inflate: bool,
3026 ) {
3027 self.clip_tree_builder.push_clip_chain(Some(clip_chain_id), false, false);
3028
3029 self.pending_shadow_items.push_back(ShadowItem::Shadow(PendingShadow {
3032 shadow,
3033 spatial_node_index,
3034 should_inflate,
3035 }));
3036 }
3037
3038 pub fn pop_all_shadows(
3039 &mut self,
3040 ) {
3041 assert!(!self.pending_shadow_items.is_empty(), "popped shadows, but none were present");
3042
3043 let mut items = mem::replace(&mut self.pending_shadow_items, VecDeque::new());
3044
3045 while let Some(item) = items.pop_front() {
3059 match item {
3060 ShadowItem::Shadow(pending_shadow) => {
3061 let std_deviation = pending_shadow.shadow.blur_radius * 0.5;
3065
3066 let mut prim_list = PrimitiveList::empty();
3069 let blur_filter = Filter::Blur {
3070 width: std_deviation,
3071 height: std_deviation,
3072 should_inflate: pending_shadow.should_inflate,
3073 edge_mode: BlurEdgeMode::Duplicate,
3074 };
3075 let blur_is_noop = blur_filter.is_noop();
3076
3077 for item in &items {
3078 let (instance, info, spatial_node_index) = match item {
3079 ShadowItem::Image(ref pending_image) => {
3080 self.create_shadow_prim(
3081 &pending_shadow,
3082 pending_image,
3083 blur_is_noop,
3084 )
3085 }
3086 ShadowItem::LineDecoration(ref pending_line_dec) => {
3087 self.create_shadow_prim(
3088 &pending_shadow,
3089 pending_line_dec,
3090 blur_is_noop,
3091 )
3092 }
3093 ShadowItem::NormalBorder(ref pending_border) => {
3094 self.create_shadow_prim(
3095 &pending_shadow,
3096 pending_border,
3097 blur_is_noop,
3098 )
3099 }
3100 ShadowItem::Primitive(ref pending_primitive) => {
3101 self.create_shadow_prim(
3102 &pending_shadow,
3103 pending_primitive,
3104 blur_is_noop,
3105 )
3106 }
3107 ShadowItem::TextRun(ref pending_text_run) => {
3108 self.create_shadow_prim(
3109 &pending_shadow,
3110 pending_text_run,
3111 blur_is_noop,
3112 )
3113 }
3114 _ => {
3115 continue;
3116 }
3117 };
3118
3119 if blur_is_noop {
3120 self.add_primitive_to_draw_list(
3121 instance,
3122 info.rect,
3123 spatial_node_index,
3124 info.flags,
3125 );
3126 } else {
3127 prim_list.add_prim(
3128 instance,
3129 info.rect,
3130 spatial_node_index,
3131 info.flags,
3132 &mut self.prim_instances,
3133 &self.clip_tree_builder,
3134 );
3135 }
3136 }
3137
3138 if !prim_list.is_empty() {
3141 assert!(!blur_filter.is_noop());
3146 let composite_mode = Some(PictureCompositeMode::Filter(blur_filter));
3147 let composite_mode_key = composite_mode.clone().into();
3148 let raster_space = RasterSpace::Screen;
3149
3150 let shadow_pic_index = PictureIndex(self.prim_store.pictures
3152 .alloc()
3153 .init(PicturePrimitive::new_image(
3154 composite_mode,
3155 Picture3DContext::Out,
3156 PrimitiveFlags::IS_BACKFACE_VISIBLE,
3157 prim_list,
3158 pending_shadow.spatial_node_index,
3159 raster_space,
3160 PictureFlags::empty(),
3161 None,
3162 ))
3163 );
3164
3165 let shadow_pic_key = PictureKey::new(
3166 Picture { composite_mode_key, raster_space },
3167 );
3168
3169 let shadow_prim_data_handle = self.interners
3170 .picture
3171 .intern(&shadow_pic_key, || ());
3172
3173 let clip_node_id = self.clip_tree_builder.build_clip_set(api::ClipChainId::INVALID);
3174
3175 let shadow_prim_instance = PrimitiveInstance::new(
3176 PrimitiveInstanceKind::Picture {
3177 data_handle: shadow_prim_data_handle,
3178 pic_index: shadow_pic_index,
3179 },
3180 self.clip_tree_builder.build_for_picture(clip_node_id),
3181 );
3182
3183 self.add_primitive_to_draw_list(
3186 shadow_prim_instance,
3187 LayoutRect::zero(),
3188 pending_shadow.spatial_node_index,
3189 PrimitiveFlags::IS_BACKFACE_VISIBLE,
3190 );
3191 }
3192
3193 self.clip_tree_builder.pop_clip();
3194 }
3195 ShadowItem::Image(pending_image) => {
3196 self.add_shadow_prim_to_draw_list(
3197 pending_image,
3198 )
3199 },
3200 ShadowItem::LineDecoration(pending_line_dec) => {
3201 self.add_shadow_prim_to_draw_list(
3202 pending_line_dec,
3203 )
3204 },
3205 ShadowItem::NormalBorder(pending_border) => {
3206 self.add_shadow_prim_to_draw_list(
3207 pending_border,
3208 )
3209 },
3210 ShadowItem::Primitive(pending_primitive) => {
3211 self.add_shadow_prim_to_draw_list(
3212 pending_primitive,
3213 )
3214 },
3215 ShadowItem::TextRun(pending_text_run) => {
3216 self.add_shadow_prim_to_draw_list(
3217 pending_text_run,
3218 )
3219 },
3220 }
3221 }
3222
3223 debug_assert!(items.is_empty());
3224 self.pending_shadow_items = items;
3225 }
3226
3227 fn create_shadow_prim<P>(
3228 &mut self,
3229 pending_shadow: &PendingShadow,
3230 pending_primitive: &PendingPrimitive<P>,
3231 blur_is_noop: bool,
3232 ) -> (PrimitiveInstance, LayoutPrimitiveInfo, SpatialNodeIndex)
3233 where
3234 P: InternablePrimitive + CreateShadow,
3235 Interners: AsMut<Interner<P>>,
3236 {
3237 let mut info = pending_primitive.info.clone();
3243 info.rect = info.rect.translate(pending_shadow.shadow.offset);
3244 info.clip_rect = info.clip_rect.translate(pending_shadow.shadow.offset);
3245
3246 let clip_set = self.clip_tree_builder.build_for_prim(
3247 pending_primitive.clip_node_id,
3248 &info,
3249 &[],
3250 &mut self.interners,
3251 );
3252
3253 let shadow_prim_instance = self.create_primitive(
3255 &info,
3256 clip_set,
3257 pending_primitive.prim.create_shadow(
3258 &pending_shadow.shadow,
3259 blur_is_noop,
3260 self.raster_space_stack.last().cloned().unwrap(),
3261 ),
3262 );
3263
3264 (shadow_prim_instance, info, pending_primitive.spatial_node_index)
3265 }
3266
3267 fn add_shadow_prim_to_draw_list<P>(
3268 &mut self,
3269 pending_primitive: PendingPrimitive<P>,
3270 ) where
3271 P: InternablePrimitive + IsVisible,
3272 Interners: AsMut<Interner<P>>,
3273 {
3274 if pending_primitive.prim.is_visible() {
3277 let clip_set = self.clip_tree_builder.build_for_prim(
3278 pending_primitive.clip_node_id,
3279 &pending_primitive.info,
3280 &[],
3281 &mut self.interners,
3282 );
3283
3284 self.add_prim_to_draw_list(
3285 &pending_primitive.info,
3286 pending_primitive.spatial_node_index,
3287 clip_set,
3288 pending_primitive.prim,
3289 );
3290 }
3291 }
3292
3293 pub fn add_clear_rectangle(
3294 &mut self,
3295 spatial_node_index: SpatialNodeIndex,
3296 clip_node_id: ClipNodeId,
3297 info: &LayoutPrimitiveInfo,
3298 ) {
3299 self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
3302
3303 self.add_primitive(
3304 spatial_node_index,
3305 clip_node_id,
3306 info,
3307 Vec::new(),
3308 PrimitiveKeyKind::Clear,
3309 );
3310
3311 self.add_tile_cache_barrier_if_needed(SliceFlags::empty());
3312 }
3313
3314 pub fn add_line(
3315 &mut self,
3316 spatial_node_index: SpatialNodeIndex,
3317 clip_node_id: ClipNodeId,
3318 info: &LayoutPrimitiveInfo,
3319 wavy_line_thickness: f32,
3320 orientation: LineOrientation,
3321 color: ColorF,
3322 style: LineStyle,
3323 ) {
3324 let size = get_line_decoration_size(
3328 &info.rect.size(),
3329 orientation,
3330 style,
3331 wavy_line_thickness,
3332 );
3333
3334 let cache_key = size.map(|size| {
3335 LineDecorationCacheKey {
3336 style,
3337 orientation,
3338 wavy_line_thickness: Au::from_f32_px(wavy_line_thickness),
3339 size: size.to_au(),
3340 }
3341 });
3342
3343 self.add_primitive(
3344 spatial_node_index,
3345 clip_node_id,
3346 &info,
3347 Vec::new(),
3348 LineDecoration {
3349 cache_key,
3350 color: color.into(),
3351 },
3352 );
3353 }
3354
3355 pub fn add_border(
3356 &mut self,
3357 spatial_node_index: SpatialNodeIndex,
3358 clip_node_id: ClipNodeId,
3359 info: &LayoutPrimitiveInfo,
3360 border_item: &BorderDisplayItem,
3361 gradient_stops: ItemRange<GradientStop>,
3362 ) {
3363 match border_item.details {
3364 BorderDetails::NinePatch(ref border) => {
3365 let nine_patch = NinePatchDescriptor {
3366 width: border.width,
3367 height: border.height,
3368 slice: border.slice,
3369 fill: border.fill,
3370 repeat_horizontal: border.repeat_horizontal,
3371 repeat_vertical: border.repeat_vertical,
3372 widths: border_item.widths.into(),
3373 };
3374
3375 match border.source {
3376 NinePatchBorderSource::Image(key, rendering) => {
3377 let prim = ImageBorder {
3378 request: ImageRequest {
3379 key,
3380 rendering,
3381 tile: None,
3382 },
3383 nine_patch,
3384 };
3385
3386 self.add_nonshadowable_primitive(
3387 spatial_node_index,
3388 clip_node_id,
3389 info,
3390 Vec::new(),
3391 prim,
3392 );
3393 }
3394 NinePatchBorderSource::Gradient(gradient) => {
3395 let prim = match self.create_linear_gradient_prim(
3396 &info,
3397 gradient.start_point,
3398 gradient.end_point,
3399 read_gradient_stops(gradient_stops),
3400 gradient.extend_mode,
3401 LayoutSize::new(border.height as f32, border.width as f32),
3402 LayoutSize::zero(),
3403 Some(Box::new(nine_patch)),
3404 EdgeAaSegmentMask::all(),
3405 ) {
3406 Some(prim) => prim,
3407 None => return,
3408 };
3409
3410 self.add_nonshadowable_primitive(
3411 spatial_node_index,
3412 clip_node_id,
3413 info,
3414 Vec::new(),
3415 prim,
3416 );
3417 }
3418 NinePatchBorderSource::RadialGradient(gradient) => {
3419 let prim = self.create_radial_gradient_prim(
3420 &info,
3421 gradient.center,
3422 gradient.start_offset * gradient.radius.width,
3423 gradient.end_offset * gradient.radius.width,
3424 gradient.radius.width / gradient.radius.height,
3425 read_gradient_stops(gradient_stops),
3426 gradient.extend_mode,
3427 LayoutSize::new(border.height as f32, border.width as f32),
3428 LayoutSize::zero(),
3429 Some(Box::new(nine_patch)),
3430 );
3431
3432 self.add_nonshadowable_primitive(
3433 spatial_node_index,
3434 clip_node_id,
3435 info,
3436 Vec::new(),
3437 prim,
3438 );
3439 }
3440 NinePatchBorderSource::ConicGradient(gradient) => {
3441 let prim = self.create_conic_gradient_prim(
3442 &info,
3443 gradient.center,
3444 gradient.angle,
3445 gradient.start_offset,
3446 gradient.end_offset,
3447 gradient_stops,
3448 gradient.extend_mode,
3449 LayoutSize::new(border.height as f32, border.width as f32),
3450 LayoutSize::zero(),
3451 Some(Box::new(nine_patch)),
3452 );
3453
3454 self.add_nonshadowable_primitive(
3455 spatial_node_index,
3456 clip_node_id,
3457 info,
3458 Vec::new(),
3459 prim,
3460 );
3461 }
3462 };
3463 }
3464 BorderDetails::Normal(ref border) => {
3465 self.add_normal_border(
3466 info,
3467 border,
3468 border_item.widths,
3469 spatial_node_index,
3470 clip_node_id,
3471 );
3472 }
3473 }
3474 }
3475
3476 pub fn create_linear_gradient_prim(
3477 &mut self,
3478 info: &LayoutPrimitiveInfo,
3479 start_point: LayoutPoint,
3480 end_point: LayoutPoint,
3481 stops: Vec<GradientStopKey>,
3482 extend_mode: ExtendMode,
3483 stretch_size: LayoutSize,
3484 mut tile_spacing: LayoutSize,
3485 nine_patch: Option<Box<NinePatchDescriptor>>,
3486 edge_aa_mask: EdgeAaSegmentMask,
3487 ) -> Option<LinearGradient> {
3488 let mut prim_rect = info.rect;
3489 simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3490
3491 let mut has_hard_stops = false;
3492 let mut is_entirely_transparent = true;
3493 let mut prev_stop = None;
3494 for stop in &stops {
3495 if Some(stop.offset) == prev_stop {
3496 has_hard_stops = true;
3497 }
3498 prev_stop = Some(stop.offset);
3499 if stop.color.a > 0 {
3500 is_entirely_transparent = false;
3501 }
3502 }
3503
3504 if is_entirely_transparent {
3507 return None;
3508 }
3509
3510 let reverse_stops = start_point.x > end_point.x ||
3517 (start_point.x == end_point.x && start_point.y > end_point.y);
3518
3519 let (sp, ep) = if reverse_stops {
3523 (end_point, start_point)
3524 } else {
3525 (start_point, end_point)
3526 };
3527
3528 let max = gradient::LINEAR_MAX_CACHED_SIZE;
3532 let caching_causes_artifacts = has_hard_stops && (stretch_size.width > max || stretch_size.height > max);
3533
3534 let is_tiled = prim_rect.width() > stretch_size.width
3535 || prim_rect.height() > stretch_size.height;
3536 let cached = (!self.config.is_software || is_tiled) && !caching_causes_artifacts;
3540
3541 Some(LinearGradient {
3542 extend_mode,
3543 start_point: sp.into(),
3544 end_point: ep.into(),
3545 stretch_size: stretch_size.into(),
3546 tile_spacing: tile_spacing.into(),
3547 stops,
3548 reverse_stops,
3549 nine_patch,
3550 cached,
3551 edge_aa_mask,
3552 enable_dithering: self.config.enable_dithering,
3553 })
3554 }
3555
3556 pub fn create_radial_gradient_prim(
3557 &mut self,
3558 info: &LayoutPrimitiveInfo,
3559 center: LayoutPoint,
3560 start_radius: f32,
3561 end_radius: f32,
3562 ratio_xy: f32,
3563 stops: Vec<GradientStopKey>,
3564 extend_mode: ExtendMode,
3565 stretch_size: LayoutSize,
3566 mut tile_spacing: LayoutSize,
3567 nine_patch: Option<Box<NinePatchDescriptor>>,
3568 ) -> RadialGradient {
3569 let mut prim_rect = info.rect;
3570 simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3571
3572 let params = RadialGradientParams {
3573 start_radius,
3574 end_radius,
3575 ratio_xy,
3576 };
3577
3578 RadialGradient {
3579 extend_mode,
3580 center: center.into(),
3581 params,
3582 stretch_size: stretch_size.into(),
3583 tile_spacing: tile_spacing.into(),
3584 nine_patch,
3585 stops,
3586 }
3587 }
3588
3589 pub fn create_conic_gradient_prim(
3590 &mut self,
3591 info: &LayoutPrimitiveInfo,
3592 center: LayoutPoint,
3593 angle: f32,
3594 start_offset: f32,
3595 end_offset: f32,
3596 stops: ItemRange<GradientStop>,
3597 extend_mode: ExtendMode,
3598 stretch_size: LayoutSize,
3599 mut tile_spacing: LayoutSize,
3600 nine_patch: Option<Box<NinePatchDescriptor>>,
3601 ) -> ConicGradient {
3602 let mut prim_rect = info.rect;
3603 simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3604
3605 let stops = stops.iter().map(|stop| {
3606 GradientStopKey {
3607 offset: stop.offset,
3608 color: stop.color.into(),
3609 }
3610 }).collect();
3611
3612 ConicGradient {
3613 extend_mode,
3614 center: center.into(),
3615 params: ConicGradientParams { angle, start_offset, end_offset },
3616 stretch_size: stretch_size.into(),
3617 tile_spacing: tile_spacing.into(),
3618 nine_patch,
3619 stops,
3620 }
3621 }
3622
3623 pub fn add_text(
3624 &mut self,
3625 spatial_node_index: SpatialNodeIndex,
3626 clip_node_id: ClipNodeId,
3627 prim_info: &LayoutPrimitiveInfo,
3628 font_instance_key: &FontInstanceKey,
3629 text_color: &ColorF,
3630 glyph_range: ItemRange<GlyphInstance>,
3631 glyph_options: Option<GlyphOptions>,
3632 ref_frame_offset: LayoutVector2D,
3633 ) {
3634 let offset = self.current_external_scroll_offset(spatial_node_index) + ref_frame_offset;
3635
3636 let text_run = {
3637 let shared_key = self.fonts.instance_keys.map_key(font_instance_key);
3638 let font_instance = match self.fonts.instances.get_font_instance(shared_key) {
3639 Some(instance) => instance,
3640 None => {
3641 warn!("Unknown font instance key");
3642 debug!("key={:?} shared={:?}", font_instance_key, shared_key);
3643 return;
3644 }
3645 };
3646
3647 if font_instance.size <= FontSize::zero() {
3649 return;
3650 }
3651
3652 let mut render_mode = self.config
3656 .default_font_render_mode
3657 .limit_by(font_instance.render_mode);
3658 let mut flags = font_instance.flags;
3659 if let Some(options) = glyph_options {
3660 render_mode = render_mode.limit_by(options.render_mode);
3661 flags |= options.flags;
3662 }
3663
3664 let font = FontInstance::new(
3665 font_instance,
3666 (*text_color).into(),
3667 render_mode,
3668 flags,
3669 );
3670
3671 let prim_offset = prim_info.rect.min.to_vector() - offset;
3676 let glyphs = glyph_range
3677 .iter()
3678 .map(|glyph| {
3679 GlyphInstance {
3680 index: glyph.index,
3681 point: glyph.point - prim_offset,
3682 }
3683 })
3684 .collect();
3685
3686 let requested_raster_space = self.raster_space_stack
3689 .last()
3690 .cloned()
3691 .unwrap();
3692
3693 TextRun {
3694 glyphs,
3695 font,
3696 shadow: false,
3697 requested_raster_space,
3698 reference_frame_offset: ref_frame_offset,
3699 }
3700 };
3701
3702 self.add_primitive(
3703 spatial_node_index,
3704 clip_node_id,
3705 prim_info,
3706 Vec::new(),
3707 text_run,
3708 );
3709 }
3710
3711 pub fn add_image(
3712 &mut self,
3713 spatial_node_index: SpatialNodeIndex,
3714 clip_node_id: ClipNodeId,
3715 info: &LayoutPrimitiveInfo,
3716 stretch_size: LayoutSize,
3717 mut tile_spacing: LayoutSize,
3718 image_key: ImageKey,
3719 image_rendering: ImageRendering,
3720 alpha_type: AlphaType,
3721 color: ColorF,
3722 ) {
3723 let mut prim_rect = info.rect;
3724 simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
3725 let info = LayoutPrimitiveInfo {
3726 rect: prim_rect,
3727 .. *info
3728 };
3729
3730 self.add_primitive(
3731 spatial_node_index,
3732 clip_node_id,
3733 &info,
3734 Vec::new(),
3735 Image {
3736 key: image_key,
3737 tile_spacing: tile_spacing.into(),
3738 stretch_size: stretch_size.into(),
3739 color: color.into(),
3740 image_rendering,
3741 alpha_type,
3742 },
3743 );
3744 }
3745
3746 pub fn add_yuv_image(
3747 &mut self,
3748 spatial_node_index: SpatialNodeIndex,
3749 clip_node_id: ClipNodeId,
3750 info: &LayoutPrimitiveInfo,
3751 yuv_data: YuvData,
3752 color_depth: ColorDepth,
3753 color_space: YuvColorSpace,
3754 color_range: ColorRange,
3755 image_rendering: ImageRendering,
3756 ) {
3757 let format = yuv_data.get_format();
3758 let yuv_key = match yuv_data {
3759 YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
3760 YuvData::P010(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
3761 YuvData::NV16(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
3762 YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) => [plane_0, plane_1, plane_2],
3763 YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY],
3764 };
3765
3766 self.add_nonshadowable_primitive(
3767 spatial_node_index,
3768 clip_node_id,
3769 info,
3770 Vec::new(),
3771 YuvImage {
3772 color_depth,
3773 yuv_key,
3774 format,
3775 color_space,
3776 color_range,
3777 image_rendering,
3778 },
3779 );
3780 }
3781
3782 fn add_primitive_instance_to_3d_root(
3783 &mut self,
3784 prim: ExtendedPrimitiveInstance,
3785 ) {
3786 for sc in self.sc_stack.iter_mut().rev() {
3788 match sc.context_3d {
3789 Picture3DContext::In { root_data: Some(ref mut prims), .. } => {
3790 prims.push(prim);
3791 break;
3792 }
3793 Picture3DContext::In { .. } => {}
3794 Picture3DContext::Out => panic!("Unable to find 3D root"),
3795 }
3796 }
3797 }
3798
3799 #[allow(dead_code)]
3800 pub fn add_backdrop_filter(
3801 &mut self,
3802 spatial_node_index: SpatialNodeIndex,
3803 clip_node_id: ClipNodeId,
3804 info: &LayoutPrimitiveInfo,
3805 filters: Vec<Filter>,
3806 filter_datas: Vec<FilterData>,
3807 filter_primitives: Vec<FilterPrimitive>,
3808 ) {
3809 let filter_spatial_node_index = SpatialNodeIndex::UNKNOWN;
3813
3814 self.make_current_slice_atomic_if_required();
3815
3816 let clip_leaf_id = self.clip_tree_builder.build_for_prim(
3820 clip_node_id,
3821 info,
3822 &[],
3823 &mut self.interners,
3824 );
3825
3826 let backdrop_capture_instance = self.create_primitive(
3829 info,
3830 clip_leaf_id,
3831 BackdropCapture {
3832 },
3833 );
3834
3835 let mut prim_list = PrimitiveList::empty();
3838 prim_list.add_prim(
3839 backdrop_capture_instance,
3840 info.rect,
3841 spatial_node_index,
3842 info.flags,
3843 &mut self.prim_instances,
3844 &self.clip_tree_builder,
3845 );
3846
3847 let mut source = PictureChainBuilder::from_prim_list(
3848 prim_list,
3849 info.flags,
3850 filter_spatial_node_index,
3851 RasterSpace::Screen,
3852 true,
3853 );
3854
3855 source = self.wrap_prim_with_filters(
3858 source,
3859 clip_node_id,
3860 filters,
3861 filter_primitives,
3862 filter_datas,
3863 true,
3864 LayoutVector2D::zero(),
3865 );
3866
3867 if source.has_picture() {
3870 source = source.add_picture(
3871 PictureCompositeMode::IntermediateSurface,
3872 clip_node_id,
3873 Picture3DContext::Out,
3874 &mut self.interners,
3875 &mut self.prim_store,
3876 &mut self.prim_instances,
3877 &mut self.clip_tree_builder,
3878 );
3879
3880 let filtered_instance = source.finalize(
3881 clip_node_id,
3882 &mut self.interners,
3883 &mut self.prim_store,
3884 &mut self.clip_tree_builder,
3885 None,
3886 );
3887
3888 let output_pic_index = match filtered_instance.kind {
3891 PrimitiveInstanceKind::Picture { pic_index, .. } => pic_index,
3892 _ => panic!("bug: not a picture"),
3893 };
3894
3895 let sc_index = self.sc_stack.iter().rposition(|sc| {
3898 !sc.flags.contains(StackingContextFlags::WRAPS_BACKDROP_FILTER)
3899 });
3900
3901 match sc_index {
3902 Some(sc_index) => {
3903 self.sc_stack[sc_index].prim_list.add_prim(
3904 filtered_instance,
3905 info.rect,
3906 filter_spatial_node_index,
3907 info.flags,
3908 &mut self.prim_instances,
3909 &self.clip_tree_builder,
3910 );
3911 }
3912 None => {
3913 self.tile_cache_builder.add_prim(
3914 filtered_instance,
3915 info.rect,
3916 filter_spatial_node_index,
3917 info.flags,
3918 self.spatial_tree,
3919 self.interners,
3920 &self.quality_settings,
3921 &mut self.prim_instances,
3922 &self.clip_tree_builder,
3923 );
3924 }
3925 }
3926
3927 let mut backdrop_render_instance = self.create_primitive(
3929 info,
3930 clip_leaf_id,
3931 BackdropRender {
3932 },
3933 );
3934
3935 match backdrop_render_instance.kind {
3938 PrimitiveInstanceKind::BackdropRender { ref mut pic_index, .. } => {
3939 assert_eq!(*pic_index, PictureIndex::INVALID);
3940 *pic_index = output_pic_index;
3941 }
3942 _ => panic!("bug: unexpected prim kind"),
3943 }
3944
3945 self.add_primitive_to_draw_list(
3946 backdrop_render_instance,
3947 info.rect,
3948 spatial_node_index,
3949 info.flags,
3950 );
3951 }
3952 }
3953
3954 #[must_use]
3955 fn wrap_prim_with_filters(
3956 &mut self,
3957 mut source: PictureChainBuilder,
3958 clip_node_id: ClipNodeId,
3959 mut filter_ops: Vec<Filter>,
3960 mut filter_primitives: Vec<FilterPrimitive>,
3961 filter_datas: Vec<FilterData>,
3962 is_backdrop_filter: bool,
3963 context_offset: LayoutVector2D,
3964 ) -> PictureChainBuilder {
3965 assert!(filter_ops.is_empty() || filter_primitives.is_empty(),
3969 "Filter ops and filter primitives are not allowed on the same stacking context.");
3970
3971 let mut current_filter_data_index = 0;
3973 if let Some(Filter::SVGGraphNode(..)) = filter_ops.first() {
3978 const BUFFER_LIMIT: usize = SVGFE_GRAPH_MAX;
3987 const SVGFE_INFLATE: i16 = 1;
3990
3991 let mut reference_for_buffer_id: [FilterGraphPictureReference; BUFFER_LIMIT] = [
4011 FilterGraphPictureReference{
4012 buffer_id: FilterOpGraphPictureBufferId::BufferId(-1),
4016 subregion: LayoutRect::zero(), offset: LayoutVector2D::zero(),
4018 inflate: 0,
4019 source_padding: LayoutRect::zero(),
4020 target_padding: LayoutRect::zero(),
4021 }; BUFFER_LIMIT];
4022 let mut filters: Vec<(FilterGraphNode, FilterGraphOp)> = Vec::new();
4023 filters.reserve(BUFFER_LIMIT);
4024 for (original_id, parsefilter) in filter_ops.iter().enumerate() {
4025 if filters.len() >= BUFFER_LIMIT {
4026 return source;
4029 }
4030
4031 let newfilter = match parsefilter {
4032 Filter::SVGGraphNode(parsenode, op) => {
4033 let clip_region = parsenode.subregion
4037 .translate(context_offset);
4038
4039 let mut newnode = FilterGraphNode {
4040 kept_by_optimizer: false,
4041 linear: parsenode.linear,
4042 inflate: SVGFE_INFLATE,
4043 inputs: Vec::new(),
4044 subregion: clip_region,
4045 };
4046
4047 let mut remapped_inputs: Vec<FilterGraphPictureReference> = Vec::new();
4050 remapped_inputs.reserve_exact(parsenode.inputs.len());
4051 for input in &parsenode.inputs {
4052 match input.buffer_id {
4053 FilterOpGraphPictureBufferId::BufferId(buffer_id) => {
4054 let pic = *reference_for_buffer_id
4057 .get(buffer_id as usize)
4058 .expect("BufferId not valid?");
4059 let offset = input.offset;
4064 let subregion = pic.subregion
4065 .translate(offset);
4066 let source_padding = LayoutRect::zero()
4067 .translate(-offset);
4068 let target_padding = LayoutRect::zero()
4069 .translate(offset);
4070 remapped_inputs.push(
4071 FilterGraphPictureReference {
4072 buffer_id: pic.buffer_id,
4073 subregion,
4074 offset,
4075 inflate: pic.inflate,
4076 source_padding,
4077 target_padding,
4078 });
4079 }
4080 FilterOpGraphPictureBufferId::None => panic!("Unsupported FilterOpGraphPictureBufferId"),
4081 }
4082 }
4083
4084 fn union_unchecked(a: LayoutRect, b: LayoutRect) -> LayoutRect {
4085 let mut r = a;
4086 if r.min.x > b.min.x {r.min.x = b.min.x}
4087 if r.min.y > b.min.y {r.min.y = b.min.y}
4088 if r.max.x < b.max.x {r.max.x = b.max.x}
4089 if r.max.y < b.max.y {r.max.y = b.max.y}
4090 r
4091 }
4092
4093 match op {
4094 FilterGraphOp::SVGFEFlood{..} |
4095 FilterGraphOp::SVGFESourceAlpha |
4096 FilterGraphOp::SVGFESourceGraphic |
4097 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} |
4098 FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} |
4099 FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} |
4100 FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {
4101 assert!(remapped_inputs.len() == 0);
4102 (newnode.clone(), op.clone())
4103 }
4104 FilterGraphOp::SVGFEColorMatrix{..} |
4105 FilterGraphOp::SVGFEIdentity |
4106 FilterGraphOp::SVGFEImage{..} |
4107 FilterGraphOp::SVGFEOpacity{..} |
4108 FilterGraphOp::SVGFEToAlpha => {
4109 assert!(remapped_inputs.len() == 1);
4110 newnode.inputs = remapped_inputs;
4111 (newnode.clone(), op.clone())
4112 }
4113 FilterGraphOp::SVGFEComponentTransfer => {
4114 assert!(remapped_inputs.len() == 1);
4115 let filter_data =
4117 &filter_datas[current_filter_data_index];
4118 let filter_data = filter_data.sanitize();
4119 current_filter_data_index = current_filter_data_index + 1;
4120
4121 let creates_pixels =
4136 if let Some(a) = filter_data.r_values.get(3) {
4137 *a >= (0.5/255.0)
4138 } else {
4139 false
4140 };
4141 let filter_data_key = SFilterDataKey {
4142 data:
4143 SFilterData {
4144 r_func: SFilterDataComponent::from_functype_values(
4145 filter_data.func_r_type, &filter_data.r_values),
4146 g_func: SFilterDataComponent::from_functype_values(
4147 filter_data.func_g_type, &filter_data.g_values),
4148 b_func: SFilterDataComponent::from_functype_values(
4149 filter_data.func_b_type, &filter_data.b_values),
4150 a_func: SFilterDataComponent::from_functype_values(
4151 filter_data.func_a_type, &filter_data.a_values),
4152 },
4153 };
4154
4155 let handle = self.interners
4156 .filter_data
4157 .intern(&filter_data_key, || ());
4158
4159 newnode.inputs = remapped_inputs;
4160 (newnode.clone(), FilterGraphOp::SVGFEComponentTransferInterned{handle, creates_pixels})
4161 }
4162 FilterGraphOp::SVGFEComponentTransferInterned{..} => unreachable!(),
4163 FilterGraphOp::SVGFETile => {
4164 assert!(remapped_inputs.len() == 1);
4165 remapped_inputs[0].source_padding =
4167 LayoutRect::max_rect();
4168 remapped_inputs[0].target_padding =
4169 LayoutRect::max_rect();
4170 newnode.inputs = remapped_inputs;
4171 (newnode.clone(), op.clone())
4172 }
4173 FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{kernel_unit_length_x, kernel_unit_length_y, ..} |
4174 FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{kernel_unit_length_x, kernel_unit_length_y, ..} |
4175 FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{kernel_unit_length_x, kernel_unit_length_y, ..} |
4176 FilterGraphOp::SVGFEMorphologyDilate{radius_x: kernel_unit_length_x, radius_y: kernel_unit_length_y} => {
4177 assert!(remapped_inputs.len() == 1);
4178 let padding = LayoutSize::new(
4179 kernel_unit_length_x.ceil(),
4180 kernel_unit_length_y.ceil(),
4181 );
4182 remapped_inputs[0].source_padding =
4185 remapped_inputs[0].source_padding
4186 .inflate(padding.width, padding.height);
4187 remapped_inputs[0].target_padding =
4190 remapped_inputs[0].target_padding
4191 .inflate(padding.width, padding.height);
4192 newnode.inputs = remapped_inputs;
4193 (newnode.clone(), op.clone())
4194 },
4195 FilterGraphOp::SVGFEDiffuseLightingDistant{kernel_unit_length_x, kernel_unit_length_y, ..} |
4196 FilterGraphOp::SVGFEDiffuseLightingPoint{kernel_unit_length_x, kernel_unit_length_y, ..} |
4197 FilterGraphOp::SVGFEDiffuseLightingSpot{kernel_unit_length_x, kernel_unit_length_y, ..} |
4198 FilterGraphOp::SVGFESpecularLightingDistant{kernel_unit_length_x, kernel_unit_length_y, ..} |
4199 FilterGraphOp::SVGFESpecularLightingPoint{kernel_unit_length_x, kernel_unit_length_y, ..} |
4200 FilterGraphOp::SVGFESpecularLightingSpot{kernel_unit_length_x, kernel_unit_length_y, ..} |
4201 FilterGraphOp::SVGFEMorphologyErode{radius_x: kernel_unit_length_x, radius_y: kernel_unit_length_y} => {
4202 assert!(remapped_inputs.len() == 1);
4203 let padding = LayoutSize::new(
4204 kernel_unit_length_x.ceil(),
4205 kernel_unit_length_y.ceil(),
4206 );
4207 remapped_inputs[0].source_padding =
4210 remapped_inputs[0].source_padding
4211 .inflate(padding.width, padding.height);
4212 remapped_inputs[0].target_padding =
4215 remapped_inputs[0].target_padding
4216 .inflate(padding.width, padding.height);
4217 newnode.inputs = remapped_inputs;
4218 (newnode.clone(), op.clone())
4219 },
4220 FilterGraphOp::SVGFEDisplacementMap { scale, .. } => {
4221 assert!(remapped_inputs.len() == 2);
4222 let padding = LayoutSize::new(
4223 scale.ceil(),
4224 scale.ceil(),
4225 );
4226 remapped_inputs[0].source_padding =
4231 remapped_inputs[0].source_padding
4232 .inflate(padding.width, padding.height);
4233 remapped_inputs[1].source_padding =
4234 remapped_inputs[1].source_padding
4235 .inflate(padding.width, padding.height);
4236 remapped_inputs[0].target_padding =
4237 remapped_inputs[0].target_padding
4238 .inflate(padding.width, padding.height);
4239 remapped_inputs[1].target_padding =
4240 remapped_inputs[1].target_padding
4241 .inflate(padding.width, padding.height);
4242 newnode.inputs = remapped_inputs;
4243 (newnode.clone(), op.clone())
4244 },
4245 FilterGraphOp::SVGFEDropShadow{ dx, dy, std_deviation_x, std_deviation_y, .. } => {
4246 assert!(remapped_inputs.len() == 1);
4247 let padding = LayoutSize::new(
4248 std_deviation_x.ceil() * BLUR_SAMPLE_SCALE,
4249 std_deviation_y.ceil() * BLUR_SAMPLE_SCALE,
4250 );
4251 remapped_inputs[0].source_padding =
4253 union_unchecked(
4254 remapped_inputs[0].source_padding,
4255 remapped_inputs[0].source_padding
4256 .inflate(padding.width, padding.height)
4257 .translate(
4258 LayoutVector2D::new(-dx, -dy)
4259 )
4260 );
4261 remapped_inputs[0].target_padding =
4264 union_unchecked(
4265 remapped_inputs[0].target_padding,
4266 remapped_inputs[0].target_padding
4267 .inflate(padding.width, padding.height)
4268 .translate(
4269 LayoutVector2D::new(*dx, *dy)
4270 )
4271 );
4272 newnode.inputs = remapped_inputs;
4273 (newnode.clone(), op.clone())
4274 },
4275 FilterGraphOp::SVGFEGaussianBlur{std_deviation_x, std_deviation_y} => {
4276 assert!(remapped_inputs.len() == 1);
4277 let padding = LayoutSize::new(
4278 std_deviation_x.ceil() * BLUR_SAMPLE_SCALE,
4279 std_deviation_y.ceil() * BLUR_SAMPLE_SCALE,
4280 );
4281 remapped_inputs[0].source_padding =
4283 remapped_inputs[0].source_padding
4284 .inflate(padding.width, padding.height);
4285 remapped_inputs[0].target_padding =
4287 remapped_inputs[0].target_padding
4288 .inflate(padding.width, padding.height);
4289 newnode.inputs = remapped_inputs;
4290 (newnode.clone(), op.clone())
4291 }
4292 FilterGraphOp::SVGFEBlendColor |
4293 FilterGraphOp::SVGFEBlendColorBurn |
4294 FilterGraphOp::SVGFEBlendColorDodge |
4295 FilterGraphOp::SVGFEBlendDarken |
4296 FilterGraphOp::SVGFEBlendDifference |
4297 FilterGraphOp::SVGFEBlendExclusion |
4298 FilterGraphOp::SVGFEBlendHardLight |
4299 FilterGraphOp::SVGFEBlendHue |
4300 FilterGraphOp::SVGFEBlendLighten |
4301 FilterGraphOp::SVGFEBlendLuminosity|
4302 FilterGraphOp::SVGFEBlendMultiply |
4303 FilterGraphOp::SVGFEBlendNormal |
4304 FilterGraphOp::SVGFEBlendOverlay |
4305 FilterGraphOp::SVGFEBlendSaturation |
4306 FilterGraphOp::SVGFEBlendScreen |
4307 FilterGraphOp::SVGFEBlendSoftLight |
4308 FilterGraphOp::SVGFECompositeArithmetic{..} |
4309 FilterGraphOp::SVGFECompositeATop |
4310 FilterGraphOp::SVGFECompositeIn |
4311 FilterGraphOp::SVGFECompositeLighter |
4312 FilterGraphOp::SVGFECompositeOut |
4313 FilterGraphOp::SVGFECompositeOver |
4314 FilterGraphOp::SVGFECompositeXOR => {
4315 assert!(remapped_inputs.len() == 2);
4316 newnode.inputs = remapped_inputs;
4317 (newnode, op.clone())
4318 }
4319 }
4320 }
4321 Filter::Opacity(valuebinding, value) => {
4322 let pic = reference_for_buffer_id[original_id as usize - 1];
4328 (
4329 FilterGraphNode {
4330 kept_by_optimizer: false,
4331 linear: false,
4332 inflate: SVGFE_INFLATE,
4333 inputs: [pic].to_vec(),
4334 subregion: pic.subregion,
4335 },
4336 FilterGraphOp::SVGFEOpacity{
4337 valuebinding: *valuebinding,
4338 value: *value,
4339 },
4340 )
4341 }
4342 _ => {
4343 log!(Level::Warn, "wrap_prim_with_filters: unexpected filter after SVG filters filter[{:?}]={:?}", original_id, parsefilter);
4344 return source;
4348 }
4349 };
4350 let id = filters.len();
4351 filters.push(newfilter);
4352
4353 reference_for_buffer_id[original_id] = FilterGraphPictureReference {
4356 buffer_id: FilterOpGraphPictureBufferId::BufferId(id as i16),
4357 subregion: filters[id].0.subregion,
4358 offset: LayoutVector2D::zero(),
4359 inflate: filters[id].0.inflate,
4360 source_padding: LayoutRect::zero(),
4361 target_padding: LayoutRect::zero(),
4362 };
4363 }
4364
4365 if filters.len() >= BUFFER_LIMIT {
4366 return source;
4369 }
4370
4371 let mut kept_node_by_buffer_id = [false; BUFFER_LIMIT];
4379 kept_node_by_buffer_id[filters.len() - 1] = true;
4380 for (index, (node, _op)) in filters.iter_mut().enumerate().rev() {
4381 let mut keep = false;
4382 if let Some(k) = kept_node_by_buffer_id.get(index) {
4384 if *k {
4385 keep = true;
4386 }
4387 }
4388 if keep {
4389 node.kept_by_optimizer = true;
4393 for input in &node.inputs {
4394 if let FilterOpGraphPictureBufferId::BufferId(id) = input.buffer_id {
4395 if let Some(k) = kept_node_by_buffer_id.get_mut(id as usize) {
4396 *k = true;
4397 }
4398 }
4399 }
4400 }
4401 }
4402
4403 let mut invalid_dag = false;
4406 for (id, (node, _op)) in filters.iter().enumerate() {
4407 for input in &node.inputs {
4408 if let FilterOpGraphPictureBufferId::BufferId(buffer_id) = input.buffer_id {
4409 if buffer_id < 0 || buffer_id as usize >= id {
4410 invalid_dag = true;
4411 }
4412 }
4413 }
4414 }
4415
4416 if invalid_dag {
4417 log!(Level::Warn, "List of FilterOp::SVGGraphNode filter primitives appears to be invalid!");
4418 for (id, (node, op)) in filters.iter().enumerate() {
4419 log!(Level::Warn, " node: buffer=BufferId({}) op={} inflate={} subregion {:?} linear={} kept={}",
4420 id, op.kind(), node.inflate,
4421 node.subregion,
4422 node.linear,
4423 node.kept_by_optimizer,
4424 );
4425 for input in &node.inputs {
4426 log!(Level::Warn, "input: buffer={} inflate={} subregion {:?} offset {:?} target_padding={:?} source_padding={:?}",
4427 match input.buffer_id {
4428 FilterOpGraphPictureBufferId::BufferId(id) => format!("BufferId({})", id),
4429 FilterOpGraphPictureBufferId::None => "None".into(),
4430 },
4431 input.inflate,
4432 input.subregion,
4433 input.offset,
4434 input.target_padding,
4435 input.source_padding,
4436 );
4437 }
4438 }
4439 }
4440 if invalid_dag {
4441 return source;
4443 }
4444
4445 let composite_mode = PictureCompositeMode::SVGFEGraph(
4446 filters,
4447 );
4448
4449 source = source.add_picture(
4450 composite_mode,
4451 clip_node_id,
4452 Picture3DContext::Out,
4453 &mut self.interners,
4454 &mut self.prim_store,
4455 &mut self.prim_instances,
4456 &mut self.clip_tree_builder,
4457 );
4458
4459 return source;
4460 }
4461
4462 for filter in &mut filter_ops {
4464 let composite_mode = match filter {
4465 Filter::ComponentTransfer => {
4466 let filter_data =
4467 &filter_datas[current_filter_data_index];
4468 let filter_data = filter_data.sanitize();
4469 current_filter_data_index = current_filter_data_index + 1;
4470 if filter_data.is_identity() {
4471 continue
4472 } else {
4473 let filter_data_key = SFilterDataKey {
4474 data:
4475 SFilterData {
4476 r_func: SFilterDataComponent::from_functype_values(
4477 filter_data.func_r_type, &filter_data.r_values),
4478 g_func: SFilterDataComponent::from_functype_values(
4479 filter_data.func_g_type, &filter_data.g_values),
4480 b_func: SFilterDataComponent::from_functype_values(
4481 filter_data.func_b_type, &filter_data.b_values),
4482 a_func: SFilterDataComponent::from_functype_values(
4483 filter_data.func_a_type, &filter_data.a_values),
4484 },
4485 };
4486
4487 let handle = self.interners
4488 .filter_data
4489 .intern(&filter_data_key, || ());
4490 PictureCompositeMode::ComponentTransferFilter(handle)
4491 }
4492 }
4493 Filter::SVGGraphNode(_, _) => {
4494 panic!("SVGGraphNode encountered in regular CSS filter chain?");
4496 }
4497 _ => {
4498 if filter.is_noop() {
4499 continue;
4500 } else {
4501 let mut filter = filter.clone();
4502
4503 if is_backdrop_filter {
4507 if let Filter::Blur { ref mut should_inflate, ref mut edge_mode, .. } = filter {
4508 *should_inflate = false;
4509 *edge_mode = BlurEdgeMode::Mirror;
4510 }
4511 }
4512
4513 PictureCompositeMode::Filter(filter)
4514 }
4515 }
4516 };
4517
4518 source = source.add_picture(
4519 composite_mode,
4520 clip_node_id,
4521 Picture3DContext::Out,
4522 &mut self.interners,
4523 &mut self.prim_store,
4524 &mut self.prim_instances,
4525 &mut self.clip_tree_builder,
4526 );
4527 }
4528
4529 if !filter_primitives.is_empty() {
4530 let filter_datas = filter_datas.iter()
4531 .map(|filter_data| filter_data.sanitize())
4532 .map(|filter_data| {
4533 SFilterData {
4534 r_func: SFilterDataComponent::from_functype_values(
4535 filter_data.func_r_type, &filter_data.r_values),
4536 g_func: SFilterDataComponent::from_functype_values(
4537 filter_data.func_g_type, &filter_data.g_values),
4538 b_func: SFilterDataComponent::from_functype_values(
4539 filter_data.func_b_type, &filter_data.b_values),
4540 a_func: SFilterDataComponent::from_functype_values(
4541 filter_data.func_a_type, &filter_data.a_values),
4542 }
4543 })
4544 .collect();
4545
4546 for primitive in &mut filter_primitives {
4548 primitive.sanitize();
4549 }
4550
4551 let composite_mode = PictureCompositeMode::SvgFilter(
4552 filter_primitives,
4553 filter_datas,
4554 );
4555
4556 source = source.add_picture(
4557 composite_mode,
4558 clip_node_id,
4559 Picture3DContext::Out,
4560 &mut self.interners,
4561 &mut self.prim_store,
4562 &mut self.prim_instances,
4563 &mut self.clip_tree_builder,
4564 );
4565 }
4566
4567 source
4568 }
4569}
4570
4571
4572pub trait CreateShadow {
4573 fn create_shadow(
4574 &self,
4575 shadow: &Shadow,
4576 blur_is_noop: bool,
4577 current_raster_space: RasterSpace,
4578 ) -> Self;
4579}
4580
4581pub trait IsVisible {
4582 fn is_visible(&self) -> bool;
4583}
4584
4585struct ExtendedPrimitiveInstance {
4589 instance: PrimitiveInstance,
4590 spatial_node_index: SpatialNodeIndex,
4591 flags: PrimitiveFlags,
4592}
4593
4594struct StackingContextInfo {
4597 pop_containing_block: bool,
4599 pop_stacking_context: bool,
4601 set_tile_cache_barrier: bool,
4603 needs_extra_stacking_context: bool,
4607}
4608
4609struct FlattenedStackingContext {
4613 prim_list: PrimitiveList,
4615
4616 prim_flags: PrimitiveFlags,
4618
4619 spatial_node_index: SpatialNodeIndex,
4621
4622 clip_node_id: ClipNodeId,
4624
4625 composite_ops: CompositeOps,
4628
4629 blit_reason: BlitReason,
4632
4633 transform_style: TransformStyle,
4635
4636 context_3d: Picture3DContext<ExtendedPrimitiveInstance>,
4638
4639 flags: StackingContextFlags,
4641
4642 raster_space: RasterSpace,
4644
4645 subregion_offset: LayoutVector2D,
4647}
4648
4649impl FlattenedStackingContext {
4650 pub fn is_3d(&self) -> bool {
4652 self.transform_style == TransformStyle::Preserve3D && self.composite_ops.is_empty()
4653 }
4654
4655 pub fn is_redundant(
4657 context_3d: &Picture3DContext<ExtendedPrimitiveInstance>,
4658 composite_ops: &CompositeOps,
4659 blit_reason: BlitReason,
4660 parent: Option<&FlattenedStackingContext>,
4661 prim_flags: PrimitiveFlags,
4662 ) -> bool {
4663 if let Picture3DContext::In { .. } = context_3d {
4665 return false;
4666 }
4667
4668 if composite_ops.has_valid_filters() {
4670 return false;
4671 }
4672
4673 if composite_ops.mix_blend_mode.is_some() {
4675 match parent {
4676 Some(ref parent) => {
4677 if !parent.prim_list.is_empty() {
4680 return false;
4681 }
4682 }
4683 None => {
4684 return false;
4688 }
4689 }
4690 }
4691
4692 if !blit_reason.is_empty() {
4694 return false;
4695 }
4696
4697 if !prim_flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE) {
4699 return false;
4700 }
4701
4702 true
4704 }
4705
4706 pub fn cut_item_sequence(
4708 &mut self,
4709 prim_store: &mut PrimitiveStore,
4710 interners: &mut Interners,
4711 composite_mode: Option<PictureCompositeMode>,
4712 flat_items_context_3d: Picture3DContext<OrderedPictureChild>,
4713 clip_tree_builder: &mut ClipTreeBuilder,
4714 ) -> Option<(PictureIndex, PrimitiveInstance)> {
4715 if self.prim_list.is_empty() {
4716 return None
4717 }
4718
4719 let pic_index = PictureIndex(prim_store.pictures
4720 .alloc()
4721 .init(PicturePrimitive::new_image(
4722 composite_mode.clone(),
4723 flat_items_context_3d,
4724 self.prim_flags,
4725 mem::replace(&mut self.prim_list, PrimitiveList::empty()),
4726 self.spatial_node_index,
4727 self.raster_space,
4728 PictureFlags::empty(),
4729 None
4730 ))
4731 );
4732
4733 let prim_instance = create_prim_instance(
4734 pic_index,
4735 composite_mode.into(),
4736 self.raster_space,
4737 self.clip_node_id,
4738 interners,
4739 clip_tree_builder,
4740 );
4741
4742 Some((pic_index, prim_instance))
4743 }
4744}
4745
4746pub struct PendingPrimitive<T> {
4750 spatial_node_index: SpatialNodeIndex,
4751 clip_node_id: ClipNodeId,
4752 info: LayoutPrimitiveInfo,
4753 prim: T,
4754}
4755
4756pub struct PendingShadow {
4759 shadow: Shadow,
4760 should_inflate: bool,
4761 spatial_node_index: SpatialNodeIndex,
4762}
4763
4764pub enum ShadowItem {
4765 Shadow(PendingShadow),
4766 Image(PendingPrimitive<Image>),
4767 LineDecoration(PendingPrimitive<LineDecoration>),
4768 NormalBorder(PendingPrimitive<NormalBorderPrim>),
4769 Primitive(PendingPrimitive<PrimitiveKeyKind>),
4770 TextRun(PendingPrimitive<TextRun>),
4771}
4772
4773impl From<PendingPrimitive<Image>> for ShadowItem {
4774 fn from(image: PendingPrimitive<Image>) -> Self {
4775 ShadowItem::Image(image)
4776 }
4777}
4778
4779impl From<PendingPrimitive<LineDecoration>> for ShadowItem {
4780 fn from(line_dec: PendingPrimitive<LineDecoration>) -> Self {
4781 ShadowItem::LineDecoration(line_dec)
4782 }
4783}
4784
4785impl From<PendingPrimitive<NormalBorderPrim>> for ShadowItem {
4786 fn from(border: PendingPrimitive<NormalBorderPrim>) -> Self {
4787 ShadowItem::NormalBorder(border)
4788 }
4789}
4790
4791impl From<PendingPrimitive<PrimitiveKeyKind>> for ShadowItem {
4792 fn from(container: PendingPrimitive<PrimitiveKeyKind>) -> Self {
4793 ShadowItem::Primitive(container)
4794 }
4795}
4796
4797impl From<PendingPrimitive<TextRun>> for ShadowItem {
4798 fn from(text_run: PendingPrimitive<TextRun>) -> Self {
4799 ShadowItem::TextRun(text_run)
4800 }
4801}
4802
4803fn create_prim_instance(
4804 pic_index: PictureIndex,
4805 composite_mode_key: PictureCompositeKey,
4806 raster_space: RasterSpace,
4807 clip_node_id: ClipNodeId,
4808 interners: &mut Interners,
4809 clip_tree_builder: &mut ClipTreeBuilder,
4810) -> PrimitiveInstance {
4811 let pic_key = PictureKey::new(
4812 Picture {
4813 composite_mode_key,
4814 raster_space,
4815 },
4816 );
4817
4818 let data_handle = interners
4819 .picture
4820 .intern(&pic_key, || ());
4821
4822 PrimitiveInstance::new(
4823 PrimitiveInstanceKind::Picture {
4824 data_handle,
4825 pic_index,
4826 },
4827 clip_tree_builder.build_for_picture(
4828 clip_node_id,
4829 ),
4830 )
4831}
4832
4833fn filter_ops_for_compositing(
4834 input_filters: ItemRange<FilterOp>,
4835) -> Vec<Filter> {
4836 input_filters.iter().map(|filter| filter.into()).collect()
4840}
4841
4842fn filter_datas_for_compositing(
4843 input_filter_datas: &[TempFilterData],
4844) -> Vec<FilterData> {
4845 let mut filter_datas = vec![];
4849 for temp_filter_data in input_filter_datas {
4850 let func_types : Vec<ComponentTransferFuncType> = temp_filter_data.func_types.iter().collect();
4851 debug_assert!(func_types.len() == 4);
4852 filter_datas.push( FilterData {
4853 func_r_type: func_types[0],
4854 r_values: temp_filter_data.r_values.iter().collect(),
4855 func_g_type: func_types[1],
4856 g_values: temp_filter_data.g_values.iter().collect(),
4857 func_b_type: func_types[2],
4858 b_values: temp_filter_data.b_values.iter().collect(),
4859 func_a_type: func_types[3],
4860 a_values: temp_filter_data.a_values.iter().collect(),
4861 });
4862 }
4863 filter_datas
4864}
4865
4866fn filter_primitives_for_compositing(
4867 input_filter_primitives: ItemRange<FilterPrimitive>,
4868) -> Vec<FilterPrimitive> {
4869 input_filter_primitives.iter().map(|primitive| primitive).collect()
4874}
4875
4876fn process_repeat_size(
4877 snapped_rect: &LayoutRect,
4878 unsnapped_rect: &LayoutRect,
4879 repeat_size: LayoutSize,
4880) -> LayoutSize {
4881 const EPSILON: f32 = 0.001;
4889 LayoutSize::new(
4890 if repeat_size.width.approx_eq_eps(&unsnapped_rect.width(), &EPSILON) {
4891 snapped_rect.width()
4892 } else {
4893 repeat_size.width
4894 },
4895 if repeat_size.height.approx_eq_eps(&unsnapped_rect.height(), &EPSILON) {
4896 snapped_rect.height()
4897 } else {
4898 repeat_size.height
4899 },
4900 )
4901}
4902
4903fn read_gradient_stops(stops: ItemRange<GradientStop>) -> Vec<GradientStopKey> {
4904 stops.iter().map(|stop| {
4905 GradientStopKey {
4906 offset: stop.offset,
4907 color: stop.color.into(),
4908 }
4909 }).collect()
4910}
4911
4912pub struct SceneRecycler {
4916 pub tx: Sender<BuiltScene>,
4917 rx: Receiver<BuiltScene>,
4918
4919 pub prim_store: PrimitiveStore,
4922 pub clip_store: ClipStore,
4923 pub picture_graph: PictureGraph,
4924 pub prim_instances: Vec<PrimitiveInstance>,
4925 pub surfaces: Vec<SurfaceInfo>,
4926 pub hit_testing_scene: Option<HitTestingScene>,
4927 pub clip_tree_builder: Option<ClipTreeBuilder>,
4928 id_to_index_mapper_stack: Vec<NodeIdToIndexMapper>,
4937 sc_stack: Vec<FlattenedStackingContext>,
4938 containing_block_stack: Vec<SpatialNodeIndex>,
4939 raster_space_stack: Vec<RasterSpace>,
4940 pending_shadow_items: VecDeque<ShadowItem>,
4941 iframe_size: Vec<LayoutSize>,
4942}
4943
4944impl SceneRecycler {
4945 pub fn new() -> Self {
4946 let (tx, rx) = unbounded_channel();
4947 SceneRecycler {
4948 tx,
4949 rx,
4950
4951 prim_instances: Vec::new(),
4952 surfaces: Vec::new(),
4953 prim_store: PrimitiveStore::new(&PrimitiveStoreStats::empty()),
4954 clip_store: ClipStore::new(),
4955 picture_graph: PictureGraph::new(),
4956 hit_testing_scene: None,
4957 clip_tree_builder: None,
4958
4959 id_to_index_mapper_stack: Vec::new(),
4960 sc_stack: Vec::new(),
4961 containing_block_stack: Vec::new(),
4962 raster_space_stack: Vec::new(),
4963 pending_shadow_items: VecDeque::new(),
4964 iframe_size: Vec::new(),
4965 }
4966 }
4967
4968 #[inline(never)]
4974 pub fn recycle_built_scene(&mut self) {
4975 let Ok(scene) = self.rx.try_recv() else {
4976 return;
4977 };
4978
4979 self.prim_store = scene.prim_store;
4980 self.clip_store = scene.clip_store;
4981 self.prim_store.reset();
4985 self.clip_store.reset();
4986 self.hit_testing_scene = Arc::try_unwrap(scene.hit_testing_scene).ok();
4987 self.picture_graph = scene.picture_graph;
4988 self.prim_instances = scene.prim_instances;
4989 self.surfaces = scene.surfaces;
4990 if let Some(clip_tree_builder) = &mut self.clip_tree_builder {
4991 clip_tree_builder.recycle_tree(scene.clip_tree);
4992 }
4993
4994 while let Ok(_) = self.rx.try_recv() {
4995 }
4998
4999 }
5001}