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#ifndef FLUTTER_FLOW_EMBEDDED_VIEWS_H_
6#define FLUTTER_FLOW_EMBEDDED_VIEWS_H_
7
8#include <memory>
9#include <vector>
10
11#include "flutter/display_list/dl_builder.h"
12#include "flutter/display_list/skia/dl_sk_canvas.h"
13#include "flutter/flow/surface_frame.h"
14#include "flutter/fml/memory/ref_counted.h"
15#include "flutter/fml/raster_thread_merger.h"
16#include "third_party/skia/include/core/SkMatrix.h"
17#include "third_party/skia/include/core/SkPath.h"
18#include "third_party/skia/include/core/SkRRect.h"
19#include "third_party/skia/include/core/SkRect.h"
20#include "third_party/skia/include/core/SkSize.h"
21
22#if IMPELLER_SUPPORTS_RENDERING
23#include "flutter/impeller/aiks/aiks_context.h" // nogncheck
24#include "flutter/impeller/renderer/context.h" // nogncheck
25#else // IMPELLER_SUPPORTS_RENDERING
26namespace impeller {
27class Context;
28class AiksContext;
29} // namespace impeller
30#endif // !IMPELLER_SUPPORTS_RENDERING
31
32class GrDirectContext;
33
34namespace flutter {
35
36enum MutatorType {
37 kClipRect,
38 kClipRRect,
39 kClipPath,
40 kTransform,
41 kOpacity,
42 kBackdropFilter
43};
44
45// Represents an image filter mutation.
46//
47// Should be used for image_filter_layer and backdrop_filter_layer.
48// TODO(cyanglaz): Refactor this into a ImageFilterMutator class.
49// https://github.com/flutter/flutter/issues/108470
50class ImageFilterMutation {
51 public:
52 ImageFilterMutation(std::shared_ptr<const DlImageFilter> filter,
53 const SkRect& filter_rect)
54 : filter_(filter), filter_rect_(filter_rect) {}
55
56 const DlImageFilter& GetFilter() const { return *filter_; }
57 const SkRect& GetFilterRect() const { return filter_rect_; }
58
59 bool operator==(const ImageFilterMutation& other) const {
60 return *filter_ == *other.filter_ && filter_rect_ == other.filter_rect_;
61 }
62
63 bool operator!=(const ImageFilterMutation& other) const {
64 return !operator==(other);
65 }
66
67 private:
68 std::shared_ptr<const DlImageFilter> filter_;
69 const SkRect filter_rect_;
70};
71
72// Stores mutation information like clipping or kTransform.
73//
74// The `type` indicates the type of the mutation: kClipRect, kTransform and etc.
75// Each `type` is paired with an object that supports the mutation. For example,
76// if the `type` is kClipRect, `rect()` is used the represent the rect to be
77// clipped. One mutation object must only contain one type of mutation.
78class Mutator {
79 public:
80 Mutator(const Mutator& other) {
81 type_ = other.type_;
82 switch (other.type_) {
83 case kClipRect:
84 rect_ = other.rect_;
85 break;
86 case kClipRRect:
87 rrect_ = other.rrect_;
88 break;
89 case kClipPath:
90 path_ = new SkPath(*other.path_);
91 break;
92 case kTransform:
93 matrix_ = other.matrix_;
94 break;
95 case kOpacity:
96 alpha_ = other.alpha_;
97 break;
98 case kBackdropFilter:
99 filter_mutation_ = other.filter_mutation_;
100 break;
101 default:
102 break;
103 }
104 }
105
106 explicit Mutator(const SkRect& rect) : type_(kClipRect), rect_(rect) {}
107 explicit Mutator(const SkRRect& rrect) : type_(kClipRRect), rrect_(rrect) {}
108 explicit Mutator(const SkPath& path)
109 : type_(kClipPath), path_(new SkPath(path)) {}
110 explicit Mutator(const SkMatrix& matrix)
111 : type_(kTransform), matrix_(matrix) {}
112 explicit Mutator(const int& alpha) : type_(kOpacity), alpha_(alpha) {}
113 explicit Mutator(std::shared_ptr<const DlImageFilter> filter,
114 const SkRect& filter_rect)
115 : type_(kBackdropFilter),
116 filter_mutation_(
117 std::make_shared<ImageFilterMutation>(args&: filter, args: filter_rect)) {}
118
119 const MutatorType& GetType() const { return type_; }
120 const SkRect& GetRect() const { return rect_; }
121 const SkRRect& GetRRect() const { return rrect_; }
122 const SkPath& GetPath() const { return *path_; }
123 const SkMatrix& GetMatrix() const { return matrix_; }
124 const ImageFilterMutation& GetFilterMutation() const {
125 return *filter_mutation_;
126 }
127 const int& GetAlpha() const { return alpha_; }
128 float GetAlphaFloat() const { return (alpha_ / 255.0f); }
129
130 bool operator==(const Mutator& other) const {
131 if (type_ != other.type_) {
132 return false;
133 }
134 switch (type_) {
135 case kClipRect:
136 return rect_ == other.rect_;
137 case kClipRRect:
138 return rrect_ == other.rrect_;
139 case kClipPath:
140 return *path_ == *other.path_;
141 case kTransform:
142 return matrix_ == other.matrix_;
143 case kOpacity:
144 return alpha_ == other.alpha_;
145 case kBackdropFilter:
146 return *filter_mutation_ == *other.filter_mutation_;
147 }
148
149 return false;
150 }
151
152 bool operator!=(const Mutator& other) const { return !operator==(other); }
153
154 bool IsClipType() {
155 return type_ == kClipRect || type_ == kClipRRect || type_ == kClipPath;
156 }
157
158 ~Mutator() {
159 if (type_ == kClipPath) {
160 delete path_;
161 }
162 };
163
164 private:
165 MutatorType type_;
166
167 // TODO(cyanglaz): Remove union.
168 // https://github.com/flutter/flutter/issues/108470
169 union {
170 SkRect rect_;
171 SkRRect rrect_;
172 SkMatrix matrix_;
173 SkPath* path_;
174 int alpha_;
175 };
176
177 std::shared_ptr<ImageFilterMutation> filter_mutation_;
178}; // Mutator
179
180// A stack of mutators that can be applied to an embedded platform view.
181//
182// The stack may include mutators like transforms and clips, each mutator
183// applies to all the mutators that are below it in the stack and to the
184// embedded view.
185//
186// For example consider the following stack: [T1, T2, T3], where T1 is the top
187// of the stack and T3 is the bottom of the stack. Applying this mutators stack
188// to a platform view P1 will result in T1(T2(T3(P1))).
189class MutatorsStack {
190 public:
191 MutatorsStack() = default;
192
193 void PushClipRect(const SkRect& rect);
194 void PushClipRRect(const SkRRect& rrect);
195 void PushClipPath(const SkPath& path);
196 void PushTransform(const SkMatrix& matrix);
197 void PushOpacity(const int& alpha);
198 // `filter_rect` is in global coordinates.
199 void PushBackdropFilter(const std::shared_ptr<const DlImageFilter>& filter,
200 const SkRect& filter_rect);
201
202 // Removes the `Mutator` on the top of the stack
203 // and destroys it.
204 void Pop();
205
206 void PopTo(size_t stack_count);
207
208 // Returns a reverse iterator pointing to the top of the stack, which is the
209 // mutator that is furtherest from the leaf node.
210 const std::vector<std::shared_ptr<Mutator>>::const_reverse_iterator Top()
211 const;
212 // Returns a reverse iterator pointing to the bottom of the stack, which is
213 // the mutator that is closeset from the leaf node.
214 const std::vector<std::shared_ptr<Mutator>>::const_reverse_iterator Bottom()
215 const;
216
217 // Returns an iterator pointing to the beginning of the mutator vector, which
218 // is the mutator that is furtherest from the leaf node.
219 const std::vector<std::shared_ptr<Mutator>>::const_iterator Begin() const;
220
221 // Returns an iterator pointing to the end of the mutator vector, which is the
222 // mutator that is closest from the leaf node.
223 const std::vector<std::shared_ptr<Mutator>>::const_iterator End() const;
224
225 bool is_empty() const { return vector_.empty(); }
226 size_t stack_count() const { return vector_.size(); }
227
228 bool operator==(const MutatorsStack& other) const {
229 if (vector_.size() != other.vector_.size()) {
230 return false;
231 }
232 for (size_t i = 0; i < vector_.size(); i++) {
233 if (*vector_[i] != *other.vector_[i]) {
234 return false;
235 }
236 }
237 return true;
238 }
239
240 bool operator==(const std::vector<Mutator>& other) const {
241 if (vector_.size() != other.size()) {
242 return false;
243 }
244 for (size_t i = 0; i < vector_.size(); i++) {
245 if (*vector_[i] != other[i]) {
246 return false;
247 }
248 }
249 return true;
250 }
251
252 bool operator!=(const MutatorsStack& other) const {
253 return !operator==(other);
254 }
255
256 bool operator!=(const std::vector<Mutator>& other) const {
257 return !operator==(other);
258 }
259
260 private:
261 std::vector<std::shared_ptr<Mutator>> vector_;
262}; // MutatorsStack
263
264class EmbeddedViewParams {
265 public:
266 EmbeddedViewParams() = default;
267
268 EmbeddedViewParams(SkMatrix matrix,
269 SkSize size_points,
270 MutatorsStack mutators_stack)
271 : matrix_(matrix),
272 size_points_(size_points),
273 mutators_stack_(mutators_stack) {
274 SkPath path;
275 SkRect starting_rect = SkRect::MakeSize(size: size_points);
276 path.addRect(rect: starting_rect);
277 path.transform(matrix);
278 final_bounding_rect_ = path.getBounds();
279 }
280
281 // The transformation Matrix corresponding to the sum of all the
282 // transformations in the platform view's mutator stack.
283 const SkMatrix& transformMatrix() const { return matrix_; };
284 // The original size of the platform view before any mutation matrix is
285 // applied.
286 const SkSize& sizePoints() const { return size_points_; };
287 // The mutators stack contains the detailed step by step mutations for this
288 // platform view.
289 const MutatorsStack& mutatorsStack() const { return mutators_stack_; };
290 // The bounding rect of the platform view after applying all the mutations.
291 //
292 // Clippings are ignored.
293 const SkRect& finalBoundingRect() const { return final_bounding_rect_; }
294
295 // Pushes the stored DlImageFilter object to the mutators stack.
296 //
297 // `filter_rect` is in global coordinates.
298 void PushImageFilter(std::shared_ptr<const DlImageFilter> filter,
299 const SkRect& filter_rect) {
300 mutators_stack_.PushBackdropFilter(filter, filter_rect);
301 }
302
303 bool operator==(const EmbeddedViewParams& other) const {
304 return size_points_ == other.size_points_ &&
305 mutators_stack_ == other.mutators_stack_ &&
306 final_bounding_rect_ == other.final_bounding_rect_ &&
307 matrix_ == other.matrix_;
308 }
309
310 private:
311 SkMatrix matrix_;
312 SkSize size_points_;
313 MutatorsStack mutators_stack_;
314 SkRect final_bounding_rect_;
315};
316
317enum class PostPrerollResult {
318 // Frame has successfully rasterized.
319 kSuccess,
320 // Frame is submitted twice. This is currently only used when
321 // thread configuration change occurs.
322 kResubmitFrame,
323 // Frame is dropped and a new frame with the same layer tree is
324 // attempted. This is currently only used when thread configuration
325 // change occurs.
326 kSkipAndRetryFrame
327};
328
329// The |EmbedderViewSlice| represents the details of recording all of
330// the layer tree rendering operations that appear between before, after
331// and between the embedded views. The Slice used to abstract away
332// implementations that were based on either an SkPicture or a
333// DisplayListBuilder but more recently all of the embedder recordings
334// have standardized on the DisplayList.
335class EmbedderViewSlice {
336 public:
337 virtual ~EmbedderViewSlice() = default;
338 virtual DlCanvas* canvas() = 0;
339 virtual void end_recording() = 0;
340 virtual std::list<SkRect> searchNonOverlappingDrawnRects(
341 const SkRect& query) const = 0;
342 virtual void render_into(DlCanvas* canvas) = 0;
343};
344
345class DisplayListEmbedderViewSlice : public EmbedderViewSlice {
346 public:
347 DisplayListEmbedderViewSlice(SkRect view_bounds);
348 ~DisplayListEmbedderViewSlice() override = default;
349
350 DlCanvas* canvas() override;
351 void end_recording() override;
352 std::list<SkRect> searchNonOverlappingDrawnRects(
353 const SkRect& query) const override;
354 void render_into(DlCanvas* canvas) override;
355 void dispatch(DlOpReceiver& receiver);
356 bool is_empty();
357 bool recording_ended();
358
359 private:
360 std::unique_ptr<DisplayListBuilder> builder_;
361 sk_sp<DisplayList> display_list_;
362};
363
364// Facilitates embedding of platform views within the flow layer tree.
365//
366// Used on iOS, Android (hybrid composite mode), and on embedded platforms
367// that provide a system compositor as part of the project arguments.
368class ExternalViewEmbedder {
369 // TODO(cyanglaz): Make embedder own the `EmbeddedViewParams`.
370
371 public:
372 ExternalViewEmbedder() = default;
373
374 virtual ~ExternalViewEmbedder() = default;
375
376 // Usually, the root canvas is not owned by the view embedder. However, if
377 // the view embedder wants to provide a canvas to the rasterizer, it may
378 // return one here. This canvas takes priority over the canvas materialized
379 // from the on-screen render target.
380 virtual DlCanvas* GetRootCanvas() = 0;
381
382 // Call this in-lieu of |SubmitFrame| to clear pre-roll state and
383 // sets the stage for the next pre-roll.
384 virtual void CancelFrame() = 0;
385
386 // Indicates the beginning of a frame.
387 //
388 // The `raster_thread_merger` will be null if |SupportsDynamicThreadMerging|
389 // returns false.
390 virtual void BeginFrame(
391 SkISize frame_size,
392 GrDirectContext* context,
393 double device_pixel_ratio,
394 fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) = 0;
395
396 virtual void PrerollCompositeEmbeddedView(
397 int64_t view_id,
398 std::unique_ptr<EmbeddedViewParams> params) = 0;
399
400 // This needs to get called after |Preroll| finishes on the layer tree.
401 // Returns kResubmitFrame if the frame needs to be processed again, this is
402 // after it does any requisite tasks needed to bring itself to a valid state.
403 // Returns kSuccess if the view embedder is already in a valid state.
404 virtual PostPrerollResult PostPrerollAction(
405 fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
406 return PostPrerollResult::kSuccess;
407 }
408
409 // Must be called on the UI thread.
410 virtual DlCanvas* CompositeEmbeddedView(int64_t view_id) = 0;
411
412 // Implementers must submit the frame by calling frame.Submit().
413 //
414 // This method can mutate the root Skia canvas before submitting the frame.
415 //
416 // It can also allocate frames for overlay surfaces to compose hybrid views.
417 virtual void SubmitFrame(
418 GrDirectContext* context,
419 const std::shared_ptr<impeller::AiksContext>& aiks_context,
420 std::unique_ptr<SurfaceFrame> frame);
421
422 // This method provides the embedder a way to do additional tasks after
423 // |SubmitFrame|. For example, merge task runners if `should_resubmit_frame`
424 // is true.
425 //
426 // For example on the iOS embedder, threads are merged in this call.
427 // A new frame on the platform thread starts immediately. If the GPU thread
428 // still has some task running, there could be two frames being rendered
429 // concurrently, which causes undefined behaviors.
430 //
431 // The `raster_thread_merger` will be null if |SupportsDynamicThreadMerging|
432 // returns false.
433 virtual void EndFrame(
434 bool should_resubmit_frame,
435 fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {}
436
437 // Whether the embedder should support dynamic thread merging.
438 //
439 // Returning `true` results a |RasterThreadMerger| instance to be created.
440 // * See also |BegineFrame| and |EndFrame| for getting the
441 // |RasterThreadMerger| instance.
442 virtual bool SupportsDynamicThreadMerging();
443
444 // Called when the rasterizer is being torn down.
445 // This method provides a way to release resources associated with the current
446 // embedder.
447 virtual void Teardown();
448
449 // Change the flag about whether it is used in this frame, it will be set to
450 // true when 'BeginFrame' and false when 'EndFrame'.
451 void SetUsedThisFrame(bool used_this_frame) {
452 used_this_frame_ = used_this_frame;
453 }
454
455 // Whether it is used in this frame, returns true between 'BeginFrame' and
456 // 'EndFrame', otherwise returns false.
457 bool GetUsedThisFrame() const { return used_this_frame_; }
458
459 // Pushes the platform view id of a visited platform view to a list of
460 // visited platform views.
461 virtual void PushVisitedPlatformView(int64_t view_id) {}
462
463 // Pushes a DlImageFilter object to each platform view within a list of
464 // visited platform views.
465 //
466 // `filter_rect` is in global coordinates.
467 //
468 // See also: |PushVisitedPlatformView| for pushing platform view ids to the
469 // visited platform views list.
470 virtual void PushFilterToVisitedPlatformViews(
471 std::shared_ptr<const DlImageFilter> filter,
472 const SkRect& filter_rect) {}
473
474 private:
475 bool used_this_frame_ = false;
476
477 FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder);
478
479}; // ExternalViewEmbedder
480
481} // namespace flutter
482
483#endif // FLUTTER_FLOW_EMBEDDED_VIEWS_H_
484

source code of flutter_engine/flutter/flow/embedded_views.h