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 | |
12 | namespace flutter { |
13 | |
14 | std::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 | |
49 | CompositorContext::CompositorContext() |
50 | : texture_registry_(std::make_shared<TextureRegistry>()), |
51 | raster_time_(fixed_refresh_rate_updater_), |
52 | ui_time_(fixed_refresh_rate_updater_) {} |
53 | |
54 | CompositorContext::CompositorContext(Stopwatch::RefreshRateUpdater& updater) |
55 | : texture_registry_(std::make_shared<TextureRegistry>()), |
56 | raster_time_(updater), |
57 | ui_time_(updater) {} |
58 | |
59 | CompositorContext::~CompositorContext() = default; |
60 | |
61 | void CompositorContext::BeginFrame(ScopedFrame& frame, |
62 | bool enable_instrumentation) { |
63 | if (enable_instrumentation) { |
64 | raster_time_.Start(); |
65 | } |
66 | } |
67 | |
68 | void CompositorContext::EndFrame(ScopedFrame& frame, |
69 | bool enable_instrumentation) { |
70 | if (enable_instrumentation) { |
71 | raster_time_.Stop(); |
72 | } |
73 | } |
74 | |
75 | std::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 | |
91 | CompositorContext::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 | |
113 | CompositorContext::ScopedFrame::~ScopedFrame() { |
114 | context_.EndFrame(frame&: *this, enable_instrumentation: instrumentation_enabled_); |
115 | } |
116 | |
117 | RasterStatus 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 | |
160 | void 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 | |
186 | void 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. |
208 | constexpr float kImpellerRepaintRatio = 0.7f; |
209 | |
210 | bool 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 | |
225 | void CompositorContext::OnGrContextCreated() { |
226 | texture_registry_->OnGrContextCreated(); |
227 | raster_cache_.Clear(); |
228 | } |
229 | |
230 | void CompositorContext::OnGrContextDestroyed() { |
231 | texture_registry_->OnGrContextDestroyed(); |
232 | raster_cache_.Clear(); |
233 | } |
234 | |
235 | } // namespace flutter |
236 | |