tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

profiler.rs (75890B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 //! # Overlay profiler
      6 //!
      7 //! ## Profiler UI string syntax
      8 //!
      9 //! Comma-separated list of of tokens with trailing and leading spaces trimmed.
     10 //! Each tokens can be:
     11 //! - A counter name with an optional prefix. The name corresponds to the displayed name (see the
     12 //!   counters vector below.
     13 //!   - By default (no prefix) the counter is shown as average + max over half a second.
     14 //!   - With a '#' prefix the counter is shown as a graph.
     15 //!   - With a '*' prefix the counter is shown as a change indicator.
     16 //!   - Some special counters such as GPU time queries have specific visualizations ignoring prefixes.
     17 //! - A preset name to append the preset to the UI (see PROFILER_PRESETS).
     18 //! - An empty token to insert a bit of vertical space.
     19 //! - A '|' token to start a new column.
     20 //! - A '_' token to start a new row.
     21 
     22 use api::{ColorF, ColorU, RenderCommandInfo};
     23 #[cfg(feature = "debugger")]
     24 use api::debugger::{ProfileCounterUpdate, ProfileCounterId};
     25 use glyph_rasterizer::profiler::GlyphRasterizeProfiler;
     26 use crate::renderer::DebugRenderer;
     27 use crate::device::query::GpuTimer;
     28 use euclid::{Point2D, Rect, Size2D, vec2, default};
     29 use crate::internal_types::FastHashMap;
     30 use crate::renderer::{FullFrameStats, init::wr_has_been_initialized};
     31 use api::units::DeviceIntSize;
     32 use std::collections::vec_deque::VecDeque;
     33 use std::fmt::{Write, Debug};
     34 use std::f32;
     35 use std::ops::Range;
     36 use std::time::Duration;
     37 
     38 macro_rules! set_text {
     39    ($dst:expr, $($arg:tt)*) => {
     40        $dst.clear();
     41        write!($dst, $($arg)*).unwrap();
     42    };
     43 }
     44 
     45 const GRAPH_WIDTH: f32 = 1024.0;
     46 const GRAPH_HEIGHT: f32 = 320.0;
     47 const GRAPH_PADDING: f32 = 8.0;
     48 const GRAPH_FRAME_HEIGHT: f32 = 16.0;
     49 const PROFILE_SPACING: f32 = 15.0;
     50 const PROFILE_PADDING: f32 = 10.0;
     51 const BACKGROUND_COLOR: ColorU = ColorU { r: 20, g: 20, b: 20, a: 220 };
     52 
     53 const ONE_SECOND_NS: u64 = 1_000_000_000;
     54 
     55 /// Profiler UI string presets. Defined in the profiler UI string syntax, can contain other presets.
     56 static PROFILER_PRESETS: &'static[(&'static str, &'static str)] = &[
     57    // Default view, doesn't show everything, but still shows quite a bit.
     58    (&"Default", &"FPS,|,Slow indicators,_,Time graphs,|,Frame times, ,Transaction times, ,Frame stats, ,Memory, ,Interners,_,GPU time queries,_,Paint phase graph"),
     59    // Smaller, less intrusive overview
     60    (&"Compact", &"FPS, ,Frame times, ,Frame stats"),
     61    // Even less intrusive, only slow transactions and frame indicators.
     62    (&"Slow indicators", &"*Slow transaction,*Slow frame"),
     63 
     64    // Counters:
     65 
     66    // Timing information for per layout transaction stages.
     67    (&"Transaction times", &"DisplayList,Scene building,Content send,API send"),
     68    // Timing information for per-frame stages.
     69    (&"Frame times", &"Frame CPU total,Frame building,Visibility,Prepare,Batching,Glyph resolve,Texture cache update,Shader build time,Renderer,GPU"),
     70    // Stats about the content of the frame.
     71    (&"Frame stats", &"Primitives,Visible primitives,Draw calls,Vertices,Color passes,Alpha passes,Rendered picture tiles,Rasterized glyphs"),
     72    // Texture cache allocation stats.
     73    (&"Texture cache stats", &"Atlas textures mem, Standalone textures mem, Picture tiles mem, Render targets mem, Depth targets mem, Atlas items mem,
     74        Texture cache standalone pressure, Texture cache eviction count, Texture cache youngest evicted, ,
     75        Atlas RGBA8 linear pixels, Atlas RGBA8 glyphs pixels, Atlas A8 glyphs pixels, Atlas A8 pixels, Atlas A16 pixels, Atlas RGBA8 nearest pixels,
     76        Atlas RGBA8 linear textures, Atlas RGBA8 glyphs textures, Atlas A8 glyphs textures, Atlas A8 textures, Atlas A16 textures, Atlas RGBA8 nearest textures,
     77        Atlas RGBA8 linear pressure, Atlas RGBA8 glyphs pressure, Atlas A8 glyphs pressure, Atlas A8 pressure, Atlas A16 pressure, Atlas RGBA8 nearest pressure,"
     78    ),
     79    // Graphs to investigate driver overhead of texture cache updates.
     80    (&"Texture upload perf", &"#Texture cache update,#Texture cache upload, ,#Staging CPU allocation,#Staging GPU allocation,#Staging CPU copy,#Staging GPU copy,#Upload time, ,#Upload copy batches,#Rasterized glyphs, ,#Cache texture creation,#Cache texture deletion"),
     81 
     82    // Graphs:
     83 
     84    // Graph overview of time spent in WebRender's main stages.
     85    (&"Time graphs", &"#DisplayList,#Scene building,#Blob rasterization, ,#Frame CPU total,#Frame building,#Renderer,#Texture cache update, ,#GPU,"),
     86    // Useful when investigating render backend bottlenecks.
     87    (&"Backend graphs", &"#Frame building, #Visibility, #Prepare, #Batching, #Glyph resolve"),
     88    // Useful when investigating renderer bottlenecks.
     89    (&"Renderer graphs", &"#Rendered picture tiles,#Draw calls,#Rasterized glyphs,#Texture uploads,#Texture uploads mem, ,#Texture cache update,#Renderer,"),
     90 
     91    // Misc:
     92 
     93    (&"GPU Memory", &"External image mem, Atlas textures mem, Standalone textures mem, Picture tiles mem, Render targets mem, Depth targets mem, Atlas items mem, GPU cache mem, GPU buffer mem, GPU total mem"),
     94    (&"CPU Memory", &"Image templates, Image templates mem, Font templates,Font templates mem, DisplayList mem"),
     95    (&"Memory", &"$CPU,CPU Memory, ,$GPU,GPU Memory"),
     96    (&"Interners", "Interned primitives,Interned clips,Interned pictures,Interned text runs,Interned normal borders,Interned image borders,Interned images,Interned YUV images,Interned line decorations,Interned linear gradients,Interned radial gradients,Interned conic gradients,Interned filter data,Interned backdrop renders, Interned backdrop captures"),
     97    // Gpu sampler queries (need the pref gfx.webrender.debug.gpu-sampler-queries).
     98    (&"GPU samplers", &"Alpha targets samplers,Transparent pass samplers,Opaque pass samplers,Total samplers"),
     99 
    100    (&"Render reasons", &"Reason scene, Reason animated property, Reason resource update, Reason async image, Reason clear resources, Reason APZ, Reason resize, Reason widget, Reason cache flush, Reason snapshot, Reason resource hook, Reason config change, Reason content sync, Reason flush, On vsync, Reason testing, Reason other"),
    101 
    102    (&"Slow frame breakdown", &"Total slow frames CPU, Total slow frames GPU, Slow: frame build, Slow: upload, Slow: render, Slow: draw calls, Slow: targets, Slow: blobs, Slow: after scene, Slow scroll frames"),
    103 
    104    (&"Compositor", &"Compositor surface underlays,Compositor surface overlays,Compositor surface blits"),
    105    (&"Video", &"FPS,_,#Rendered picture tiles,_,Compositor"),
    106 ];
    107 
    108 fn find_preset(name: &str) -> Option<&'static str> {
    109    for preset in PROFILER_PRESETS {
    110        if preset.0 == name {
    111            return Some(preset.1);
    112        }
    113    }
    114 
    115    None
    116 }
    117 
    118 // The indices here must match the PROFILE_COUNTERS array (checked at runtime).
    119 pub const FRAME_BUILDING_TIME: usize = 0;
    120 pub const FRAME_VISIBILITY_TIME: usize = 1;
    121 pub const FRAME_PREPARE_TIME: usize = 2;
    122 pub const FRAME_BATCHING_TIME: usize = 3;
    123 
    124 pub const RENDERER_TIME: usize = 4;
    125 pub const TOTAL_FRAME_CPU_TIME: usize = 5;
    126 pub const GPU_TIME: usize = 6;
    127 
    128 pub const CONTENT_SEND_TIME: usize = 7;
    129 pub const API_SEND_TIME: usize = 8;
    130 
    131 pub const DISPLAY_LIST_BUILD_TIME: usize = 9;
    132 pub const DISPLAY_LIST_MEM: usize = 10;
    133 
    134 pub const SCENE_BUILD_TIME: usize = 11;
    135 
    136 pub const SLOW_FRAME: usize = 12;
    137 pub const SLOW_TXN: usize = 13;
    138 
    139 pub const FRAME_TIME: usize = 14;
    140 
    141 pub const TEXTURE_UPLOADS: usize = 15;
    142 pub const TEXTURE_UPLOADS_MEM: usize = 16;
    143 pub const TEXTURE_CACHE_UPDATE_TIME: usize = 17;
    144 pub const CPU_TEXTURE_ALLOCATION_TIME: usize = 18;
    145 pub const STAGING_TEXTURE_ALLOCATION_TIME: usize = 19;
    146 pub const UPLOAD_CPU_COPY_TIME: usize = 20;
    147 pub const UPLOAD_GPU_COPY_TIME: usize = 21;
    148 pub const UPLOAD_TIME: usize = 22;
    149 pub const UPLOAD_NUM_COPY_BATCHES: usize = 23;
    150 pub const TOTAL_UPLOAD_TIME: usize = 24;
    151 pub const CREATE_CACHE_TEXTURE_TIME: usize = 25;
    152 pub const DELETE_CACHE_TEXTURE_TIME: usize = 26;
    153 
    154 pub const RASTERIZED_BLOBS: usize = 27;
    155 pub const RASTERIZED_BLOB_TILES: usize = 28;
    156 pub const RASTERIZED_BLOBS_PX: usize = 29;
    157 pub const BLOB_RASTERIZATION_TIME: usize = 30;
    158 
    159 pub const RASTERIZED_GLYPHS: usize = 31;
    160 pub const GLYPH_RESOLVE_TIME: usize = 32;
    161 
    162 pub const DRAW_CALLS: usize = 33;
    163 pub const VERTICES: usize = 34;
    164 pub const PRIMITIVES: usize = 35;
    165 pub const VISIBLE_PRIMITIVES: usize = 36;
    166 
    167 pub const USED_TARGETS: usize = 37;
    168 pub const CREATED_TARGETS: usize = 38;
    169 pub const PICTURE_CACHE_SLICES: usize = 39;
    170 
    171 pub const COLOR_PASSES: usize = 40;
    172 pub const ALPHA_PASSES: usize = 41;
    173 pub const PICTURE_TILES: usize = 42;
    174 pub const RENDERED_PICTURE_TILES: usize = 43;
    175 
    176 pub const FONT_TEMPLATES: usize = 44;
    177 pub const FONT_TEMPLATES_MEM: usize = 45;
    178 pub const IMAGE_TEMPLATES: usize = 46;
    179 pub const IMAGE_TEMPLATES_MEM: usize = 47;
    180 
    181 // Atlas items represents the area occupied by items in the cache textures.
    182 // The actual texture memory allocated is ATLAS_TEXTURES_MEM.
    183 pub const ATLAS_ITEMS_MEM: usize = 48;
    184 pub const ATLAS_A8_PIXELS: usize = 49;
    185 pub const ATLAS_A8_TEXTURES: usize = 50;
    186 pub const ATLAS_A16_PIXELS: usize = 51;
    187 pub const ATLAS_A16_TEXTURES: usize = 52;
    188 pub const ATLAS_RGBA8_LINEAR_PIXELS: usize = 53;
    189 pub const ATLAS_RGBA8_LINEAR_TEXTURES: usize = 54;
    190 pub const ATLAS_RGBA8_NEAREST_PIXELS: usize = 55;
    191 pub const ATLAS_RGBA8_NEAREST_TEXTURES: usize = 56;
    192 pub const ATLAS_RGBA8_GLYPHS_PIXELS: usize = 57;
    193 pub const ATLAS_RGBA8_GLYPHS_TEXTURES: usize = 58;
    194 pub const ATLAS_A8_GLYPHS_PIXELS: usize = 59;
    195 pub const ATLAS_A8_GLYPHS_TEXTURES: usize = 60;
    196 pub const ATLAS_COLOR8_LINEAR_PRESSURE: usize = 61;
    197 pub const ATLAS_COLOR8_NEAREST_PRESSURE: usize = 62;
    198 pub const ATLAS_COLOR8_GLYPHS_PRESSURE: usize = 63;
    199 pub const ATLAS_ALPHA8_PRESSURE: usize = 64;
    200 pub const ATLAS_ALPHA8_GLYPHS_PRESSURE: usize = 65;
    201 pub const ATLAS_ALPHA16_PRESSURE: usize = 66;
    202 pub const ATLAS_STANDALONE_PRESSURE: usize = 67;
    203 
    204 pub const TEXTURE_CACHE_EVICTION_COUNT: usize = 68;
    205 pub const TEXTURE_CACHE_YOUNGEST_EVICTION: usize = 69;
    206 pub const EXTERNAL_IMAGE_BYTES: usize = 70;
    207 pub const ATLAS_TEXTURES_MEM: usize = 71;
    208 pub const STANDALONE_TEXTURES_MEM: usize = 72;
    209 pub const PICTURE_TILES_MEM: usize = 73;
    210 pub const RENDER_TARGET_MEM: usize = 74;
    211 
    212 pub const ALPHA_TARGETS_SAMPLERS: usize = 75;
    213 pub const TRANSPARENT_PASS_SAMPLERS: usize = 76;
    214 pub const OPAQUE_PASS_SAMPLERS: usize = 77;
    215 pub const TOTAL_SAMPLERS: usize = 78;
    216 
    217 pub const INTERNED_PRIMITIVES: usize = 79;
    218 pub const INTERNED_CLIPS: usize = 80;
    219 pub const INTERNED_TEXT_RUNS: usize = 81;
    220 pub const INTERNED_NORMAL_BORDERS: usize = 82;
    221 pub const INTERNED_IMAGE_BORDERS: usize = 83;
    222 pub const INTERNED_IMAGES: usize = 84;
    223 pub const INTERNED_YUV_IMAGES: usize = 85;
    224 pub const INTERNED_LINE_DECORATIONS: usize = 86;
    225 pub const INTERNED_LINEAR_GRADIENTS: usize = 87;
    226 pub const INTERNED_RADIAL_GRADIENTS: usize = 88;
    227 pub const INTERNED_CONIC_GRADIENTS: usize = 89;
    228 pub const INTERNED_PICTURES: usize = 90;
    229 pub const INTERNED_FILTER_DATA: usize = 91;
    230 pub const INTERNED_BACKDROP_CAPTURES: usize = 92;
    231 pub const INTERNED_BACKDROP_RENDERS: usize = 93;
    232 pub const INTERNED_POLYGONS: usize = 94;
    233 pub const INTERNED_BOX_SHADOWS: usize = 95;
    234 pub const DEPTH_TARGETS_MEM: usize = 96;
    235 
    236 pub const SHADER_BUILD_TIME: usize = 97;
    237 
    238 pub const RENDER_REASON_FIRST: usize = 98;
    239 pub const RENDER_REASON_SCENE: usize = 99;
    240 pub const RENDER_REASON_ANIMATED_PROPERTY: usize = 100;
    241 pub const RENDER_REASON_RESOURCE_UPDATE: usize = 101;
    242 pub const RENDER_REASON_ASYNC_IMAGE: usize = 102;
    243 pub const RENDER_REASON_CLEAR_RESOURCES: usize = 103;
    244 pub const RENDER_REASON_APZ: usize = 104;
    245 pub const RENDER_REASON_RESIZE: usize = 105;
    246 pub const RENDER_REASON_WIDGET: usize = 106;
    247 pub const RENDER_REASON_TEXTURE_CACHE_FLUSH: usize = 107;
    248 pub const RENDER_REASON_SNAPSHOT: usize = 108;
    249 pub const RENDER_REASON_POST_RESOURCE_UPDATE_HOOKS: usize = 109;
    250 pub const RENDER_REASON_CONFIG_CHANGE: usize = 110;
    251 pub const RENDER_REASON_CONTENT_SYNC: usize = 111;
    252 pub const RENDER_REASON_FLUSH: usize = 112;
    253 pub const RENDER_REASON_TESTING: usize = 113;
    254 pub const RENDER_REASON_OTHER: usize = 114;
    255 pub const RENDER_REASON_VSYNC: usize = 115;
    256 
    257 pub const TEXTURES_CREATED: usize = 116;
    258 pub const TEXTURES_DELETED: usize = 117;
    259 
    260 pub const SLOW_FRAME_CPU_COUNT: usize = 118;
    261 pub const SLOW_FRAME_GPU_COUNT: usize = 119;
    262 pub const SLOW_FRAME_BUILD_COUNT: usize = 120;
    263 pub const SLOW_UPLOAD_COUNT: usize = 121;
    264 pub const SLOW_RENDER_COUNT: usize = 122;
    265 pub const SLOW_DRAW_CALLS_COUNT: usize = 123;
    266 pub const SLOW_TARGETS_COUNT: usize = 124;
    267 pub const SLOW_BLOB_COUNT: usize = 125;
    268 pub const SLOW_SCROLL_AFTER_SCENE_COUNT: usize = 126;
    269 
    270 pub const GPU_BUFFER_MEM: usize = 127;
    271 pub const GPU_TOTAL_MEM: usize = 128;
    272 
    273 pub const FRAME_SEND_TIME: usize = 129;
    274 pub const UPDATE_DOCUMENT_TIME: usize = 130;
    275 
    276 pub const COMPOSITOR_SURFACE_UNDERLAYS: usize = 131;
    277 pub const COMPOSITOR_SURFACE_OVERLAYS: usize = 132;
    278 pub const COMPOSITOR_SURFACE_BLITS: usize = 133;
    279 
    280 pub const NUM_PROFILER_EVENTS: usize = 134;
    281 
    282 pub struct Profiler {
    283    counters: Vec<Counter>,
    284    gpu_frames: ProfilerFrameCollection,
    285    frame_stats: ProfilerFrameCollection,
    286    slow_scroll_frames: ProfilerFrameCollection,
    287 
    288    start: u64,
    289    avg_over_period: u64,
    290    num_graph_samples: usize,
    291    slow_cpu_frame_threshold: f32,
    292 
    293    // For FPS computation. Updated in update().
    294    frame_timestamps_within_last_second: Vec<u64>,
    295 
    296    /// Total number of slow frames on the CPU.
    297    slow_frame_cpu_count: u64,
    298    /// Total number of slow frames on the GPU.
    299    slow_frame_gpu_count: u64,
    300    /// Slow frames dominated by frame building.
    301    slow_frame_build_count: u64,
    302    /// Slow frames dominated by draw call submission.
    303    slow_render_count: u64,
    304    /// Slow frames dominated by texture uploads.
    305    slow_upload_count: u64,
    306    /// Slow renders with a high number of draw calls.
    307    slow_draw_calls_count: u64,
    308    /// Slow renders with a high number of render targets.
    309    slow_targets_count: u64,
    310    /// Slow uploads with a high number of blob tiles.
    311    slow_blob_count: u64,
    312    /// Slow scrolling or animation frame after a scene build.
    313    slow_scroll_after_scene_count: u64,
    314 
    315    ui: Vec<Item>,
    316 }
    317 
    318 impl Profiler {
    319    pub fn new() -> Self {
    320 
    321        fn float(name: &'static str, unit: &'static str, index: usize, expected: Expected<f64>) -> CounterDescriptor {
    322            CounterDescriptor { name, unit, show_as: ShowAs::Float, index, expected }
    323        }
    324 
    325        fn int(name: &'static str, unit: &'static str, index: usize, expected: Expected<i64>) -> CounterDescriptor {
    326            CounterDescriptor { name, unit, show_as: ShowAs::Int, index, expected: expected.into_float() }
    327        }
    328 
    329        // Not in the list below:
    330        // - "GPU time queries" shows the details of the GPU time queries if selected as a graph.
    331        // - "GPU cache bars" shows some info about the GPU cache.
    332 
    333        // TODO: This should be a global variable but to keep things readable we need to be able to
    334        // use match in const fn which isn't supported by the current rustc version in gecko's build
    335        // system.
    336        let profile_counters = &[
    337            float("Frame building", "ms", FRAME_BUILDING_TIME, expected(0.0..6.0).avg(0.0..3.0)),
    338            float("Visibility", "ms", FRAME_VISIBILITY_TIME, expected(0.0..3.0).avg(0.0..2.0)),
    339            float("Prepare", "ms", FRAME_PREPARE_TIME, expected(0.0..3.0).avg(0.0..2.0)),
    340            float("Batching", "ms", FRAME_BATCHING_TIME, expected(0.0..3.0).avg(0.0..2.0)),
    341 
    342            float("Renderer", "ms", RENDERER_TIME, expected(0.0..8.0).avg(0.0..5.0)),
    343            float("Frame CPU total", "ms", TOTAL_FRAME_CPU_TIME, expected(0.0..15.0).avg(0.0..6.0)),
    344            float("GPU", "ms", GPU_TIME, expected(0.0..15.0).avg(0.0..8.0)),
    345 
    346            float("Content send", "ms", CONTENT_SEND_TIME, expected(0.0..1.0).avg(0.0..1.0)),
    347            float("API send", "ms", API_SEND_TIME, expected(0.0..1.0).avg(0.0..0.4)),
    348            float("DisplayList", "ms", DISPLAY_LIST_BUILD_TIME, expected(0.0..5.0).avg(0.0..3.0)),
    349            float("DisplayList mem", "MB", DISPLAY_LIST_MEM, expected(0.0..20.0)),
    350            float("Scene building", "ms", SCENE_BUILD_TIME, expected(0.0..4.0).avg(0.0..3.0)),
    351 
    352            float("Slow frame", "", SLOW_FRAME, expected(0.0..0.0)),
    353            float("Slow transaction", "", SLOW_TXN, expected(0.0..0.0)),
    354 
    355            float("Frame", "ms", FRAME_TIME, Expected::none()),
    356 
    357            int("Texture uploads", "", TEXTURE_UPLOADS, expected(0..10)),
    358            float("Texture uploads mem", "MB", TEXTURE_UPLOADS_MEM, expected(0.0..10.0)),
    359            float("Texture cache update", "ms", TEXTURE_CACHE_UPDATE_TIME, expected(0.0..3.0)),
    360            float("Staging CPU allocation", "ms", CPU_TEXTURE_ALLOCATION_TIME, Expected::none()),
    361            float("Staging GPU allocation", "ms", STAGING_TEXTURE_ALLOCATION_TIME, Expected::none()),
    362            float("Staging CPU copy", "ms", UPLOAD_CPU_COPY_TIME, Expected::none()),
    363            float("Staging GPU copy", "ms", UPLOAD_GPU_COPY_TIME, Expected::none()),
    364            float("Upload time", "ms", UPLOAD_TIME, Expected::none()),
    365            int("Upload copy batches", "", UPLOAD_NUM_COPY_BATCHES, Expected::none()),
    366            float("Texture cache upload", "ms", TOTAL_UPLOAD_TIME, expected(0.0..5.0)),
    367            float("Cache texture creation", "ms", CREATE_CACHE_TEXTURE_TIME, expected(0.0..2.0)),
    368            float("Cache texture deletion", "ms", DELETE_CACHE_TEXTURE_TIME, expected(0.0..1.0)),
    369 
    370            int("Rasterized blobs", "", RASTERIZED_BLOBS, expected(0..15)),
    371            int("Rasterized blob tiles", "", RASTERIZED_BLOB_TILES, expected(0..15)),
    372            int("Rasterized blob pixels", "px", RASTERIZED_BLOBS_PX, expected(0..300_000)),
    373            float("Blob rasterization", "ms", BLOB_RASTERIZATION_TIME, expected(0.0..8.0)),
    374 
    375            int("Rasterized glyphs", "", RASTERIZED_GLYPHS, expected(0..15)),
    376            float("Glyph resolve", "ms", GLYPH_RESOLVE_TIME, expected(0.0..4.0)),
    377 
    378            int("Draw calls", "", DRAW_CALLS, expected(1..120).avg(1..90)),
    379            int("Vertices", "", VERTICES, expected(10..5000)),
    380            int("Primitives", "", PRIMITIVES, expected(10..5000)),
    381            int("Visible primitives", "", VISIBLE_PRIMITIVES, expected(1..5000)),
    382 
    383            int("Used targets", "", USED_TARGETS, expected(1..4)),
    384            int("Created targets", "", CREATED_TARGETS, expected(0..3)),
    385            int("Picture cache slices", "", PICTURE_CACHE_SLICES, expected(0..5)),
    386 
    387            int("Color passes", "", COLOR_PASSES, expected(1..4)),
    388            int("Alpha passes", "", ALPHA_PASSES, expected(0..3)),
    389            int("Picture tiles", "", PICTURE_TILES, expected(0..15)),
    390            int("Rendered picture tiles", "", RENDERED_PICTURE_TILES, expected(0..5)),
    391 
    392            int("Font templates", "", FONT_TEMPLATES, expected(0..40)),
    393            float("Font templates mem", "MB", FONT_TEMPLATES_MEM, expected(0.0..20.0)),
    394            int("Image templates", "", IMAGE_TEMPLATES, expected(0..100)),
    395            float("Image templates mem", "MB", IMAGE_TEMPLATES_MEM, expected(0.0..50.0)),
    396 
    397            float("Atlas items mem", "MB", ATLAS_ITEMS_MEM, expected(0.0..100.0)),
    398            int("Atlas A8 pixels", "px", ATLAS_A8_PIXELS, expected(0..1_000_000)),
    399            int("Atlas A8 textures", "", ATLAS_A8_TEXTURES, expected(0..2)),
    400            int("Atlas A16 pixels", "px", ATLAS_A16_PIXELS, expected(0..260_000)),
    401            int("Atlas A16 textures", "", ATLAS_A16_TEXTURES, expected(0..2)),
    402            int("Atlas RGBA8 linear pixels", "px", ATLAS_RGBA8_LINEAR_PIXELS, expected(0..8_000_000)),
    403            int("Atlas RGBA8 linear textures", "", ATLAS_RGBA8_LINEAR_TEXTURES, expected(0..3)),
    404            int("Atlas RGBA8 nearest pixels", "px", ATLAS_RGBA8_NEAREST_PIXELS, expected(0..260_000)),
    405            int("Atlas RGBA8 nearest textures", "", ATLAS_RGBA8_NEAREST_TEXTURES, expected(0..2)),
    406            int("Atlas RGBA8 glyphs pixels", "px", ATLAS_RGBA8_GLYPHS_PIXELS, expected(0..4_000_000)),
    407            int("Atlas RGBA8 glyphs textures", "", ATLAS_RGBA8_GLYPHS_TEXTURES, expected(0..2)),
    408            int("Atlas A8 glyphs pixels", "px", ATLAS_A8_GLYPHS_PIXELS, expected(0..4_000_000)),
    409            int("Atlas A8 glyphs textures", "", ATLAS_A8_GLYPHS_TEXTURES, expected(0..2)),
    410            float("Atlas RGBA8 linear pressure", "", ATLAS_COLOR8_LINEAR_PRESSURE, expected(0.0..1.0)),
    411            float("Atlas RGBA8 nearest pressure", "", ATLAS_COLOR8_NEAREST_PRESSURE, expected(0.0..1.0)),
    412            float("Atlas RGBA8 glyphs pressure", "", ATLAS_COLOR8_GLYPHS_PRESSURE, expected(0.0..1.0)),
    413            float("Atlas A8 pressure", "", ATLAS_ALPHA8_PRESSURE, expected(0.0..1.0)),
    414            float("Atlas A8 glyphs pressure", "", ATLAS_ALPHA8_GLYPHS_PRESSURE, expected(0.0..1.0)),
    415            float("Atlas A16 pressure", "", ATLAS_ALPHA16_PRESSURE, expected(0.0..1.0)),
    416            float("Texture cache standalone pressure", "", ATLAS_STANDALONE_PRESSURE, expected(0.0..1.0)),
    417 
    418            int("Texture cache eviction count", "items", TEXTURE_CACHE_EVICTION_COUNT, Expected::none()),
    419            int("Texture cache youngest evicted", "frames", TEXTURE_CACHE_YOUNGEST_EVICTION, Expected::none()),
    420            float("External image mem", "MB", EXTERNAL_IMAGE_BYTES, Expected::none()),
    421            float("Atlas textures mem", "MB", ATLAS_TEXTURES_MEM, Expected::none()),
    422            float("Standalone textures mem", "MB", STANDALONE_TEXTURES_MEM, Expected::none()),
    423            float("Picture tiles mem", "MB", PICTURE_TILES_MEM, expected(0.0..150.0)),
    424            float("Render targets mem", "MB", RENDER_TARGET_MEM, Expected::none()),
    425 
    426            float("Alpha targets samplers", "%", ALPHA_TARGETS_SAMPLERS, Expected::none()),
    427            float("Transparent pass samplers", "%", TRANSPARENT_PASS_SAMPLERS, Expected::none()),
    428            float("Opaque pass samplers", "%", OPAQUE_PASS_SAMPLERS, Expected::none()),
    429            float("Total samplers", "%", TOTAL_SAMPLERS, Expected::none()),
    430 
    431            int("Interned primitives", "", INTERNED_PRIMITIVES, Expected::none()),
    432            int("Interned clips", "", INTERNED_CLIPS, Expected::none()),
    433            int("Interned text runs", "", INTERNED_TEXT_RUNS, Expected::none()),
    434            int("Interned normal borders", "", INTERNED_NORMAL_BORDERS, Expected::none()),
    435            int("Interned image borders", "", INTERNED_IMAGE_BORDERS, Expected::none()),
    436            int("Interned images", "", INTERNED_IMAGES, Expected::none()),
    437            int("Interned YUV images", "", INTERNED_YUV_IMAGES, Expected::none()),
    438            int("Interned line decorations", "", INTERNED_LINE_DECORATIONS, Expected::none()),
    439            int("Interned linear gradients", "", INTERNED_LINEAR_GRADIENTS, Expected::none()),
    440            int("Interned radial gradients", "", INTERNED_RADIAL_GRADIENTS, Expected::none()),
    441            int("Interned conic gradients", "", INTERNED_CONIC_GRADIENTS, Expected::none()),
    442            int("Interned pictures", "", INTERNED_PICTURES, Expected::none()),
    443            int("Interned filter data", "", INTERNED_FILTER_DATA, Expected::none()),
    444            int("Interned backdrop captures", "", INTERNED_BACKDROP_CAPTURES, Expected::none()),
    445            int("Interned backdrop renders", "", INTERNED_BACKDROP_RENDERS, Expected::none()),
    446            int("Interned polygons", "", INTERNED_POLYGONS, Expected::none()),
    447            int("Interned box-shadows", "", INTERNED_BOX_SHADOWS, Expected::none()),
    448 
    449            float("Depth targets mem", "MB", DEPTH_TARGETS_MEM, Expected::none()),
    450            float("Shader build time", "ms", SHADER_BUILD_TIME, Expected::none()),
    451            // We use the expected range to highlight render reasons that are happening.
    452            float("Reason First", "", RENDER_REASON_FIRST, expected(0.0..0.01)),
    453            float("Reason scene", "", RENDER_REASON_SCENE, expected(0.0..0.01)),
    454            float("Reason animated property", "", RENDER_REASON_ANIMATED_PROPERTY, expected(0.0..0.01)),
    455            float("Reason resource update", "", RENDER_REASON_RESOURCE_UPDATE, expected(0.0..0.01)),
    456            float("Reason async image", "", RENDER_REASON_ASYNC_IMAGE, expected(0.0..0.01)),
    457            float("Reason clear resources", "", RENDER_REASON_CLEAR_RESOURCES, expected(0.0..0.01)),
    458            float("Reason APZ", "", RENDER_REASON_APZ, expected(0.0..0.01)),
    459            float("Reason resize", "", RENDER_REASON_RESIZE, expected(0.0..0.01)),
    460            float("Reason widget", "", RENDER_REASON_WIDGET, expected(0.0..0.01)),
    461            float("Reason cache flush", "", RENDER_REASON_TEXTURE_CACHE_FLUSH, expected(0.0..0.01)),
    462            float("Reason snapshot", "", RENDER_REASON_SNAPSHOT, expected(0.0..0.01)),
    463            float("Reason resource hook", "", RENDER_REASON_POST_RESOURCE_UPDATE_HOOKS, expected(0.0..0.01)),
    464            float("Reason config change", "", RENDER_REASON_CONFIG_CHANGE, expected(0.0..0.01)),
    465            float("Reason content sync", "", RENDER_REASON_CONTENT_SYNC, expected(0.0..0.01)),
    466            float("Reason flush", "", RENDER_REASON_FLUSH, expected(0.0..0.01)),
    467            float("Reason testing", "", RENDER_REASON_TESTING, expected(0.0..0.01)),
    468            float("Reason other", "", RENDER_REASON_OTHER, expected(0.0..0.01)),
    469            float("On vsync", "", RENDER_REASON_VSYNC, expected(0.0..0.01)),
    470 
    471            int("Textures created", "", TEXTURES_CREATED, expected(0..5)),
    472            int("Textures deleted", "", TEXTURES_DELETED, Expected::none()),
    473 
    474            int("Total slow frames CPU", "", SLOW_FRAME_CPU_COUNT, Expected::none()),
    475            int("Total slow frames GPU", "", SLOW_FRAME_GPU_COUNT, Expected::none()),
    476            int("Slow: frame build", "%", SLOW_FRAME_BUILD_COUNT, Expected::none()),
    477            int("Slow: upload", "%", SLOW_UPLOAD_COUNT, Expected::none()),
    478            int("Slow: render", "%", SLOW_RENDER_COUNT, Expected::none()),
    479            int("Slow: draw calls", "%", SLOW_DRAW_CALLS_COUNT, Expected::none()),
    480            int("Slow: targets", "%", SLOW_TARGETS_COUNT, Expected::none()),
    481            int("Slow: blobs", "%", SLOW_BLOB_COUNT, Expected::none()),
    482            int("Slow: after scene", "%", SLOW_SCROLL_AFTER_SCENE_COUNT, Expected::none()),
    483 
    484            float("GPU buffer mem", "MB", GPU_BUFFER_MEM, Expected::none()),
    485            float("GPU total mem", "MB", GPU_TOTAL_MEM, Expected::none()),
    486 
    487            float("Frame send", "ms", FRAME_SEND_TIME, Expected::none()),
    488            float("Update document", "ms", UPDATE_DOCUMENT_TIME, Expected::none()),
    489 
    490            int("Compositor surface underlays", "", COMPOSITOR_SURFACE_UNDERLAYS, Expected::none()),
    491            int("Compositor surface overlays", "", COMPOSITOR_SURFACE_OVERLAYS, Expected::none()),
    492            int("Compositor surface blits", "", COMPOSITOR_SURFACE_BLITS, Expected::none()),
    493        ];
    494 
    495        let mut counters = Vec::with_capacity(profile_counters.len());
    496 
    497        for (idx, descriptor) in profile_counters.iter().enumerate() {
    498            debug_assert_eq!(descriptor.index, idx);
    499            counters.push(Counter::new(descriptor));
    500        }
    501 
    502        Profiler {
    503            gpu_frames: ProfilerFrameCollection::new(),
    504            frame_stats: ProfilerFrameCollection::new(),
    505            slow_scroll_frames: ProfilerFrameCollection::new(),
    506 
    507            counters,
    508            start: zeitstempel::now(),
    509            avg_over_period: ONE_SECOND_NS / 2,
    510            slow_cpu_frame_threshold: 10.0,
    511 
    512            num_graph_samples: 500, // Would it be useful to control this via a pref?
    513            frame_timestamps_within_last_second: Vec::new(),
    514 
    515            slow_frame_cpu_count: 0,
    516            slow_frame_gpu_count: 0,
    517            slow_frame_build_count: 0,
    518            slow_render_count: 0,
    519            slow_upload_count: 0,
    520            slow_draw_calls_count: 0,
    521            slow_targets_count: 0,
    522            slow_blob_count: 0,
    523            slow_scroll_after_scene_count: 0,
    524 
    525            ui: Vec::new(),
    526        }
    527    }
    528 
    529    pub fn set_parameter(&mut self, param: &api::Parameter) {
    530        match param {
    531            api::Parameter::Float(api::FloatParameter::SlowCpuFrameThreshold, threshold) => {
    532                self.slow_cpu_frame_threshold = *threshold;
    533            }
    534            _ => {}
    535        }
    536    }
    537 
    538    /// Sum a few counters and if the total amount is larger than a threshold, update
    539    /// a specific counter.
    540    ///
    541    /// This is useful to monitor slow frame and slow transactions.
    542    fn update_slow_event(&mut self, dst_counter: usize, counters: &[usize], threshold: f64) -> bool {
    543        let mut total = 0.0;
    544        for &counter in counters {
    545            if self.counters[counter].value.is_finite() {
    546                total += self.counters[counter].value;
    547            }
    548        }
    549 
    550        if total > threshold {
    551            self.counters[dst_counter].set(total);
    552            return true;
    553        }
    554 
    555        false
    556    }
    557 
    558    fn classify_slow_cpu_frame(&mut self) {
    559        let is_apz = self.counters[RENDER_REASON_ANIMATED_PROPERTY].value > 0.5
    560            || self.counters[RENDER_REASON_APZ].value > 0.5;
    561        if !is_apz {
    562            // Only consider slow frames affecting scrolling for now.
    563            return;
    564        }
    565 
    566        let frame = CpuFrameTimings::new(&self.counters);
    567        self.slow_scroll_frames.push(frame.to_profiler_frame());
    568 
    569        if self.counters[RENDER_REASON_SCENE].value > 0.5 {
    570            self.slow_scroll_after_scene_count += 1;
    571        }
    572 
    573        let frame_build = self.counters[FRAME_BUILDING_TIME].value;
    574        let uploads = self.counters[TEXTURE_CACHE_UPDATE_TIME].value;
    575        let renderer = self.counters[RENDERER_TIME].value - uploads;
    576        let mut reasons = [
    577            (frame_build, &mut self.slow_frame_build_count, SLOW_FRAME_BUILD_COUNT,),
    578            (renderer, &mut self.slow_render_count, SLOW_RENDER_COUNT,),
    579            (uploads, &mut self.slow_upload_count, SLOW_UPLOAD_COUNT,),
    580        ];
    581 
    582        reasons.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
    583 
    584        *reasons[0].1 += 1;
    585        let reason = reasons[0].2;
    586        std::mem::drop(reasons);
    587 
    588        self.slow_frame_cpu_count += 1;
    589 
    590        if reason == SLOW_RENDER_COUNT {
    591            let draw_calls = self.counters[DRAW_CALLS].value;
    592            if draw_calls > 200.0 {
    593                self.slow_draw_calls_count += 1;
    594            }
    595 
    596            let render_passes = self.counters[COLOR_PASSES].value + self.counters[ALPHA_PASSES].value;
    597            if render_passes > 20.0 {
    598                self.slow_targets_count += 1;
    599            }
    600        }
    601 
    602        if reason == SLOW_UPLOAD_COUNT {
    603            let count = self.counters[TEXTURE_UPLOADS].value;
    604            let blob_tiles = self.counters[RASTERIZED_BLOB_TILES].value;
    605            // This is an approximation: we rasterize blobs for the whole displayport and
    606            // only upload blob tiles for the current viewport. That said, the presence of
    607            // a high number of blob tiles compared to the total number of uploads is still
    608            // a good indication that blob images are the likely cause of the slow upload
    609            // time, or at least contributing to it to a large extent.
    610            if blob_tiles > count * 0.5 {
    611                self.slow_blob_count += 1;
    612            }
    613        }
    614    }
    615 
    616    #[cfg(feature = "debugger")]
    617    pub fn collect_updates_for_debugger(&self) -> Vec<ProfileCounterUpdate> {
    618        let mut updates = Vec::new();
    619 
    620        for (i, counter) in self.counters.iter().enumerate() {
    621            if let Some(value) = counter.get() {
    622                updates.push(ProfileCounterUpdate {
    623                    id: ProfileCounterId(i),
    624                    value,
    625                });
    626            }
    627        }
    628 
    629        updates
    630    }
    631 
    632    // Call at the end of every frame, after setting the counter values and before drawing the counters.
    633    pub fn update(&mut self) {
    634        let now = zeitstempel::now();
    635        let update_avg = (now - self.start) > self.avg_over_period;
    636        if update_avg {
    637            self.start = now;
    638        }
    639        let one_second_ago = now - ONE_SECOND_NS;
    640        self.frame_timestamps_within_last_second.retain(|t| *t > one_second_ago);
    641        self.frame_timestamps_within_last_second.push(now);
    642 
    643        let slow_cpu = self.update_slow_event(
    644            SLOW_FRAME,
    645            &[TOTAL_FRAME_CPU_TIME],
    646            self.slow_cpu_frame_threshold as f64,
    647        );
    648        self.update_slow_event(
    649            SLOW_TXN,
    650            &[DISPLAY_LIST_BUILD_TIME, CONTENT_SEND_TIME, SCENE_BUILD_TIME],
    651            80.0
    652        );
    653 
    654        if slow_cpu {
    655            self.classify_slow_cpu_frame();
    656        }
    657 
    658        let div = 100.0 / self.slow_frame_cpu_count as f64;
    659        self.counters[SLOW_FRAME_CPU_COUNT].set(self.slow_frame_cpu_count as f64);
    660        self.counters[SLOW_FRAME_GPU_COUNT].set(self.slow_frame_gpu_count as f64);
    661        self.counters[SLOW_FRAME_BUILD_COUNT].set(self.slow_frame_build_count as f64 * div);
    662        self.counters[SLOW_RENDER_COUNT].set(self.slow_render_count as f64 * div);
    663        self.counters[SLOW_UPLOAD_COUNT].set(self.slow_upload_count as f64 * div);
    664        self.counters[SLOW_DRAW_CALLS_COUNT].set(self.slow_draw_calls_count as f64 * div);
    665        self.counters[SLOW_TARGETS_COUNT].set(self.slow_targets_count as f64 * div);
    666        self.counters[SLOW_BLOB_COUNT].set(self.slow_blob_count as f64 * div);
    667        self.counters[SLOW_SCROLL_AFTER_SCENE_COUNT].set(self.slow_scroll_after_scene_count as f64 * div);
    668 
    669        self.update_total_gpu_mem();
    670 
    671        for counter in &mut self.counters {
    672            counter.update(update_avg);
    673        }
    674    }
    675 
    676    pub fn update_frame_stats(&mut self, stats: FullFrameStats) {
    677        if stats.gecko_display_list_time != 0.0 {
    678          self.frame_stats.push(stats.into());
    679        }
    680    }
    681 
    682    pub fn update_total_gpu_mem(&mut self) {
    683        let mut total = 0.0;
    684        for counter in [
    685            EXTERNAL_IMAGE_BYTES,
    686            ATLAS_TEXTURES_MEM,
    687            STANDALONE_TEXTURES_MEM,
    688            PICTURE_TILES_MEM,
    689            RENDER_TARGET_MEM,
    690            DEPTH_TARGETS_MEM,
    691            ATLAS_ITEMS_MEM,
    692            GPU_BUFFER_MEM,
    693        ] {
    694            if let Some(val) = self.counters[counter].get() {
    695                total += val;
    696            }
    697        }
    698        self.counters[GPU_TOTAL_MEM].set(total);
    699    }
    700 
    701    pub fn set_gpu_time_queries(&mut self, gpu_queries: Vec<GpuTimer>) {
    702        let mut gpu_time_ns = 0;
    703        for sample in &gpu_queries {
    704            gpu_time_ns += sample.time_ns;
    705        }
    706 
    707        self.gpu_frames.push(ProfilerFrame {
    708          total_time: gpu_time_ns,
    709          samples: gpu_queries
    710        });
    711 
    712        let gpu_time = ns_to_ms(gpu_time_ns);
    713        self.counters[GPU_TIME].set_f64(gpu_time);
    714        if gpu_time > 12.0 {
    715            self.slow_frame_gpu_count += 1;
    716        }
    717    }
    718 
    719    // Find the index of a counter by its name.
    720    pub fn index_of(&self, name: &str) -> Option<usize> {
    721        self.counters.iter().position(|counter| counter.name == name)
    722    }
    723 
    724    // Define the profiler UI, see comment about the syntax at the top of this file.
    725    pub fn set_ui(&mut self, names: &str) {
    726        let mut selection = Vec::new();
    727 
    728        self.append_to_ui(&mut selection, names);
    729 
    730        if selection == self.ui {
    731            return;
    732        }
    733 
    734        for counter in &mut self.counters {
    735            counter.disable_graph();
    736        }
    737 
    738        for item in &selection {
    739            if let Item::Graph(idx) = item {
    740                self.counters[*idx].enable_graph(self.num_graph_samples);
    741            }
    742        }
    743 
    744        self.ui = selection;
    745    }
    746 
    747    fn append_to_ui(&mut self, selection: &mut Vec<Item>, names: &str) {
    748        // Group successive counters together.
    749        fn flush_counters(counters: &mut Vec<usize>, selection: &mut Vec<Item>) {
    750            if !counters.is_empty() {
    751                selection.push(Item::Counters(std::mem::take(counters)))
    752            }
    753        }
    754 
    755        let mut counters = Vec::new();
    756 
    757        for name in names.split(",") {
    758            let name = name.trim();
    759            let is_graph = name.starts_with("#");
    760            let is_indicator = name.starts_with("*");
    761            let is_string = name.starts_with("$");
    762            let name = if is_graph || is_indicator {
    763                &name[1..]
    764            } else {
    765                name
    766            };
    767            // See comment about the ui string syntax at the top of this file.
    768            match name {
    769                "" => {
    770                    flush_counters(&mut counters, selection);
    771                    selection.push(Item::Space);
    772                }
    773                "|" => {
    774                    flush_counters(&mut counters, selection);
    775                    selection.push(Item::Column);
    776                }
    777                "_" => {
    778                    flush_counters(&mut counters, selection);
    779                    selection.push(Item::Row);
    780                }
    781                "FPS" => {
    782                    flush_counters(&mut counters, selection);
    783                    selection.push(Item::Fps);
    784                }
    785                "GPU time queries" => {
    786                    flush_counters(&mut counters, selection);
    787                    selection.push(Item::GpuTimeQueries);
    788                }
    789                "Paint phase graph" => {
    790                    flush_counters(&mut counters, selection);
    791                    selection.push(Item::PaintPhaseGraph);
    792                }
    793                "Slow scroll frames" => {
    794                    flush_counters(&mut counters, selection);
    795                    selection.push(Item::SlowScrollFrames);
    796                }
    797                _ => {
    798                    if is_string {
    799                        selection.push(Item::Text(name[1..].into()));
    800                    } else if let Some(idx) = self.index_of(name) {
    801                        if is_graph {
    802                            flush_counters(&mut counters, selection);
    803                            selection.push(Item::Graph(idx));
    804                        } else if is_indicator {
    805                            flush_counters(&mut counters, selection);
    806                            selection.push(Item::ChangeIndicator(idx));
    807                        } else {
    808                            counters.push(idx);
    809                        }
    810                    } else if let Some(preset_str) = find_preset(name) {
    811                        flush_counters(&mut counters, selection);
    812                        self.append_to_ui(selection, preset_str);
    813                    } else {
    814                        selection.push(Item::Text(format!("Unknown counter: {}", name)));
    815                    }
    816                }
    817            }
    818        }
    819 
    820        flush_counters(&mut counters, selection);
    821    }
    822 
    823    pub fn set_counters(&mut self, counters: &mut TransactionProfile) {
    824        for (id, evt) in counters.events.iter_mut().enumerate() {
    825            if let Event::Value(val) = *evt {
    826                self.counters[id].set(val);
    827            }
    828            *evt = Event::None;
    829        }
    830    }
    831 
    832    #[cfg(feature = "debugger")]
    833    pub fn counters(&self) -> &[Counter] {
    834        &self.counters
    835    }
    836 
    837    fn draw_counters(
    838        counters: &[Counter],
    839        selected: &[usize],
    840        mut x: f32, mut y: f32,
    841        text_buffer: &mut String,
    842        debug_renderer: &mut DebugRenderer,
    843    ) -> default::Rect<f32> {
    844        let line_height = debug_renderer.line_height();
    845 
    846        x += PROFILE_PADDING;
    847        y += PROFILE_PADDING;
    848        let origin = default::Point2D::new(x, y);
    849        y += line_height * 0.5;
    850 
    851        let mut total_rect = Rect::zero();
    852 
    853        let mut color_index = 0;
    854        let colors = [
    855            // Regular values,
    856            ColorU::new(255, 255, 255, 255),
    857            ColorU::new(255, 255, 0, 255),
    858            // Unexpected values,
    859            ColorU::new(255, 80, 0, 255),
    860            ColorU::new(255, 0, 0, 255),
    861        ];
    862 
    863        for idx in selected {
    864            // If The index is invalid, add some vertical space.
    865            let counter = &counters[*idx];
    866 
    867            let rect = debug_renderer.add_text(
    868                x, y,
    869                counter.name,
    870                colors[color_index],
    871                None,
    872            );
    873            color_index = (color_index + 1) % 2;
    874 
    875            total_rect = total_rect.union(&rect);
    876            y += line_height;
    877        }
    878 
    879        color_index = 0;
    880        x = total_rect.max_x() + 60.0;
    881        y = origin.y + line_height * 0.5;
    882 
    883        for idx in selected {
    884            let counter = &counters[*idx];
    885            let expected_offset = if counter.has_unexpected_avg_max() { 2 } else { 0 };
    886 
    887            counter.write_value(text_buffer);
    888 
    889            let rect = debug_renderer.add_text(
    890                x,
    891                y,
    892                &text_buffer,
    893                colors[color_index + expected_offset],
    894                None,
    895            );
    896            color_index = (color_index + 1) % 2;
    897 
    898            total_rect = total_rect.union(&rect);
    899            y += line_height;
    900        }
    901 
    902        total_rect = total_rect
    903            .union(&Rect { origin, size: Size2D::new(1.0, 1.0) })
    904            .inflate(PROFILE_PADDING, PROFILE_PADDING);
    905 
    906        debug_renderer.add_quad(
    907            total_rect.min_x(),
    908            total_rect.min_y(),
    909            total_rect.max_x(),
    910            total_rect.max_y(),
    911            BACKGROUND_COLOR,
    912            BACKGROUND_COLOR,
    913        );
    914 
    915        total_rect
    916    }
    917 
    918    fn draw_graph(
    919        counter: &Counter,
    920        x: f32,
    921        y: f32,
    922        text_buffer: &mut String,
    923        debug_renderer: &mut DebugRenderer,
    924    ) -> default::Rect<f32> {
    925        let graph = counter.graph.as_ref().unwrap();
    926 
    927        let max_samples = graph.values.capacity() as f32;
    928 
    929        let size = Size2D::new(max_samples, 100.0);
    930        let line_height = debug_renderer.line_height();
    931        let graph_rect = Rect::new(Point2D::new(x + PROFILE_PADDING, y + PROFILE_PADDING), size);
    932        let mut rect = graph_rect.inflate(PROFILE_PADDING, PROFILE_PADDING);
    933 
    934        let stats = graph.stats();
    935 
    936        let text_color = ColorU::new(255, 255, 0, 255);
    937        let text_origin = rect.origin + vec2(rect.size.width, 25.0);
    938        set_text!(text_buffer, "{} ({})", counter.name, counter.unit);
    939        debug_renderer.add_text(
    940            text_origin.x,
    941            text_origin.y,
    942            if counter.unit == "" { counter.name } else { text_buffer },
    943            ColorU::new(0, 255, 0, 255),
    944            None,
    945        );
    946 
    947        set_text!(text_buffer, "Samples: {}", stats.samples);
    948 
    949        debug_renderer.add_text(
    950            text_origin.x,
    951            text_origin.y + line_height,
    952            text_buffer,
    953            text_color,
    954            None,
    955        );
    956 
    957        if stats.samples > 0 {
    958            set_text!(text_buffer, "Min: {:.2} {}", stats.min, counter.unit);
    959            debug_renderer.add_text(
    960                text_origin.x,
    961                text_origin.y + line_height * 2.0,
    962                text_buffer,
    963                text_color,
    964                None,
    965            );
    966 
    967            set_text!(text_buffer, "Avg: {:.2} {}", stats.avg, counter.unit);
    968            debug_renderer.add_text(
    969                text_origin.x,
    970                text_origin.y + line_height * 3.0,
    971                text_buffer,
    972                text_color,
    973                None,
    974            );
    975 
    976            set_text!(text_buffer, "Max: {:.2} {}", stats.max, counter.unit);
    977            debug_renderer.add_text(
    978                text_origin.x,
    979                text_origin.y + line_height * 4.0,
    980                text_buffer,
    981                text_color,
    982                None,
    983            );
    984        }
    985 
    986        rect.size.width += 220.0;
    987        debug_renderer.add_quad(
    988            rect.min_x(),
    989            rect.min_y(),
    990            rect.max_x(),
    991            rect.max_y(),
    992            BACKGROUND_COLOR,
    993            BACKGROUND_COLOR,
    994        );
    995 
    996        let bx1 = graph_rect.max_x();
    997        let by1 = graph_rect.max_y();
    998 
    999        let w = graph_rect.size.width / max_samples;
   1000        let h = graph_rect.size.height;
   1001 
   1002        let color_t0 = ColorU::new(0, 255, 0, 255);
   1003        let color_b0 = ColorU::new(0, 180, 0, 255);
   1004 
   1005        let color_t2 = ColorU::new(255, 0, 0, 255);
   1006        let color_b2 = ColorU::new(180, 0, 0, 255);
   1007 
   1008        if stats.max > 0.0 {
   1009            for (index, sample) in graph.values.iter().enumerate() {
   1010                if !sample.is_finite() {
   1011                    // NAN means no sample this frame.
   1012                    continue;
   1013                }
   1014                let sample = *sample as f32;
   1015                let x1 = bx1 - index as f32 * w;
   1016                let x0 = x1 - w;
   1017 
   1018                let y0 = by1 - (sample / stats.max as f32) as f32 * h;
   1019                let y1 = by1;
   1020 
   1021                let (color_top, color_bottom) = if counter.is_unexpected_value(sample as f64) {
   1022                    (color_t2, color_b2)
   1023                } else {
   1024                    (color_t0, color_b0)
   1025                };
   1026 
   1027                debug_renderer.add_quad(x0, y0, x1, y1, color_top, color_bottom);
   1028            }
   1029        }
   1030 
   1031        rect
   1032    }
   1033 
   1034 
   1035    fn draw_change_indicator(
   1036        counter: &Counter,
   1037        x: f32, y: f32,
   1038        debug_renderer: &mut DebugRenderer
   1039    ) -> default::Rect<f32> {
   1040        let height = 10.0;
   1041        let width = 20.0;
   1042 
   1043        // Draw the indicator red instead of blue if is is not within expected ranges.
   1044        let color = if counter.has_unexpected_value() || counter.has_unexpected_avg_max() {
   1045            ColorU::new(255, 20, 20, 255)
   1046        } else {
   1047            ColorU::new(0, 100, 250, 255)
   1048        };
   1049 
   1050        let tx = counter.change_indicator as f32 * width;
   1051        debug_renderer.add_quad(
   1052            x,
   1053            y,
   1054            x + 15.0 * width,
   1055            y + height,
   1056            ColorU::new(0, 0, 0, 150),
   1057            ColorU::new(0, 0, 0, 150),
   1058        );
   1059 
   1060        debug_renderer.add_quad(
   1061            x + tx,
   1062            y,
   1063            x + tx + width,
   1064            y + height,
   1065            color,
   1066            ColorU::new(25, 25, 25, 255),
   1067        );
   1068 
   1069        Rect {
   1070            origin: Point2D::new(x, y),
   1071            size: Size2D::new(15.0 * width + 20.0, height),
   1072        }
   1073    }
   1074 
   1075    // Draws a frame graph for a given frame collection.
   1076    fn draw_frame_graph(
   1077        frame_collection: &ProfilerFrameCollection,
   1078        x: f32, y: f32,
   1079        debug_renderer: &mut DebugRenderer,
   1080    ) -> default::Rect<f32> {
   1081        let mut has_data = false;
   1082        for frame in &frame_collection.frames {
   1083            if !frame.samples.is_empty() {
   1084                has_data = true;
   1085                break;
   1086            }
   1087        }
   1088 
   1089        if !has_data {
   1090            return Rect::zero();
   1091        }
   1092 
   1093        let graph_rect = Rect::new(
   1094            Point2D::new(x + GRAPH_PADDING, y + GRAPH_PADDING),
   1095            Size2D::new(GRAPH_WIDTH, GRAPH_HEIGHT),
   1096        );
   1097        let bounding_rect = graph_rect.inflate(GRAPH_PADDING, GRAPH_PADDING);
   1098 
   1099        debug_renderer.add_quad(
   1100            bounding_rect.origin.x,
   1101            bounding_rect.origin.y,
   1102            bounding_rect.origin.x + bounding_rect.size.width,
   1103            bounding_rect.origin.y + bounding_rect.size.height,
   1104            BACKGROUND_COLOR,
   1105            BACKGROUND_COLOR,
   1106        );
   1107 
   1108        let w = graph_rect.size.width;
   1109        let mut y0 = graph_rect.origin.y;
   1110 
   1111        let mut max_time = frame_collection.frames
   1112            .iter()
   1113            .max_by_key(|f| f.total_time)
   1114            .unwrap()
   1115            .total_time as f32;
   1116 
   1117        // If the max time is lower than 16ms, fix the scale
   1118        // at 16ms so that the graph is easier to interpret.
   1119        let baseline_ns = 16_000_000.0; // 16ms
   1120        max_time = max_time.max(baseline_ns);
   1121 
   1122        let mut tags_present = FastHashMap::default();
   1123 
   1124        for frame in &frame_collection.frames {
   1125            let y1 = y0 + GRAPH_FRAME_HEIGHT;
   1126 
   1127            let mut current_ns = 0;
   1128            for sample in &frame.samples {
   1129                let x0 = graph_rect.origin.x + w * current_ns as f32 / max_time;
   1130                current_ns += sample.time_ns;
   1131                let x1 = graph_rect.origin.x + w * current_ns as f32 / max_time;
   1132                let mut bottom_color = sample.tag.color;
   1133                bottom_color.a *= 0.5;
   1134 
   1135                debug_renderer.add_quad(
   1136                    x0,
   1137                    y0,
   1138                    x1,
   1139                    y1,
   1140                    sample.tag.color.into(),
   1141                    bottom_color.into(),
   1142                );
   1143 
   1144                tags_present.insert(sample.tag.label, sample.tag.color);
   1145            }
   1146 
   1147            y0 = y1;
   1148        }
   1149 
   1150        let mut tags_present: Vec<_> = tags_present.iter().collect();
   1151        tags_present.sort_by_key(|item| item.0);
   1152 
   1153        // If the max time is higher than 16ms, show a vertical line at the
   1154        // 16ms mark.
   1155        if max_time > baseline_ns {
   1156            let x = graph_rect.origin.x + w * baseline_ns as f32 / max_time;
   1157            let height = frame_collection.frames.len() as f32 * GRAPH_FRAME_HEIGHT;
   1158 
   1159            debug_renderer.add_quad(
   1160                x,
   1161                graph_rect.origin.y,
   1162                x + 4.0,
   1163                graph_rect.origin.y + height,
   1164                ColorU::new(120, 00, 00, 150),
   1165                ColorU::new(120, 00, 00, 100),
   1166            );
   1167        }
   1168 
   1169 
   1170        // Add a legend to see which color correspond to what primitive.
   1171        const LEGEND_SIZE: f32 = 20.0;
   1172        const PADDED_LEGEND_SIZE: f32 = 25.0;
   1173        if !tags_present.is_empty() {
   1174            debug_renderer.add_quad(
   1175                bounding_rect.max_x() + GRAPH_PADDING,
   1176                bounding_rect.origin.y,
   1177                bounding_rect.max_x() + GRAPH_PADDING + 200.0,
   1178                bounding_rect.origin.y + tags_present.len() as f32 * PADDED_LEGEND_SIZE + GRAPH_PADDING,
   1179                BACKGROUND_COLOR,
   1180                BACKGROUND_COLOR,
   1181            );
   1182        }
   1183 
   1184        for (i, (label, &color)) in tags_present.iter().enumerate() {
   1185            let x0 = bounding_rect.origin.x + bounding_rect.size.width + GRAPH_PADDING * 2.0;
   1186            let y0 = bounding_rect.origin.y + GRAPH_PADDING + i as f32 * PADDED_LEGEND_SIZE;
   1187 
   1188            debug_renderer.add_quad(
   1189                x0, y0, x0 + LEGEND_SIZE, y0 + LEGEND_SIZE,
   1190                color.into(),
   1191                color.into(),
   1192            );
   1193 
   1194            debug_renderer.add_text(
   1195                x0 + PADDED_LEGEND_SIZE,
   1196                y0 + LEGEND_SIZE * 0.75,
   1197                label,
   1198                ColorU::new(255, 255, 0, 255),
   1199                None,
   1200            );
   1201        }
   1202 
   1203        bounding_rect
   1204    }
   1205 
   1206    pub fn draw_profile(
   1207        &mut self,
   1208        _frame_index: u64,
   1209        debug_renderer: &mut DebugRenderer,
   1210        device_size: DeviceIntSize,
   1211    ) {
   1212        let x_start = 20.0;
   1213        let mut y_start = 150.0;
   1214        let default_column_width = 400.0;
   1215 
   1216        // set_text!(..) into this string instead of using format!(..) to avoid
   1217        // unnecessary allocations.
   1218        let mut text_buffer = String::with_capacity(32);
   1219 
   1220        let mut column_width = default_column_width;
   1221        let mut max_y = y_start;
   1222 
   1223        let mut x = x_start;
   1224        let mut y = y_start;
   1225 
   1226        for elt in &self.ui {
   1227            let rect = match elt {
   1228                Item::Counters(indices) => {
   1229                    Profiler::draw_counters(&self.counters, &indices, x, y, &mut text_buffer, debug_renderer)
   1230                }
   1231                Item::Graph(idx) => {
   1232                    Profiler::draw_graph(&self.counters[*idx], x, y, &mut text_buffer, debug_renderer)
   1233                }
   1234                Item::ChangeIndicator(idx) => {
   1235                    Profiler::draw_change_indicator(&self.counters[*idx], x, y, debug_renderer)
   1236                }
   1237                Item::GpuTimeQueries => {
   1238                    Profiler::draw_frame_graph(&self.gpu_frames, x, y, debug_renderer)
   1239                }
   1240                Item::PaintPhaseGraph => {
   1241                    Profiler::draw_frame_graph(&self.frame_stats, x, y, debug_renderer)
   1242                }
   1243                Item::SlowScrollFrames => {
   1244                    Profiler::draw_frame_graph(&self.slow_scroll_frames, x, y, debug_renderer)
   1245                }
   1246                Item::Text(text) => {
   1247                    let p = 10.0;
   1248                    let mut rect = debug_renderer.add_text(
   1249                        x + p,
   1250                        y + p,
   1251                        &text,
   1252                        ColorU::new(255, 255, 255, 255),
   1253                        None,
   1254                    );
   1255                    rect = rect.inflate(p, p);
   1256 
   1257                    debug_renderer.add_quad(
   1258                        rect.origin.x,
   1259                        rect.origin.y,
   1260                        rect.max_x(),
   1261                        rect.max_y(),
   1262                        BACKGROUND_COLOR,
   1263                        BACKGROUND_COLOR,
   1264                    );
   1265 
   1266                    rect
   1267                }
   1268                Item::Fps => {
   1269                    let fps = self.frame_timestamps_within_last_second.len();
   1270                    set_text!(&mut text_buffer, "{} fps", fps);
   1271                    let mut rect = debug_renderer.add_text(
   1272                        x + PROFILE_PADDING,
   1273                        y + PROFILE_PADDING + 5.0,
   1274                        &text_buffer,
   1275                        ColorU::new(255, 255, 255, 255),
   1276                        None,
   1277                    );
   1278                    rect = rect.inflate(PROFILE_PADDING, PROFILE_PADDING);
   1279 
   1280                    debug_renderer.add_quad(
   1281                        rect.min_x(),
   1282                        rect.min_y(),
   1283                        rect.max_x(),
   1284                        rect.max_y(),
   1285                        BACKGROUND_COLOR,
   1286                        BACKGROUND_COLOR,
   1287                    );
   1288 
   1289                    rect
   1290                }
   1291                Item::Space => {
   1292                    Rect { origin: Point2D::new(x, y), size: Size2D::new(0.0, PROFILE_SPACING) }
   1293                }
   1294                Item::Column => {
   1295                    max_y = max_y.max(y);
   1296                    x += column_width + PROFILE_SPACING;
   1297                    y = y_start;
   1298                    column_width = default_column_width;
   1299 
   1300                    continue;
   1301                }
   1302                Item::Row => {
   1303                    max_y = max_y.max(y);
   1304                    y_start = max_y + PROFILE_SPACING;
   1305                    y = y_start;
   1306                    x = x_start;
   1307                    column_width = default_column_width;
   1308 
   1309                    continue;
   1310                }
   1311            };
   1312 
   1313            column_width = column_width.max(rect.size.width);
   1314            y = rect.max_y();
   1315 
   1316            if y > device_size.height as f32 - 100.0 {
   1317                max_y = max_y.max(y);
   1318                x += column_width + PROFILE_SPACING;
   1319                y = y_start;
   1320                column_width = default_column_width;
   1321            }
   1322        }
   1323    }
   1324 
   1325    #[cfg(feature = "capture")]
   1326    pub fn dump_stats(&self, sink: &mut dyn std::io::Write) -> std::io::Result<()> {
   1327        for counter in &self.counters {
   1328            if counter.value.is_finite() {
   1329                writeln!(sink, "{} {:?}{}", counter.name, counter.value, counter.unit)?;
   1330            }
   1331        }
   1332 
   1333        Ok(())
   1334    }
   1335 }
   1336 
   1337 /// Defines the interface for hooking up an external profiler to WR.
   1338 pub trait ProfilerHooks : Send + Sync {
   1339    /// Register a thread with the profiler.
   1340    fn register_thread(&self, thread_name: &str);
   1341 
   1342    /// Unregister a thread with the profiler.
   1343    fn unregister_thread(&self);
   1344 
   1345    /// Called at the beginning of a profile scope.
   1346    fn begin_marker(&self, label: &str);
   1347 
   1348    /// Called at the end of a profile scope.
   1349    fn end_marker(&self, label: &str);
   1350 
   1351    /// Called to mark an event happening.
   1352    fn event_marker(&self, label: &str);
   1353 
   1354    /// Called with a duration to indicate a text marker that just ended. Text
   1355    /// markers allow different types of entries to be recorded on the same row
   1356    /// in the timeline, by adding labels to the entry.
   1357    ///
   1358    /// This variant is also useful when the caller only wants to record events
   1359    /// longer than a certain threshold, and thus they don't know in advance
   1360    /// whether the event will qualify.
   1361    fn add_text_marker(&self, label: &str, text: &str, duration: Duration);
   1362 
   1363    /// Returns true if the current thread is being profiled.
   1364    fn thread_is_being_profiled(&self) -> bool;
   1365 }
   1366 
   1367 /// The current global profiler callbacks, if set by embedder.
   1368 pub static mut PROFILER_HOOKS: Option<&'static dyn ProfilerHooks> = None;
   1369 
   1370 /// Set the profiler callbacks, or None to disable the profiler.
   1371 /// This function must only ever be called before any WR instances
   1372 /// have been created, or the hooks will not be set.
   1373 pub fn set_profiler_hooks(hooks: Option<&'static dyn ProfilerHooks>) {
   1374    if !wr_has_been_initialized() {
   1375        unsafe {
   1376            PROFILER_HOOKS = hooks;
   1377        }
   1378    }
   1379 }
   1380 
   1381 /// A simple RAII style struct to manage a profile scope.
   1382 pub struct ProfileScope {
   1383    name: &'static str,
   1384 }
   1385 
   1386 
   1387 /// Register a thread with the Gecko Profiler.
   1388 pub fn register_thread(thread_name: &str) {
   1389    unsafe {
   1390        if let Some(ref hooks) = PROFILER_HOOKS {
   1391            hooks.register_thread(thread_name);
   1392        }
   1393    }
   1394 }
   1395 
   1396 
   1397 /// Unregister a thread with the Gecko Profiler.
   1398 pub fn unregister_thread() {
   1399    unsafe {
   1400        if let Some(ref hooks) = PROFILER_HOOKS {
   1401            hooks.unregister_thread();
   1402        }
   1403    }
   1404 }
   1405 
   1406 /// Records a marker of the given duration that just ended.
   1407 pub fn add_text_marker(label: &str, text: &str, duration: Duration) {
   1408    unsafe {
   1409        if let Some(ref hooks) = PROFILER_HOOKS {
   1410            hooks.add_text_marker(label, text, duration);
   1411        }
   1412    }
   1413 }
   1414 
   1415 /// Records a marker of the given duration that just ended.
   1416 pub fn add_event_marker(label: &str) {
   1417    unsafe {
   1418        if let Some(ref hooks) = PROFILER_HOOKS {
   1419            hooks.event_marker(label);
   1420        }
   1421    }
   1422 }
   1423 
   1424 /// Returns true if the current thread is being profiled.
   1425 pub fn thread_is_being_profiled() -> bool {
   1426    unsafe {
   1427        PROFILER_HOOKS.map_or(false, |h| h.thread_is_being_profiled())
   1428    }
   1429 }
   1430 
   1431 impl ProfileScope {
   1432    /// Begin a new profile scope
   1433    pub fn new(name: &'static str) -> Self {
   1434        unsafe {
   1435            if let Some(ref hooks) = PROFILER_HOOKS {
   1436                hooks.begin_marker(name);
   1437            }
   1438        }
   1439 
   1440        ProfileScope {
   1441            name,
   1442        }
   1443    }
   1444 }
   1445 
   1446 impl Drop for ProfileScope {
   1447    fn drop(&mut self) {
   1448        unsafe {
   1449            if let Some(ref hooks) = PROFILER_HOOKS {
   1450                hooks.end_marker(self.name);
   1451            }
   1452        }
   1453    }
   1454 }
   1455 
   1456 /// A helper macro to define profile scopes.
   1457 macro_rules! profile_marker {
   1458    ($string:expr) => {
   1459        let _scope = $crate::profiler::ProfileScope::new($string);
   1460    };
   1461 }
   1462 
   1463 #[derive(Debug, Clone)]
   1464 pub struct GpuProfileTag {
   1465    pub label: &'static str,
   1466    pub color: ColorF,
   1467 }
   1468 
   1469 /// Ranges of expected value for a profile counter.
   1470 #[derive(Clone, Debug)]
   1471 pub struct Expected<T> {
   1472    pub range: Option<Range<T>>,
   1473    pub avg: Option<Range<T>>,
   1474 }
   1475 
   1476 impl<T> Expected<T> {
   1477     const fn none() -> Self {
   1478        Expected {
   1479            range: None,
   1480            avg: None,
   1481        }
   1482    }
   1483 }
   1484 
   1485 const fn expected<T>(range: Range<T>) -> Expected<T> {
   1486    Expected {
   1487        range: Some(range),
   1488        avg: None,
   1489    }
   1490 }
   1491 
   1492 impl Expected<f64> {
   1493    const fn avg(mut self, avg: Range<f64>) -> Self {
   1494        self.avg = Some(avg);
   1495        self
   1496    }
   1497 }
   1498 
   1499 impl Expected<i64> {
   1500    const fn avg(mut self, avg: Range<i64>) -> Self {
   1501        self.avg = Some(avg);
   1502        self
   1503    }
   1504 
   1505    fn into_float(self) -> Expected<f64> {
   1506        Expected {
   1507            range: match self.range {
   1508                Some(r) => Some(r.start as f64 .. r.end as f64),
   1509                None => None,
   1510            },
   1511            avg: match self.avg {
   1512                Some(r) => Some(r.start as f64 .. r.end as f64),
   1513                None => None,
   1514            },
   1515        }
   1516    }
   1517 }
   1518 
   1519 pub struct CounterDescriptor {
   1520    pub name: &'static str,
   1521    pub unit: &'static str,
   1522    pub index: usize,
   1523    pub show_as: ShowAs,
   1524    pub expected: Expected<f64>,
   1525 }
   1526 
   1527 #[derive(Debug)]
   1528 pub struct Counter {
   1529    pub name: &'static str,
   1530    pub unit: &'static str,
   1531    pub show_as: ShowAs,
   1532    pub expected: Expected<f64>,
   1533 
   1534    ///
   1535    value: f64,
   1536    /// Number of samples in the current time slice.
   1537    num_samples: u64,
   1538    /// Sum of the values recorded during the current time slice.
   1539    sum: f64,
   1540    /// The max value in in-progress time slice.
   1541    next_max: f64,
   1542    /// The max value of the previous time slice (displayed).
   1543    max: f64,
   1544    /// The average value of the previous time slice (displayed).
   1545    avg: f64,
   1546    /// Incremented when the counter changes.
   1547    change_indicator: u8,
   1548 
   1549    graph: Option<Graph>,
   1550 }
   1551 
   1552 impl Counter {
   1553    pub fn new(descriptor: &CounterDescriptor) -> Self {
   1554        Counter {
   1555            name: descriptor.name,
   1556            unit: descriptor.unit,
   1557            show_as: descriptor.show_as,
   1558            expected: descriptor.expected.clone(),
   1559            value: std::f64::NAN,
   1560            num_samples: 0,
   1561            sum: 0.0,
   1562            next_max: 0.0,
   1563            max: 0.0,
   1564            avg: 0.0,
   1565            change_indicator: 0,
   1566            graph: None,
   1567        }
   1568    }
   1569    pub fn set_f64(&mut self, val: f64) {
   1570        self.value = val;
   1571    }
   1572 
   1573    pub fn set<T>(&mut self, val: T) where T: Into<f64> {
   1574        self.set_f64(val.into());
   1575    }
   1576 
   1577    pub fn get(&self) -> Option<f64> {
   1578        if self.value.is_finite() {
   1579            Some(self.value)
   1580        } else {
   1581            None
   1582        }
   1583    }
   1584 
   1585    pub fn write_value(&self, output: &mut String) {
   1586        match self.show_as {
   1587            ShowAs::Float => {
   1588                set_text!(output, "{:.2} {} (max: {:.2})", self.avg, self.unit, self.max);
   1589            }
   1590            ShowAs::Int => {
   1591                set_text!(output, "{:.0} {} (max: {:.0})", self.avg.round(), self.unit, self.max.round());
   1592            }
   1593        }
   1594    }
   1595 
   1596    pub fn enable_graph(&mut self, max_samples: usize) {
   1597        if self.graph.is_some() {
   1598            return;
   1599        }
   1600 
   1601        self.graph = Some(Graph::new(max_samples));
   1602    }
   1603 
   1604    pub fn disable_graph(&mut self) {
   1605        self.graph = None;
   1606    }
   1607 
   1608    pub fn is_unexpected_value(&self, value: f64) -> bool {
   1609        if let Some(range) = &self.expected.range {
   1610            return value.is_finite() && value >= range.end;
   1611        }
   1612 
   1613        false
   1614    }
   1615 
   1616    pub fn has_unexpected_value(&self) -> bool {
   1617        self.is_unexpected_value(self.value)
   1618    }
   1619 
   1620    pub fn has_unexpected_avg_max(&self) -> bool {
   1621        if let Some(range) = &self.expected.range {
   1622            if self.max.is_finite() && self.max >= range.end {
   1623                return true;
   1624            }
   1625        }
   1626 
   1627        if let Some(range) = &self.expected.avg {
   1628            if self.avg < range.start || self.avg >= range.end {
   1629                return true;
   1630            }
   1631        }
   1632 
   1633        false
   1634    }
   1635 
   1636    fn update(&mut self, update_avg: bool) {
   1637        let updated = self.value.is_finite();
   1638        if updated {
   1639            self.next_max = self.next_max.max(self.value);
   1640            self.sum += self.value;
   1641            self.num_samples += 1;
   1642            self.change_indicator = (self.change_indicator + 1) % 15;
   1643        }
   1644 
   1645        if let Some(graph) = &mut self.graph {
   1646            graph.set(self.value);
   1647        }
   1648 
   1649        self.value = std::f64::NAN;
   1650 
   1651        if update_avg {
   1652            if self.num_samples > 0 {
   1653                self.avg = self.sum / self.num_samples as f64;
   1654                self.max = self.next_max;
   1655            } else {
   1656                // There has been no sample in the averaging window, just show zero.
   1657                self.avg = 0.0;
   1658                self.max = 0.0;
   1659            }
   1660            self.sum = 0.0;
   1661            self.num_samples = 0;
   1662            self.next_max = std::f64::MIN;
   1663        }
   1664    }
   1665 }
   1666 
   1667 #[derive(Copy, Clone, Debug)]
   1668 pub enum Event {
   1669    Start(u64),
   1670    Value(f64),
   1671    None,
   1672 }
   1673 
   1674 // std::convert::From/TryFrom can't deal with integer to f64 so we roll our own...
   1675 pub trait EventValue {
   1676    fn into_f64(self) -> f64;
   1677 }
   1678 
   1679 impl EventValue for f64 { fn into_f64(self) -> f64 { self } }
   1680 impl EventValue for f32 { fn into_f64(self) -> f64 { self as f64 } }
   1681 impl EventValue for u32 { fn into_f64(self) -> f64 { self as f64 } }
   1682 impl EventValue for i32 { fn into_f64(self) -> f64 { self as f64 } }
   1683 impl EventValue for u64 { fn into_f64(self) -> f64 { self as f64 } }
   1684 impl EventValue for usize { fn into_f64(self) -> f64 { self as f64 } }
   1685 
   1686 /// A container for profiling information that moves along the rendering pipeline
   1687 /// and is handed off to the profiler at the end.
   1688 pub struct TransactionProfile {
   1689    pub events: Vec<Event>,
   1690 }
   1691 
   1692 impl TransactionProfile {
   1693    pub fn new() -> Self {
   1694        TransactionProfile {
   1695            events: vec![Event::None; NUM_PROFILER_EVENTS],
   1696        }
   1697    }
   1698 
   1699    pub fn start_time(&mut self, id: usize) {
   1700        let ns = zeitstempel::now();
   1701        self.events[id] = Event::Start(ns);
   1702    }
   1703 
   1704    pub fn end_time(&mut self, id: usize) -> f64 {
   1705        self.end_time_if_started(id).unwrap()
   1706    }
   1707 
   1708    /// Similar to end_time, but doesn't panic if not matched with start_time.
   1709    pub fn end_time_if_started(&mut self, id: usize) -> Option<f64> {
   1710        if let Event::Start(start) = self.events[id] {
   1711            let now = zeitstempel::now();
   1712            let time_ns = now - start;
   1713 
   1714            let time_ms = ns_to_ms(time_ns);
   1715            self.events[id] = Event::Value(time_ms);
   1716 
   1717            Some(time_ms)
   1718        } else {
   1719            None
   1720        }
   1721    }
   1722 
   1723    pub fn set<T>(&mut self, id: usize, value: T) where T: EventValue {
   1724        self.set_f64(id, value.into_f64());
   1725    }
   1726 
   1727 
   1728    pub fn set_f64(&mut self, id: usize, value: f64) {
   1729        self.events[id] = Event::Value(value);
   1730    }
   1731 
   1732    pub fn get(&self, id: usize) -> Option<f64> {
   1733        if let Event::Value(val) = self.events[id] {
   1734            Some(val)
   1735        } else {
   1736            None
   1737        }
   1738    }
   1739 
   1740    pub fn get_or(&self, id: usize, or: f64) -> f64 {
   1741        self.get(id).unwrap_or(or)
   1742    }
   1743 
   1744    pub fn add<T>(&mut self, id: usize, n: T) where T: EventValue {
   1745        let n = n.into_f64();
   1746 
   1747        let evt = &mut self.events[id];
   1748 
   1749        let val = match *evt {
   1750            Event::Value(v) => v + n,
   1751            Event::None => n,
   1752            Event::Start(..) => { panic!(); }
   1753        };
   1754 
   1755        *evt = Event::Value(val);
   1756    }
   1757 
   1758    pub fn inc(&mut self, id: usize) {
   1759        self.add(id, 1.0);
   1760    }
   1761 
   1762    pub fn take(&mut self) -> Self {
   1763        TransactionProfile {
   1764            events: std::mem::take(&mut self.events),
   1765        }
   1766    }
   1767 
   1768    pub fn take_and_reset(&mut self) -> Self {
   1769        let events = std::mem::take(&mut self.events);
   1770 
   1771        *self = TransactionProfile::new();
   1772 
   1773        TransactionProfile { events }
   1774    }
   1775 
   1776    pub fn merge(&mut self, other: &mut Self) {
   1777        for i in 0..self.events.len() {
   1778            match (self.events[i], other.events[i]) {
   1779                (Event::Value(v1), Event::Value(v2)) => {
   1780                    self.events[i] = Event::Value(v1.max(v2));
   1781                }
   1782                (Event::Value(_), _) => {}
   1783                (_, Event::Value(v2)) => {
   1784                    self.events[i] = Event::Value(v2);
   1785                }
   1786                (Event::None, evt) => {
   1787                    self.events[i] = evt;
   1788                }
   1789                (Event::Start(s1), Event::Start(s2)) => {
   1790                    self.events[i] = Event::Start(s1.max(s2));
   1791                }
   1792                _=> {}
   1793            }
   1794            other.events[i] = Event::None;
   1795        }
   1796    }
   1797 
   1798    pub fn clear(&mut self) {
   1799        for evt in &mut self.events {
   1800            *evt = Event::None;
   1801        }
   1802    }
   1803 }
   1804 
   1805 impl GlyphRasterizeProfiler for TransactionProfile {
   1806    fn start_time(&mut self) {
   1807        let id = GLYPH_RESOLVE_TIME;
   1808        let ns = zeitstempel::now();
   1809        self.events[id] = Event::Start(ns);
   1810    }
   1811 
   1812    fn end_time(&mut self) -> f64 {
   1813        let id = GLYPH_RESOLVE_TIME;
   1814        self.end_time_if_started(id).unwrap()
   1815    }
   1816 
   1817    fn set(&mut self, value: f64) {
   1818        let id = RASTERIZED_GLYPHS;
   1819        self.set_f64(id, value);
   1820    }
   1821 }
   1822 
   1823 #[derive(Debug)]
   1824 pub struct GraphStats {
   1825    pub min: f64,
   1826    pub avg: f64,
   1827    pub max: f64,
   1828    pub sum: f64,
   1829    pub samples: usize,
   1830 }
   1831 
   1832 #[derive(Debug)]
   1833 pub struct Graph {
   1834    values: VecDeque<f64>,
   1835 }
   1836 
   1837 impl Graph {
   1838    fn new(max_samples: usize) -> Self {
   1839        let mut values = VecDeque::new();
   1840        values.reserve(max_samples);
   1841 
   1842        Graph { values }
   1843    }
   1844 
   1845    fn set(&mut self, val: f64) {
   1846        if self.values.len() == self.values.capacity() {
   1847            self.values.pop_back();
   1848        }
   1849        self.values.push_front(val);
   1850    }
   1851 
   1852    pub fn stats(&self) -> GraphStats {
   1853        let mut stats = GraphStats {
   1854            min: f64::MAX,
   1855            avg: 0.0,
   1856            max: -f64::MAX,
   1857            sum: 0.0,
   1858            samples: 0,
   1859        };
   1860 
   1861        let mut samples = 0;
   1862        for value in &self.values {
   1863            if value.is_finite() {
   1864                stats.min = stats.min.min(*value);
   1865                stats.max = stats.max.max(*value);
   1866                stats.sum += *value;
   1867                samples += 1;
   1868            }
   1869        }
   1870 
   1871        if samples > 0 {
   1872            stats.avg = stats.sum / samples as f64;
   1873            stats.samples = samples;
   1874        }
   1875 
   1876        stats
   1877    }
   1878 }
   1879 
   1880 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
   1881 pub enum ShowAs {
   1882    Float,
   1883    Int,
   1884 }
   1885 
   1886 struct ProfilerFrame {
   1887    total_time: u64,
   1888    samples: Vec<GpuTimer>,
   1889 }
   1890 
   1891 struct ProfilerFrameCollection {
   1892    frames: VecDeque<ProfilerFrame>,
   1893 }
   1894 
   1895 impl ProfilerFrameCollection {
   1896    fn new() -> Self {
   1897        ProfilerFrameCollection {
   1898            frames: VecDeque::new(),
   1899        }
   1900    }
   1901 
   1902    fn push(&mut self, frame: ProfilerFrame) {
   1903        if self.frames.len() == 20 {
   1904            self.frames.pop_back();
   1905        }
   1906        self.frames.push_front(frame);
   1907    }
   1908 }
   1909 
   1910 impl From<FullFrameStats> for ProfilerFrame {
   1911  fn from(stats: FullFrameStats) -> ProfilerFrame {
   1912    let new_sample = |time, label, color| -> GpuTimer {
   1913      let tag = GpuProfileTag {
   1914        label,
   1915        color
   1916      };
   1917 
   1918      let time_ns = ms_to_ns(time);
   1919 
   1920      GpuTimer {
   1921        tag, time_ns
   1922      }
   1923    };
   1924 
   1925    let samples = vec![
   1926      new_sample(stats.gecko_display_list_time, "Gecko DL", ColorF { r: 0.0, g: 1.0, b: 0.0, a: 1.0 }),
   1927      new_sample(stats.wr_display_list_time, "WR DL", ColorF { r: 0.0, g: 1.0, b: 1.0, a: 1.0 }),
   1928      new_sample(stats.scene_build_time, "Scene Build", ColorF { r: 1.0, g: 0.0, b: 1.0, a: 1.0 }),
   1929      new_sample(stats.frame_build_time, "Frame Build", ColorF { r: 1.0, g: 0.0, b: 0.0, a: 1.0 }),
   1930    ];
   1931 
   1932    ProfilerFrame {
   1933      total_time: ms_to_ns(stats.total()),
   1934      samples
   1935    }
   1936  }
   1937 }
   1938 
   1939 pub struct CpuFrameTimings {
   1940    pub total: f64,
   1941    pub api_send: f64,
   1942    pub update_document: f64,
   1943    pub visibility: f64,
   1944    pub prepare: f64,
   1945    pub glyph_resolve: f64,
   1946    pub batching: f64,
   1947    pub frame_building_other: f64,
   1948    pub frame_send: f64,
   1949    pub uploads: f64,
   1950    pub draw_calls: f64,
   1951    pub unknown: f64,
   1952 }
   1953 
   1954 impl CpuFrameTimings {
   1955    pub fn new(counters: &[Counter]) -> Self {
   1956        let total = counters[TOTAL_FRAME_CPU_TIME].get().unwrap_or(0.0);
   1957        let api_send = counters[API_SEND_TIME].get().unwrap_or(0.0);
   1958        let visibility = counters[FRAME_VISIBILITY_TIME].get().unwrap_or(0.0);
   1959        let prepare = counters[FRAME_PREPARE_TIME].get().unwrap_or(0.0);
   1960        let glyph_resolve = counters[GLYPH_RESOLVE_TIME].get().unwrap_or(0.0);
   1961        let batching = counters[FRAME_BATCHING_TIME].get().unwrap_or(0.0);
   1962        let frame_send = counters[FRAME_SEND_TIME].get().unwrap_or(0.0);
   1963        let renderer = counters[RENDERER_TIME].get().unwrap_or(0.0);
   1964        let uploads = counters[TEXTURE_CACHE_UPDATE_TIME].get().unwrap_or(0.0);
   1965        let frame_build = visibility + prepare + glyph_resolve + batching;
   1966        let update_document = counters[UPDATE_DOCUMENT_TIME].get().unwrap_or(0.0) - frame_build;
   1967        let draw_calls = renderer - uploads;
   1968        let unknown = (total - (api_send + update_document + frame_build + frame_send + renderer)).max(0.0);
   1969        let frame_building_other = (counters[FRAME_BUILDING_TIME].get().unwrap_or(0.0) - frame_build).max(0.0);
   1970 
   1971        CpuFrameTimings {
   1972            total,
   1973            api_send,
   1974            update_document,
   1975            visibility,
   1976            prepare,
   1977            glyph_resolve,
   1978            batching,
   1979            frame_building_other,
   1980            frame_send,
   1981            uploads,
   1982            draw_calls,
   1983            unknown,
   1984        }
   1985    }
   1986 
   1987    fn to_profiler_frame(&self) -> ProfilerFrame {
   1988        fn sample(time_ms: f64, label: &'static str, color: ColorF) -> GpuTimer {
   1989            let time_ns = ms_to_ns(time_ms);
   1990            GpuTimer {
   1991                time_ns,
   1992                tag: GpuProfileTag { label, color },
   1993            }
   1994        }
   1995 
   1996        ProfilerFrame {
   1997            total_time: ms_to_ns(self.total),
   1998            // Number the label so that they are displayed in order.
   1999            samples: vec![
   2000                // Compositor -> frame building
   2001                sample(self.api_send, "01. send", ColorF { r: 0.5, g: 0.5, b: 0.5, a: 1.0 }),
   2002                // Frame building
   2003                sample(self.update_document, "02. update document", ColorF { r: 0.2, g: 0.2, b: 0.7, a: 1.0 }),
   2004                sample(self.visibility, "03. visibility", ColorF { r: 0.0, g: 0.5, b: 0.9, a: 1.0 }),
   2005                sample(self.prepare, "04. prepare", ColorF { r: 0.0, g: 0.4, b: 0.3, a: 1.0 }),
   2006                sample(self.glyph_resolve, "05. glyph resolve", ColorF { r: 0.0, g: 0.7, b: 0.4, a: 1.0 }),
   2007                sample(self.batching, "06. batching", ColorF { r: 0.2, g: 0.3, b: 0.7, a: 1.0 }),
   2008                sample(self.frame_building_other, "07. frame build (other)", ColorF { r: 0.1, g: 0.7, b: 0.7, a: 1.0 }),
   2009                // Frame building -> renderer
   2010                sample(self.frame_send, "08. frame send", ColorF { r: 1.0, g: 0.8, b: 0.8, a: 1.0 }),
   2011                // Renderer
   2012                sample(self.uploads, "09. texture uploads", ColorF { r: 0.8, g: 0.0, b: 0.3, a: 1.0 }),
   2013                sample(self.draw_calls, "10. draw calls", ColorF { r: 1.0, g: 0.5, b: 0.0, a: 1.0 }),
   2014                // Unaccounted time
   2015                sample(self.unknown, "11. unknown", ColorF { r: 0.3, g: 0.3, b: 0.3, a: 1.0 }),
   2016            ],
   2017        }
   2018    }
   2019 }
   2020 
   2021 pub fn ns_to_ms(ns: u64) -> f64 {
   2022    ns as f64 / 1_000_000.0
   2023 }
   2024 
   2025 pub fn ms_to_ns(ms: f64) -> u64 {
   2026  (ms * 1_000_000.0) as u64
   2027 }
   2028 
   2029 pub fn bytes_to_mb(bytes: usize) -> f64 {
   2030    bytes as f64 / 1_000_000.0
   2031 }
   2032 
   2033 #[derive(Debug, PartialEq)]
   2034 enum Item {
   2035    Counters(Vec<usize>),
   2036    Graph(usize),
   2037    ChangeIndicator(usize),
   2038    Fps,
   2039    GpuTimeQueries,
   2040    PaintPhaseGraph,
   2041    SlowScrollFrames,
   2042    Text(String),
   2043    Space,
   2044    Column,
   2045    Row,
   2046 }
   2047 
   2048 pub struct RenderCommandLog {
   2049    items: Vec<RenderCommandInfo>,
   2050    current_shader: &'static str,
   2051 }
   2052 
   2053 impl RenderCommandLog {
   2054    pub fn new() -> Self {
   2055        RenderCommandLog {
   2056            items: Vec::new(),
   2057            current_shader: "",
   2058        }
   2059    }
   2060 
   2061    pub fn get(&self) -> &[RenderCommandInfo] {
   2062        &self.items
   2063    }
   2064 
   2065    pub fn clear(&mut self) {
   2066        self.current_shader = "";
   2067        self.items.clear();
   2068    }
   2069 
   2070    pub fn set_shader(&mut self, shader: &'static str) {
   2071        self.current_shader = shader;
   2072    }
   2073 
   2074    pub fn begin_render_target(&mut self, label: &str, size: DeviceIntSize) {
   2075        self.items.push(RenderCommandInfo::RenderTarget { kind: label.into(), size })
   2076    }
   2077 
   2078    pub fn draw(&mut self, instances: u32) {
   2079        self.items.push(RenderCommandInfo::DrawCall {
   2080            shader: self.current_shader.into(),
   2081            instances,
   2082        });
   2083    }
   2084 }