From 4743eb364353208f9eebfda015bd5c5df4569db5 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 29 Aug 2023 16:49:04 -0700 Subject: [PATCH 01/12] Add a DlStopwatchVisualizer, and use it if Impeller is enabled. --- ci/licenses_golden/licenses_flutter | 4 + flow/BUILD.gn | 2 + flow/layers/performance_overlay_layer.cc | 25 ++++-- flow/stopwatch.cc | 4 + flow/stopwatch.h | 3 + flow/stopwatch_dl.cc | 105 +++++++++++++++++++++++ flow/stopwatch_dl.h | 29 +++++++ flow/stopwatch_unittests.cc | 7 ++ 8 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 flow/stopwatch_dl.cc create mode 100644 flow/stopwatch_dl.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 3ae421ecf59f3..f1de8a48d1b40 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -846,6 +846,8 @@ ORIGIN: ../../../flutter/flow/raster_cache_util.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/skia_gpu_object.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/stopwatch.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/stopwatch.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/flow/stopwatch_dl.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/flow/stopwatch_dl.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/stopwatch_sk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/stopwatch_sk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/surface.cc + ../../../flutter/LICENSE @@ -3594,6 +3596,8 @@ FILE: ../../../flutter/flow/raster_cache_util.h FILE: ../../../flutter/flow/skia_gpu_object.h FILE: ../../../flutter/flow/stopwatch.cc FILE: ../../../flutter/flow/stopwatch.h +FILE: ../../../flutter/flow/stopwatch_dl.cc +FILE: ../../../flutter/flow/stopwatch_dl.h FILE: ../../../flutter/flow/stopwatch_sk.cc FILE: ../../../flutter/flow/stopwatch_sk.h FILE: ../../../flutter/flow/surface.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 5d69ae2e166fa..ceb51c297e52e 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -77,6 +77,8 @@ source_set("flow") { "skia_gpu_object.h", "stopwatch.cc", "stopwatch.h", + "stopwatch_dl.cc", + "stopwatch_dl.h", "stopwatch_sk.cc", "stopwatch_sk.h", "surface.cc", diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc index 4275a996fdb35..911131fd0cfbc 100644 --- a/flow/layers/performance_overlay_layer.cc +++ b/flow/layers/performance_overlay_layer.cc @@ -6,8 +6,11 @@ #include #include +#include #include +#include "flow/stopwatch.h" +#include "flow/stopwatch_dl.h" #include "flow/stopwatch_sk.h" #include "third_party/skia/include/core/SkFont.h" #include "third_party/skia/include/core/SkTextBlob.h" @@ -16,6 +19,7 @@ namespace flutter { namespace { void VisualizeStopWatch(DlCanvas* canvas, + const bool impeller_enabled, const Stopwatch& stopwatch, SkScalar x, SkScalar y, @@ -30,11 +34,15 @@ void VisualizeStopWatch(DlCanvas* canvas, if (show_graph) { SkRect visualization_rect = SkRect::MakeXYWH(x, y, width, height); + std::unique_ptr visualizer; - // TODO(matanlurey): Select a visualizer based on the current backend. - // https://github.com/flutter/flutter/issues/126009 - SkStopwatchVisualizer visualizer = SkStopwatchVisualizer(stopwatch); - visualizer.Visualize(canvas, visualization_rect); + if (impeller_enabled) { + visualizer = std::make_unique(stopwatch); + } else { + visualizer = std::make_unique(stopwatch); + } + + visualizer->Visualize(canvas, visualization_rect); } if (show_labels) { @@ -105,12 +113,13 @@ void PerformanceOverlayLayer::Paint(PaintContext& context) const { auto mutator = context.state_stack.save(); VisualizeStopWatch( - context.canvas, context.raster_time, x, y, width, height - padding, - options_ & kVisualizeRasterizerStatistics, + context.canvas, context.impeller_enabled, context.raster_time, x, y, + width, height - padding, options_ & kVisualizeRasterizerStatistics, options_ & kDisplayRasterizerStatistics, "Raster", font_path_); - VisualizeStopWatch(context.canvas, context.ui_time, x, y + height, width, - height - padding, options_ & kVisualizeEngineStatistics, + VisualizeStopWatch(context.canvas, context.impeller_enabled, context.ui_time, + x, y + height, width, height - padding, + options_ & kVisualizeEngineStatistics, options_ & kDisplayEngineStatistics, "UI", font_path_); } diff --git a/flow/stopwatch.cc b/flow/stopwatch.cc index b8e8d3007a6cd..d61a591bd1ce5 100644 --- a/flow/stopwatch.cc +++ b/flow/stopwatch.cc @@ -48,6 +48,10 @@ const fml::TimeDelta& Stopwatch::GetLap(size_t index) const { return laps_[index]; } +size_t Stopwatch::GetLapsCount() const { + return laps_.size(); +} + size_t Stopwatch::GetCurrentSample() const { return current_sample_; } diff --git a/flow/stopwatch.h b/flow/stopwatch.h index d6e8f0da7fbcc..5755b8d460b44 100644 --- a/flow/stopwatch.h +++ b/flow/stopwatch.h @@ -32,6 +32,9 @@ class Stopwatch { const fml::TimeDelta& GetLap(size_t index) const; + /// Return a reference to all the laps. + size_t GetLapsCount() const; + size_t GetCurrentSample() const; const fml::TimeDelta& LastLap() const; diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc new file mode 100644 index 0000000000000..457590e4b0302 --- /dev/null +++ b/flow/stopwatch_dl.cc @@ -0,0 +1,105 @@ +// 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/flow/stopwatch_dl.h" +#include "display_list/dl_blend_mode.h" +#include "display_list/dl_canvas.h" +#include "display_list/dl_paint.h" +#include "include/core/SkRect.h" + +namespace flutter { + +static const size_t kMaxSamples = 120; +static const size_t kMaxFrameMarkers = 8; + +void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, + const SkRect& rect) const { + DlPaint paint; + + // Establish the graph position. + auto const x = 0; + auto const y = 0; + auto const width = rect.width(); + auto const height = rect.height(); + + // Scale the graph to show time frames up to those that are 3x the frame time. + auto const one_frame_ms = stopwatch_.GetFrameBudget().count(); + auto const max_interval = one_frame_ms * 3.0; + auto const max_unit_interval = UnitFrameInterval(max_interval); + auto const sample_unit_width = (1.0 / kMaxSamples); + + // Erase all pixels. + { + paint.setColor(0x99FFFFFF); + paint.setBlendMode(DlBlendMode::kSrc); + canvas->DrawRect(rect, paint); + } + + // Draw blue timing bars. + // + // Unlike the Skia version, we redraw the entire graph every time, since + // the Impeller backend is less sensitive to overdraw. + { + paint.setColor(0xAA0000FF); + paint.setBlendMode(DlBlendMode::kSrcOver); + + for (auto i = size_t(0); i < stopwatch_.GetLapsCount(); i++) { + auto const l = x * width * (static_cast(i) / kMaxSamples); + auto const t = y * height * + (1.0 - (UnitHeight(stopwatch_.GetLap(i).ToMillisecondsF(), + max_unit_interval))); + auto const r = l + width * sample_unit_width; + auto const b = height; + canvas->DrawRect(SkRect::MakeLTRB(l, t, r, b), paint); + } + } + + // Draw horizontal frame markers. + { + paint.setStrokeWidth(0); + paint.setDrawStyle(DlDrawStyle::kStroke); + paint.setColor(0xCC000000); + + if (max_interval > one_frame_ms) { + // Paint the horizontal markers. + auto count = static_cast(max_interval / one_frame_ms); + + // Limit the number of markers to a reasonable amount. + if (count > kMaxFrameMarkers) { + count = 1; + } + + for (auto i = size_t(0); i < count; i++) { + auto const frame_height = + height * (1.0 - (UnitFrameInterval(i + 1) * one_frame_ms) / + max_unit_interval); + canvas->DrawLine(SkPoint::Make(x, frame_height), + SkPoint::Make(width, y + frame_height), paint); + } + } + } + + // Paint the vertical marker for the current frame. + { + paint.setDrawStyle(DlDrawStyle::kFill); + paint.setBlendMode(DlBlendMode::kSrcOver); + if (UnitFrameInterval(stopwatch_.LastLap().ToMillisecondsF()) > 1.0) { + // budget exceeded. + paint.setColor(DlColor::kRed()); + } else { + // within budget. + paint.setColor(DlColor::kGreen()); + } + + auto const l = + x + width * (static_cast(stopwatch_.GetCurrentSample()) / + kMaxSamples); + auto const t = y; + auto const r = l + width * sample_unit_width; + auto const b = height; + canvas->DrawRect(SkRect::MakeLTRB(l, t, r, b), paint); + } +} + +} // namespace flutter diff --git a/flow/stopwatch_dl.h b/flow/stopwatch_dl.h new file mode 100644 index 0000000000000..afe1b957db0e9 --- /dev/null +++ b/flow/stopwatch_dl.h @@ -0,0 +1,29 @@ +// 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_STOPWATCH_DL_H_ +#define FLUTTER_FLOW_STOPWATCH_DL_H_ + +#include "flow/stopwatch.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// A stopwatch visualizer that uses DisplayList (|DlCanvas|) to draw. +/// +/// @note This is the newer non-backend specific version, that works in both +/// Skia and Impeller. The older Skia-specific version is +/// |SkStopwatchVisualizer|, which still should be used for Skia-specific +/// optimizations. +class DlStopwatchVisualizer : public StopwatchVisualizer { + public: + explicit DlStopwatchVisualizer(const Stopwatch& stopwatch) + : StopwatchVisualizer(stopwatch) {} + + void Visualize(DlCanvas* canvas, const SkRect& rect) const override; +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_STOPWATCH_DL_H_ diff --git a/flow/stopwatch_unittests.cc b/flow/stopwatch_unittests.cc index 3ded5142a9fbb..50a635285033a 100644 --- a/flow/stopwatch_unittests.cc +++ b/flow/stopwatch_unittests.cc @@ -57,5 +57,12 @@ TEST(Instrumentation, GetCurrentSampleTest) { EXPECT_EQ(stopwatch.GetCurrentSample(), size_t(1)); } +TEST(Instrumentation, GetLapsCount) { + fml::Milliseconds frame_budget_90fps = fml::RefreshRateToFrameBudget(90); + FixedRefreshRateStopwatch stopwatch(frame_budget_90fps); + stopwatch.SetLapTime(fml::TimeDelta::FromMilliseconds(10)); + EXPECT_EQ(stopwatch.GetLapsCount(), size_t(120)); +} + } // namespace testing } // namespace flutter From 4bf417852a33e9c5e06e0857b66e427124e83747 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 29 Aug 2023 17:24:14 -0700 Subject: [PATCH 02/12] Mostly working, still debugging. --- flow/stopwatch_dl.cc | 48 ++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index 457590e4b0302..72206c3b820d4 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -6,6 +6,7 @@ #include "display_list/dl_blend_mode.h" #include "display_list/dl_canvas.h" #include "display_list/dl_paint.h" +#include "include/core/SkPath.h" #include "include/core/SkRect.h" namespace flutter { @@ -32,27 +33,44 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, // Erase all pixels. { paint.setColor(0x99FFFFFF); - paint.setBlendMode(DlBlendMode::kSrc); canvas->DrawRect(rect, paint); } - // Draw blue timing bars. - // - // Unlike the Skia version, we redraw the entire graph every time, since - // the Impeller backend is less sensitive to overdraw. + // Prepare a path for the data; we start at the height of the last point so + // it looks like we wrap around. { - paint.setColor(0xAA0000FF); - paint.setBlendMode(DlBlendMode::kSrcOver); - + SkPath path; + path.setIsVolatile(true); + path.moveTo(x, height); + path.lineTo( + x, + y + height * (1.0 - (UnitHeight(stopwatch_.GetLap(0).ToMillisecondsF(), + max_unit_interval)))); + + double unit_x; + double next_x = 0.0; for (auto i = size_t(0); i < stopwatch_.GetLapsCount(); i++) { - auto const l = x * width * (static_cast(i) / kMaxSamples); - auto const t = y * height * - (1.0 - (UnitHeight(stopwatch_.GetLap(i).ToMillisecondsF(), - max_unit_interval))); - auto const r = l + width * sample_unit_width; - auto const b = height; - canvas->DrawRect(SkRect::MakeLTRB(l, t, r, b), paint); + unit_x = next_x; + next_x = static_cast(i + 1) / kMaxSamples; + + auto const unit_y = + y + height * (1.0 - UnitHeight(stopwatch_.GetLap(i).ToMillisecondsF(), + max_unit_interval)); + path.lineTo(x + width * unit_x, unit_y); + path.lineTo(x + width * next_x, unit_y); } + + path.lineTo( + width, + y + height * + (1.0 - UnitHeight( + stopwatch_.GetLap(kMaxSamples - 1).ToMillisecondsF(), + max_unit_interval))); + path.lineTo(width, height); + path.close(); + + paint.setColor(0xAA0000FF); + canvas->DrawPath(path, paint); } // Draw horizontal frame markers. From 74e142d7ed84a8182372e91177b043f3067a4216 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 29 Aug 2023 17:50:51 -0700 Subject: [PATCH 03/12] The graph is now complete. --- flow/stopwatch_dl.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index 72206c3b820d4..c3645712f5692 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -19,8 +19,8 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, DlPaint paint; // Establish the graph position. - auto const x = 0; - auto const y = 0; + auto const x = rect.x(); + auto const y = rect.y(); auto const width = rect.width(); auto const height = rect.height(); @@ -41,11 +41,11 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, { SkPath path; path.setIsVolatile(true); - path.moveTo(x, height); + path.moveTo(x, rect.bottom()); path.lineTo( x, - y + height * (1.0 - (UnitHeight(stopwatch_.GetLap(0).ToMillisecondsF(), - max_unit_interval)))); + y + height * (1.0 - UnitHeight(stopwatch_.GetLap(0).ToMillisecondsF(), + max_unit_interval))); double unit_x; double next_x = 0.0; @@ -53,20 +53,20 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, unit_x = next_x; next_x = static_cast(i + 1) / kMaxSamples; - auto const unit_y = + auto const sample_y = y + height * (1.0 - UnitHeight(stopwatch_.GetLap(i).ToMillisecondsF(), max_unit_interval)); - path.lineTo(x + width * unit_x, unit_y); - path.lineTo(x + width * next_x, unit_y); + path.lineTo(x + width * unit_x, sample_y); + path.lineTo(x + width * next_x, sample_y); } path.lineTo( - width, + rect.right(), y + height * (1.0 - UnitHeight( stopwatch_.GetLap(kMaxSamples - 1).ToMillisecondsF(), max_unit_interval))); - path.lineTo(width, height); + path.lineTo(rect.right(), rect.bottom()); path.close(); paint.setColor(0xAA0000FF); @@ -92,7 +92,7 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, auto const frame_height = height * (1.0 - (UnitFrameInterval(i + 1) * one_frame_ms) / max_unit_interval); - canvas->DrawLine(SkPoint::Make(x, frame_height), + canvas->DrawLine(SkPoint::Make(x, y + frame_height), SkPoint::Make(width, y + frame_height), paint); } } @@ -115,7 +115,7 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, kMaxSamples); auto const t = y; auto const r = l + width * sample_unit_width; - auto const b = height; + auto const b = rect.bottom(); canvas->DrawRect(SkRect::MakeLTRB(l, t, r, b), paint); } } From 41428f36418460421230195d34d14cf1a3df9d58 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 29 Aug 2023 21:02:49 -0700 Subject: [PATCH 04/12] Stop for tonight, mostly working, needs TLC. --- flow/stopwatch_dl.cc | 106 +++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 34 deletions(-) diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index c3645712f5692..0939fc2c6f762 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -3,17 +3,50 @@ // found in the LICENSE file. #include "flutter/flow/stopwatch_dl.h" +#include #include "display_list/dl_blend_mode.h" #include "display_list/dl_canvas.h" #include "display_list/dl_paint.h" -#include "include/core/SkPath.h" +#include "display_list/dl_vertices.h" #include "include/core/SkRect.h" +#include "include/private/base/SkPoint_impl.h" namespace flutter { +/// Returns 6 vertices representing a rectangle. +/// +/// Rather than using a path, which we'll end up tessellating per frame, we +/// create a vertices object and add the rectangles (2x triangles) to it. +/// +/// The goal is minimally invasive rendering for the performance monitor. +std::shared_ptr FromRectLTRB(const SkScalar left, + const SkScalar top, + const SkScalar right, + const SkScalar bottom) { + // FIXME: Convert this into a helper class with AddRect and AddLine. + // FIXME: Move the helper class into stopwatch_dl_vertices_helper and test it. + auto const top_left = SkPoint::Make(left, top); + auto const top_right = SkPoint::Make(right, top); + auto const bottom_right = SkPoint::Make(right, bottom); + auto const bottom_left = SkPoint::Make(left, bottom); + const SkPoint vertices[6] = { + top_left, // tl tr + top_right, // br + bottom_right, // + bottom_right, // tl + bottom_left, // bl br + top_left // + }; + return DlVertices::Make(DlVertexMode::kTriangles, 6, vertices, nullptr, + nullptr); +} + static const size_t kMaxSamples = 120; static const size_t kMaxFrameMarkers = 8; +// FIXME: Clean up this method in general, including all the functions and +// as-needed split into multiple methods and give better names and comments +// to everything. void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, const SkRect& rect) const { DlPaint paint; @@ -23,6 +56,7 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, auto const y = rect.y(); auto const width = rect.width(); auto const height = rect.height(); + auto const bottom = rect.bottom(); // Scale the graph to show time frames up to those that are 3x the frame time. auto const one_frame_ms = stopwatch_.GetFrameBudget().count(); @@ -30,6 +64,14 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, auto const max_unit_interval = UnitFrameInterval(max_interval); auto const sample_unit_width = (1.0 / kMaxSamples); + // Determine how many lines to draw. + auto horizontal_markers = static_cast(max_interval / one_frame_ms); + + // Limit the number of markers to a reasonable amount. + if (horizontal_markers > kMaxFrameMarkers) { + horizontal_markers = 1; + } + // Erase all pixels. { paint.setColor(0x99FFFFFF); @@ -39,38 +81,29 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, // Prepare a path for the data; we start at the height of the last point so // it looks like we wrap around. { - SkPath path; - path.setIsVolatile(true); - path.moveTo(x, rect.bottom()); - path.lineTo( - x, - y + height * (1.0 - UnitHeight(stopwatch_.GetLap(0).ToMillisecondsF(), - max_unit_interval))); - - double unit_x; - double next_x = 0.0; for (auto i = size_t(0); i < stopwatch_.GetLapsCount(); i++) { - unit_x = next_x; - next_x = static_cast(i + 1) / kMaxSamples; - - auto const sample_y = - y + height * (1.0 - UnitHeight(stopwatch_.GetLap(i).ToMillisecondsF(), - max_unit_interval)); - path.lineTo(x + width * unit_x, sample_y); - path.lineTo(x + width * next_x, sample_y); + auto const sample_unit_height = + (1.0 - UnitHeight(stopwatch_.GetLap(i).ToMillisecondsF(), + max_unit_interval)); + + auto const bar_width = width * sample_unit_width; + auto const bar_height = height * sample_unit_height; + auto const bar_left = x + width * sample_unit_width * i; + + // FIXME: This doesn't currently work, I get a reversed effect where cheap + // frames are shown as almost full height and expensive frames are shown + // as almost empty. + paint.setColor(0xAA0000FF); + // FIXME: We should be collecting all of our vertices into a single array + // and then drawing them all at once, rather than drawing each one as we + // go. Ideally use DlColor as well and do literally 1 DrawVertices call + // at the end of this function. + canvas->DrawVertices(FromRectLTRB(/*left=*/bar_left, + /*top=*/y + height - bar_height, + /*right=*/bar_left + bar_width, + /*bottom=*/y + height), + DlBlendMode::kSrc, paint); } - - path.lineTo( - rect.right(), - y + height * - (1.0 - UnitHeight( - stopwatch_.GetLap(kMaxSamples - 1).ToMillisecondsF(), - max_unit_interval))); - path.lineTo(rect.right(), rect.bottom()); - path.close(); - - paint.setColor(0xAA0000FF); - canvas->DrawPath(path, paint); } // Draw horizontal frame markers. @@ -92,8 +125,13 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, auto const frame_height = height * (1.0 - (UnitFrameInterval(i + 1) * one_frame_ms) / max_unit_interval); - canvas->DrawLine(SkPoint::Make(x, y + frame_height), - SkPoint::Make(width, y + frame_height), paint); + + // Draw a skinny rectangle (i.e. line). + canvas->DrawVertices(FromRectLTRB(/*left=*/x, + /*top=*/y + frame_height, + /*right=*/width, + /*bottom=*/y + frame_height + 1), + DlBlendMode::kSrc, paint); } } } @@ -116,7 +154,7 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, auto const t = y; auto const r = l + width * sample_unit_width; auto const b = rect.bottom(); - canvas->DrawRect(SkRect::MakeLTRB(l, t, r, b), paint); + canvas->DrawVertices(FromRectLTRB(l, t, r, b), DlBlendMode::kSrc, paint); } } From c12e1da64374986290b5058dc93d54f778656ef1 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 30 Aug 2023 13:11:06 -0700 Subject: [PATCH 05/12] Fix bar height. --- flow/stopwatch_dl.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index 0939fc2c6f762..2938b91ff6429 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -9,7 +9,6 @@ #include "display_list/dl_paint.h" #include "display_list/dl_vertices.h" #include "include/core/SkRect.h" -#include "include/private/base/SkPoint_impl.h" namespace flutter { @@ -90,16 +89,13 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, auto const bar_height = height * sample_unit_height; auto const bar_left = x + width * sample_unit_width * i; - // FIXME: This doesn't currently work, I get a reversed effect where cheap - // frames are shown as almost full height and expensive frames are shown - // as almost empty. paint.setColor(0xAA0000FF); // FIXME: We should be collecting all of our vertices into a single array // and then drawing them all at once, rather than drawing each one as we // go. Ideally use DlColor as well and do literally 1 DrawVertices call // at the end of this function. canvas->DrawVertices(FromRectLTRB(/*left=*/bar_left, - /*top=*/y + height - bar_height, + /*top=*/y + bar_height, /*right=*/bar_left + bar_width, /*bottom=*/y + height), DlBlendMode::kSrc, paint); From 20303332e5c70aa93b640be232591c0fdf8baffe Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 30 Aug 2023 13:52:36 -0700 Subject: [PATCH 06/12] Only make a single DrawVertices call. --- flow/stopwatch_dl.cc | 110 ++++++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 28 deletions(-) diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index 2938b91ff6429..93b16880bcbd1 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -4,14 +4,81 @@ #include "flutter/flow/stopwatch_dl.h" #include +#include #include "display_list/dl_blend_mode.h" #include "display_list/dl_canvas.h" +#include "display_list/dl_color.h" #include "display_list/dl_paint.h" #include "display_list/dl_vertices.h" #include "include/core/SkRect.h" +#include "include/private/base/SkPoint_impl.h" namespace flutter { +/// @brief Provides canvas-like painting methods that actually build vertices. +/// +/// The goal is minimally invasive rendering for the performance monitor. +/// +/// The methods in this class are intended to be used by |DlStopwatchVisualizer| +/// only. The rationale is the creating lines, rectangles, and paths (while OK +/// for general apps) would cause non-trivial work for the performance monitor +/// due to tessellation per-frame. +/// +/// @note A goal of this class was to make updating the performance monitor +/// (and keeping it in sync with the |SkStopwatchVisualizer|) as easy as +/// possible (i.e. not having to do triangle-math). +class DlVertexPainter final { + public: + /// Draws a rectangle with the given color to a buffer. + void DrawRect(const SkRect& rect, const DlColor& color) { + // Draw 6 vertices representing 2 triangles. + auto const left = rect.x(); + auto const top = rect.y(); + auto const right = rect.right(); + auto const bottom = rect.bottom(); + + auto const vertices = std::array{ + SkPoint::Make(left, top), // tl tr + SkPoint::Make(right, top), // br + SkPoint::Make(right, bottom), // + SkPoint::Make(right, bottom), // tl + SkPoint::Make(left, bottom), // bl br + SkPoint::Make(left, top) // + }; + + auto const colors = std::array{ + color, // tl tr + color, // br + color, // + color, // tl + color, // bl br + color // + }; + + vertices_.insert(vertices_.end(), vertices.begin(), vertices.end()); + colors_.insert(colors_.end(), colors.begin(), colors.end()); + } + + /// Converts the buffered vertices into a |DlVertices| object. + /// + /// @note This method clears the buffer. + std::shared_ptr IntoVertices() { + auto const result = DlVertices::Make( + /*mode=*/DlVertexMode::kTriangles, + /*vertex_count=*/vertices_.size(), + /*vertices=*/vertices_.data(), + /*texture_coordinates=*/nullptr, + /*colors=*/colors_.data()); + vertices_.clear(); + colors_.clear(); + return result; + } + + private: + std::vector vertices_; + std::vector colors_; +}; + /// Returns 6 vertices representing a rectangle. /// /// Rather than using a path, which we'll end up tessellating per frame, we @@ -48,6 +115,7 @@ static const size_t kMaxFrameMarkers = 8; // to everything. void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, const SkRect& rect) const { + auto painter = DlVertexPainter(); DlPaint paint; // Establish the graph position. @@ -71,11 +139,8 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, horizontal_markers = 1; } - // Erase all pixels. - { - paint.setColor(0x99FFFFFF); - canvas->DrawRect(rect, paint); - } + // Provide a semi-transparent background for the graph. + painter.DrawRect(rect, 0x99FFFFFF); // Prepare a path for the data; we start at the height of the last point so // it looks like we wrap around. @@ -89,25 +154,16 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, auto const bar_height = height * sample_unit_height; auto const bar_left = x + width * sample_unit_width * i; - paint.setColor(0xAA0000FF); - // FIXME: We should be collecting all of our vertices into a single array - // and then drawing them all at once, rather than drawing each one as we - // go. Ideally use DlColor as well and do literally 1 DrawVertices call - // at the end of this function. - canvas->DrawVertices(FromRectLTRB(/*left=*/bar_left, + painter.DrawRect(SkRect::MakeLTRB(/*left=*/bar_left, /*top=*/y + bar_height, /*right=*/bar_left + bar_width, - /*bottom=*/y + height), - DlBlendMode::kSrc, paint); + /*bottom=*/bottom), + 0xAA0000FF); } } // Draw horizontal frame markers. { - paint.setStrokeWidth(0); - paint.setDrawStyle(DlDrawStyle::kStroke); - paint.setColor(0xCC000000); - if (max_interval > one_frame_ms) { // Paint the horizontal markers. auto count = static_cast(max_interval / one_frame_ms); @@ -122,36 +178,34 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, height * (1.0 - (UnitFrameInterval(i + 1) * one_frame_ms) / max_unit_interval); - // Draw a skinny rectangle (i.e. line). - canvas->DrawVertices(FromRectLTRB(/*left=*/x, + // Draw a skinny rectangle (i.e. a line). + painter.DrawRect(SkRect::MakeLTRB(/*left=*/x, /*top=*/y + frame_height, /*right=*/width, /*bottom=*/y + frame_height + 1), - DlBlendMode::kSrc, paint); + 0xCC000000); } } } // Paint the vertical marker for the current frame. { - paint.setDrawStyle(DlDrawStyle::kFill); - paint.setBlendMode(DlBlendMode::kSrcOver); + DlColor color = DlColor::kGreen(); if (UnitFrameInterval(stopwatch_.LastLap().ToMillisecondsF()) > 1.0) { // budget exceeded. - paint.setColor(DlColor::kRed()); - } else { - // within budget. - paint.setColor(DlColor::kGreen()); + color = DlColor::kRed(); } - auto const l = x + width * (static_cast(stopwatch_.GetCurrentSample()) / kMaxSamples); auto const t = y; auto const r = l + width * sample_unit_width; auto const b = rect.bottom(); - canvas->DrawVertices(FromRectLTRB(l, t, r, b), DlBlendMode::kSrc, paint); + painter.DrawRect(SkRect::MakeLTRB(l, t, r, b), color); } + + // Actually draw. + canvas->DrawVertices(painter.IntoVertices(), DlBlendMode::kSrc, paint); } } // namespace flutter From dc2cb797c36217941f3ea55310bdce4a99554e8b Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 30 Aug 2023 14:08:24 -0700 Subject: [PATCH 07/12] Add stopwatch_dl_unittests and write a test. --- ci/licenses_golden/excluded_files | 1 + flow/BUILD.gn | 1 + flow/stopwatch_dl.cc | 108 ++++++++++++------------------ flow/stopwatch_dl.h | 27 ++++++++ flow/stopwatch_dl_unittests.cc | 73 ++++++++++++++++++++ 5 files changed, 143 insertions(+), 67 deletions(-) create mode 100644 flow/stopwatch_dl_unittests.cc diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 968cc72f3e10d..07cfad821a1b2 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -74,6 +74,7 @@ ../../../flutter/flow/mutators_stack_unittests.cc ../../../flutter/flow/raster_cache_unittests.cc ../../../flutter/flow/skia_gpu_object_unittests.cc +../../../flutter/flow/stopwatch_dl_unittests.cc ../../../flutter/flow/stopwatch_unittests.cc ../../../flutter/flow/surface_frame_unittests.cc ../../../flutter/flow/testing diff --git a/flow/BUILD.gn b/flow/BUILD.gn index ceb51c297e52e..53de13474e316 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -170,6 +170,7 @@ if (enable_unittests) { "mutators_stack_unittests.cc", "raster_cache_unittests.cc", "skia_gpu_object_unittests.cc", + "stopwatch_dl_unittests.cc", "stopwatch_unittests.cc", "surface_frame_unittests.cc", "testing/mock_layer_unittests.cc", diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index 93b16880bcbd1..a633465eddcde 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -11,74 +11,9 @@ #include "display_list/dl_paint.h" #include "display_list/dl_vertices.h" #include "include/core/SkRect.h" -#include "include/private/base/SkPoint_impl.h" namespace flutter { -/// @brief Provides canvas-like painting methods that actually build vertices. -/// -/// The goal is minimally invasive rendering for the performance monitor. -/// -/// The methods in this class are intended to be used by |DlStopwatchVisualizer| -/// only. The rationale is the creating lines, rectangles, and paths (while OK -/// for general apps) would cause non-trivial work for the performance monitor -/// due to tessellation per-frame. -/// -/// @note A goal of this class was to make updating the performance monitor -/// (and keeping it in sync with the |SkStopwatchVisualizer|) as easy as -/// possible (i.e. not having to do triangle-math). -class DlVertexPainter final { - public: - /// Draws a rectangle with the given color to a buffer. - void DrawRect(const SkRect& rect, const DlColor& color) { - // Draw 6 vertices representing 2 triangles. - auto const left = rect.x(); - auto const top = rect.y(); - auto const right = rect.right(); - auto const bottom = rect.bottom(); - - auto const vertices = std::array{ - SkPoint::Make(left, top), // tl tr - SkPoint::Make(right, top), // br - SkPoint::Make(right, bottom), // - SkPoint::Make(right, bottom), // tl - SkPoint::Make(left, bottom), // bl br - SkPoint::Make(left, top) // - }; - - auto const colors = std::array{ - color, // tl tr - color, // br - color, // - color, // tl - color, // bl br - color // - }; - - vertices_.insert(vertices_.end(), vertices.begin(), vertices.end()); - colors_.insert(colors_.end(), colors.begin(), colors.end()); - } - - /// Converts the buffered vertices into a |DlVertices| object. - /// - /// @note This method clears the buffer. - std::shared_ptr IntoVertices() { - auto const result = DlVertices::Make( - /*mode=*/DlVertexMode::kTriangles, - /*vertex_count=*/vertices_.size(), - /*vertices=*/vertices_.data(), - /*texture_coordinates=*/nullptr, - /*colors=*/colors_.data()); - vertices_.clear(); - colors_.clear(); - return result; - } - - private: - std::vector vertices_; - std::vector colors_; -}; - /// Returns 6 vertices representing a rectangle. /// /// Rather than using a path, which we'll end up tessellating per frame, we @@ -89,8 +24,6 @@ std::shared_ptr FromRectLTRB(const SkScalar left, const SkScalar top, const SkScalar right, const SkScalar bottom) { - // FIXME: Convert this into a helper class with AddRect and AddLine. - // FIXME: Move the helper class into stopwatch_dl_vertices_helper and test it. auto const top_left = SkPoint::Make(left, top); auto const top_right = SkPoint::Make(right, top); auto const bottom_right = SkPoint::Make(right, bottom); @@ -208,4 +141,45 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, canvas->DrawVertices(painter.IntoVertices(), DlBlendMode::kSrc, paint); } +void DlVertexPainter::DrawRect(const SkRect& rect, const DlColor& color) { + // Draw 6 vertices representing 2 triangles. + auto const left = rect.x(); + auto const top = rect.y(); + auto const right = rect.right(); + auto const bottom = rect.bottom(); + + auto const vertices = std::array{ + SkPoint::Make(left, top), // tl tr + SkPoint::Make(right, top), // br + SkPoint::Make(right, bottom), // + SkPoint::Make(right, bottom), // tl + SkPoint::Make(left, bottom), // bl br + SkPoint::Make(left, top) // + }; + + auto const colors = std::array{ + color, // tl tr + color, // br + color, // + color, // tl + color, // bl br + color // + }; + + vertices_.insert(vertices_.end(), vertices.begin(), vertices.end()); + colors_.insert(colors_.end(), colors.begin(), colors.end()); +} + +std::shared_ptr DlVertexPainter::IntoVertices() { + auto const result = DlVertices::Make( + /*mode=*/DlVertexMode::kTriangles, + /*vertex_count=*/vertices_.size(), + /*vertices=*/vertices_.data(), + /*texture_coordinates=*/nullptr, + /*colors=*/colors_.data()); + vertices_.clear(); + colors_.clear(); + return result; +} + } // namespace flutter diff --git a/flow/stopwatch_dl.h b/flow/stopwatch_dl.h index afe1b957db0e9..d15dc40b49473 100644 --- a/flow/stopwatch_dl.h +++ b/flow/stopwatch_dl.h @@ -24,6 +24,33 @@ class DlStopwatchVisualizer : public StopwatchVisualizer { void Visualize(DlCanvas* canvas, const SkRect& rect) const override; }; +/// @brief Provides canvas-like painting methods that actually build vertices. +/// +/// The goal is minimally invasive rendering for the performance monitor. +/// +/// The methods in this class are intended to be used by |DlStopwatchVisualizer| +/// only. The rationale is the creating lines, rectangles, and paths (while OK +/// for general apps) would cause non-trivial work for the performance monitor +/// due to tessellation per-frame. +/// +/// @note A goal of this class was to make updating the performance monitor +/// (and keeping it in sync with the |SkStopwatchVisualizer|) as easy as +/// possible (i.e. not having to do triangle-math). +class DlVertexPainter final { + public: + /// Draws a rectangle with the given color to a buffer. + void DrawRect(const SkRect& rect, const DlColor& color); + + /// Converts the buffered vertices into a |DlVertices| object. + /// + /// @note This method clears the buffer. + std::shared_ptr IntoVertices(); + + private: + std::vector vertices_; + std::vector colors_; +}; + } // namespace flutter #endif // FLUTTER_FLOW_STOPWATCH_DL_H_ diff --git a/flow/stopwatch_dl_unittests.cc b/flow/stopwatch_dl_unittests.cc new file mode 100644 index 0000000000000..f8d2900f84674 --- /dev/null +++ b/flow/stopwatch_dl_unittests.cc @@ -0,0 +1,73 @@ +// 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/flow/stopwatch_dl.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +static SkRect MakeRectFromVertices(SkPoint vertices[6]) { + // "Combine" the vertices to form a rectangle. + auto const left = std::min(vertices[0].x(), vertices[5].x()); + auto const top = std::min(vertices[0].y(), vertices[1].y()); + auto const right = std::max(vertices[1].x(), vertices[2].x()); + auto const bottom = std::max(vertices[2].y(), vertices[3].y()); + + return SkRect::MakeLTRB(left, top, right, bottom); +} + +TEST(DlVertexPainter, DrawRectIntoVertices) { + auto painter = DlVertexPainter(); + + // Paint a red rectangle. + painter.DrawRect(SkRect::MakeLTRB(0, 0, 10, 10), DlColor::kRed()); + + // Paint a blue rectangle. + painter.DrawRect(SkRect::MakeLTRB(10, 10, 20, 20), DlColor::kBlue()); + + // Convert the buffered vertices into a |DlVertices| object. + auto vertices = painter.IntoVertices(); + + // Verify the vertices. + EXPECT_EQ(vertices->mode(), DlVertexMode::kTriangles); + EXPECT_EQ(vertices->vertex_count(), 3 * 2 * 2); + + auto const points = vertices->vertices(); + + { + // Extract the first 6 vertices (first rectangle). + SkPoint first_rect_vertices[6]; + std::copy(points, points + 6, first_rect_vertices); + EXPECT_EQ(MakeRectFromVertices(first_rect_vertices), + SkRect::MakeLTRB(0, 0, 10, 10)); + } + + { + // Extract the next 6 vertices (second rectangle). + SkPoint second_rect_vertices[6]; + std::copy(points + 6, points + 12, second_rect_vertices); + EXPECT_EQ(MakeRectFromVertices(second_rect_vertices), + SkRect::MakeLTRB(10, 10, 20, 20)); + } + + // Verify the colors (first 6 vertices are red, next 6 are blue). + auto const colors = vertices->colors(); + EXPECT_EQ(colors[0], DlColor::kRed()); + EXPECT_EQ(colors[1], DlColor::kRed()); + EXPECT_EQ(colors[2], DlColor::kRed()); + EXPECT_EQ(colors[3], DlColor::kRed()); + EXPECT_EQ(colors[4], DlColor::kRed()); + EXPECT_EQ(colors[5], DlColor::kRed()); + + EXPECT_EQ(colors[6], DlColor::kBlue()); + EXPECT_EQ(colors[7], DlColor::kBlue()); + EXPECT_EQ(colors[8], DlColor::kBlue()); + EXPECT_EQ(colors[9], DlColor::kBlue()); + EXPECT_EQ(colors[10], DlColor::kBlue()); + EXPECT_EQ(colors[11], DlColor::kBlue()); +} + +} // namespace testing +} // namespace flutter From 7943593bdcf5c81c0e0595ce54a976c21c16e15b Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 30 Aug 2023 15:02:47 -0700 Subject: [PATCH 08/12] Remove dead code. --- flow/stopwatch_dl.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index a633465eddcde..262e9893c50b1 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -64,14 +64,6 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, auto const max_unit_interval = UnitFrameInterval(max_interval); auto const sample_unit_width = (1.0 / kMaxSamples); - // Determine how many lines to draw. - auto horizontal_markers = static_cast(max_interval / one_frame_ms); - - // Limit the number of markers to a reasonable amount. - if (horizontal_markers > kMaxFrameMarkers) { - horizontal_markers = 1; - } - // Provide a semi-transparent background for the graph. painter.DrawRect(rect, 0x99FFFFFF); From 77a4b782ce0d4a7df8c8f3a16f9146c12cafa429 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 30 Aug 2023 15:18:00 -0700 Subject: [PATCH 09/12] Remove dead code. --- flow/stopwatch_dl.cc | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index 262e9893c50b1..b1fb2fce5d26a 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -14,32 +14,6 @@ namespace flutter { -/// Returns 6 vertices representing a rectangle. -/// -/// Rather than using a path, which we'll end up tessellating per frame, we -/// create a vertices object and add the rectangles (2x triangles) to it. -/// -/// The goal is minimally invasive rendering for the performance monitor. -std::shared_ptr FromRectLTRB(const SkScalar left, - const SkScalar top, - const SkScalar right, - const SkScalar bottom) { - auto const top_left = SkPoint::Make(left, top); - auto const top_right = SkPoint::Make(right, top); - auto const bottom_right = SkPoint::Make(right, bottom); - auto const bottom_left = SkPoint::Make(left, bottom); - const SkPoint vertices[6] = { - top_left, // tl tr - top_right, // br - bottom_right, // - bottom_right, // tl - bottom_left, // bl br - top_left // - }; - return DlVertices::Make(DlVertexMode::kTriangles, 6, vertices, nullptr, - nullptr); -} - static const size_t kMaxSamples = 120; static const size_t kMaxFrameMarkers = 8; From 3fcbe0008c25d04d04f6639bd60c43198626ca3c Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 30 Aug 2023 15:20:44 -0700 Subject: [PATCH 10/12] Remove FIXME. --- flow/stopwatch_dl.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index b1fb2fce5d26a..b5f86a9b7853f 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -17,9 +17,6 @@ namespace flutter { static const size_t kMaxSamples = 120; static const size_t kMaxFrameMarkers = 8; -// FIXME: Clean up this method in general, including all the functions and -// as-needed split into multiple methods and give better names and comments -// to everything. void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, const SkRect& rect) const { auto painter = DlVertexPainter(); From f85ee9eee89e56a1e4f0f8b04dec623b6db30c05 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 30 Aug 2023 15:29:16 -0700 Subject: [PATCH 11/12] kSrcOut. --- flow/stopwatch_dl.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index b5f86a9b7853f..49678ea3367b6 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -101,7 +101,8 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, } // Actually draw. - canvas->DrawVertices(painter.IntoVertices(), DlBlendMode::kSrc, paint); + // Note we use kSrcOut, because some of the colors above have opacity < 1.0. + canvas->DrawVertices(painter.IntoVertices(), DlBlendMode::kSrcOut, paint); } void DlVertexPainter::DrawRect(const SkRect& rect, const DlColor& color) { From 99d0cd140f69cc114f0d66dd8dcd1ecab3e2ec00 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Wed, 30 Aug 2023 15:34:42 -0700 Subject: [PATCH 12/12] Fix type. --- flow/stopwatch_dl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flow/stopwatch_dl.cc b/flow/stopwatch_dl.cc index 49678ea3367b6..20a4958460614 100644 --- a/flow/stopwatch_dl.cc +++ b/flow/stopwatch_dl.cc @@ -101,8 +101,8 @@ void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, } // Actually draw. - // Note we use kSrcOut, because some of the colors above have opacity < 1.0. - canvas->DrawVertices(painter.IntoVertices(), DlBlendMode::kSrcOut, paint); + // Note we use kSrcOver, because some of the colors above have opacity < 1.0. + canvas->DrawVertices(painter.IntoVertices(), DlBlendMode::kSrcOver, paint); } void DlVertexPainter::DrawRect(const SkRect& rect, const DlColor& color) {