[embedder] embedder external view adopt DisplayListEmbedderViewSlice (flutter/engine#40578)

[embedder] embedder external view adopt DisplayListEmbedderViewSlice
This commit is contained in:
Chris Yang
2023-04-10 11:54:39 -07:00
committed by GitHub
parent c13cb33ecb
commit 5da580cfb2
17 changed files with 1146 additions and 26 deletions

View File

@@ -207,6 +207,7 @@
../../../flutter/shell/common/animator_unittests.cc
../../../flutter/shell/common/canvas_spy_unittests.cc
../../../flutter/shell/common/context_options_unittests.cc
../../../flutter/shell/common/dl_op_spy_unittests.cc
../../../flutter/shell/common/engine_unittests.cc
../../../flutter/shell/common/fixtures
../../../flutter/shell/common/input_events_unittests.cc

View File

@@ -2113,6 +2113,8 @@ ORIGIN: ../../../flutter/shell/common/display.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/common/display.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/common/display_manager.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/common/display_manager.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/common/dl_op_spy.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/common/dl_op_spy.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/common/engine.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/common/engine.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/common/pipeline.cc + ../../../flutter/LICENSE
@@ -4701,6 +4703,8 @@ FILE: ../../../flutter/shell/common/display.cc
FILE: ../../../flutter/shell/common/display.h
FILE: ../../../flutter/shell/common/display_manager.cc
FILE: ../../../flutter/shell/common/display_manager.h
FILE: ../../../flutter/shell/common/dl_op_spy.cc
FILE: ../../../flutter/shell/common/dl_op_spy.h
FILE: ../../../flutter/shell/common/engine.cc
FILE: ../../../flutter/shell/common/engine.h
FILE: ../../../flutter/shell/common/pipeline.cc

View File

@@ -9,8 +9,7 @@ namespace flutter {
/// A enum define the blend mode.
/// Blends are operators that take in two colors (source, destination) and
/// return a new color. Blends are operators that take in two colors (source,
/// destination) and return a new color. Many of these operate the same on all 4
/// return a new color. Many of these operate the same on all 4
/// components: red, green, blue, alpha. For these, we just document what
/// happens to one component, rather than naming each one separately. Different
/// color types might have different representations for color components:

View File

@@ -30,6 +30,18 @@ void DisplayListEmbedderViewSlice::render_into(DlCanvas* canvas) {
canvas->DrawDisplayList(display_list_);
}
void DisplayListEmbedderViewSlice::dispatch(DlOpReceiver& receiver) {
display_list_->Dispatch(receiver);
}
bool DisplayListEmbedderViewSlice::is_empty() {
return display_list_->bounds().isEmpty();
}
bool DisplayListEmbedderViewSlice::recording_ended() {
return builder_ == nullptr;
}
void ExternalViewEmbedder::SubmitFrame(GrDirectContext* context,
std::unique_ptr<SurfaceFrame> frame) {
frame->Submit();

View File

@@ -352,6 +352,9 @@ class DisplayListEmbedderViewSlice : public EmbedderViewSlice {
std::list<SkRect> searchNonOverlappingDrawnRects(
const SkRect& query) const override;
void render_into(DlCanvas* canvas) override;
void dispatch(DlOpReceiver& receiver);
bool is_empty();
bool recording_ended();
private:
std::unique_ptr<DisplayListBuilder> builder_;

View File

@@ -77,6 +77,8 @@ source_set("common") {
"display.h",
"display_manager.cc",
"display_manager.h",
"dl_op_spy.cc",
"dl_op_spy.h",
"engine.cc",
"engine.h",
"pipeline.cc",
@@ -287,6 +289,7 @@ if (enable_unittests) {
"animator_unittests.cc",
"canvas_spy_unittests.cc",
"context_options_unittests.cc",
"dl_op_spy_unittests.cc",
"engine_unittests.cc",
"input_events_unittests.cc",
"persistent_cache_unittests.cc",

View File

@@ -0,0 +1,138 @@
// 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.
#include "flutter/shell/common/dl_op_spy.h"
namespace flutter {
bool DlOpSpy::did_draw() {
return did_draw_;
}
void DlOpSpy::setColor(DlColor color) {
if (color.isTransparent()) {
will_draw_ = false;
} else {
will_draw_ = true;
}
}
void DlOpSpy::setColorSource(const DlColorSource* source) {
if (!source) {
return;
}
const DlColorColorSource* color_source = source->asColor();
if (color_source && color_source->color().isTransparent()) {
will_draw_ = false;
return;
}
will_draw_ = true;
}
void DlOpSpy::save() {}
void DlOpSpy::saveLayer(const SkRect* bounds,
const SaveLayerOptions options,
const DlImageFilter* backdrop) {}
void DlOpSpy::restore() {}
void DlOpSpy::drawColor(DlColor color, DlBlendMode mode) {
did_draw_ |= !color.isTransparent();
}
void DlOpSpy::drawPaint() {
did_draw_ |= will_draw_;
}
// TODO(cyanglaz): check whether the shape (line, rect, oval, etc) needs to be
// evaluated. https://github.com/flutter/flutter/issues/123803
void DlOpSpy::drawLine(const SkPoint& p0, const SkPoint& p1) {
did_draw_ |= will_draw_;
}
void DlOpSpy::drawRect(const SkRect& rect) {
did_draw_ |= will_draw_;
}
void DlOpSpy::drawOval(const SkRect& bounds) {
did_draw_ |= will_draw_;
}
void DlOpSpy::drawCircle(const SkPoint& center, SkScalar radius) {
did_draw_ |= will_draw_;
}
void DlOpSpy::drawRRect(const SkRRect& rrect) {
did_draw_ |= will_draw_;
}
void DlOpSpy::drawDRRect(const SkRRect& outer, const SkRRect& inner) {
did_draw_ |= will_draw_;
}
void DlOpSpy::drawPath(const SkPath& path) {
did_draw_ |= will_draw_;
}
void DlOpSpy::drawArc(const SkRect& oval_bounds,
SkScalar start_degrees,
SkScalar sweep_degrees,
bool use_center) {
did_draw_ |= will_draw_;
}
void DlOpSpy::drawPoints(PointMode mode,
uint32_t count,
const SkPoint points[]) {
did_draw_ |= will_draw_;
}
void DlOpSpy::drawVertices(const DlVertices* vertices, DlBlendMode mode) {
did_draw_ |= will_draw_;
}
// In theory, below drawImage methods can produce a transparent screen when a
// transparent image is provided. The operation of determine whether an image is
// transparent needs examine all the pixels in the image object, which is slow.
// Drawing a completely transparent image is not a valid use case, thus, such
// case is ignored.
void DlOpSpy::drawImage(const sk_sp<DlImage> image,
const SkPoint point,
DlImageSampling sampling,
bool render_with_attributes) {
did_draw_ = true;
}
void DlOpSpy::drawImageRect(const sk_sp<DlImage> image,
const SkRect& src,
const SkRect& dst,
DlImageSampling sampling,
bool render_with_attributes,
SrcRectConstraint constraint) {
did_draw_ = true;
}
void DlOpSpy::drawImageNine(const sk_sp<DlImage> image,
const SkIRect& center,
const SkRect& dst,
DlFilterMode filter,
bool render_with_attributes) {
did_draw_ = true;
}
void DlOpSpy::drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const SkRect tex[],
const DlColor colors[],
int count,
DlBlendMode mode,
DlImageSampling sampling,
const SkRect* cull_rect,
bool render_with_attributes) {
did_draw_ = true;
}
void DlOpSpy::drawDisplayList(const sk_sp<DisplayList> display_list,
SkScalar opacity) {
if (did_draw_ || opacity == 0) {
return;
}
DlOpSpy receiver;
display_list->Dispatch(receiver);
did_draw_ |= receiver.did_draw();
}
void DlOpSpy::drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) {
did_draw_ |= will_draw_;
}
void DlOpSpy::drawShadow(const SkPath& path,
const DlColor color,
const SkScalar elevation,
bool transparent_occluder,
SkScalar dpr) {
did_draw_ |= !color.isTransparent();
}
} // namespace flutter

View File

@@ -0,0 +1,109 @@
// 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_DISPLAY_LIST_DL_OP_SPY_H_
#define FLUTTER_DISPLAY_LIST_DL_OP_SPY_H_
#include "flutter/display_list/dl_op_receiver.h"
#include "flutter/display_list/utils/dl_receiver_utils.h"
namespace flutter {
//------------------------------------------------------------------------------
/// Receives to drawing commands of a DisplayListBuilder.
///
/// This is used to determine whether any non-transparent pixels will be drawn
/// on the canvas.
/// All the drawImage operations are considered drawing non-transparent pixels.
///
/// To use this class, dispatch the operations from DisplayList to a concrete
/// DlOpSpy object, and check the result of `did_draw` method.
///
/// ```
/// DlOpSpy dl_op_spy;
/// display_list.Dispatch(dl_op_spy);
/// bool did_draw = dl_op_spy.did_draw()
/// ```
///
class DlOpSpy final : public virtual DlOpReceiver,
private IgnoreAttributeDispatchHelper,
private IgnoreClipDispatchHelper,
private IgnoreTransformDispatchHelper {
public:
//----------------------------------------------------------------------------
/// @brief Returns true if any non transparent content has been drawn.
bool did_draw();
private:
void setColor(DlColor color) override;
void setColorSource(const DlColorSource* source) override;
void save() override;
void saveLayer(const SkRect* bounds,
const SaveLayerOptions options,
const DlImageFilter* backdrop) override;
void restore() override;
void drawColor(DlColor color, DlBlendMode mode) override;
void drawPaint() override;
void drawLine(const SkPoint& p0, const SkPoint& p1) override;
void drawRect(const SkRect& rect) override;
void drawOval(const SkRect& bounds) override;
void drawCircle(const SkPoint& center, SkScalar radius) override;
void drawRRect(const SkRRect& rrect) override;
void drawDRRect(const SkRRect& outer, const SkRRect& inner) override;
void drawPath(const SkPath& path) override;
void drawArc(const SkRect& oval_bounds,
SkScalar start_degrees,
SkScalar sweep_degrees,
bool use_center) override;
void drawPoints(PointMode mode,
uint32_t count,
const SkPoint points[]) override;
void drawVertices(const DlVertices* vertices, DlBlendMode mode) override;
void drawImage(const sk_sp<DlImage> image,
const SkPoint point,
DlImageSampling sampling,
bool render_with_attributes) override;
void drawImageRect(
const sk_sp<DlImage> image,
const SkRect& src,
const SkRect& dst,
DlImageSampling sampling,
bool render_with_attributes,
SrcRectConstraint constraint = SrcRectConstraint::kFast) override;
void drawImageNine(const sk_sp<DlImage> image,
const SkIRect& center,
const SkRect& dst,
DlFilterMode filter,
bool render_with_attributes) override;
void drawAtlas(const sk_sp<DlImage> atlas,
const SkRSXform xform[],
const SkRect tex[],
const DlColor colors[],
int count,
DlBlendMode mode,
DlImageSampling sampling,
const SkRect* cull_rect,
bool render_with_attributes) override;
void drawDisplayList(const sk_sp<DisplayList> display_list,
SkScalar opacity = SK_Scalar1) override;
void drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) override;
void drawShadow(const SkPath& path,
const DlColor color,
const SkScalar elevation,
bool transparent_occluder,
SkScalar dpr) override;
// Indicates if the attributes are set to values that will modify the
// destination. For now, the test only checks if there is a non-transparent
// color set.
bool will_draw_ = true;
bool did_draw_ = false;
};
} // namespace flutter
#endif // FLUTTER_DISPLAY_LIST_DL_OP_SPY_H_

View File

@@ -0,0 +1,524 @@
// 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.
#include "flutter/display_list/display_list.h"
#include "flutter/display_list/dl_builder.h"
#include "flutter/shell/common/dl_op_spy.h"
#include "flutter/testing/testing.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace flutter {
namespace testing {
TEST(DlOpSpy, DidDrawIsFalseByDefault) {
DlOpSpy dl_op_spy;
ASSERT_FALSE(dl_op_spy.did_draw());
}
TEST(DlOpSpy, SetColor) {
{ // No Color set.
DisplayListBuilder builder;
DlPaint paint;
builder.DrawRect(SkRect::MakeWH(5, 5), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // Set transparent color.
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawRect(SkRect::MakeWH(5, 5), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
{ // Set black color.
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawRect(SkRect::MakeWH(5, 5), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, SetColorSource) {
{ // Set null source
DisplayListBuilder builder;
DlPaint paint;
paint.setColorSource(nullptr);
builder.DrawRect(SkRect::MakeWH(5, 5), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // Set transparent color.
DisplayListBuilder builder;
DlPaint paint;
auto color = DlColor::kTransparent();
DlColorColorSource color_source_transparent(color);
paint.setColorSource(color_source_transparent.shared());
builder.DrawRect(SkRect::MakeWH(5, 5), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
{ // Set black color.
DisplayListBuilder builder;
DlPaint paint;
auto color = DlColor::kBlack();
DlColorColorSource color_source_transparent(color);
paint.setColorSource(color_source_transparent.shared());
builder.DrawRect(SkRect::MakeWH(5, 5), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawColor) {
{ // Black color source.
DisplayListBuilder builder;
auto color = DlColor::kBlack();
builder.DrawColor(color, DlBlendMode::kSrc);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // Transparent color source.
DisplayListBuilder builder;
auto color = DlColor::kTransparent();
builder.DrawColor(color, DlBlendMode::kSrc);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawPaint) {
{ // Transparent color in paint.
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawPaint(paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
{ // black color in paint.
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawPaint(paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawLine) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawLine(SkPoint::Make(0, 1), SkPoint::Make(1, 2), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawLine(SkPoint::Make(0, 1), SkPoint::Make(1, 2), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, DrawRect) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawRect(SkRect::MakeWH(5, 5), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawRect(SkRect::MakeWH(5, 5), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, drawOval) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawOval(SkRect::MakeWH(5, 5), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawOval(SkRect::MakeWH(5, 5), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, drawCircle) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawCircle(SkPoint::Make(5, 5), 1.0, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawCircle(SkPoint::Make(5, 5), 1.0, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, drawRRect) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawRRect(SkRRect::MakeRect(SkRect::MakeWH(5, 5)), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawRRect(SkRRect::MakeRect(SkRect::MakeWH(5, 5)), paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, drawPath) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawPath(SkPath::Line(SkPoint::Make(0, 1), SkPoint::Make(1, 1)),
paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawPath(SkPath::Line(SkPoint::Make(0, 1), SkPoint::Make(1, 1)),
paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, drawArc) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawArc(SkRect::MakeWH(5, 5), 0, 1, true, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawArc(SkRect::MakeWH(5, 5), 0, 1, true, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, drawPoints) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
const SkPoint points[] = {SkPoint::Make(5, 4)};
builder.DrawPoints(DlCanvas::PointMode::kPoints, 1, points, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
const SkPoint points[] = {SkPoint::Make(5, 4)};
builder.DrawPoints(DlCanvas::PointMode::kPoints, 1, points, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, drawVertices) {
{ // black
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
const SkPoint vertices[] = {SkPoint::Make(5, 5)};
const SkPoint texture_coordinates[] = {SkPoint::Make(5, 5)};
const DlColor colors[] = {DlColor::kBlack()};
auto dl_vertices = DlVertices::Make(DlVertexMode::kTriangles, 1, vertices,
texture_coordinates, colors, 0);
builder.DrawVertices(dl_vertices.get(), DlBlendMode::kSrc, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
const SkPoint vertices[] = {SkPoint::Make(5, 5)};
const SkPoint texture_coordinates[] = {SkPoint::Make(5, 5)};
const DlColor colors[] = {DlColor::kBlack()};
auto dl_vertices = DlVertices::Make(DlVertexMode::kTriangles, 1, vertices,
texture_coordinates, colors, 0);
builder.DrawVertices(dl_vertices.get(), DlBlendMode::kSrc, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, Images) {
{ // DrawImage
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
SkImageInfo info =
SkImageInfo::Make(50, 50, SkColorType::kRGBA_8888_SkColorType,
SkAlphaType::kPremul_SkAlphaType);
SkBitmap bitmap;
bitmap.allocPixels(info, 0);
auto sk_image = SkImage::MakeFromBitmap(bitmap);
builder.DrawImage(DlImage::Make(sk_image), SkPoint::Make(5, 5),
DlImageSampling::kLinear);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // DrawImageRect
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
SkImageInfo info =
SkImageInfo::Make(50, 50, SkColorType::kRGBA_8888_SkColorType,
SkAlphaType::kPremul_SkAlphaType);
SkBitmap bitmap;
bitmap.allocPixels(info, 0);
auto sk_image = SkImage::MakeFromBitmap(bitmap);
builder.DrawImageRect(DlImage::Make(sk_image), SkRect::MakeWH(5, 5),
SkRect::MakeWH(5, 5), DlImageSampling::kLinear);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // DrawImageNine
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
SkImageInfo info =
SkImageInfo::Make(50, 50, SkColorType::kRGBA_8888_SkColorType,
SkAlphaType::kPremul_SkAlphaType);
SkBitmap bitmap;
bitmap.allocPixels(info, 0);
auto sk_image = SkImage::MakeFromBitmap(bitmap);
builder.DrawImageNine(DlImage::Make(sk_image), SkIRect::MakeWH(5, 5),
SkRect::MakeWH(5, 5), DlFilterMode::kLinear);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // DrawAtlas
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
SkImageInfo info =
SkImageInfo::Make(50, 50, SkColorType::kRGBA_8888_SkColorType,
SkAlphaType::kPremul_SkAlphaType);
SkBitmap bitmap;
bitmap.allocPixels(info, 0);
auto sk_image = SkImage::MakeFromBitmap(bitmap);
const SkRSXform xform[] = {};
const SkRect tex[] = {};
const DlColor colors[] = {};
SkRect cull_rect = SkRect::MakeWH(5, 5);
builder.DrawAtlas(DlImage::Make(sk_image), xform, tex, colors, 0,
DlBlendMode::kSrc, DlImageSampling::kLinear, &cull_rect);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, drawDisplayList) {
{ // Recursive Transparent DisplayList
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawPaint(paint);
sk_sp<DisplayList> dl = builder.Build();
DisplayListBuilder builder_parent;
DlPaint paint_parent(DlColor::kTransparent());
builder_parent.DrawPaint(paint_parent);
builder_parent.DrawDisplayList(dl, 1);
sk_sp<DisplayList> dl2 = builder_parent.Build();
DlOpSpy dl_op_spy;
dl2->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
{ // Sub non-transparent DisplayList,
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawPaint(paint);
sk_sp<DisplayList> dl = builder.Build();
DisplayListBuilder builder_parent;
DlPaint paint_parent(DlColor::kTransparent());
builder_parent.DrawPaint(paint_parent);
builder_parent.DrawDisplayList(dl, 1);
sk_sp<DisplayList> dl2 = builder_parent.Build();
DlOpSpy dl_op_spy;
dl2->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // Sub non-transparent DisplayList, 0 opacity
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
builder.DrawPaint(paint);
sk_sp<DisplayList> dl = builder.Build();
DisplayListBuilder builder_parent;
DlPaint paint_parent(DlColor::kTransparent());
builder_parent.DrawPaint(paint_parent);
builder_parent.DrawDisplayList(dl, 0);
sk_sp<DisplayList> dl2 = builder_parent.Build();
DlOpSpy dl_op_spy;
dl2->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
{ // Parent non-transparent DisplayList
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
builder.DrawPaint(paint);
sk_sp<DisplayList> dl = builder.Build();
DisplayListBuilder builder_parent;
DlPaint paint_parent(DlColor::kBlack());
builder_parent.DrawPaint(paint_parent);
builder_parent.DrawDisplayList(dl, 0);
sk_sp<DisplayList> dl2 = builder_parent.Build();
DlOpSpy dl_op_spy;
dl2->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, drawTextBlob) {
{ // Non-transparent color.
DisplayListBuilder builder;
DlPaint paint(DlColor::kBlack());
std::string string = "xx";
SkFont font;
auto text_blob = SkTextBlob::MakeFromString(string.c_str(), font);
builder.DrawTextBlob(text_blob, 1, 1, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent color.
DisplayListBuilder builder;
DlPaint paint(DlColor::kTransparent());
std::string string = "xx";
SkFont font;
auto text_blob = SkTextBlob::MakeFromString(string.c_str(), font);
builder.DrawTextBlob(text_blob, 1, 1, paint);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
TEST(DlOpSpy, drawShadow) {
{ // valid shadow
DisplayListBuilder builder;
DlPaint paint;
DlColor color = DlColor::kBlack();
SkPath path = SkPath::Line(SkPoint::Make(0, 1), SkPoint::Make(1, 1));
builder.DrawShadow(path, color, 1, false, 1);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_TRUE(dl_op_spy.did_draw());
}
{ // transparent color
DisplayListBuilder builder;
DlPaint paint;
DlColor color = DlColor::kTransparent();
SkPath path = SkPath::Line(SkPoint::Make(0, 1), SkPoint::Make(1, 1));
builder.DrawShadow(path, color, 1, false, 1);
sk_sp<DisplayList> dl = builder.Build();
DlOpSpy dl_op_spy;
dl->Dispatch(dl_op_spy);
ASSERT_FALSE(dl_op_spy.did_draw());
}
}
} // namespace testing
} // namespace flutter

View File

@@ -198,6 +198,7 @@ test_fixtures("fixtures") {
"fixtures/compositor_software.png",
"fixtures/compositor_with_platform_layer_on_bottom.png",
"fixtures/compositor_with_root_layer_only.png",
"fixtures/compositor_platform_layer_with_no_overlay.png",
"fixtures/dpr_noxform.png",
"fixtures/dpr_xform.png",
"fixtures/gradient.png",

View File

@@ -4,7 +4,7 @@
#include "flutter/shell/platform/embedder/embedder_external_view.h"
#include "flutter/fml/trace_event.h"
#include "flutter/shell/common/canvas_spy.h"
#include "flutter/shell/common/dl_op_spy.h"
namespace flutter {
@@ -30,10 +30,8 @@ EmbedderExternalView::EmbedderExternalView(
surface_transformation_(surface_transformation),
view_identifier_(view_identifier),
embedded_view_params_(std::move(params)),
recorder_(std::make_unique<SkPictureRecorder>()),
canvas_spy_(std::make_unique<CanvasSpy>(
recorder_->beginRecording(frame_size.width(), frame_size.height()))) {
}
slice_(std::make_unique<DisplayListEmbedderViewSlice>(
SkRect::Make(frame_size))) {}
EmbedderExternalView::~EmbedderExternalView() = default;
@@ -43,7 +41,7 @@ EmbedderExternalView::CreateRenderTargetDescriptor() const {
}
DlCanvas* EmbedderExternalView::GetCanvas() {
return canvas_spy_->GetSpyingCanvas();
return slice_->canvas();
}
SkISize EmbedderExternalView::GetRenderSurfaceSize() const {
@@ -58,8 +56,15 @@ bool EmbedderExternalView::HasPlatformView() const {
return view_identifier_.platform_view_id.has_value();
}
bool EmbedderExternalView::HasEngineRenderedContents() const {
return canvas_spy_->DidDrawIntoCanvas();
bool EmbedderExternalView::HasEngineRenderedContents() {
if (has_engine_rendered_contents_.has_value()) {
return has_engine_rendered_contents_.value();
}
TryEndRecording();
DlOpSpy dl_op_spy;
slice_->dispatch(dl_op_spy);
has_engine_rendered_contents_ = dl_op_spy.did_draw() && !slice_->is_empty();
return has_engine_rendered_contents_.value();
}
EmbedderExternalView::ViewIdentifier EmbedderExternalView::GetViewIdentifier()
@@ -73,16 +78,11 @@ const EmbeddedViewParams* EmbedderExternalView::GetEmbeddedViewParams() const {
bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target) {
TRACE_EVENT0("flutter", "EmbedderExternalView::Render");
TryEndRecording();
FML_DCHECK(HasEngineRenderedContents())
<< "Unnecessarily asked to render into a render target when there was "
"nothing to render.";
auto picture = recorder_->finishRecordingAsPicture();
if (!picture) {
return false;
}
auto surface = render_target.GetRenderSurface();
if (!surface) {
return false;
@@ -95,13 +95,22 @@ bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target) {
if (!canvas) {
return false;
}
canvas->setMatrix(surface_transformation_);
canvas->clear(SK_ColorTRANSPARENT);
canvas->drawPicture(picture);
canvas->flush();
DlSkCanvasAdapter dl_canvas(canvas);
int restore_count = dl_canvas.GetSaveCount();
dl_canvas.SetTransform(surface_transformation_);
dl_canvas.Clear(DlColor::kTransparent());
slice_->render_into(&dl_canvas);
dl_canvas.RestoreToCount(restore_count);
dl_canvas.Flush();
return true;
}
void EmbedderExternalView::TryEndRecording() const {
if (slice_->recording_ended()) {
return;
}
slice_->end_recording();
}
} // namespace flutter

View File

@@ -12,9 +12,7 @@
#include "flutter/flow/embedded_views.h"
#include "flutter/fml/hash_combine.h"
#include "flutter/fml/macros.h"
#include "flutter/shell/common/canvas_spy.h"
#include "flutter/shell/platform/embedder/embedder_render_target.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
namespace flutter {
@@ -97,7 +95,7 @@ class EmbedderExternalView {
bool HasPlatformView() const;
bool HasEngineRenderedContents() const;
bool HasEngineRenderedContents();
ViewIdentifier GetViewIdentifier() const;
@@ -112,12 +110,16 @@ class EmbedderExternalView {
bool Render(const EmbedderRenderTarget& render_target);
private:
// End the recording of the slice.
// Noop if the slice's recording has already ended.
void TryEndRecording() const;
const SkISize render_surface_size_;
const SkMatrix surface_transformation_;
ViewIdentifier view_identifier_;
std::unique_ptr<EmbeddedViewParams> embedded_view_params_;
std::unique_ptr<SkPictureRecorder> recorder_;
std::unique_ptr<CanvasSpy> canvas_spy_;
std::unique_ptr<DisplayListEmbedderViewSlice> slice_;
std::optional<bool> has_engine_rendered_contents_;
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalView);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -455,6 +455,73 @@ void can_composite_platform_views_with_known_scene() {
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_composite_platform_views_transparent_overlay() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
Color red = Color.fromARGB(127, 255, 0, 0);
Color blue = Color.fromARGB(127, 0, 0, 255);
Color transparent = Color(0xFFFFFF);
Size size = Size(50.0, 150.0);
SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
// 10 (Index 0)
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.pushOffset(20.0, 20.0);
// 20 (Index 1)
builder.addPlatformView(1,
width: size.width, height: size.height); // green - platform
builder.pop();
// 30 (Index 2)
builder.addPicture(
Offset(30.0, 30.0), CreateColoredBox(transparent, size)); // transparent picture, no layer should be created.
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
signalNativeTest(); // Signal 2
};
signalNativeTest(); // Signal 1
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_composite_platform_views_no_overlay() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
Color red = Color.fromARGB(127, 255, 0, 0);
Color blue = Color.fromARGB(127, 0, 0, 255);
Size size = Size(50.0, 150.0);
SceneBuilder builder = SceneBuilder();
builder.pushOffset(0.0, 0.0);
// 10 (Index 0)
builder.addPicture(
Offset(10.0, 10.0), CreateColoredBox(red, size)); // red - flutter
builder.pushOffset(20.0, 20.0);
// 20 (Index 1)
builder.addPlatformView(1,
width: size.width, height: size.height); // green - platform
builder.pop();
builder.pop();
PlatformDispatcher.instance.views.first.render(builder.build());
signalNativeTest(); // Signal 2
};
signalNativeTest(); // Signal 1
PlatformDispatcher.instance.scheduleFrame();
}
@pragma('vm:entry-point')
void can_composite_platform_views_with_root_layer_only() {
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {

View File

@@ -817,6 +817,250 @@ TEST_F(EmbedderTest,
ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
}
//------------------------------------------------------------------------------
/// Test the layer structure and pixels rendered when using a custom software
/// compositor, with a transparent overlay
///
TEST_F(EmbedderTest, NoLayerCreatedForTransparentOverlayOnTopOfPlatformLayer) {
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views_transparent_overlay");
builder.SetRenderTargetType(
EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
fml::CountDownLatch latch(4);
auto scene_image = context.GetNextSceneImage();
context.GetCompositor().SetNextPresentCallback(
[&](const FlutterLayer** layers, size_t layers_count) {
ASSERT_EQ(layers_count, 2u);
// Layer Root
{
FlutterBackingStore backing_store = *layers[0]->backing_store;
backing_store.type = kFlutterBackingStoreTypeSoftware;
backing_store.did_update = true;
backing_store.software.height = 600;
FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypeBackingStore;
layer.backing_store = &backing_store;
layer.size = FlutterSizeMake(800.0, 600.0);
layer.offset = FlutterPointMake(0.0, 0.0);
ASSERT_EQ(*layers[0], layer);
}
// Layer 1
{
FlutterPlatformView platform_view = *layers[1]->platform_view;
platform_view.struct_size = sizeof(platform_view);
platform_view.identifier = 1;
FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypePlatformView;
layer.platform_view = &platform_view;
layer.size = FlutterSizeMake(50.0, 150.0);
layer.offset = FlutterPointMake(20.0, 20.0);
ASSERT_EQ(*layers[1], layer);
}
latch.CountDown();
});
context.GetCompositor().SetPlatformViewRendererCallback(
[&](const FlutterLayer& layer, GrDirectContext*
/* don't use because software compositor */) -> sk_sp<SkImage> {
auto surface = CreateRenderSurface(
layer, nullptr /* null because software compositor */);
auto canvas = surface->getCanvas();
FML_CHECK(canvas != nullptr);
switch (layer.platform_view->identifier) {
case 1: {
SkPaint paint;
// See dart test for total order.
paint.setColor(SK_ColorGREEN);
paint.setAlpha(127);
const auto& rect =
SkRect::MakeWH(layer.size.width, layer.size.height);
canvas->drawRect(rect, paint);
latch.CountDown();
} break;
default:
// Asked to render an unknown platform view.
FML_CHECK(false)
<< "Test was asked to composite an unknown platform view.";
}
return surface->makeImageSnapshot();
});
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
[&latch](Dart_NativeArguments args) { latch.CountDown(); }));
auto engine = builder.LaunchEngine();
// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
event.width = 800;
event.height = 600;
event.pixel_ratio = 1.0;
event.physical_view_inset_top = 0.0;
event.physical_view_inset_right = 0.0;
event.physical_view_inset_bottom = 0.0;
event.physical_view_inset_left = 0.0;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);
ASSERT_TRUE(engine.is_valid());
latch.Wait();
// TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
// platforms.
#if !defined(FML_OS_LINUX)
GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
#endif // FML_OS_LINUX
ASSERT_TRUE(ImageMatchesFixture(
"compositor_platform_layer_with_no_overlay.png", scene_image));
// There should no present calls on the root surface.
ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
}
//------------------------------------------------------------------------------
/// Test the layer structure and pixels rendered when using a custom software
/// compositor, with a no overlay
///
TEST_F(EmbedderTest, NoLayerCreatedForNoOverlayOnTopOfPlatformLayer) {
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig(SkISize::Make(800, 600));
builder.SetCompositor();
builder.SetDartEntrypoint("can_composite_platform_views_no_overlay");
builder.SetRenderTargetType(
EmbedderTestBackingStoreProducer::RenderTargetType::kSoftwareBuffer);
fml::CountDownLatch latch(4);
auto scene_image = context.GetNextSceneImage();
context.GetCompositor().SetNextPresentCallback(
[&](const FlutterLayer** layers, size_t layers_count) {
ASSERT_EQ(layers_count, 2u);
// Layer Root
{
FlutterBackingStore backing_store = *layers[0]->backing_store;
backing_store.type = kFlutterBackingStoreTypeSoftware;
backing_store.did_update = true;
backing_store.software.height = 600;
FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypeBackingStore;
layer.backing_store = &backing_store;
layer.size = FlutterSizeMake(800.0, 600.0);
layer.offset = FlutterPointMake(0.0, 0.0);
ASSERT_EQ(*layers[0], layer);
}
// Layer 1
{
FlutterPlatformView platform_view = *layers[1]->platform_view;
platform_view.struct_size = sizeof(platform_view);
platform_view.identifier = 1;
FlutterLayer layer = {};
layer.struct_size = sizeof(layer);
layer.type = kFlutterLayerContentTypePlatformView;
layer.platform_view = &platform_view;
layer.size = FlutterSizeMake(50.0, 150.0);
layer.offset = FlutterPointMake(20.0, 20.0);
ASSERT_EQ(*layers[1], layer);
}
latch.CountDown();
});
context.GetCompositor().SetPlatformViewRendererCallback(
[&](const FlutterLayer& layer, GrDirectContext*
/* don't use because software compositor */) -> sk_sp<SkImage> {
auto surface = CreateRenderSurface(
layer, nullptr /* null because software compositor */);
auto canvas = surface->getCanvas();
FML_CHECK(canvas != nullptr);
switch (layer.platform_view->identifier) {
case 1: {
SkPaint paint;
// See dart test for total order.
paint.setColor(SK_ColorGREEN);
paint.setAlpha(127);
const auto& rect =
SkRect::MakeWH(layer.size.width, layer.size.height);
canvas->drawRect(rect, paint);
latch.CountDown();
} break;
default:
// Asked to render an unknown platform view.
FML_CHECK(false)
<< "Test was asked to composite an unknown platform view.";
}
return surface->makeImageSnapshot();
});
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
[&latch](Dart_NativeArguments args) { latch.CountDown(); }));
auto engine = builder.LaunchEngine();
// Send a window metrics events so frames may be scheduled.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(event);
event.width = 800;
event.height = 600;
event.pixel_ratio = 1.0;
event.physical_view_inset_top = 0.0;
event.physical_view_inset_right = 0.0;
event.physical_view_inset_bottom = 0.0;
event.physical_view_inset_left = 0.0;
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
kSuccess);
ASSERT_TRUE(engine.is_valid());
latch.Wait();
// TODO(https://github.com/flutter/flutter/issues/53784): enable this on all
// platforms.
#if !defined(FML_OS_LINUX)
GTEST_SKIP() << "Skipping golden tests on non-Linux OSes";
#endif // FML_OS_LINUX
ASSERT_TRUE(ImageMatchesFixture(
"compositor_platform_layer_with_no_overlay.png", scene_image));
// There should no present calls on the root surface.
ASSERT_EQ(context.GetSurfacePresentCount(), 0u);
}
//------------------------------------------------------------------------------
/// Test that an engine can be initialized but not run.
///

View File

@@ -164,6 +164,8 @@ class FlatlandExternalViewEmbedder final
std::optional<flutter::EmbeddedViewParams> embedded_view_params;
std::unique_ptr<SkPictureRecorder> recorder;
// TODO(cyanglaz: use DlOpSpy instead.
// https://github.com/flutter/flutter/issues/123805
std::unique_ptr<flutter::CanvasSpy> canvas_spy;
SkISize surface_size;
sk_sp<SkPicture> picture;

View File

@@ -156,6 +156,8 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
std::optional<flutter::EmbeddedViewParams> embedded_view_params;
std::unique_ptr<SkPictureRecorder> recorder;
// TODO(cyanglaz: use DlOpSpy instead.
// https://github.com/flutter/flutter/issues/123805
std::unique_ptr<flutter::CanvasSpy> canvas_spy;
SkISize surface_size;
sk_sp<SkPicture> picture;