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