diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index fa78d2aec93bc..1aa98fd072b92 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -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 diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index bfa34e3874d74..32bc556843d16 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2107,6 +2107,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 @@ -4689,6 +4691,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 diff --git a/display_list/dl_blend_mode.h b/display_list/dl_blend_mode.h index 61c67f63b1e48..a1c7b1231e98a 100644 --- a/display_list/dl_blend_mode.h +++ b/display_list/dl_blend_mode.h @@ -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: diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index 0d4152f9ce723..b76c9730af903 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -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 frame) { frame->Submit(); diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 1654820fa2011..4162e79a15af6 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -352,6 +352,9 @@ class DisplayListEmbedderViewSlice : public EmbedderViewSlice { std::list 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 builder_; diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 32d63365b4d39..1bff582d2b41f 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -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", diff --git a/shell/common/dl_op_spy.cc b/shell/common/dl_op_spy.cc new file mode 100644 index 0000000000000..15910fc2e85de --- /dev/null +++ b/shell/common/dl_op_spy.cc @@ -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 image, + const SkPoint point, + DlImageSampling sampling, + bool render_with_attributes) { + did_draw_ = true; +} +void DlOpSpy::drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + DlImageSampling sampling, + bool render_with_attributes, + SrcRectConstraint constraint) { + did_draw_ = true; +} +void DlOpSpy::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + DlFilterMode filter, + bool render_with_attributes) { + did_draw_ = true; +} +void DlOpSpy::drawAtlas(const sk_sp 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 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 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 diff --git a/shell/common/dl_op_spy.h b/shell/common/dl_op_spy.h new file mode 100644 index 0000000000000..1cb29ff7219aa --- /dev/null +++ b/shell/common/dl_op_spy.h @@ -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 image, + const SkPoint point, + DlImageSampling sampling, + bool render_with_attributes) override; + void drawImageRect( + const sk_sp image, + const SkRect& src, + const SkRect& dst, + DlImageSampling sampling, + bool render_with_attributes, + SrcRectConstraint constraint = SrcRectConstraint::kFast) override; + void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + DlFilterMode filter, + bool render_with_attributes) override; + void drawAtlas(const sk_sp 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 display_list, + SkScalar opacity = SK_Scalar1) override; + void drawTextBlob(const sk_sp 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_ diff --git a/shell/common/dl_op_spy_unittests.cc b/shell/common/dl_op_spy_unittests.cc new file mode 100644 index 0000000000000..fc0070902f289 --- /dev/null +++ b/shell/common/dl_op_spy_unittests.cc @@ -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 dl = builder.Build(); + + DisplayListBuilder builder_parent; + DlPaint paint_parent(DlColor::kTransparent()); + builder_parent.DrawPaint(paint_parent); + builder_parent.DrawDisplayList(dl, 1); + sk_sp 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 dl = builder.Build(); + + DisplayListBuilder builder_parent; + DlPaint paint_parent(DlColor::kTransparent()); + builder_parent.DrawPaint(paint_parent); + builder_parent.DrawDisplayList(dl, 1); + sk_sp 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 dl = builder.Build(); + + DisplayListBuilder builder_parent; + DlPaint paint_parent(DlColor::kTransparent()); + builder_parent.DrawPaint(paint_parent); + builder_parent.DrawDisplayList(dl, 0); + sk_sp 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 dl = builder.Build(); + + DisplayListBuilder builder_parent; + DlPaint paint_parent(DlColor::kBlack()); + builder_parent.DrawPaint(paint_parent); + builder_parent.DrawDisplayList(dl, 0); + sk_sp 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 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 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 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 dl = builder.Build(); + DlOpSpy dl_op_spy; + dl->Dispatch(dl_op_spy); + ASSERT_FALSE(dl_op_spy.did_draw()); + } +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index ce4e1edea7880..9c827ac38bd3c 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -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", diff --git a/shell/platform/embedder/embedder_external_view.cc b/shell/platform/embedder/embedder_external_view.cc index 34044b41b493d..cd72a3e4978b6 100644 --- a/shell/platform/embedder/embedder_external_view.cc +++ b/shell/platform/embedder/embedder_external_view.cc @@ -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()), - canvas_spy_(std::make_unique( - recorder_->beginRecording(frame_size.width(), frame_size.height()))) { -} + slice_(std::make_unique( + 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 diff --git a/shell/platform/embedder/embedder_external_view.h b/shell/platform/embedder/embedder_external_view.h index a7648dfd16a5b..2b8e26d200155 100644 --- a/shell/platform/embedder/embedder_external_view.h +++ b/shell/platform/embedder/embedder_external_view.h @@ -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 embedded_view_params_; - std::unique_ptr recorder_; - std::unique_ptr canvas_spy_; + std::unique_ptr slice_; + std::optional has_engine_rendered_contents_; FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalView); }; diff --git a/shell/platform/embedder/fixtures/compositor_platform_layer_with_no_overlay.png b/shell/platform/embedder/fixtures/compositor_platform_layer_with_no_overlay.png new file mode 100644 index 0000000000000..20109dfd893d4 Binary files /dev/null and b/shell/platform/embedder/fixtures/compositor_platform_layer_with_no_overlay.png differ diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 24fde2dfa5370..c0611f3ef059b 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -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) { diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 1518e242d81bb..8b03077fb5878 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -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 { + 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 { + 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. /// diff --git a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h index 402cdc159735d..00a845cde2e85 100644 --- a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h @@ -164,6 +164,8 @@ class FlatlandExternalViewEmbedder final std::optional embedded_view_params; std::unique_ptr recorder; + // TODO(cyanglaz: use DlOpSpy instead. + // https://github.com/flutter/flutter/issues/123805 std::unique_ptr canvas_spy; SkISize surface_size; sk_sp picture; diff --git a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h index 11ba6832020ad..eb37d2e7e2d1f 100644 --- a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h @@ -156,6 +156,8 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder { std::optional embedded_view_params; std::unique_ptr recorder; + // TODO(cyanglaz: use DlOpSpy instead. + // https://github.com/flutter/flutter/issues/123805 std::unique_ptr canvas_spy; SkISize surface_size; sk_sp picture;