Flutter Engine
container_layer.cc
Go to the documentation of this file.
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 
9 namespace flutter {
10 
12 
13 #ifdef FLUTTER_ENABLE_DIFF_CONTEXT
14 
15 void ContainerLayer::Diff(DiffContext* context, const Layer* old_layer) {
16  auto old_container = static_cast<const ContainerLayer*>(old_layer);
17  DiffContext::AutoSubtreeRestore subtree(context);
18  DiffChildren(context, old_container);
19  context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion());
20 }
21 
22 void ContainerLayer::PreservePaintRegion(DiffContext* context) {
23  Layer::PreservePaintRegion(context);
24  for (auto& layer : layers_) {
25  layer->PreservePaintRegion(context);
26  }
27 }
28 
29 void ContainerLayer::DiffChildren(DiffContext* context,
30  const ContainerLayer* old_layer) {
31  if (context->IsSubtreeDirty()) {
32  for (auto& layer : layers_) {
33  layer->Diff(context, nullptr);
34  }
35  return;
36  }
37  FML_DCHECK(old_layer);
38 
39  const auto& prev_layers = old_layer->layers_;
40 
41  // first mismatched element
42  int new_children_top = 0;
43  int old_children_top = 0;
44 
45  // last mismatched element
46  int new_children_bottom = layers_.size() - 1;
47  int old_children_bottom = prev_layers.size() - 1;
48 
49  while ((old_children_top <= old_children_bottom) &&
50  (new_children_top <= new_children_bottom)) {
51  if (!layers_[new_children_top]->IsReplacing(
52  context, prev_layers[old_children_top].get())) {
53  break;
54  }
55  ++new_children_top;
56  ++old_children_top;
57  }
58 
59  while ((old_children_top <= old_children_bottom) &&
60  (new_children_top <= new_children_bottom)) {
61  if (!layers_[new_children_bottom]->IsReplacing(
62  context, prev_layers[old_children_bottom].get())) {
63  break;
64  }
65  --new_children_bottom;
66  --old_children_bottom;
67  }
68 
69  // old layers that don't match
70  for (int i = old_children_top; i <= old_children_bottom; ++i) {
71  auto layer = prev_layers[i];
72  context->AddDamage(context->GetOldLayerPaintRegion(layer.get()));
73  }
74 
75  for (int i = 0; i < static_cast<int>(layers_.size()); ++i) {
76  if (i < new_children_top || i > new_children_bottom) {
77  int i_prev =
78  i < new_children_top ? i : prev_layers.size() - (layers_.size() - i);
79  auto layer = layers_[i];
80  auto prev_layer = prev_layers[i_prev];
81  auto paint_region = context->GetOldLayerPaintRegion(prev_layer.get());
82  if (layer == prev_layer && !paint_region.has_readback()) {
83  // for retained layers, stop processing the subtree and add existing
84  // region; We know current subtree is not dirty (every ancestor up to
85  // here matches) so the retained subtree will render identically to
86  // previous frame; We can only do this if there is no readback in the
87  // subtree. Layers that do readback must be able to register readback
88  // inside Diff
89  context->AddExistingPaintRegion(paint_region);
90 
91  // While we don't need to diff retained layers, we still need to
92  // associate their paint region with current layer tree so that we can
93  // retrieve it in next frame diff
94  layer->PreservePaintRegion(context);
95  } else {
96  layer->Diff(context, prev_layer.get());
97  }
98  } else {
99  DiffContext::AutoSubtreeRestore subtree(context);
100  context->MarkSubtreeDirty();
101  auto layer = layers_[i];
102  layer->Diff(context, nullptr);
103  }
104  }
105 }
106 
107 #endif // FLUTTER_ENABLE_DIFF_CONTEXT
108 
109 void ContainerLayer::Add(std::shared_ptr<Layer> layer) {
110  layers_.emplace_back(std::move(layer));
111 }
112 
113 void ContainerLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
114  TRACE_EVENT0("flutter", "ContainerLayer::Preroll");
115 
116  SkRect child_paint_bounds = SkRect::MakeEmpty();
117  PrerollChildren(context, matrix, &child_paint_bounds);
118  set_paint_bounds(child_paint_bounds);
119 }
120 
121 void ContainerLayer::Paint(PaintContext& context) const {
122  FML_DCHECK(needs_painting(context));
123 
124  PaintChildren(context);
125 }
126 
128  const SkMatrix& child_matrix,
129  SkRect* child_paint_bounds) {
130  // Platform views have no children, so context->has_platform_view should
131  // always be false.
132  FML_DCHECK(!context->has_platform_view);
133  bool child_has_platform_view = false;
134  bool child_has_texture_layer = false;
135  for (auto& layer : layers_) {
136  // Reset context->has_platform_view to false so that layers aren't treated
137  // as if they have a platform view based on one being previously found in a
138  // sibling tree.
139  context->has_platform_view = false;
140 
141  layer->Preroll(context, child_matrix);
142  child_paint_bounds->join(layer->paint_bounds());
143 
144  child_has_platform_view =
145  child_has_platform_view || context->has_platform_view;
146  child_has_texture_layer =
147  child_has_texture_layer || context->has_texture_layer;
148  }
149 
150  context->has_platform_view = child_has_platform_view;
151  context->has_texture_layer = child_has_texture_layer;
152  set_subtree_has_platform_view(child_has_platform_view);
153 }
154 
156  // We can no longer call FML_DCHECK here on the needs_painting(context)
157  // condition as that test is only valid for the PaintContext that
158  // is initially handed to a layer's Paint() method. By the time the
159  // layer calls PaintChildren(), though, it may have modified the
160  // PaintContext so the test doesn't work in this "context".
161 
162  // Intentionally not tracing here as there should be no self-time
163  // and the trace event on this common function has a small overhead.
164  for (auto& layer : layers_) {
165  if (layer->needs_painting(context)) {
166  layer->Paint(context);
167  }
168  }
169 }
170 
172  Layer* layer,
173  const SkMatrix& matrix) {
174  if (!context->has_platform_view && !context->has_texture_layer &&
175  context->raster_cache &&
176  SkRect::Intersects(context->cull_rect, layer->paint_bounds())) {
177  context->raster_cache->Prepare(context, layer, matrix);
178  }
179 }
180 
182  // Ensure the layer has only one direct child.
183  //
184  // Any children will actually be added as children of this empty
185  // ContainerLayer which can be accessed via ::GetContainerLayer().
186  // If only one child is ever added to this layer then that child
187  // will become the layer returned from ::GetCacheableChild().
188  // If multiple child layers are added, then this implicit container
189  // child becomes the cacheable child, but at the potential cost of
190  // not being as stable in the raster cache from frame to frame.
191  ContainerLayer::Add(std::make_shared<ContainerLayer>());
192 }
193 
194 #ifdef FLUTTER_ENABLE_DIFF_CONTEXT
195 void MergedContainerLayer::DiffChildren(DiffContext* context,
196  const ContainerLayer* old_layer) {
197  if (context->IsSubtreeDirty()) {
198  GetChildContainer()->Diff(context, nullptr);
199  return;
200  }
201  FML_DCHECK(old_layer);
202 
203  // For MergedContainerLayer we want to diff children of ChildContainer
204  // instead of the ChildContainer itself. This works around the fact
205  // that ChildContainerLayer is ephemeral and its original_layer_id_ is always
206  // different.
207  auto layer = static_cast<const MergedContainerLayer*>(old_layer);
208  GetChildContainer()->DiffChildren(context, layer->GetChildContainer());
209 }
210 #endif
211 
212 void MergedContainerLayer::Add(std::shared_ptr<Layer> layer) {
213  GetChildContainer()->Add(std::move(layer));
214 }
215 
217  FML_DCHECK(layers().size() == 1);
218 
219  return static_cast<ContainerLayer*>(layers()[0].get());
220 }
221 
223  ContainerLayer* child_container = GetChildContainer();
224  if (child_container->layers().size() == 1) {
225  return child_container->layers()[0].get();
226  }
227 
228  return child_container;
229 }
230 
231 } // namespace flutter
void set_subtree_has_platform_view(bool value)
Definition: layer.h:219
void Add(std::shared_ptr< Layer > layer) override
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:90
#define FML_DCHECK(condition)
Definition: logging.h:86
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
const std::vector< std::shared_ptr< Layer > > & layers() const
bool Prepare(PrerollContext *context, SkPicture *picture, bool is_complex, bool will_change, const SkMatrix &untranslated_matrix, const SkPoint &offset=SkPoint())
void PrerollChildren(PrerollContext *context, const SkMatrix &child_matrix, SkRect *child_paint_bounds)
void Paint(PaintContext &context) const override
bool needs_painting(PaintContext &context) const
Definition: layer.h:249
Layer * GetCacheableChild() const
Returns the best choice for a Layer object that can be used in RasterCache operations to cache the ch...
ContainerLayer * GetChildContainer() const
Returns the ContainerLayer used to hold all of the children of the MergedContainerLayer. Note that this may not be the best layer to use for caching the children.
const SkRect & paint_bounds() const
Definition: layer.h:227
virtual void Add(std::shared_ptr< Layer > layer)
void Preroll(PrerollContext *context, const SkMatrix &matrix) override
void set_paint_bounds(const SkRect &paint_bounds)
Definition: layer.h:240
RasterCache * raster_cache
Definition: layer.h:43
static void TryToPrepareRasterCache(PrerollContext *context, Layer *layer, const SkMatrix &matrix)
void PaintChildren(PaintContext &context) const