1use api::{ColorF, ColorU, RenderCommandInfo};
23#[cfg(feature = "debugger")]
24use api::debugger::{ProfileCounterUpdate, ProfileCounterId};
25use glyph_rasterizer::profiler::GlyphRasterizeProfiler;
26use crate::renderer::DebugRenderer;
27use crate::device::query::GpuTimer;
28use euclid::{Point2D, Rect, Size2D, vec2, default};
29use crate::internal_types::FastHashMap;
30use crate::renderer::{FullFrameStats, init::wr_has_been_initialized};
31use api::units::DeviceIntSize;
32use std::collections::vec_deque::VecDeque;
33use std::fmt::{Write, Debug};
34use std::f32;
35use std::ops::Range;
36use std::time::Duration;
37
38macro_rules! set_text {
39 ($dst:expr, $($arg:tt)*) => {
40 $dst.clear();
41 write!($dst, $($arg)*).unwrap();
42 };
43}
44
45const GRAPH_WIDTH: f32 = 1024.0;
46const GRAPH_HEIGHT: f32 = 320.0;
47const GRAPH_PADDING: f32 = 8.0;
48const GRAPH_FRAME_HEIGHT: f32 = 16.0;
49const PROFILE_SPACING: f32 = 15.0;
50const PROFILE_PADDING: f32 = 10.0;
51const BACKGROUND_COLOR: ColorU = ColorU { r: 20, g: 20, b: 20, a: 220 };
52
53const ONE_SECOND_NS: u64 = 1_000_000_000;
54
55static PROFILER_PRESETS: &'static[(&'static str, &'static str)] = &[
57 (&"Default", &"FPS,|,Slow indicators,_,Time graphs,|,Frame times, ,Transaction times, ,Frame stats, ,Memory, ,Interners,_,GPU time queries,_,Paint phase graph"),
59 (&"Compact", &"FPS, ,Frame times, ,Frame stats"),
61 (&"Slow indicators", &"*Slow transaction,*Slow frame"),
63
64 (&"Transaction times", &"DisplayList,Scene building,Content send,API send"),
68 (&"Frame times", &"Frame CPU total,Frame building,Visibility,Prepare,Batching,Glyph resolve,Texture cache update,Shader build time,Renderer,GPU"),
70 (&"Frame stats", &"Primitives,Visible primitives,Draw calls,Vertices,Color passes,Alpha passes,Rendered picture tiles,Rasterized glyphs"),
72 (&"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 (&"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 (&"Time graphs", &"#DisplayList,#Scene building,#Blob rasterization, ,#Frame CPU total,#Frame building,#Renderer,#Texture cache update, ,#GPU,"),
86 (&"Backend graphs", &"#Frame building, #Visibility, #Prepare, #Batching, #Glyph resolve"),
88 (&"Renderer graphs", &"#Rendered picture tiles,#Draw calls,#Rasterized glyphs,#Texture uploads,#Texture uploads mem, ,#Texture cache update,#Renderer,"),
90
91 (&"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 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
108fn 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
118pub const FRAME_BUILDING_TIME: usize = 0;
120pub const FRAME_VISIBILITY_TIME: usize = 1;
121pub const FRAME_PREPARE_TIME: usize = 2;
122pub const FRAME_BATCHING_TIME: usize = 3;
123
124pub const RENDERER_TIME: usize = 4;
125pub const TOTAL_FRAME_CPU_TIME: usize = 5;
126pub const GPU_TIME: usize = 6;
127
128pub const CONTENT_SEND_TIME: usize = 7;
129pub const API_SEND_TIME: usize = 8;
130
131pub const DISPLAY_LIST_BUILD_TIME: usize = 9;
132pub const DISPLAY_LIST_MEM: usize = 10;
133
134pub const SCENE_BUILD_TIME: usize = 11;
135
136pub const SLOW_FRAME: usize = 12;
137pub const SLOW_TXN: usize = 13;
138
139pub const FRAME_TIME: usize = 14;
140
141pub const TEXTURE_UPLOADS: usize = 15;
142pub const TEXTURE_UPLOADS_MEM: usize = 16;
143pub const TEXTURE_CACHE_UPDATE_TIME: usize = 17;
144pub const CPU_TEXTURE_ALLOCATION_TIME: usize = 18;
145pub const STAGING_TEXTURE_ALLOCATION_TIME: usize = 19;
146pub const UPLOAD_CPU_COPY_TIME: usize = 20;
147pub const UPLOAD_GPU_COPY_TIME: usize = 21;
148pub const UPLOAD_TIME: usize = 22;
149pub const UPLOAD_NUM_COPY_BATCHES: usize = 23;
150pub const TOTAL_UPLOAD_TIME: usize = 24;
151pub const CREATE_CACHE_TEXTURE_TIME: usize = 25;
152pub const DELETE_CACHE_TEXTURE_TIME: usize = 26;
153
154pub const RASTERIZED_BLOBS: usize = 27;
155pub const RASTERIZED_BLOB_TILES: usize = 28;
156pub const RASTERIZED_BLOBS_PX: usize = 29;
157pub const BLOB_RASTERIZATION_TIME: usize = 30;
158
159pub const RASTERIZED_GLYPHS: usize = 31;
160pub const GLYPH_RESOLVE_TIME: usize = 32;
161
162pub const DRAW_CALLS: usize = 33;
163pub const VERTICES: usize = 34;
164pub const PRIMITIVES: usize = 35;
165pub const VISIBLE_PRIMITIVES: usize = 36;
166
167pub const USED_TARGETS: usize = 37;
168pub const CREATED_TARGETS: usize = 38;
169pub const PICTURE_CACHE_SLICES: usize = 39;
170
171pub const COLOR_PASSES: usize = 40;
172pub const ALPHA_PASSES: usize = 41;
173pub const PICTURE_TILES: usize = 42;
174pub const RENDERED_PICTURE_TILES: usize = 43;
175
176pub const FONT_TEMPLATES: usize = 44;
177pub const FONT_TEMPLATES_MEM: usize = 45;
178pub const IMAGE_TEMPLATES: usize = 46;
179pub const IMAGE_TEMPLATES_MEM: usize = 47;
180
181pub const ATLAS_ITEMS_MEM: usize = 48;
184pub const ATLAS_A8_PIXELS: usize = 49;
185pub const ATLAS_A8_TEXTURES: usize = 50;
186pub const ATLAS_A16_PIXELS: usize = 51;
187pub const ATLAS_A16_TEXTURES: usize = 52;
188pub const ATLAS_RGBA8_LINEAR_PIXELS: usize = 53;
189pub const ATLAS_RGBA8_LINEAR_TEXTURES: usize = 54;
190pub const ATLAS_RGBA8_NEAREST_PIXELS: usize = 55;
191pub const ATLAS_RGBA8_NEAREST_TEXTURES: usize = 56;
192pub const ATLAS_RGBA8_GLYPHS_PIXELS: usize = 57;
193pub const ATLAS_RGBA8_GLYPHS_TEXTURES: usize = 58;
194pub const ATLAS_A8_GLYPHS_PIXELS: usize = 59;
195pub const ATLAS_A8_GLYPHS_TEXTURES: usize = 60;
196pub const ATLAS_COLOR8_LINEAR_PRESSURE: usize = 61;
197pub const ATLAS_COLOR8_NEAREST_PRESSURE: usize = 62;
198pub const ATLAS_COLOR8_GLYPHS_PRESSURE: usize = 63;
199pub const ATLAS_ALPHA8_PRESSURE: usize = 64;
200pub const ATLAS_ALPHA8_GLYPHS_PRESSURE: usize = 65;
201pub const ATLAS_ALPHA16_PRESSURE: usize = 66;
202pub const ATLAS_STANDALONE_PRESSURE: usize = 67;
203
204pub const TEXTURE_CACHE_EVICTION_COUNT: usize = 68;
205pub const TEXTURE_CACHE_YOUNGEST_EVICTION: usize = 69;
206pub const EXTERNAL_IMAGE_BYTES: usize = 70;
207pub const ATLAS_TEXTURES_MEM: usize = 71;
208pub const STANDALONE_TEXTURES_MEM: usize = 72;
209pub const PICTURE_TILES_MEM: usize = 73;
210pub const RENDER_TARGET_MEM: usize = 74;
211
212pub const ALPHA_TARGETS_SAMPLERS: usize = 75;
213pub const TRANSPARENT_PASS_SAMPLERS: usize = 76;
214pub const OPAQUE_PASS_SAMPLERS: usize = 77;
215pub const TOTAL_SAMPLERS: usize = 78;
216
217pub const INTERNED_PRIMITIVES: usize = 79;
218pub const INTERNED_CLIPS: usize = 80;
219pub const INTERNED_TEXT_RUNS: usize = 81;
220pub const INTERNED_NORMAL_BORDERS: usize = 82;
221pub const INTERNED_IMAGE_BORDERS: usize = 83;
222pub const INTERNED_IMAGES: usize = 84;
223pub const INTERNED_YUV_IMAGES: usize = 85;
224pub const INTERNED_LINE_DECORATIONS: usize = 86;
225pub const INTERNED_LINEAR_GRADIENTS: usize = 87;
226pub const INTERNED_RADIAL_GRADIENTS: usize = 88;
227pub const INTERNED_CONIC_GRADIENTS: usize = 89;
228pub const INTERNED_PICTURES: usize = 90;
229pub const INTERNED_FILTER_DATA: usize = 91;
230pub const INTERNED_BACKDROP_CAPTURES: usize = 92;
231pub const INTERNED_BACKDROP_RENDERS: usize = 93;
232pub const INTERNED_POLYGONS: usize = 94;
233pub const INTERNED_BOX_SHADOWS: usize = 95;
234pub const DEPTH_TARGETS_MEM: usize = 96;
235
236pub const SHADER_BUILD_TIME: usize = 97;
237
238pub const RENDER_REASON_FIRST: usize = 98;
239pub const RENDER_REASON_SCENE: usize = 99;
240pub const RENDER_REASON_ANIMATED_PROPERTY: usize = 100;
241pub const RENDER_REASON_RESOURCE_UPDATE: usize = 101;
242pub const RENDER_REASON_ASYNC_IMAGE: usize = 102;
243pub const RENDER_REASON_CLEAR_RESOURCES: usize = 103;
244pub const RENDER_REASON_APZ: usize = 104;
245pub const RENDER_REASON_RESIZE: usize = 105;
246pub const RENDER_REASON_WIDGET: usize = 106;
247pub const RENDER_REASON_TEXTURE_CACHE_FLUSH: usize = 107;
248pub const RENDER_REASON_SNAPSHOT: usize = 108;
249pub const RENDER_REASON_POST_RESOURCE_UPDATE_HOOKS: usize = 109;
250pub const RENDER_REASON_CONFIG_CHANGE: usize = 110;
251pub const RENDER_REASON_CONTENT_SYNC: usize = 111;
252pub const RENDER_REASON_FLUSH: usize = 112;
253pub const RENDER_REASON_TESTING: usize = 113;
254pub const RENDER_REASON_OTHER: usize = 114;
255pub const RENDER_REASON_VSYNC: usize = 115;
256
257pub const TEXTURES_CREATED: usize = 116;
258pub const TEXTURES_DELETED: usize = 117;
259
260pub const SLOW_FRAME_CPU_COUNT: usize = 118;
261pub const SLOW_FRAME_GPU_COUNT: usize = 119;
262pub const SLOW_FRAME_BUILD_COUNT: usize = 120;
263pub const SLOW_UPLOAD_COUNT: usize = 121;
264pub const SLOW_RENDER_COUNT: usize = 122;
265pub const SLOW_DRAW_CALLS_COUNT: usize = 123;
266pub const SLOW_TARGETS_COUNT: usize = 124;
267pub const SLOW_BLOB_COUNT: usize = 125;
268pub const SLOW_SCROLL_AFTER_SCENE_COUNT: usize = 126;
269
270pub const GPU_BUFFER_MEM: usize = 127;
271pub const GPU_TOTAL_MEM: usize = 128;
272
273pub const FRAME_SEND_TIME: usize = 129;
274pub const UPDATE_DOCUMENT_TIME: usize = 130;
275
276pub const COMPOSITOR_SURFACE_UNDERLAYS: usize = 131;
277pub const COMPOSITOR_SURFACE_OVERLAYS: usize = 132;
278pub const COMPOSITOR_SURFACE_BLITS: usize = 133;
279
280pub const NUM_PROFILER_EVENTS: usize = 134;
281
282pub 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 frame_timestamps_within_last_second: Vec<u64>,
295
296 slow_frame_cpu_count: u64,
298 slow_frame_gpu_count: u64,
300 slow_frame_build_count: u64,
302 slow_render_count: u64,
304 slow_upload_count: u64,
306 slow_draw_calls_count: u64,
308 slow_targets_count: u64,
310 slow_blob_count: u64,
312 slow_scroll_after_scene_count: u64,
314
315 ui: Vec<Item>,
316}
317
318impl 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 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 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, 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 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 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 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 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 pub fn index_of(&self, name: &str) -> Option<usize> {
721 self.counters.iter().position(|counter| counter.name == name)
722 }
723
724 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 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 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 ColorU::new(255, 255, 255, 255),
857 ColorU::new(255, 255, 0, 255),
858 ColorU::new(255, 80, 0, 255),
860 ColorU::new(255, 0, 0, 255),
861 ];
862
863 for idx in selected {
864 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 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 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 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 let baseline_ns = 16_000_000.0; 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 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 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 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
1337pub trait ProfilerHooks : Send + Sync {
1339 fn register_thread(&self, thread_name: &str);
1341
1342 fn unregister_thread(&self);
1344
1345 fn begin_marker(&self, label: &str);
1347
1348 fn end_marker(&self, label: &str);
1350
1351 fn event_marker(&self, label: &str);
1353
1354 fn add_text_marker(&self, label: &str, text: &str, duration: Duration);
1362
1363 fn thread_is_being_profiled(&self) -> bool;
1365}
1366
1367pub static mut PROFILER_HOOKS: Option<&'static dyn ProfilerHooks> = None;
1369
1370pub 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
1381pub struct ProfileScope {
1383 name: &'static str,
1384}
1385
1386
1387pub 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
1397pub fn unregister_thread() {
1399 unsafe {
1400 if let Some(ref hooks) = PROFILER_HOOKS {
1401 hooks.unregister_thread();
1402 }
1403 }
1404}
1405
1406pub 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
1415pub 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
1424pub fn thread_is_being_profiled() -> bool {
1426 unsafe {
1427 PROFILER_HOOKS.map_or(false, |h| h.thread_is_being_profiled())
1428 }
1429}
1430
1431impl ProfileScope {
1432 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
1446impl 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
1456macro_rules! profile_marker {
1458 ($string:expr) => {
1459 let _scope = $crate::profiler::ProfileScope::new($string);
1460 };
1461}
1462
1463#[derive(Debug, Clone)]
1464pub struct GpuProfileTag {
1465 pub label: &'static str,
1466 pub color: ColorF,
1467}
1468
1469#[derive(Clone, Debug)]
1471pub struct Expected<T> {
1472 pub range: Option<Range<T>>,
1473 pub avg: Option<Range<T>>,
1474}
1475
1476impl<T> Expected<T> {
1477 const fn none() -> Self {
1478 Expected {
1479 range: None,
1480 avg: None,
1481 }
1482 }
1483}
1484
1485const fn expected<T>(range: Range<T>) -> Expected<T> {
1486 Expected {
1487 range: Some(range),
1488 avg: None,
1489 }
1490}
1491
1492impl Expected<f64> {
1493 const fn avg(mut self, avg: Range<f64>) -> Self {
1494 self.avg = Some(avg);
1495 self
1496 }
1497}
1498
1499impl 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
1519pub 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)]
1528pub struct Counter {
1529 pub name: &'static str,
1530 pub unit: &'static str,
1531 pub show_as: ShowAs,
1532 pub expected: Expected<f64>,
1533
1534 value: f64,
1536 num_samples: u64,
1538 sum: f64,
1540 next_max: f64,
1542 max: f64,
1544 avg: f64,
1546 change_indicator: u8,
1548
1549 graph: Option<Graph>,
1550}
1551
1552impl 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 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)]
1668pub enum Event {
1669 Start(u64),
1670 Value(f64),
1671 None,
1672}
1673
1674pub trait EventValue {
1676 fn into_f64(self) -> f64;
1677}
1678
1679impl EventValue for f64 { fn into_f64(self) -> f64 { self } }
1680impl EventValue for f32 { fn into_f64(self) -> f64 { self as f64 } }
1681impl EventValue for u32 { fn into_f64(self) -> f64 { self as f64 } }
1682impl EventValue for i32 { fn into_f64(self) -> f64 { self as f64 } }
1683impl EventValue for u64 { fn into_f64(self) -> f64 { self as f64 } }
1684impl EventValue for usize { fn into_f64(self) -> f64 { self as f64 } }
1685
1686pub struct TransactionProfile {
1689 pub events: Vec<Event>,
1690}
1691
1692impl 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 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
1805impl 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)]
1824pub 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)]
1833pub struct Graph {
1834 values: VecDeque<f64>,
1835}
1836
1837impl 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)]
1881pub enum ShowAs {
1882 Float,
1883 Int,
1884}
1885
1886struct ProfilerFrame {
1887 total_time: u64,
1888 samples: Vec<GpuTimer>,
1889}
1890
1891struct ProfilerFrameCollection {
1892 frames: VecDeque<ProfilerFrame>,
1893}
1894
1895impl 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
1910impl 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
1939pub 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
1954impl 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 samples: vec![
2000 sample(self.api_send, "01. send", ColorF { r: 0.5, g: 0.5, b: 0.5, a: 1.0 }),
2002 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 sample(self.frame_send, "08. frame send", ColorF { r: 1.0, g: 0.8, b: 0.8, a: 1.0 }),
2011 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 sample(self.unknown, "11. unknown", ColorF { r: 0.3, g: 0.3, b: 0.3, a: 1.0 }),
2016 ],
2017 }
2018 }
2019}
2020
2021pub fn ns_to_ms(ns: u64) -> f64 {
2022 ns as f64 / 1_000_000.0
2023}
2024
2025pub fn ms_to_ns(ms: f64) -> u64 {
2026 (ms * 1_000_000.0) as u64
2027}
2028
2029pub fn bytes_to_mb(bytes: usize) -> f64 {
2030 bytes as f64 / 1_000_000.0
2031}
2032
2033#[derive(Debug, PartialEq)]
2034enum 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
2048pub struct RenderCommandLog {
2049 items: Vec<RenderCommandInfo>,
2050 current_shader: &'static str,
2051}
2052
2053impl 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}