blob: f8b67aee206e146342e5eebc1353b32eeb7dc920 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/layers/performance_overlay_layer.h"
#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/SkFontMgr.h"
#include "third_party/skia/include/core/SkTextBlob.h"
#include "third_party/skia/include/core/SkTypeface.h"
#include "txt/platform.h"
#ifdef IMPELLER_SUPPORTS_RENDERING
#include "impeller/typographer/backends/skia/text_frame_skia.h" // nogncheck
#endif // IMPELLER_SUPPORTS_RENDERING
namespace flutter {
namespace {
void VisualizeStopWatch(DlCanvas* canvas,
const bool impeller_enabled,
const Stopwatch& stopwatch,
SkScalar x,
SkScalar y,
SkScalar width,
SkScalar height,
bool show_graph,
bool show_labels,
const std::string& label_prefix,
std::vector<DlPoint>& point_storage,
std::vector<DlColor>& color_storage,
const SkFont& font) {
const int label_x = 8; // distance from x
const int label_y = -10; // distance from y+height
if (show_graph) {
DlRect visualization_rect = DlRect::MakeXYWH(x, y, width, height);
if (impeller_enabled) {
DlStopwatchVisualizer(stopwatch, point_storage, color_storage)
.Visualize(canvas, visualization_rect);
} else {
SkStopwatchVisualizer(stopwatch).Visualize(canvas, visualization_rect);
}
}
if (show_labels) {
auto text = PerformanceOverlayLayer::MakeStatisticsText(stopwatch, font,
label_prefix);
// Historically SK_ColorGRAY (== 0xFF888888) was used here
DlPaint paint(DlColor(0xFF888888));
#ifdef IMPELLER_SUPPORTS_RENDERING
if (impeller_enabled) {
canvas->DrawTextFrame(impeller::MakeTextFrameFromTextBlobSkia(text),
x + label_x, y + height + label_y, paint);
return;
}
#endif // IMPELLER_SUPPORTS_RENDERING
canvas->DrawTextBlob(text, x + label_x, y + height + label_y, paint);
}
}
} // namespace
// static
SkFont PerformanceOverlayLayer::MakeStatisticsFont(std::string_view font_path) {
sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
if (font_path == "") {
if (sk_sp<SkTypeface> face = font_mgr->matchFamilyStyle(nullptr, {})) {
return SkFont(face, 15);
} else {
// In Skia's Android fontmgr, matchFamilyStyle can return null instead
// of falling back to a default typeface. If that's the case, we can use
// legacyMakeTypeface, which *does* use that default typeface.
return SkFont(font_mgr->legacyMakeTypeface(nullptr, {}), 15);
}
} else {
return SkFont(font_mgr->makeFromFile(font_path.data()), 15);
}
}
// static
sk_sp<SkTextBlob> PerformanceOverlayLayer::MakeStatisticsText(
const Stopwatch& stopwatch,
const SkFont& font,
std::string_view label_prefix) {
// Make sure there's not an empty typeface returned, or we won't see any text.
FML_DCHECK(font.getTypeface()->countGlyphs() > 0);
double max_ms_per_frame = stopwatch.MaxDelta().ToMillisecondsF();
double average_ms_per_frame = stopwatch.AverageDelta().ToMillisecondsF();
std::stringstream stream;
stream.setf(std::ios::fixed | std::ios::showpoint);
stream << std::setprecision(1);
stream << label_prefix << " " << "max " << max_ms_per_frame << " ms/frame, "
<< "avg " << average_ms_per_frame << " ms/frame";
auto text = stream.str();
return SkTextBlob::MakeFromText(text.c_str(), text.size(), font,
SkTextEncoding::kUTF8);
}
PerformanceOverlayLayer::PerformanceOverlayLayer(uint64_t options,
const char* font_path)
: options_(options) {
if (font_path != nullptr) {
font_path_ = font_path;
}
}
void PerformanceOverlayLayer::Diff(DiffContext* context,
const Layer* old_layer) {
DiffContext::AutoSubtreeRestore subtree(context);
if (!context->IsSubtreeDirty()) {
FML_DCHECK(old_layer);
auto prev = old_layer->as_performance_overlay_layer();
context->MarkSubtreeDirty(context->GetOldLayerPaintRegion(prev));
}
context->AddLayerBounds(paint_bounds());
context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion());
}
void PerformanceOverlayLayer::Paint(PaintContext& context) const {
const int padding = 8;
if (!options_) {
return;
}
DlScalar x = paint_bounds().GetX() + padding;
DlScalar y = paint_bounds().GetY() + padding;
DlScalar width = paint_bounds().GetWidth() - (padding * 2);
DlScalar height = paint_bounds().GetHeight() / 2;
auto mutator = context.state_stack.save();
// Cached storage for vertex output.
std::vector<DlPoint> vertices_storage;
std::vector<DlColor> color_storage;
SkFont font = MakeStatisticsFont(font_path_);
VisualizeStopWatch(context.canvas, context.impeller_enabled,
context.raster_time, x, y, width, height - padding,
options_ & kVisualizeRasterizerStatistics,
options_ & kDisplayRasterizerStatistics, "Raster",
vertices_storage, color_storage, font);
VisualizeStopWatch(context.canvas, context.impeller_enabled, context.ui_time,
x, y + height, width, height - padding,
options_ & kVisualizeEngineStatistics,
options_ & kDisplayEngineStatistics, "UI",
vertices_storage, color_storage, font);
}
} // namespace flutter