| // 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, |
| const std::string& font_path) { |
| const int label_x = 8; // distance from x |
| const int label_y = -10; // distance from y+height |
| |
| if (show_graph) { |
| SkRect visualization_rect = SkRect::MakeXYWH(x, y, width, height); |
| std::unique_ptr<StopwatchVisualizer> visualizer; |
| |
| if (impeller_enabled) { |
| visualizer = std::make_unique<DlStopwatchVisualizer>(stopwatch); |
| } else { |
| visualizer = std::make_unique<SkStopwatchVisualizer>(stopwatch); |
| } |
| |
| visualizer->Visualize(canvas, visualization_rect); |
| } |
| |
| if (show_labels) { |
| auto text = PerformanceOverlayLayer::MakeStatisticsText( |
| stopwatch, label_prefix, font_path); |
| // 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 |
| |
| sk_sp<SkTextBlob> PerformanceOverlayLayer::MakeStatisticsText( |
| const Stopwatch& stopwatch, |
| const std::string& label_prefix, |
| const std::string& font_path) { |
| SkFont font; |
| sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager(); |
| if (font_path == "") { |
| if (sk_sp<SkTypeface> face = font_mgr->matchFamilyStyle(nullptr, {})) { |
| font = 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. |
| font = SkFont(font_mgr->legacyMakeTypeface(nullptr, {}), 15); |
| } |
| } else { |
| font = SkFont(font_mgr->makeFromFile(font_path.c_str()), 15); |
| } |
| // 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; |
| } |
| |
| SkScalar x = paint_bounds().x() + padding; |
| SkScalar y = paint_bounds().y() + padding; |
| SkScalar width = paint_bounds().width() - (padding * 2); |
| SkScalar height = paint_bounds().height() / 2; |
| auto mutator = context.state_stack.save(); |
| |
| VisualizeStopWatch( |
| context.canvas, context.impeller_enabled, context.raster_time, x, y, |
| width, height - padding, options_ & kVisualizeRasterizerStatistics, |
| options_ & kDisplayRasterizerStatistics, "Raster", font_path_); |
| |
| VisualizeStopWatch(context.canvas, context.impeller_enabled, context.ui_time, |
| x, y + height, width, height - padding, |
| options_ & kVisualizeEngineStatistics, |
| options_ & kDisplayEngineStatistics, "UI", font_path_); |
| } |
| |
| } // namespace flutter |