1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/flow/layers/container_layer.h"
6
7#include <optional>
8
9namespace flutter {
10
11ContainerLayer::ContainerLayer() {}
12
13void ContainerLayer::Add(std::shared_ptr<Layer> layer) {
14 layers_.emplace_back(std::move(layer));
15}
16
17void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
18 TRACE_EVENT0("flutter", "ContainerLayer::Preroll");
19
20 SkRect child_paint_bounds = SkRect::MakeEmpty();
21 PrerollChildren(context, matrix, &child_paint_bounds);
22 set_paint_bounds(child_paint_bounds);
23}
24
25void ContainerLayer::Paint(PaintContext& context) const {
26 FML_DCHECK(needs_painting());
27
28 PaintChildren(context);
29}
30
31void ContainerLayer::PrerollChildren(PrerollContext* context,
32 const SkMatrix& child_matrix,
33 SkRect* child_paint_bounds) {
34#if defined(LEGACY_FUCHSIA_EMBEDDER)
35 // If there is embedded Fuchsia content in the scene (a ChildSceneLayer),
36 // Layers that appear above the embedded content will be turned into their own
37 // Scenic layers.
38 child_layer_exists_below_ = context->child_scene_layer_exists_below;
39 context->child_scene_layer_exists_below = false;
40#endif
41
42 // Platform views have no children, so context->has_platform_view should
43 // always be false.
44 FML_DCHECK(!context->has_platform_view);
45 bool child_has_platform_view = false;
46 for (auto& layer : layers_) {
47 // Reset context->has_platform_view to false so that layers aren't treated
48 // as if they have a platform view based on one being previously found in a
49 // sibling tree.
50 context->has_platform_view = false;
51
52 layer->Preroll(context, child_matrix);
53
54 if (layer->needs_system_composite()) {
55 set_needs_system_composite(true);
56 }
57 child_paint_bounds->join(layer->paint_bounds());
58
59 child_has_platform_view =
60 child_has_platform_view || context->has_platform_view;
61 }
62
63 context->has_platform_view = child_has_platform_view;
64
65#if defined(LEGACY_FUCHSIA_EMBEDDER)
66 if (child_layer_exists_below_) {
67 set_needs_system_composite(true);
68 }
69 context->child_scene_layer_exists_below =
70 context->child_scene_layer_exists_below || child_layer_exists_below_;
71#endif
72}
73
74void ContainerLayer::PaintChildren(PaintContext& context) const {
75 FML_DCHECK(needs_painting());
76
77 // Intentionally not tracing here as there should be no self-time
78 // and the trace event on this common function has a small overhead.
79 for (auto& layer : layers_) {
80 if (layer->needs_painting()) {
81 layer->Paint(context);
82 }
83 }
84}
85
86void ContainerLayer::TryToPrepareRasterCache(PrerollContext* context,
87 Layer* layer,
88 const SkMatrix& matrix) {
89 if (!context->has_platform_view && context->raster_cache &&
90 SkRect::Intersects(context->cull_rect, layer->paint_bounds())) {
91 context->raster_cache->Prepare(context, layer, matrix);
92 }
93}
94
95#if defined(LEGACY_FUCHSIA_EMBEDDER)
96
97void ContainerLayer::CheckForChildLayerBelow(PrerollContext* context) {
98 // All ContainerLayers make the check in PrerollChildren.
99}
100
101void ContainerLayer::UpdateScene(SceneUpdateContext& context) {
102 UpdateSceneChildren(context);
103}
104
105void ContainerLayer::UpdateSceneChildren(SceneUpdateContext& context) {
106 FML_DCHECK(needs_system_composite());
107
108 std::optional<SceneUpdateContext::Frame> frame;
109 if (child_layer_exists_below_) {
110 frame.emplace(
111 context, SkRRect::MakeRect(paint_bounds()), SK_ColorTRANSPARENT,
112 SkScalarRoundToInt(context.alphaf() * 255), "flutter::ContainerLayer");
113 frame->AddPaintLayer(this);
114 }
115
116 for (auto& layer : layers_) {
117 if (layer->needs_system_composite()) {
118 layer->UpdateScene(context);
119 }
120 }
121}
122
123#endif
124
125MergedContainerLayer::MergedContainerLayer() {
126 // Ensure the layer has only one direct child.
127 //
128 // Any children will actually be added as children of this empty
129 // ContainerLayer which can be accessed via ::GetContainerLayer().
130 // If only one child is ever added to this layer then that child
131 // will become the layer returned from ::GetCacheableChild().
132 // If multiple child layers are added, then this implicit container
133 // child becomes the cacheable child, but at the potential cost of
134 // not being as stable in the raster cache from frame to frame.
135 ContainerLayer::Add(std::make_shared<ContainerLayer>());
136}
137
138void MergedContainerLayer::Add(std::shared_ptr<Layer> layer) {
139 GetChildContainer()->Add(std::move(layer));
140}
141
142ContainerLayer* MergedContainerLayer::GetChildContainer() const {
143 FML_DCHECK(layers().size() == 1);
144
145 return static_cast<ContainerLayer*>(layers()[0].get());
146}
147
148Layer* MergedContainerLayer::GetCacheableChild() const {
149 ContainerLayer* child_container = GetChildContainer();
150 if (child_container->layers().size() == 1) {
151 return child_container->layers()[0].get();
152 }
153
154 return child_container;
155}
156
157} // namespace flutter
158