blob: 84ad68f41faa6fed0b376f068f397609867a3a7e [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLOW_EMBEDDED_VIEWS_H_
#define FLUTTER_FLOW_EMBEDDED_VIEWS_H_
#include <vector>
#include "flutter/display_list/display_list_builder.h"
#include "flutter/flow/rtree.h"
#include "flutter/flow/surface_frame.h"
#include "flutter/fml/memory/ref_counted.h"
#include "flutter/fml/raster_thread_merger.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/core/SkPoint.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkSize.h"
#include "third_party/skia/include/core/SkSurface.h"
namespace flutter {
enum MutatorType {
kClipRect,
kClipRRect,
kClipPath,
kTransform,
kOpacity,
kBackdropFilter
};
// Represents an image filter mutation.
//
// Should be used for image_filter_layer and backdrop_filter_layer.
// TODO(cyanglaz): Refactor this into a ImageFilterMutator class.
// https://github.com/flutter/flutter/issues/108470
class ImageFilterMutation {
public:
ImageFilterMutation(std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect)
: filter_(filter), filter_rect_(filter_rect) {}
const DlImageFilter& GetFilter() const { return *filter_; }
const SkRect& GetFilterRect() const { return filter_rect_; }
bool operator==(const ImageFilterMutation& other) const {
return *filter_ == *other.filter_ && filter_rect_ == other.filter_rect_;
}
bool operator!=(const ImageFilterMutation& other) const {
return !operator==(other);
}
private:
std::shared_ptr<const DlImageFilter> filter_;
const SkRect filter_rect_;
};
// Stores mutation information like clipping or kTransform.
//
// The `type` indicates the type of the mutation: kClipRect, kTransform and etc.
// Each `type` is paired with an object that supports the mutation. For example,
// if the `type` is kClipRect, `rect()` is used the represent the rect to be
// clipped. One mutation object must only contain one type of mutation.
class Mutator {
public:
Mutator(const Mutator& other) {
type_ = other.type_;
switch (other.type_) {
case kClipRect:
rect_ = other.rect_;
break;
case kClipRRect:
rrect_ = other.rrect_;
break;
case kClipPath:
path_ = new SkPath(*other.path_);
break;
case kTransform:
matrix_ = other.matrix_;
break;
case kOpacity:
alpha_ = other.alpha_;
break;
case kBackdropFilter:
filter_mutation_ = other.filter_mutation_;
break;
default:
break;
}
}
explicit Mutator(const SkRect& rect) : type_(kClipRect), rect_(rect) {}
explicit Mutator(const SkRRect& rrect) : type_(kClipRRect), rrect_(rrect) {}
explicit Mutator(const SkPath& path)
: type_(kClipPath), path_(new SkPath(path)) {}
explicit Mutator(const SkMatrix& matrix)
: type_(kTransform), matrix_(matrix) {}
explicit Mutator(const int& alpha) : type_(kOpacity), alpha_(alpha) {}
explicit Mutator(std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect)
: type_(kBackdropFilter),
filter_mutation_(
std::make_shared<ImageFilterMutation>(filter, filter_rect)) {}
const MutatorType& GetType() const { return type_; }
const SkRect& GetRect() const { return rect_; }
const SkRRect& GetRRect() const { return rrect_; }
const SkPath& GetPath() const { return *path_; }
const SkMatrix& GetMatrix() const { return matrix_; }
const ImageFilterMutation& GetFilterMutation() const {
return *filter_mutation_;
}
const int& GetAlpha() const { return alpha_; }
float GetAlphaFloat() const { return (alpha_ / 255.0); }
bool operator==(const Mutator& other) const {
if (type_ != other.type_) {
return false;
}
switch (type_) {
case kClipRect:
return rect_ == other.rect_;
case kClipRRect:
return rrect_ == other.rrect_;
case kClipPath:
return *path_ == *other.path_;
case kTransform:
return matrix_ == other.matrix_;
case kOpacity:
return alpha_ == other.alpha_;
case kBackdropFilter:
return *filter_mutation_ == *other.filter_mutation_;
}
return false;
}
bool operator!=(const Mutator& other) const { return !operator==(other); }
bool IsClipType() {
return type_ == kClipRect || type_ == kClipRRect || type_ == kClipPath;
}
~Mutator() {
if (type_ == kClipPath) {
delete path_;
}
};
private:
MutatorType type_;
// TODO(cyanglaz): Remove union.
// https://github.com/flutter/flutter/issues/108470
union {
SkRect rect_;
SkRRect rrect_;
SkMatrix matrix_;
SkPath* path_;
int alpha_;
};
std::shared_ptr<ImageFilterMutation> filter_mutation_;
}; // Mutator
// A stack of mutators that can be applied to an embedded platform view.
//
// The stack may include mutators like transforms and clips, each mutator
// applies to all the mutators that are below it in the stack and to the
// embedded view.
//
// For example consider the following stack: [T1, T2, T3], where T1 is the top
// of the stack and T3 is the bottom of the stack. Applying this mutators stack
// to a platform view P1 will result in T1(T2(T3(P1))).
class MutatorsStack {
public:
MutatorsStack() = default;
void PushClipRect(const SkRect& rect);
void PushClipRRect(const SkRRect& rrect);
void PushClipPath(const SkPath& path);
void PushTransform(const SkMatrix& matrix);
void PushOpacity(const int& alpha);
void PushBackdropFilter(const std::shared_ptr<const DlImageFilter>& filter,
const SkRect& filter_rect);
// Removes the `Mutator` on the top of the stack
// and destroys it.
void Pop();
void PopTo(size_t stack_count);
// Returns a reverse iterator pointing to the top of the stack, which is the
// mutator that is furtherest from the leaf node.
const std::vector<std::shared_ptr<Mutator>>::const_reverse_iterator Top()
const;
// Returns a reverse iterator pointing to the bottom of the stack, which is
// the mutator that is closeset from the leaf node.
const std::vector<std::shared_ptr<Mutator>>::const_reverse_iterator Bottom()
const;
// Returns an iterator pointing to the beginning of the mutator vector, which
// is the mutator that is furtherest from the leaf node.
const std::vector<std::shared_ptr<Mutator>>::const_iterator Begin() const;
// Returns an iterator pointing to the end of the mutator vector, which is the
// mutator that is closest from the leaf node.
const std::vector<std::shared_ptr<Mutator>>::const_iterator End() const;
bool is_empty() const { return vector_.empty(); }
size_t stack_count() const { return vector_.size(); }
bool operator==(const MutatorsStack& other) const {
if (vector_.size() != other.vector_.size()) {
return false;
}
for (size_t i = 0; i < vector_.size(); i++) {
if (*vector_[i] != *other.vector_[i]) {
return false;
}
}
return true;
}
bool operator==(const std::vector<Mutator>& other) const {
if (vector_.size() != other.size()) {
return false;
}
for (size_t i = 0; i < vector_.size(); i++) {
if (*vector_[i] != other[i]) {
return false;
}
}
return true;
}
bool operator!=(const MutatorsStack& other) const {
return !operator==(other);
}
bool operator!=(const std::vector<Mutator>& other) const {
return !operator==(other);
}
private:
std::vector<std::shared_ptr<Mutator>> vector_;
}; // MutatorsStack
class EmbeddedViewParams {
public:
EmbeddedViewParams() = default;
EmbeddedViewParams(SkMatrix matrix,
SkSize size_points,
MutatorsStack mutators_stack,
bool display_list_enabled = false)
: matrix_(matrix),
size_points_(size_points),
mutators_stack_(mutators_stack),
display_list_enabled_(display_list_enabled) {
SkPath path;
SkRect starting_rect = SkRect::MakeSize(size_points);
path.addRect(starting_rect);
path.transform(matrix);
final_bounding_rect_ = path.getBounds();
}
// The transformation Matrix corresponding to the sum of all the
// transformations in the platform view's mutator stack.
const SkMatrix& transformMatrix() const { return matrix_; };
// The original size of the platform view before any mutation matrix is
// applied.
const SkSize& sizePoints() const { return size_points_; };
// The mutators stack contains the detailed step by step mutations for this
// platform view.
const MutatorsStack& mutatorsStack() const { return mutators_stack_; };
// The bounding rect of the platform view after applying all the mutations.
//
// Clippings are ignored.
const SkRect& finalBoundingRect() const { return final_bounding_rect_; }
// Pushes the stored DlImageFilter object to the mutators stack.
void PushImageFilter(std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) {
mutators_stack_.PushBackdropFilter(filter, filter_rect);
}
// Whether the embedder should construct DisplayList objects to hold the
// rendering commands for each between-view slice of the layer tree.
bool display_list_enabled() const { return display_list_enabled_; }
bool operator==(const EmbeddedViewParams& other) const {
return size_points_ == other.size_points_ &&
mutators_stack_ == other.mutators_stack_ &&
final_bounding_rect_ == other.final_bounding_rect_ &&
matrix_ == other.matrix_;
}
private:
SkMatrix matrix_;
SkSize size_points_;
MutatorsStack mutators_stack_;
SkRect final_bounding_rect_;
bool display_list_enabled_;
};
enum class PostPrerollResult {
// Frame has successfully rasterized.
kSuccess,
// Frame is submitted twice. This is currently only used when
// thread configuration change occurs.
kResubmitFrame,
// Frame is dropped and a new frame with the same layer tree is
// attempted. This is currently only used when thread configuration
// change occurs.
kSkipAndRetryFrame
};
// The |PlatformViewLayer| calls |CompositeEmbeddedView| in its |Paint|
// method to replace the leaf_nodes_canvas and leaf_nodes_builder in its
// |PaintContext| for subsequent layers in the frame to render into.
// The builder value will only be supplied if the associated ScopedFrame
// is being rendered to DisplayLists. The |EmbedderPaintContext| struct
// allows the method to return both values.
struct EmbedderPaintContext {
SkCanvas* canvas;
DisplayListBuilder* builder;
};
// The |EmbedderViewSlice| represents the details of recording all of
// the layer tree rendering operations that appear between before, after
// and between the embedded views. The Slice may be recorded into an
// SkPicture or a DisplayListBuilder depending on the ScopedFrame.
class EmbedderViewSlice {
public:
virtual ~EmbedderViewSlice() = default;
virtual SkCanvas* canvas() = 0;
virtual DisplayListBuilder* builder() = 0;
virtual void end_recording() = 0;
virtual std::list<SkRect> searchNonOverlappingDrawnRects(
const SkRect& query) const = 0;
virtual void render_into(SkCanvas* canvas) = 0;
virtual void render_into(DisplayListBuilder* builder) = 0;
};
class SkPictureEmbedderViewSlice : public EmbedderViewSlice {
public:
SkPictureEmbedderViewSlice(SkRect view_bounds);
~SkPictureEmbedderViewSlice() override = default;
SkCanvas* canvas() override;
DisplayListBuilder* builder() override;
void end_recording() override;
std::list<SkRect> searchNonOverlappingDrawnRects(
const SkRect& query) const override;
void render_into(SkCanvas* canvas) override;
void render_into(DisplayListBuilder* builder) override;
private:
std::unique_ptr<SkPictureRecorder> recorder_;
sk_sp<RTree> rtree_;
sk_sp<SkPicture> picture_;
};
class DisplayListEmbedderViewSlice : public EmbedderViewSlice {
public:
DisplayListEmbedderViewSlice(SkRect view_bounds);
~DisplayListEmbedderViewSlice() override = default;
SkCanvas* canvas() override;
DisplayListBuilder* builder() override;
void end_recording() override;
std::list<SkRect> searchNonOverlappingDrawnRects(
const SkRect& query) const override;
void render_into(SkCanvas* canvas) override;
void render_into(DisplayListBuilder* builder) override;
private:
std::unique_ptr<DisplayListCanvasRecorder> recorder_;
sk_sp<DisplayList> display_list_;
};
// Facilitates embedding of platform views within the flow layer tree.
//
// Used on iOS, Android (hybrid composite mode), and on embedded platforms
// that provide a system compositor as part of the project arguments.
class ExternalViewEmbedder {
// TODO(cyanglaz): Make embedder own the `EmbeddedViewParams`.
public:
ExternalViewEmbedder() = default;
virtual ~ExternalViewEmbedder() = default;
// Usually, the root canvas is not owned by the view embedder. However, if
// the view embedder wants to provide a canvas to the rasterizer, it may
// return one here. This canvas takes priority over the canvas materialized
// from the on-screen render target.
virtual SkCanvas* GetRootCanvas() = 0;
// Call this in-lieu of |SubmitFrame| to clear pre-roll state and
// sets the stage for the next pre-roll.
virtual void CancelFrame() = 0;
// Indicates the beginning of a frame.
//
// The `raster_thread_merger` will be null if |SupportsDynamicThreadMerging|
// returns false.
virtual void BeginFrame(
SkISize frame_size,
GrDirectContext* context,
double device_pixel_ratio,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) = 0;
virtual void PrerollCompositeEmbeddedView(
int view_id,
std::unique_ptr<EmbeddedViewParams> params) = 0;
// This needs to get called after |Preroll| finishes on the layer tree.
// Returns kResubmitFrame if the frame needs to be processed again, this is
// after it does any requisite tasks needed to bring itself to a valid state.
// Returns kSuccess if the view embedder is already in a valid state.
virtual PostPrerollResult PostPrerollAction(
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
return PostPrerollResult::kSuccess;
}
virtual std::vector<SkCanvas*> GetCurrentCanvases() = 0;
virtual std::vector<DisplayListBuilder*> GetCurrentBuilders() = 0;
// Must be called on the UI thread.
virtual EmbedderPaintContext CompositeEmbeddedView(int view_id) = 0;
// Implementers must submit the frame by calling frame.Submit().
//
// This method can mutate the root Skia canvas before submitting the frame.
//
// It can also allocate frames for overlay surfaces to compose hybrid views.
virtual void SubmitFrame(GrDirectContext* context,
std::unique_ptr<SurfaceFrame> frame);
// This method provides the embedder a way to do additional tasks after
// |SubmitFrame|. For example, merge task runners if `should_resubmit_frame`
// is true.
//
// For example on the iOS embedder, threads are merged in this call.
// A new frame on the platform thread starts immediately. If the GPU thread
// still has some task running, there could be two frames being rendered
// concurrently, which causes undefined behaviors.
//
// The `raster_thread_merger` will be null if |SupportsDynamicThreadMerging|
// returns false.
virtual void EndFrame(
bool should_resubmit_frame,
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {}
// Whether the embedder should support dynamic thread merging.
//
// Returning `true` results a |RasterThreadMerger| instance to be created.
// * See also |BegineFrame| and |EndFrame| for getting the
// |RasterThreadMerger| instance.
virtual bool SupportsDynamicThreadMerging();
// Called when the rasterizer is being torn down.
// This method provides a way to release resources associated with the current
// embedder.
virtual void Teardown();
// Change the flag about whether it is used in this frame, it will be set to
// true when 'BeginFrame' and false when 'EndFrame'.
void SetUsedThisFrame(bool used_this_frame) {
used_this_frame_ = used_this_frame;
}
// Whether it is used in this frame, returns true between 'BeginFrame' and
// 'EndFrame', otherwise returns false.
bool GetUsedThisFrame() const { return used_this_frame_; }
// Pushes the platform view id of a visited platform view to a list of
// visited platform views.
virtual void PushVisitedPlatformView(int64_t view_id) {}
// Pushes a DlImageFilter object to each platform view within a list of
// visited platform views.
//
// See also: |PushVisitedPlatformView| for pushing platform view ids to the
// visited platform views list.
virtual void PushFilterToVisitedPlatformViews(
std::shared_ptr<const DlImageFilter> filter,
const SkRect& filter_rect) {}
private:
bool used_this_frame_ = false;
FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder);
}; // ExternalViewEmbedder
} // namespace flutter
#endif // FLUTTER_FLOW_EMBEDDED_VIEWS_H_