| // 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" |
| |
| namespace flutter { |
| |
| static const size_t kMaxSamples = 120; |
| static const size_t kMaxFrameMarkers = 8; |
| |
| void DlStopwatchVisualizer::Visualize(DlCanvas* canvas, |
| const DlRect& rect) const { |
| auto painter = DlVertexPainter(vertices_storage_, color_storage_); |
| DlPaint paint; |
| |
| // Establish the graph position. |
| auto const x = rect.GetX(); |
| auto const y = rect.GetY(); |
| auto const width = rect.GetWidth(); |
| auto const height = rect.GetHeight(); |
| auto const bottom = rect.GetBottom(); |
| |
| // Scale the graph to show time frames up to those that are 3x the frame time. |
| auto const one_frame_ms = 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); |
| |
| // resize backing storage to match expected lap count. |
| size_t required_storage = |
| (stopwatch_.GetLapsCount() + 2 + kMaxFrameMarkers) * 6; |
| if (vertices_storage_.size() < required_storage) { |
| vertices_storage_.resize(required_storage); |
| color_storage_.resize(required_storage); |
| } |
| |
| // Provide a semi-transparent background for the graph. |
| painter.DrawRect(rect, DlColor(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 = 0u; 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(DlRect::MakeLTRB(/*left=*/bar_left, |
| /*top=*/y + bar_height, |
| /*right=*/bar_left + bar_width, |
| /*bottom=*/bottom), |
| DlColor(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 = 0u; 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(DlRect::MakeLTRB(/*left=*/x, |
| /*top=*/y + frame_height, |
| /*right=*/width, |
| /*bottom=*/y + frame_height + 1), |
| DlColor(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.GetBottom(); |
| painter.DrawRect(DlRect::MakeLTRB(l, t, r, b), color); |
| } |
| |
| // Actually draw. |
| // Use kSrcOver blend mode so that elements under the performance overlay are |
| // partially visible. |
| paint.setBlendMode(DlBlendMode::kSrcOver); |
| // The second blend mode does nothing since the paint has no additional color |
| // sources like a tiled image or gradient. |
| canvas->DrawVertices(painter.IntoVertices(rect), DlBlendMode::kSrcOver, |
| paint); |
| } |
| |
| DlVertexPainter::DlVertexPainter(std::vector<DlPoint>& vertices_storage, |
| std::vector<DlColor>& color_storage) |
| : vertices_(vertices_storage), colors_(color_storage) {} |
| |
| void DlVertexPainter::DrawRect(const DlRect& rect, const DlColor& color) { |
| auto const left = rect.GetLeft(); |
| auto const top = rect.GetTop(); |
| auto const right = rect.GetRight(); |
| auto const bottom = rect.GetBottom(); |
| |
| FML_DCHECK(6 + colors_offset_ <= vertices_.size()); |
| FML_DCHECK(6 + colors_offset_ <= colors_.size()); |
| |
| // Draw 6 vertices representing 2 triangles. |
| vertices_[vertices_offset_++] = DlPoint(left, top); // tl tr |
| vertices_[vertices_offset_++] = DlPoint(right, top); // br |
| vertices_[vertices_offset_++] = DlPoint(right, bottom); // |
| vertices_[vertices_offset_++] = DlPoint(right, bottom); // tl |
| vertices_[vertices_offset_++] = DlPoint(left, bottom); // bl br |
| vertices_[vertices_offset_++] = DlPoint(left, top); // |
| for (auto i = 0u; i < 6u; i++) { |
| colors_[colors_offset_++] = color; |
| } |
| } |
| |
| std::shared_ptr<DlVertices> DlVertexPainter::IntoVertices( |
| const DlRect& bounds_rect) { |
| return DlVertices::Make( |
| /*mode=*/DlVertexMode::kTriangles, |
| /*vertex_count=*/vertices_.size(), |
| /*vertices=*/reinterpret_cast<SkPoint*>(vertices_.data()), |
| /*texture_coordinates=*/nullptr, |
| /*colors=*/colors_.data(), |
| /*index_count=*/0, |
| /*indices=*/nullptr, |
| /*bounds=*/&bounds_rect); |
| } |
| |
| } // namespace flutter |