Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(18)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/timeline/PaintProfilerView.js

Issue 2358253002: DevTools: extract a component for layer viewer (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /**
32 * @constructor
33 * @param {function(string=)} showImageCallback
34 * @extends {WebInspector.HBox}
35 */
36 WebInspector.PaintProfilerView = function(showImageCallback)
37 {
38 WebInspector.HBox.call(this);
39 this.element.classList.add("paint-profiler-overview", "hbox");
40 this._canvasContainer = this.element.createChild("div", "paint-profiler-canv as-container");
41 this._progressBanner = this.element.createChild("div", "banner hidden");
42 this._progressBanner.textContent = WebInspector.UIString("Profiling\u2026");
43 this._pieChart = new WebInspector.PieChart(55, this._formatPieChartTime.bind (this), true);
44 this._pieChart.element.classList.add("paint-profiler-pie-chart");
45 this.element.appendChild(this._pieChart.element);
46
47 this._showImageCallback = showImageCallback;
48
49 this._canvas = this._canvasContainer.createChild("canvas", "fill");
50 this._context = this._canvas.getContext("2d");
51 this._selectionWindow = new WebInspector.OverviewGrid.Window(this._canvasCon tainer);
52 this._selectionWindow.addEventListener(WebInspector.OverviewGrid.Events.Wind owChanged, this._onWindowChanged, this);
53
54 this._innerBarWidth = 4 * window.devicePixelRatio;
55 this._minBarHeight = window.devicePixelRatio;
56 this._barPaddingWidth = 2 * window.devicePixelRatio;
57 this._outerBarWidth = this._innerBarWidth + this._barPaddingWidth;
58
59 this._reset();
60 }
61
62 /** @enum {symbol} */
63 WebInspector.PaintProfilerView.Events = {
64 WindowChanged: Symbol("WindowChanged")
65 };
66
67 WebInspector.PaintProfilerView.prototype = {
68 onResize: function()
69 {
70 this._update();
71 },
72
73 /**
74 * @param {?WebInspector.PaintProfilerSnapshot} snapshot
75 * @param {!Array.<!WebInspector.PaintProfilerLogItem>} log
76 * @param {?DOMAgent.Rect} clipRect
77 */
78 setSnapshotAndLog: function(snapshot, log, clipRect)
79 {
80 this._reset();
81 this._snapshot = snapshot;
82 this._log = log;
83 this._logCategories = this._log.map(WebInspector.PaintProfilerView._cate goryForLogItem);
84
85 if (!this._snapshot) {
86 this._update();
87 this._pieChart.setTotal(0);
88 this._selectionWindow.setEnabled(false);
89 return;
90 }
91 this._selectionWindow.setEnabled(true);
92 this._progressBanner.classList.remove("hidden");
93 snapshot.requestImage(null, null, 1, this._showImageCallback);
94 snapshot.profile(clipRect, onProfileDone.bind(this));
95 /**
96 * @param {!Array.<!LayerTreeAgent.PaintProfile>=} profiles
97 * @this {WebInspector.PaintProfilerView}
98 */
99 function onProfileDone(profiles)
100 {
101 this._progressBanner.classList.add("hidden");
102 this._profiles = profiles;
103 this._update();
104 this._updatePieChart();
105 }
106 },
107
108 _update: function()
109 {
110 this._canvas.width = this._canvasContainer.clientWidth * window.devicePi xelRatio;
111 this._canvas.height = this._canvasContainer.clientHeight * window.device PixelRatio;
112 this._samplesPerBar = 0;
113 if (!this._profiles || !this._profiles.length)
114 return;
115
116 var maxBars = Math.floor((this._canvas.width - 2 * this._barPaddingWidth ) / this._outerBarWidth);
117 var sampleCount = this._log.length;
118 this._samplesPerBar = Math.ceil(sampleCount / maxBars);
119
120 var maxBarTime = 0;
121 var barTimes = [];
122 var barHeightByCategory = [];
123 var heightByCategory = {};
124 for (var i = 0, lastBarIndex = 0, lastBarTime = 0; i < sampleCount;) {
125 var categoryName = (this._logCategories[i] && this._logCategories[i] .name) || "misc";
126 var sampleIndex = this._log[i].commandIndex;
127 for (var row = 0; row < this._profiles.length; row++) {
128 var sample = this._profiles[row][sampleIndex];
129 lastBarTime += sample;
130 heightByCategory[categoryName] = (heightByCategory[categoryName] || 0) + sample;
131 }
132 ++i;
133 if (i - lastBarIndex === this._samplesPerBar || i === sampleCount) {
134 // Normalize by total number of samples accumulated.
135 var factor = this._profiles.length * (i - lastBarIndex);
136 lastBarTime /= factor;
137 for (categoryName in heightByCategory)
138 heightByCategory[categoryName] /= factor;
139
140 barTimes.push(lastBarTime);
141 barHeightByCategory.push(heightByCategory);
142
143 if (lastBarTime > maxBarTime)
144 maxBarTime = lastBarTime;
145 lastBarTime = 0;
146 heightByCategory = {};
147 lastBarIndex = i;
148 }
149 }
150
151 const paddingHeight = 4 * window.devicePixelRatio;
152 var scale = (this._canvas.height - paddingHeight - this._minBarHeight) / maxBarTime;
153 for (var i = 0; i < barTimes.length; ++i) {
154 for (var categoryName in barHeightByCategory[i])
155 barHeightByCategory[i][categoryName] *= (barTimes[i] * scale + t his._minBarHeight) / barTimes[i];
156 this._renderBar(i, barHeightByCategory[i]);
157 }
158 },
159
160 /**
161 * @param {number} index
162 * @param {!Object.<string, number>} heightByCategory
163 */
164 _renderBar: function(index, heightByCategory)
165 {
166 var categories = WebInspector.PaintProfilerView.categories();
167 var currentHeight = 0;
168 var x = this._barPaddingWidth + index * this._outerBarWidth;
169 for (var categoryName in categories) {
170 if (!heightByCategory[categoryName])
171 continue;
172 currentHeight += heightByCategory[categoryName];
173 var y = this._canvas.height - currentHeight;
174 this._context.fillStyle = categories[categoryName].color;
175 this._context.fillRect(x, y, this._innerBarWidth, heightByCategory[c ategoryName]);
176 }
177 },
178
179 _onWindowChanged: function()
180 {
181 this.dispatchEventToListeners(WebInspector.PaintProfilerView.Events.Wind owChanged);
182 this._updatePieChart();
183 if (this._updateImageTimer)
184 return;
185 this._updateImageTimer = setTimeout(this._updateImage.bind(this), 100);
186 },
187
188 _updatePieChart: function()
189 {
190 if (!this._profiles || !this._profiles.length)
191 return;
192 var window = this.windowBoundaries();
193 var totalTime = 0;
194 var timeByCategory = {};
195 for (var i = window.left; i < window.right; ++i) {
196 var logEntry = this._log[i];
197 var category = WebInspector.PaintProfilerView._categoryForLogItem(lo gEntry);
198 timeByCategory[category.color] = timeByCategory[category.color] || 0 ;
199 for (var j = 0; j < this._profiles.length; ++j) {
200 var time = this._profiles[j][logEntry.commandIndex];
201 totalTime += time;
202 timeByCategory[category.color] += time;
203 }
204 }
205 this._pieChart.setTotal(totalTime / this._profiles.length);
206 for (var color in timeByCategory)
207 this._pieChart.addSlice(timeByCategory[color] / this._profiles.lengt h, color);
208 },
209
210 /**
211 * @param {number} value
212 * @return {string}
213 */
214 _formatPieChartTime: function(value)
215 {
216 return Number.millisToString(value * 1000, true);
217 },
218
219 /**
220 * @return {{left: number, right: number}}
221 */
222 windowBoundaries: function()
223 {
224 var screenLeft = this._selectionWindow.windowLeft * this._canvas.width;
225 var screenRight = this._selectionWindow.windowRight * this._canvas.width ;
226 var barLeft = Math.floor(screenLeft / this._outerBarWidth);
227 var barRight = Math.floor((screenRight + this._innerBarWidth - this._bar PaddingWidth / 2) / this._outerBarWidth);
228 var stepLeft = Number.constrain(barLeft * this._samplesPerBar, 0, this._ log.length - 1);
229 var stepRight = Number.constrain(barRight * this._samplesPerBar, 0, this ._log.length);
230
231 return { left: stepLeft, right: stepRight };
232 },
233
234 _updateImage: function()
235 {
236 delete this._updateImageTimer;
237 if (!this._profiles || !this._profiles.length)
238 return;
239
240 var window = this.windowBoundaries();
241 this._snapshot.requestImage(this._log[window.left].commandIndex, this._l og[window.right - 1].commandIndex, 1, this._showImageCallback);
242 },
243
244 _reset: function()
245 {
246 this._snapshot = null;
247 this._profiles = null;
248 this._selectionWindow.reset();
249 },
250
251 __proto__: WebInspector.HBox.prototype
252 };
253
254 /**
255 * @constructor
256 * @extends {WebInspector.VBox}
257 */
258 WebInspector.PaintProfilerCommandLogView = function()
259 {
260 WebInspector.VBox.call(this);
261 this.setMinimumSize(100, 25);
262 this.element.classList.add("profiler-log-view");
263
264 this._treeOutline = new TreeOutlineInShadow();
265 this.element.appendChild(this._treeOutline.element);
266
267 this._reset();
268 }
269
270 WebInspector.PaintProfilerCommandLogView.prototype = {
271 /**
272 * @param {?WebInspector.Target} target
273 * @param {!Array.<!WebInspector.PaintProfilerLogItem>} log
274 */
275 setCommandLog: function(target, log)
276 {
277 this._target = target;
278 this._log = log;
279 this.updateWindow();
280 },
281
282 /**
283 * @param {!TreeOutline} treeOutline
284 * @param {!WebInspector.PaintProfilerLogItem} logItem
285 */
286 _appendLogItem: function(treeOutline, logItem)
287 {
288 var treeElement = new WebInspector.LogTreeElement(this, logItem);
289 treeOutline.appendChild(treeElement);
290 },
291
292 /**
293 * @param {number=} stepLeft
294 * @param {number=} stepRight
295 */
296 updateWindow: function(stepLeft, stepRight)
297 {
298 this._treeOutline.removeChildren();
299 if (!this._log.length)
300 return;
301 stepLeft = stepLeft || 0;
302 stepRight = stepRight || this._log.length;
303 for (var i = stepLeft; i < stepRight; ++i)
304 this._appendLogItem(this._treeOutline, this._log[i]);
305 },
306
307 _reset: function()
308 {
309 this._log = [];
310 },
311
312 __proto__: WebInspector.VBox.prototype
313 };
314
315 /**
316 * @constructor
317 * @param {!WebInspector.PaintProfilerCommandLogView} ownerView
318 * @param {!WebInspector.PaintProfilerLogItem} logItem
319 * @extends {TreeElement}
320 */
321 WebInspector.LogTreeElement = function(ownerView, logItem)
322 {
323 TreeElement.call(this, "", !!logItem.params);
324 this._logItem = logItem;
325 this._ownerView = ownerView;
326 this._filled = false;
327 }
328
329 WebInspector.LogTreeElement.prototype = {
330 onattach: function()
331 {
332 this._update();
333 },
334
335 onpopulate: function()
336 {
337 for (var param in this._logItem.params)
338 WebInspector.LogPropertyTreeElement._appendLogPropertyItem(this, par am, this._logItem.params[param]);
339 },
340
341 /**
342 * @param {*} param
343 * @param {string} name
344 * @return {string}
345 */
346 _paramToString: function(param, name)
347 {
348 if (typeof param !== "object")
349 return typeof param === "string" && param.length > 100 ? name : JSON .stringify(param);
350 var str = "";
351 var keyCount = 0;
352 for (var key in param) {
353 if (++keyCount > 4 || typeof param[key] === "object" || (typeof para m[key] === "string" && param[key].length > 100))
354 return name;
355 if (str)
356 str += ", ";
357 str += param[key];
358 }
359 return str;
360 },
361
362 /**
363 * @param {?Object<string, *>} params
364 * @return {string}
365 */
366 _paramsToString: function(params)
367 {
368 var str = "";
369 for (var key in params) {
370 if (str)
371 str += ", ";
372 str += this._paramToString(params[key], key);
373 }
374 return str;
375 },
376
377 _update: function()
378 {
379 var title = createDocumentFragment();
380 title.createTextChild(this._logItem.method + "(" + this._paramsToString( this._logItem.params) + ")");
381 this.title = title;
382 },
383
384 __proto__: TreeElement.prototype
385 };
386
387 /**
388 * @constructor
389 * @param {!{name: string, value}} property
390 * @extends {TreeElement}
391 */
392 WebInspector.LogPropertyTreeElement = function(property)
393 {
394 TreeElement.call(this);
395 this._property = property;
396 };
397
398 /**
399 * @param {!TreeElement} element
400 * @param {string} name
401 * @param {*} value
402 */
403 WebInspector.LogPropertyTreeElement._appendLogPropertyItem = function(element, n ame, value)
404 {
405 var treeElement = new WebInspector.LogPropertyTreeElement({name: name, value : value});
406 element.appendChild(treeElement);
407 if (value && typeof value === "object") {
408 for (var property in value)
409 WebInspector.LogPropertyTreeElement._appendLogPropertyItem(treeEleme nt, property, value[property]);
410 }
411 };
412
413 WebInspector.LogPropertyTreeElement.prototype = {
414 onattach: function()
415 {
416 var title = createDocumentFragment();
417 var nameElement = title.createChild("span", "name");
418 nameElement.textContent = this._property.name;
419 var separatorElement = title.createChild("span", "separator");
420 separatorElement.textContent = ": ";
421 if (this._property.value === null || typeof this._property.value !== "ob ject") {
422 var valueElement = title.createChild("span", "value");
423 valueElement.textContent = JSON.stringify(this._property.value);
424 valueElement.classList.add("cm-js-" + (this._property.value === null ? "null" : typeof this._property.value));
425 }
426 this.title = title;
427 },
428
429 __proto__: TreeElement.prototype
430 }
431
432 /**
433 * @return {!Object.<string, !WebInspector.PaintProfilerCategory>}
434 */
435 WebInspector.PaintProfilerView.categories = function()
436 {
437 if (WebInspector.PaintProfilerView._categories)
438 return WebInspector.PaintProfilerView._categories;
439 WebInspector.PaintProfilerView._categories = {
440 shapes: new WebInspector.PaintProfilerCategory("shapes", WebInspector.UI String("Shapes"), "rgb(255, 161, 129)"),
441 bitmap: new WebInspector.PaintProfilerCategory("bitmap", WebInspector.UI String("Bitmap"), "rgb(136, 196, 255)"),
442 text: new WebInspector.PaintProfilerCategory("text", WebInspector.UIStri ng("Text"), "rgb(180, 255, 137)"),
443 misc: new WebInspector.PaintProfilerCategory("misc", WebInspector.UIStri ng("Misc"), "rgb(206, 160, 255)")
444 };
445 return WebInspector.PaintProfilerView._categories;
446 };
447
448 /**
449 * @constructor
450 * @param {string} name
451 * @param {string} title
452 * @param {string} color
453 */
454 WebInspector.PaintProfilerCategory = function(name, title, color)
455 {
456 this.name = name;
457 this.title = title;
458 this.color = color;
459 }
460
461 /**
462 * @return {!Object.<string, !WebInspector.PaintProfilerCategory>}
463 */
464 WebInspector.PaintProfilerView._initLogItemCategories = function()
465 {
466 if (WebInspector.PaintProfilerView._logItemCategoriesMap)
467 return WebInspector.PaintProfilerView._logItemCategoriesMap;
468
469 var categories = WebInspector.PaintProfilerView.categories();
470
471 var logItemCategories = {};
472 logItemCategories["Clear"] = categories["misc"];
473 logItemCategories["DrawPaint"] = categories["misc"];
474 logItemCategories["DrawData"] = categories["misc"];
475 logItemCategories["SetMatrix"] = categories["misc"];
476 logItemCategories["PushCull"] = categories["misc"];
477 logItemCategories["PopCull"] = categories["misc"];
478 logItemCategories["Translate"] = categories["misc"];
479 logItemCategories["Scale"] = categories["misc"];
480 logItemCategories["Concat"] = categories["misc"];
481 logItemCategories["Restore"] = categories["misc"];
482 logItemCategories["SaveLayer"] = categories["misc"];
483 logItemCategories["Save"] = categories["misc"];
484 logItemCategories["BeginCommentGroup"] = categories["misc"];
485 logItemCategories["AddComment"] = categories["misc"];
486 logItemCategories["EndCommentGroup"] = categories["misc"];
487 logItemCategories["ClipRect"] = categories["misc"];
488 logItemCategories["ClipRRect"] = categories["misc"];
489 logItemCategories["ClipPath"] = categories["misc"];
490 logItemCategories["ClipRegion"] = categories["misc"];
491 logItemCategories["DrawPoints"] = categories["shapes"];
492 logItemCategories["DrawRect"] = categories["shapes"];
493 logItemCategories["DrawOval"] = categories["shapes"];
494 logItemCategories["DrawRRect"] = categories["shapes"];
495 logItemCategories["DrawPath"] = categories["shapes"];
496 logItemCategories["DrawVertices"] = categories["shapes"];
497 logItemCategories["DrawDRRect"] = categories["shapes"];
498 logItemCategories["DrawBitmap"] = categories["bitmap"];
499 logItemCategories["DrawBitmapRectToRect"] = categories["bitmap"];
500 logItemCategories["DrawBitmapMatrix"] = categories["bitmap"];
501 logItemCategories["DrawBitmapNine"] = categories["bitmap"];
502 logItemCategories["DrawSprite"] = categories["bitmap"];
503 logItemCategories["DrawPicture"] = categories["bitmap"];
504 logItemCategories["DrawText"] = categories["text"];
505 logItemCategories["DrawPosText"] = categories["text"];
506 logItemCategories["DrawPosTextH"] = categories["text"];
507 logItemCategories["DrawTextOnPath"] = categories["text"];
508
509 WebInspector.PaintProfilerView._logItemCategoriesMap = logItemCategories;
510 return logItemCategories;
511 }
512
513 /**
514 * @param {!Object} logItem
515 * @return {!WebInspector.PaintProfilerCategory}
516 */
517 WebInspector.PaintProfilerView._categoryForLogItem = function(logItem)
518 {
519 var method = logItem.method.toTitleCase();
520
521 var logItemCategories = WebInspector.PaintProfilerView._initLogItemCategorie s();
522 var result = logItemCategories[method];
523 if (!result) {
524 result = WebInspector.PaintProfilerView.categories()["misc"];
525 logItemCategories[method] = result;
526 }
527 return result;
528 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698