// Copyright 2014 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. import 'box.dart'; import 'layer.dart'; import 'object.dart'; /// The options that control whether the performance overlay displays certain /// aspects of the compositor. enum PerformanceOverlayOption { // these must be in the order needed for their index values to match the // constants in //engine/src/sky/compositor/performance_overlay_layer.h /// Display the frame time and FPS of the last frame rendered. This field is /// updated every frame. /// /// This is the time spent by the rasterizer as it tries /// to convert the layer tree obtained from the widgets into OpenGL commands /// and tries to flush them onto the screen. When the total time taken by this /// step exceeds the frame slice, a frame is lost. displayRasterizerStatistics, /// Display the rasterizer frame times as they change over a set period of /// time in the form of a graph. The y axis of the graph denotes the total /// time spent by the rasterizer as a fraction of the total frame slice. When /// the bar turns red, a frame is lost. visualizeRasterizerStatistics, /// Display the frame time and FPS at which the interface can construct a /// layer tree for the rasterizer (whose behavior is described above) to /// consume. /// /// This involves all layout, animations, etc. When the total time taken by /// this step exceeds the frame slice, a frame is lost. displayEngineStatistics, /// Display the engine frame times as they change over a set period of time /// in the form of a graph. The y axis of the graph denotes the total time /// spent by the engine as a fraction of the total frame slice. When the bar /// turns red, a frame is lost. visualizeEngineStatistics, } /// Displays performance statistics. /// /// The overlay shows two time series. The first shows how much time was /// required on this thread to produce each frame. The second shows how much /// time was required on the raster thread (formerly known as the GPU thread) /// to produce each frame. Ideally, both these values would be less than /// the total frame budget for the hardware on which the app is running. /// For example, if the hardware has a screen that updates at 60 Hz, each /// thread should ideally spend less than 16ms producing each frame. /// This ideal condition is indicated by a green vertical line for each thread. /// Otherwise, the performance overlay shows a red vertical line. /// /// The simplest way to show the performance overlay is to set /// [MaterialApp.showPerformanceOverlay] or [WidgetsApp.showPerformanceOverlay] /// to true. class RenderPerformanceOverlay extends RenderBox { /// Creates a performance overlay render object. /// /// The [optionsMask], [rasterizerThreshold], [checkerboardRasterCacheImages], /// and [checkerboardOffscreenLayers] arguments must not be null. RenderPerformanceOverlay({ int optionsMask = 0, int rasterizerThreshold = 0, bool checkerboardRasterCacheImages = false, bool checkerboardOffscreenLayers = false, }) : assert(optionsMask != null), assert(rasterizerThreshold != null), assert(checkerboardRasterCacheImages != null), assert(checkerboardOffscreenLayers != null), _optionsMask = optionsMask, _rasterizerThreshold = rasterizerThreshold, _checkerboardRasterCacheImages = checkerboardRasterCacheImages, _checkerboardOffscreenLayers = checkerboardOffscreenLayers; /// The mask is created by shifting 1 by the index of the specific /// [PerformanceOverlayOption] to enable. int get optionsMask => _optionsMask; int _optionsMask; set optionsMask(int value) { assert(value != null); if (value == _optionsMask) return; _optionsMask = value; markNeedsPaint(); } /// The rasterizer threshold is an integer specifying the number of frame /// intervals that the rasterizer must miss before it decides that the frame /// is suitable for capturing an SkPicture trace for further analysis. int get rasterizerThreshold => _rasterizerThreshold; int _rasterizerThreshold; set rasterizerThreshold(int value) { assert(value != null); if (value == _rasterizerThreshold) return; _rasterizerThreshold = value; markNeedsPaint(); } /// Whether the raster cache should checkerboard cached entries. bool get checkerboardRasterCacheImages => _checkerboardRasterCacheImages; bool _checkerboardRasterCacheImages; set checkerboardRasterCacheImages(bool value) { assert(value != null); if (value == _checkerboardRasterCacheImages) return; _checkerboardRasterCacheImages = value; markNeedsPaint(); } /// Whether the compositor should checkerboard layers rendered to offscreen bitmaps. bool get checkerboardOffscreenLayers => _checkerboardOffscreenLayers; bool _checkerboardOffscreenLayers; set checkerboardOffscreenLayers(bool value) { assert(value != null); if (value == _checkerboardOffscreenLayers) return; _checkerboardOffscreenLayers = value; markNeedsPaint(); } @override bool get sizedByParent => true; @override bool get alwaysNeedsCompositing => true; @override double computeMinIntrinsicWidth(double height) { return 0.0; } @override double computeMaxIntrinsicWidth(double height) { return 0.0; } double get _intrinsicHeight { const double kDefaultGraphHeight = 80.0; double result = 0.0; if ((optionsMask | (1 << PerformanceOverlayOption.displayRasterizerStatistics.index) > 0) || (optionsMask | (1 << PerformanceOverlayOption.visualizeRasterizerStatistics.index) > 0)) result += kDefaultGraphHeight; if ((optionsMask | (1 << PerformanceOverlayOption.displayEngineStatistics.index) > 0) || (optionsMask | (1 << PerformanceOverlayOption.visualizeEngineStatistics.index) > 0)) result += kDefaultGraphHeight; return result; } @override double computeMinIntrinsicHeight(double width) { return _intrinsicHeight; } @override double computeMaxIntrinsicHeight(double width) { return _intrinsicHeight; } @override Size computeDryLayout(BoxConstraints constraints) { return constraints.constrain(Size(double.infinity, _intrinsicHeight)); } @override void paint(PaintingContext context, Offset offset) { assert(needsCompositing); context.addLayer(PerformanceOverlayLayer( overlayRect: Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height), optionsMask: optionsMask, rasterizerThreshold: rasterizerThreshold, checkerboardRasterCacheImages: checkerboardRasterCacheImages, checkerboardOffscreenLayers: checkerboardOffscreenLayers, )); } }