forked from firka/flutter
Add a DlStopwatchVisualizer and conditionally use it for Impeller (flutter/engine#45259)
Closes https://github.com/flutter/flutter/issues/126009. One major change, as-per @jonahwilliams's feedback, is that I created a helper class called `DlVertexPainter`, which provides a `DrawRect`-like API, but just builds a buffer of vertices, resulting into just a single: ```cc // Actually draw. canvas->DrawVertices(painter.IntoVertices(), DlBlendMode::kSrc, paint); ``` Also, added test for it, since there is no way to screenshot test to Impeller overlay yet. # Impeller 
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
@@ -168,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",
|
||||
|
||||
@@ -6,8 +6,11 @@
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#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<StopwatchVisualizer> 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<DlStopwatchVisualizer>(stopwatch);
|
||||
} else {
|
||||
visualizer = std::make_unique<SkStopwatchVisualizer>(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_);
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
149
engine/src/flutter/flow/stopwatch_dl.cc
Normal file
149
engine/src/flutter/flow/stopwatch_dl.cc
Normal file
@@ -0,0 +1,149 @@
|
||||
// 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 <memory>
|
||||
#include <vector>
|
||||
#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"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
static const size_t kMaxSamples = 120;
|
||||
static const size_t kMaxFrameMarkers = 8;
|
||||
|
||||
void DlStopwatchVisualizer::Visualize(DlCanvas* canvas,
|
||||
const SkRect& rect) const {
|
||||
auto painter = DlVertexPainter();
|
||||
DlPaint paint;
|
||||
|
||||
// Establish the graph position.
|
||||
auto const x = rect.x();
|
||||
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();
|
||||
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);
|
||||
|
||||
// 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.
|
||||
{
|
||||
for (auto i = size_t(0); i < stopwatch_.GetLapsCount(); i++) {
|
||||
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;
|
||||
|
||||
painter.DrawRect(SkRect::MakeLTRB(/*left=*/bar_left,
|
||||
/*top=*/y + bar_height,
|
||||
/*right=*/bar_left + bar_width,
|
||||
/*bottom=*/bottom),
|
||||
0xAA0000FF);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw horizontal frame markers.
|
||||
{
|
||||
if (max_interval > one_frame_ms) {
|
||||
// Paint the horizontal markers.
|
||||
auto count = static_cast<size_t>(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);
|
||||
|
||||
// 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),
|
||||
0xCC000000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Paint the vertical marker for the current frame.
|
||||
{
|
||||
DlColor color = DlColor::kGreen();
|
||||
if (UnitFrameInterval(stopwatch_.LastLap().ToMillisecondsF()) > 1.0) {
|
||||
// budget exceeded.
|
||||
color = DlColor::kRed();
|
||||
}
|
||||
auto const l =
|
||||
x + width * (static_cast<double>(stopwatch_.GetCurrentSample()) /
|
||||
kMaxSamples);
|
||||
auto const t = y;
|
||||
auto const r = l + width * sample_unit_width;
|
||||
auto const b = rect.bottom();
|
||||
painter.DrawRect(SkRect::MakeLTRB(l, t, r, b), color);
|
||||
}
|
||||
|
||||
// Actually draw.
|
||||
// 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) {
|
||||
// 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, 6>{
|
||||
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<DlColor, 6>{
|
||||
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<DlVertices> 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
|
||||
56
engine/src/flutter/flow/stopwatch_dl.h
Normal file
56
engine/src/flutter/flow/stopwatch_dl.h
Normal file
@@ -0,0 +1,56 @@
|
||||
// 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;
|
||||
};
|
||||
|
||||
/// @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<DlVertices> IntoVertices();
|
||||
|
||||
private:
|
||||
std::vector<SkPoint> vertices_;
|
||||
std::vector<DlColor> colors_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_FLOW_STOPWATCH_DL_H_
|
||||
73
engine/src/flutter/flow/stopwatch_dl_unittests.cc
Normal file
73
engine/src/flutter/flow/stopwatch_dl_unittests.cc
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user