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

Side by Side Diff: chrome/browser/resources/gpu_internals/timeline_track.js

Issue 7555005: Moving the contents of chrome://gpu Profiling to chrome://tracing. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remove tabs Created 9 years, 4 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5
6 /**
7 * @fileoverview Renders an array of slices into the provided div,
8 * using a child canvas element. Uses a FastRectRenderer to draw only
9 * the visible slices.
10 */
11 cr.define('gpu', function() {
12
13 const palletteBase = [
14 {r: 138, g: 113, b: 152},
15 {r: 175, g: 112, b: 133},
16 {r: 127, g: 135, b: 225},
17 {r: 93, g: 81, b: 137},
18 {r: 116, g: 143, b: 119},
19 {r: 178, g: 214, b: 122},
20 {r: 87, g: 109, b: 147},
21 {r: 119, g: 155, b: 95},
22 {r: 114, g: 180, b: 160},
23 {r: 132, g: 85, b: 103},
24 {r: 157, g: 210, b: 150},
25 {r: 148, g: 94, b: 86},
26 {r: 164, g: 108, b: 138},
27 {r: 139, g: 191, b: 150},
28 {r: 110, g: 99, b: 145},
29 {r: 80, g: 129, b: 109},
30 {r: 125, g: 140, b: 149},
31 {r: 93, g: 124, b: 132},
32 {r: 140, g: 85, b: 140},
33 {r: 104, g: 163, b: 162},
34 {r: 132, g: 141, b: 178},
35 {r: 131, g: 105, b: 147},
36 {r: 135, g: 183, b: 98},
37 {r: 152, g: 134, b: 177},
38 {r: 141, g: 188, b: 141},
39 {r: 133, g: 160, b: 210},
40 {r: 126, g: 186, b: 148},
41 {r: 112, g: 198, b: 205},
42 {r: 180, g: 122, b: 195},
43 {r: 203, g: 144, b: 152}];
44
45 function brighten(c) {
46 return {r: Math.min(255, c.r + Math.floor(c.r * 0.45)),
47 g: Math.min(255, c.g + Math.floor(c.g * 0.45)),
48 b: Math.min(255, c.b + Math.floor(c.b * 0.45))};
49 }
50 function colorToString(c) {
51 return 'rgb(' + c.r + ',' + c.g + ',' + c.b + ')';
52 }
53
54 const selectedIdBoost = palletteBase.length;
55
56 const pallette = palletteBase.concat(palletteBase.map(brighten)).
57 map(colorToString);
58
59 var textWidthMap = { };
60 function quickMeasureText(ctx, text) {
61 var w = textWidthMap[text];
62 if (!w) {
63 w = ctx.measureText(text).width;
64 textWidthMap[text] = w;
65 }
66 return w;
67 }
68
69 function addTrack(thisTrack, slices) {
70 var track = new TimelineSliceTrack();
71
72 track.heading = '';
73 track.slices = slices;
74 track.viewport = thisTrack.viewport_;
75
76 thisTrack.tracks_.push(track);
77 thisTrack.appendChild(track);
78 }
79
80 /**
81 * Generic base class for timeline tracks
82 */
83 TimelineThreadTrack = cr.ui.define('div');
84 TimelineThreadTrack.prototype = {
85 __proto__: HTMLDivElement.prototype,
86
87 decorate: function() {
88 this.className = 'timeline-thread-track';
89 },
90
91 set thread(thread) {
92 this.thread_ = thread;
93 this.updateChildTracks_();
94 },
95
96 set viewport(v) {
97 this.viewport_ = v;
98 for (var i = 0; i < this.tracks_.length; i++)
99 this.tracks_[i].viewport = v;
100 this.invalidate();
101 },
102
103 invalidate: function() {
104 if (this.parentNode)
105 this.parentNode.invalidate();
106 },
107
108 onResize: function() {
109 for (var i = 0; i < this.tracks_.length; i++)
110 this.tracks_[i].onResize();
111 },
112
113 get firstCanvas() {
114 if (this.tracks_.length)
115 return this.tracks_[0].firstCanvas;
116 return undefined;
117 },
118
119 redraw: function() {
120 for (var i = 0; i < this.tracks_.length; i++)
121 this.tracks_[i].redraw();
122 },
123
124 updateChildTracks_: function() {
125 this.textContent = '';
126 this.tracks_ = [];
127 if (this.thread_) {
128 for (var srI = 0; srI < this.thread_.nonNestedSubRows.length; ++srI) {
129 addTrack(this, this.thread_.nonNestedSubRows[srI]);
130 }
131 for (var srI = 0; srI < this.thread_.subRows.length; ++srI) {
132 addTrack(this, this.thread_.subRows[srI]);
133 }
134 if (this.tracks_.length > 0) {
135 this.tracks_[0].heading = this.thread_.parent.pid + ': ' +
136 this.thread_.tid + ': ';
137 }
138 }
139 },
140
141 /**
142 * Picks a slice, if any, at a given location.
143 * @param {number} wX X location to search at, in worldspace.
144 * @param {number} wY Y location to search at, in offset space.
145 * offset space.
146 * @param {function():*} onHitCallback Callback to call with the slice,
147 * if one is found.
148 * @return {boolean} true if a slice was found, otherwise false.
149 */
150 pick: function(wX, wY, onHitCallback) {
151 for (var i = 0; i < this.tracks_.length; i++) {
152 var trackClientRect = this.tracks_[i].getBoundingClientRect();
153 if (wY >= trackClientRect.top && wY < trackClientRect.bottom)
154 return this.tracks_[i].pick(wX, onHitCallback);
155 }
156 return false;
157 },
158
159 /**
160 * Finds slices intersecting the given interval.
161 * @param {number} loWX Lower X bound of the interval to search, in
162 * worldspace.
163 * @param {number} hiWX Upper X bound of the interval to search, in
164 * worldspace.
165 * @param {number} loY Lower Y bound of the interval to search, in
166 * offset space.
167 * @param {number} hiY Upper Y bound of the interval to search, in
168 * offset space.
169 * @param {function():*} onHitCallback Function to call for each slice
170 * intersecting the interval.
171 */
172 pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) {
173 for (var i = 0; i < this.tracks_.length; i++) {
174 var trackClientRect = this.tracks_[i].getBoundingClientRect();
175 var a = Math.max(loY, trackClientRect.top);
176 var b = Math.min(hiY, trackClientRect.bottom);
177 if (a <= b)
178 this.tracks_[i].pickRange(loWX, hiWX, loY, hiY, onHitCallback);
179 }
180 }
181 };
182
183 /**
184 * Creates a new timeline track div element
185 * @constructor
186 * @extends {HTMLDivElement}
187 */
188 TimelineSliceTrack = cr.ui.define('div');
189
190 TimelineSliceTrack.prototype = {
191 __proto__: HTMLDivElement.prototype,
192
193 decorate: function() {
194 this.className = 'timeline-slice-track';
195 this.slices_ = null;
196
197 this.titleDiv_ = document.createElement('div');
198 this.titleDiv_.className = 'timeline-slice-track-title';
199 this.appendChild(this.titleDiv_);
200
201 this.canvasContainer_ = document.createElement('div');
202 this.canvasContainer_.className = 'timeline-slice-track-canvas-container';
203 this.appendChild(this.canvasContainer_);
204 this.canvas_ = document.createElement('canvas');
205 this.canvas_.className = 'timeline-slice-track-canvas';
206 this.canvasContainer_.appendChild(this.canvas_);
207
208 this.ctx_ = this.canvas_.getContext('2d');
209 },
210
211 set heading(text) {
212 this.titleDiv_.textContent = text;
213 },
214
215 set slices(slices) {
216 this.slices_ = slices;
217 this.invalidate();
218 },
219
220 set viewport(v) {
221 this.viewport_ = v;
222 this.invalidate();
223 },
224
225 invalidate: function() {
226 if (this.parentNode)
227 this.parentNode.invalidate();
228 },
229
230 get firstCanvas() {
231 return this.canvas_;
232 },
233
234 onResize: function() {
235 this.canvas_.width = this.canvasContainer_.clientWidth;
236 this.canvas_.height = this.canvasContainer_.clientHeight;
237 this.invalidate();
238 },
239
240 redraw: function() {
241 if (!this.viewport_)
242 return;
243 var ctx = this.ctx_;
244 var canvasW = this.canvas_.width;
245 var canvasH = this.canvas_.height;
246
247 ctx.clearRect(0, 0, canvasW, canvasH);
248
249 // culling...
250 var vp = this.viewport_;
251 var pixWidth = vp.xViewVectorToWorld(1);
252 var viewLWorld = vp.xViewToWorld(0);
253 var viewRWorld = vp.xViewToWorld(canvasW);
254
255 // Draw grid without a transform because the scale
256 // affects line width.
257 if (vp.gridEnabled) {
258 var x = vp.gridTimebase;
259 ctx.beginPath();
260 while (x < viewRWorld) {
261 if (x >= viewLWorld) {
262 // Do conversion to viewspace here rather than on
263 // x to avoid precision issues.
264 var vx = vp.xWorldToView(x);
265 ctx.moveTo(vx, 0);
266 ctx.lineTo(vx, canvasH);
267 }
268 x += vp.gridStep;
269 }
270 ctx.strokeStyle = 'rgba(255,0,0,0.25)';
271 ctx.stroke();
272 }
273
274 // begin rendering in world space
275 ctx.save();
276 vp.applyTransformToCanavs(ctx);
277
278 // tracks
279 var tr = new gpu.FastRectRenderer(ctx, viewLWorld, 2 * pixWidth,
280 2 * pixWidth, viewRWorld, pallette);
281 tr.setYandH(0, canvasH);
282 var slices = this.slices_;
283 for (var i = 0; i < slices.length; ++i) {
284 var slice = slices[i];
285 var x = slice.start;
286 // Less than 0.001 causes short events to disappear when zoomed in.
287 var w = Math.max(slice.duration, 0.001);
288 var colorId;
289 colorId = slice.selected ?
290 slice.colorId + selectedIdBoost :
291 slice.colorId;
292
293 if (w < pixWidth)
294 w = pixWidth;
295 if (slice.duration > 0) {
296 tr.fillRect(x, w, colorId);
297 } else {
298 // Instant: draw a triangle. If zoomed too far, collapse
299 // into the FastRectRenderer.
300 if (pixWidth > 0.001) {
301 tr.fillRect(x, pixWidth, colorId);
302 } else {
303 ctx.fillStyle = pallette[colorId];
304 ctx.beginPath();
305 ctx.moveTo(x - (4 * pixWidth), canvasH);
306 ctx.lineTo(x, 0);
307 ctx.lineTo(x + (4 * pixWidth), canvasH);
308 ctx.closePath();
309 ctx.fill();
310 }
311 }
312 }
313 tr.flush();
314 ctx.restore();
315
316 // labels
317 ctx.textAlign = 'center';
318 ctx.textBaseline = 'top';
319 ctx.font = '10px sans-serif';
320 ctx.strokeStyle = 'rgb(0,0,0)';
321 ctx.fillStyle = 'rgb(0,0,0)';
322 var quickDiscardThresshold = pixWidth * 20; // dont render until 20px wide
323 for (var i = 0; i < slices.length; ++i) {
324 var slice = slices[i];
325 if (slice.duration > quickDiscardThresshold) {
326 var title = slice.title;
327 if (slice.didNotFinish) {
328 title += " (Did Not Finish)";
329 }
330 function labelWidth() {
331 return quickMeasureText(ctx, title) + 2;
332 }
333 function labelWidthWorld() {
334 return pixWidth * labelWidth();
335 }
336 var elided = false;
337 while (labelWidthWorld() > slice.duration) {
338 title = title.substring(0, title.length * 0.75);
339 elided = true;
340 }
341 if (elided && title.length > 3)
342 title = title.substring(0, title.length - 3) + '...';
343 var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration);
344 ctx.fillText(title, cX, 2.5, labelWidthWorld());
345 }
346 }
347 },
348
349 /**
350 * Picks a slice, if any, at a given location.
351 * @param {number} wX X location to search at, in worldspace.
352 * @param {number} wY Y location to search at, in offset space.
353 * offset space.
354 * @param {function():*} onHitCallback Callback to call with the slice,
355 * if one is found.
356 * @return {boolean} true if a slice was found, otherwise false.
357 */
358 pick: function(wX, wY, onHitCallback) {
359 var clientRect = this.getBoundingClientRect();
360 if (wY < clientRect.top || wY >= clientRect.bottom)
361 return false;
362 var x = gpu.findLowIndexInSortedIntervals(this.slices_,
363 function(x) { return x.start; },
364 function(x) { return x.duration; },
365 wX);
366 if (x >= 0 && x < this.slices_.length) {
367 onHitCallback('slice', this, this.slices_[x]);
368 return true;
369 }
370 return false;
371 },
372
373 /**
374 * Finds slices intersecting the given interval.
375 * @param {number} loWX Lower X bound of the interval to search, in
376 * worldspace.
377 * @param {number} hiWX Upper X bound of the interval to search, in
378 * worldspace.
379 * @param {number} loY Lower Y bound of the interval to search, in
380 * offset space.
381 * @param {number} hiY Upper Y bound of the interval to search, in
382 * offset space.
383 * @param {function():*} onHitCallback Function to call for each slice
384 * intersecting the interval.
385 */
386 pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) {
387 var clientRect = this.getBoundingClientRect();
388 var a = Math.max(loY, clientRect.top);
389 var b = Math.min(hiY, clientRect.bottom);
390 if (a > b)
391 return;
392
393 var that = this;
394 function onPickHit(slice) {
395 onHitCallback('slice', that, slice);
396 }
397 gpu.iterateOverIntersectingIntervals(this.slices_,
398 function(x) { return x.start; },
399 function(x) { return x.duration; },
400 loWX, hiWX,
401 onPickHit);
402 },
403
404 /**
405 * Find the index for the given slice.
406 * @return {index} Index of the given slice, or undefined.
407 * @private
408 */
409 indexOfSlice_: function(slice) {
410 var index = gpu.findLowIndexInSortedArray(this.slices_,
411 function(x) { return x.start; },
412 slice.start);
413 while (index < this.slices_.length &&
414 slice.start == this.slices_[index].start &&
415 slice.colorId != this.slices_[index].colorId) {
416 index++;
417 }
418 return index < this.slices_.length ? index : undefined;
419 },
420
421 /**
422 * Return the next slice, if any, after the given slice.
423 * @param {slice} The previous slice.
424 * @return {slice} The next slice, or undefined.
425 * @private
426 */
427 pickNext: function(slice) {
428 var index = this.indexOfSlice_(slice);
429 if (index != undefined) {
430 if (index < this.slices_.length - 1)
431 index++;
432 else
433 index = undefined;
434 }
435 return index != undefined ? this.slices_[index] : undefined;
436 },
437
438 /**
439 * Return the previous slice, if any, before the given slice.
440 * @param {slice} A slice.
441 * @return {slice} The previous slice, or undefined.
442 */
443 pickPrevious: function(slice) {
444 var index = this.indexOfSlice_(slice);
445 if (index == 0)
446 return undefined;
447 else if ((index != undefined) && (index > 0))
448 index--;
449 return index != undefined ? this.slices_[index] : undefined;
450 },
451
452 };
453
454 return {
455 TimelineSliceTrack: TimelineSliceTrack,
456 TimelineThreadTrack: TimelineThreadTrack
457 };
458 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698