[go: up one dir, main page]

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/diff_context.h"
6#include "flutter/flow/layers/layer.h"
7
8namespace flutter {
9
10DiffContext::DiffContext(SkISize frame_size,
11 PaintRegionMap& this_frame_paint_region_map,
12 const PaintRegionMap& last_frame_paint_region_map,
13 bool has_raster_cache,
14 bool impeller_enabled)
15 : clip_tracker_(DisplayListMatrixClipTracker(kGiantRect, SkMatrix::I())),
16 rects_(std::make_shared<std::vector<SkRect>>()),
17 frame_size_(frame_size),
18 this_frame_paint_region_map_(this_frame_paint_region_map),
19 last_frame_paint_region_map_(last_frame_paint_region_map),
20 has_raster_cache_(has_raster_cache),
21 impeller_enabled_(impeller_enabled) {}
22
23void DiffContext::BeginSubtree() {
24 state_stack_.push_back(x: state_);
25
26 bool had_integral_transform = state_.integral_transform;
27 state_.rect_index = rects_->size();
28 state_.has_filter_bounds_adjustment = false;
29 state_.has_texture = false;
30 state_.integral_transform = false;
31
32 state_.clip_tracker_save_count = clip_tracker_.getSaveCount();
33 clip_tracker_.save();
34
35 if (had_integral_transform) {
36 MakeCurrentTransformIntegral();
37 }
38}
39
40void DiffContext::EndSubtree() {
41 FML_DCHECK(!state_stack_.empty());
42 if (state_.has_filter_bounds_adjustment) {
43 filter_bounds_adjustment_stack_.pop_back();
44 }
45 clip_tracker_.restoreToCount(restore_count: state_.clip_tracker_save_count);
46 state_ = state_stack_.back();
47 state_stack_.pop_back();
48}
49
50DiffContext::State::State()
51 : dirty(false),
52 rect_index(0),
53 integral_transform(false),
54 clip_tracker_save_count(0),
55 has_filter_bounds_adjustment(false),
56 has_texture(false) {}
57
58void DiffContext::PushTransform(const SkMatrix& transform) {
59 clip_tracker_.transform(matrix: transform);
60}
61
62void DiffContext::PushTransform(const SkM44& transform) {
63 clip_tracker_.transform(m44: transform);
64}
65
66void DiffContext::MakeCurrentTransformIntegral() {
67 // TODO(knopp): This is duplicated from LayerStack. Maybe should be part of
68 // clip tracker?
69 if (clip_tracker_.using_4x4_matrix()) {
70 clip_tracker_.setTransform(
71 RasterCacheUtil::GetIntegralTransCTM(ctm: clip_tracker_.matrix_4x4()));
72 } else {
73 clip_tracker_.setTransform(
74 RasterCacheUtil::GetIntegralTransCTM(ctm: clip_tracker_.matrix_3x3()));
75 }
76}
77
78void DiffContext::PushFilterBoundsAdjustment(
79 const FilterBoundsAdjustment& filter) {
80 FML_DCHECK(state_.has_filter_bounds_adjustment == false);
81 state_.has_filter_bounds_adjustment = true;
82 filter_bounds_adjustment_stack_.push_back(x: filter);
83}
84
85SkRect DiffContext::ApplyFilterBoundsAdjustment(SkRect rect) const {
86 // Apply filter bounds adjustment in reverse order
87 for (auto i = filter_bounds_adjustment_stack_.rbegin();
88 i != filter_bounds_adjustment_stack_.rend(); ++i) {
89 rect = (*i)(rect);
90 }
91 return rect;
92}
93
94void DiffContext::AlignRect(SkIRect& rect,
95 int horizontal_alignment,
96 int vertical_alignment) const {
97 auto top = rect.top();
98 auto left = rect.left();
99 auto right = rect.right();
100 auto bottom = rect.bottom();
101 if (top % vertical_alignment != 0) {
102 top -= top % vertical_alignment;
103 }
104 if (left % horizontal_alignment != 0) {
105 left -= left % horizontal_alignment;
106 }
107 if (right % horizontal_alignment != 0) {
108 right += horizontal_alignment - right % horizontal_alignment;
109 }
110 if (bottom % vertical_alignment != 0) {
111 bottom += vertical_alignment - bottom % vertical_alignment;
112 }
113 right = std::min(a: right, b: frame_size_.width());
114 bottom = std::min(a: bottom, b: frame_size_.height());
115 rect = SkIRect::MakeLTRB(l: left, t: top, r: right, b: bottom);
116}
117
118Damage DiffContext::ComputeDamage(const SkIRect& accumulated_buffer_damage,
119 int horizontal_clip_alignment,
120 int vertical_clip_alignment) const {
121 SkRect buffer_damage = SkRect::Make(irect: accumulated_buffer_damage);
122 buffer_damage.join(r: damage_);
123 SkRect frame_damage(damage_);
124
125 for (const auto& r : readbacks_) {
126 SkRect rect = SkRect::Make(irect: r.rect);
127 if (rect.intersects(r: frame_damage)) {
128 frame_damage.join(r: rect);
129 }
130 if (rect.intersects(r: buffer_damage)) {
131 buffer_damage.join(r: rect);
132 }
133 }
134
135 Damage res;
136 buffer_damage.roundOut(dst: &res.buffer_damage);
137 frame_damage.roundOut(dst: &res.frame_damage);
138
139 SkIRect frame_clip = SkIRect::MakeSize(size: frame_size_);
140 res.buffer_damage.intersect(r: frame_clip);
141 res.frame_damage.intersect(r: frame_clip);
142
143 if (horizontal_clip_alignment > 1 || vertical_clip_alignment > 1) {
144 AlignRect(rect&: res.buffer_damage, horizontal_alignment: horizontal_clip_alignment,
145 vertical_alignment: vertical_clip_alignment);
146 AlignRect(rect&: res.frame_damage, horizontal_alignment: horizontal_clip_alignment,
147 vertical_alignment: vertical_clip_alignment);
148 }
149 return res;
150}
151
152SkRect DiffContext::MapRect(const SkRect& rect) {
153 SkRect mapped_rect(rect);
154 clip_tracker_.mapRect(rect: &mapped_rect);
155 return mapped_rect;
156}
157
158bool DiffContext::PushCullRect(const SkRect& clip) {
159 clip_tracker_.clipRect(rect: clip, op: DlCanvas::ClipOp::kIntersect, is_aa: false);
160 return !clip_tracker_.device_cull_rect().isEmpty();
161}
162
163SkMatrix DiffContext::GetTransform3x3() const {
164 return clip_tracker_.matrix_3x3();
165}
166
167SkRect DiffContext::GetCullRect() const {
168 return clip_tracker_.local_cull_rect();
169}
170
171void DiffContext::MarkSubtreeDirty(const PaintRegion& previous_paint_region) {
172 FML_DCHECK(!IsSubtreeDirty());
173 if (previous_paint_region.is_valid()) {
174 AddDamage(damage: previous_paint_region);
175 }
176 state_.dirty = true;
177}
178
179void DiffContext::MarkSubtreeDirty(const SkRect& previous_paint_region) {
180 FML_DCHECK(!IsSubtreeDirty());
181 AddDamage(rect: previous_paint_region);
182 state_.dirty = true;
183}
184
185void DiffContext::AddLayerBounds(const SkRect& rect) {
186 // During painting we cull based on non-overriden transform and then
187 // override the transform right before paint. Do the same thing here to get
188 // identical paint rect.
189 auto transformed_rect = ApplyFilterBoundsAdjustment(rect: MapRect(rect));
190 if (transformed_rect.intersects(r: clip_tracker_.device_cull_rect())) {
191 if (state_.integral_transform) {
192 clip_tracker_.save();
193 MakeCurrentTransformIntegral();
194 transformed_rect = ApplyFilterBoundsAdjustment(rect: MapRect(rect));
195 clip_tracker_.restore();
196 }
197 rects_->push_back(x: transformed_rect);
198 if (IsSubtreeDirty()) {
199 AddDamage(rect: transformed_rect);
200 }
201 }
202}
203
204void DiffContext::MarkSubtreeHasTextureLayer() {
205 // Set the has_texture flag on current state and all parent states. That
206 // way we'll know that we can't skip diff for retained layers because
207 // they contain a TextureLayer.
208 for (auto& state : state_stack_) {
209 state.has_texture = true;
210 }
211 state_.has_texture = true;
212}
213
214void DiffContext::AddExistingPaintRegion(const PaintRegion& region) {
215 // Adding paint region for retained layer implies that current subtree is not
216 // dirty, so we know, for example, that the inherited transforms must match
217 FML_DCHECK(!IsSubtreeDirty());
218 if (region.is_valid()) {
219 rects_->insert(position: rects_->end(), first: region.begin(), last: region.end());
220 }
221}
222
223void DiffContext::AddReadbackRegion(const SkIRect& rect) {
224 Readback readback;
225 readback.rect = rect;
226 readback.position = rects_->size();
227 // Push empty rect as a placeholder for position in current subtree
228 rects_->push_back(x: SkRect::MakeEmpty());
229 readbacks_.push_back(x: readback);
230}
231
232PaintRegion DiffContext::CurrentSubtreeRegion() const {
233 bool has_readback = std::any_of(
234 first: readbacks_.begin(), last: readbacks_.end(),
235 pred: [&](const Readback& r) { return r.position >= state_.rect_index; });
236 return PaintRegion(rects_, state_.rect_index, rects_->size(), has_readback,
237 state_.has_texture);
238}
239
240void DiffContext::AddDamage(const PaintRegion& damage) {
241 FML_DCHECK(damage.is_valid());
242 for (const auto& r : damage) {
243 damage_.join(r);
244 }
245}
246
247void DiffContext::AddDamage(const SkRect& rect) {
248 damage_.join(r: rect);
249}
250
251void DiffContext::SetLayerPaintRegion(const Layer* layer,
252 const PaintRegion& region) {
253 this_frame_paint_region_map_[layer->unique_id()] = region;
254}
255
256PaintRegion DiffContext::GetOldLayerPaintRegion(const Layer* layer) const {
257 auto i = last_frame_paint_region_map_.find(k: layer->unique_id());
258 if (i != last_frame_paint_region_map_.end()) {
259 return i->second;
260 } else {
261 // This is valid when Layer::PreservePaintRegion is called for retained
262 // layer with zero sized parent clip (these layers are not diffed)
263 return PaintRegion();
264 }
265}
266
267void DiffContext::Statistics::LogStatistics() {
268#if !FLUTTER_RELEASE
269 FML_TRACE_COUNTER("flutter", "DiffContext", reinterpret_cast<int64_t>(this),
270 "NewPictures", new_pictures_, "PicturesTooComplexToCompare",
271 pictures_too_complex_to_compare_, "DeepComparePictures",
272 deep_compare_pictures_, "SameInstancePictures",
273 same_instance_pictures_,
274 "DifferentInstanceButEqualPictures",
275 different_instance_but_equal_pictures_);
276#endif // !FLUTTER_RELEASE
277}
278
279} // namespace flutter
280

source code of flutter_engine/flutter/flow/diff_context.cc