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/compositor_context.h"
6
7#include <optional>
8#include <utility>
9#include "flutter/flow/layers/layer_tree.h"
10#include "third_party/skia/include/core/SkCanvas.h"
11
12namespace flutter {
13
14std::optional<SkRect> FrameDamage::ComputeClipRect(
15 flutter::LayerTree& layer_tree,
16 bool has_raster_cache,
17 bool impeller_enabled) {
18 if (layer_tree.root_layer()) {
19 PaintRegionMap empty_paint_region_map;
20 DiffContext context(layer_tree.frame_size(), layer_tree.paint_region_map(),
21 prev_layer_tree_ ? prev_layer_tree_->paint_region_map()
22 : empty_paint_region_map,
23 has_raster_cache, impeller_enabled);
24 context.PushCullRect(clip: SkRect::MakeIWH(w: layer_tree.frame_size().width(),
25 h: layer_tree.frame_size().height()));
26 {
27 DiffContext::AutoSubtreeRestore subtree(&context);
28 const Layer* prev_root_layer = nullptr;
29 if (!prev_layer_tree_ ||
30 prev_layer_tree_->frame_size() != layer_tree.frame_size()) {
31 // If there is no previous layer tree assume the entire frame must be
32 // repainted.
33 context.MarkSubtreeDirty(previous_paint_region: SkRect::MakeIWH(
34 w: layer_tree.frame_size().width(), h: layer_tree.frame_size().height()));
35 } else {
36 prev_root_layer = prev_layer_tree_->root_layer();
37 }
38 layer_tree.root_layer()->Diff(context: &context, old_layer: prev_root_layer);
39 }
40
41 damage_ =
42 context.ComputeDamage(additional_damage: additional_damage_, horizontal_clip_alignment: horizontal_clip_alignment_,
43 vertical_clip_alignment: vertical_clip_alignment_);
44 return SkRect::Make(irect: damage_->buffer_damage);
45 }
46 return std::nullopt;
47}
48
49CompositorContext::CompositorContext()
50 : texture_registry_(std::make_shared<TextureRegistry>()),
51 raster_time_(fixed_refresh_rate_updater_),
52 ui_time_(fixed_refresh_rate_updater_) {}
53
54CompositorContext::CompositorContext(Stopwatch::RefreshRateUpdater& updater)
55 : texture_registry_(std::make_shared<TextureRegistry>()),
56 raster_time_(updater),
57 ui_time_(updater) {}
58
59CompositorContext::~CompositorContext() = default;
60
61void CompositorContext::BeginFrame(ScopedFrame& frame,
62 bool enable_instrumentation) {
63 if (enable_instrumentation) {
64 raster_time_.Start();
65 }
66}
67
68void CompositorContext::EndFrame(ScopedFrame& frame,
69 bool enable_instrumentation) {
70 if (enable_instrumentation) {
71 raster_time_.Stop();
72 }
73}
74
75std::unique_ptr<CompositorContext::ScopedFrame> CompositorContext::AcquireFrame(
76 GrDirectContext* gr_context,
77 DlCanvas* canvas,
78 ExternalViewEmbedder* view_embedder,
79 const SkMatrix& root_surface_transformation,
80 bool instrumentation_enabled,
81 bool surface_supports_readback,
82 fml::RefPtr<fml::RasterThreadMerger>
83 raster_thread_merger, // NOLINT(performance-unnecessary-value-param)
84 impeller::AiksContext* aiks_context) {
85 return std::make_unique<ScopedFrame>(
86 args&: *this, args&: gr_context, args&: canvas, args&: view_embedder, args: root_surface_transformation,
87 args&: instrumentation_enabled, args&: surface_supports_readback, args&: raster_thread_merger,
88 args&: aiks_context);
89}
90
91CompositorContext::ScopedFrame::ScopedFrame(
92 CompositorContext& context,
93 GrDirectContext* gr_context,
94 DlCanvas* canvas,
95 ExternalViewEmbedder* view_embedder,
96 const SkMatrix& root_surface_transformation,
97 bool instrumentation_enabled,
98 bool surface_supports_readback,
99 fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger,
100 impeller::AiksContext* aiks_context)
101 : context_(context),
102 gr_context_(gr_context),
103 canvas_(canvas),
104 aiks_context_(aiks_context),
105 view_embedder_(view_embedder),
106 root_surface_transformation_(root_surface_transformation),
107 instrumentation_enabled_(instrumentation_enabled),
108 surface_supports_readback_(surface_supports_readback),
109 raster_thread_merger_(std::move(raster_thread_merger)) {
110 context_.BeginFrame(frame&: *this, enable_instrumentation: instrumentation_enabled_);
111}
112
113CompositorContext::ScopedFrame::~ScopedFrame() {
114 context_.EndFrame(frame&: *this, enable_instrumentation: instrumentation_enabled_);
115}
116
117RasterStatus CompositorContext::ScopedFrame::Raster(
118 flutter::LayerTree& layer_tree,
119 bool ignore_raster_cache,
120 FrameDamage* frame_damage) {
121 TRACE_EVENT0("flutter", "CompositorContext::ScopedFrame::Raster");
122
123 std::optional<SkRect> clip_rect;
124 if (frame_damage) {
125 clip_rect = frame_damage->ComputeClipRect(layer_tree, has_raster_cache: !ignore_raster_cache,
126 impeller_enabled: !gr_context_);
127
128 if (aiks_context_ &&
129 !ShouldPerformPartialRepaint(damage_rect: clip_rect, layer_tree_size: layer_tree.frame_size())) {
130 clip_rect = std::nullopt;
131 frame_damage->Reset();
132 }
133 }
134
135 bool root_needs_readback = layer_tree.Preroll(
136 frame&: *this, ignore_raster_cache, cull_rect: clip_rect ? *clip_rect : kGiantRect);
137 bool needs_save_layer = root_needs_readback && !surface_supports_readback();
138 PostPrerollResult post_preroll_result = PostPrerollResult::kSuccess;
139 if (view_embedder_ && raster_thread_merger_) {
140 post_preroll_result =
141 view_embedder_->PostPrerollAction(raster_thread_merger: raster_thread_merger_);
142 }
143
144 if (post_preroll_result == PostPrerollResult::kResubmitFrame) {
145 return RasterStatus::kResubmit;
146 }
147 if (post_preroll_result == PostPrerollResult::kSkipAndRetryFrame) {
148 return RasterStatus::kSkipAndRetry;
149 }
150
151 if (aiks_context_) {
152 PaintLayerTreeImpeller(layer_tree, clip_rect, ignore_raster_cache);
153 } else {
154 PaintLayerTreeSkia(layer_tree, clip_rect, needs_save_layer,
155 ignore_raster_cache);
156 }
157 return RasterStatus::kSuccess;
158}
159
160void CompositorContext::ScopedFrame::PaintLayerTreeSkia(
161 flutter::LayerTree& layer_tree,
162 std::optional<SkRect> clip_rect,
163 bool needs_save_layer,
164 bool ignore_raster_cache) {
165 DlAutoCanvasRestore restore(canvas(), clip_rect.has_value());
166
167 if (canvas()) {
168 if (clip_rect) {
169 canvas()->ClipRect(rect: *clip_rect);
170 }
171
172 if (needs_save_layer) {
173 TRACE_EVENT0("flutter", "Canvas::saveLayer");
174 SkRect bounds = SkRect::Make(size: layer_tree.frame_size());
175 DlPaint paint;
176 paint.setBlendMode(DlBlendMode::kSrc);
177 canvas()->SaveLayer(bounds: &bounds, paint: &paint);
178 }
179 canvas()->Clear(color: DlColor::kTransparent());
180 }
181
182 // The canvas()->Restore() is taken care of by the DlAutoCanvasRestore
183 layer_tree.Paint(frame&: *this, ignore_raster_cache);
184}
185
186void CompositorContext::ScopedFrame::PaintLayerTreeImpeller(
187 flutter::LayerTree& layer_tree,
188 std::optional<SkRect> clip_rect,
189 bool ignore_raster_cache) {
190 if (canvas() && clip_rect) {
191 canvas()->Translate(tx: -clip_rect->x(), ty: -clip_rect->y());
192 }
193
194 layer_tree.Paint(frame&: *this, ignore_raster_cache);
195}
196
197/// @brief The max ratio of pixel width or height to size that is dirty which
198/// results in a partial repaint.
199///
200/// Performing a partial repaint has a small overhead - Impeller needs to
201/// allocate a fairly large resolve texture for the root pass instead of
202/// using the drawable texture, and a final blit must be performed. At a
203/// minimum, if the damage rect is the entire buffer, we must not perform
204/// a partial repaint. Beyond that, we could only experimentally
205/// determine what this value should be. From looking at the Flutter
206/// Gallery, we noticed that there are occassionally small partial
207/// repaints which shave off trivial numbers of pixels.
208constexpr float kImpellerRepaintRatio = 0.7f;
209
210bool CompositorContext::ShouldPerformPartialRepaint(
211 std::optional<SkRect> damage_rect,
212 SkISize layer_tree_size) {
213 if (!damage_rect.has_value()) {
214 return false;
215 }
216 if (damage_rect->width() >= layer_tree_size.width() &&
217 damage_rect->height() >= layer_tree_size.height()) {
218 return false;
219 }
220 auto rx = damage_rect->width() / layer_tree_size.width();
221 auto ry = damage_rect->height() / layer_tree_size.height();
222 return rx <= kImpellerRepaintRatio || ry <= kImpellerRepaintRatio;
223}
224
225void CompositorContext::OnGrContextCreated() {
226 texture_registry_->OnGrContextCreated();
227 raster_cache_.Clear();
228}
229
230void CompositorContext::OnGrContextDestroyed() {
231 texture_registry_->OnGrContextDestroyed();
232 raster_cache_.Clear();
233}
234
235} // namespace flutter
236

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