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

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: rebase 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 var tname = this.thread_.name || this.thread_.tid;
136 this.tracks_[0].heading = this.thread_.parent.pid + ': ' +
137 tname + ':';
138 this.tracks_[0].tooltip = 'pid: ' + this.thread_.parent.pid +
139 ', tid: ' + this.thread_.tid +
140 (this.thread_.name ? ', name: ' + this.thread_.name : '');
141 }
142 }
143 },
144
145 /**
146 * Picks a slice, if any, at a given location.
147 * @param {number} wX X location to search at, in worldspace.
148 * @param {number} wY Y location to search at, in offset space.
149 * offset space.
150 * @param {function():*} onHitCallback Callback to call with the slice,
151 * if one is found.
152 * @return {boolean} true if a slice was found, otherwise false.
153 */
154 pick: function(wX, wY, onHitCallback) {
155 for (var i = 0; i < this.tracks_.length; i++) {
156 var trackClientRect = this.tracks_[i].getBoundingClientRect();
157 if (wY >= trackClientRect.top && wY < trackClientRect.bottom)
158 return this.tracks_[i].pick(wX, onHitCallback);
159 }
160 return false;
161 },
162
163 /**
164 * Finds slices intersecting the given interval.
165 * @param {number} loWX Lower X bound of the interval to search, in
166 * worldspace.
167 * @param {number} hiWX Upper X bound of the interval to search, in
168 * worldspace.
169 * @param {number} loY Lower Y bound of the interval to search, in
170 * offset space.
171 * @param {number} hiY Upper Y bound of the interval to search, in
172 * offset space.
173 * @param {function():*} onHitCallback Function to call for each slice
174 * intersecting the interval.
175 */
176 pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) {
177 for (var i = 0; i < this.tracks_.length; i++) {
178 var trackClientRect = this.tracks_[i].getBoundingClientRect();
179 var a = Math.max(loY, trackClientRect.top);
180 var b = Math.min(hiY, trackClientRect.bottom);
181 if (a <= b)
182 this.tracks_[i].pickRange(loWX, hiWX, loY, hiY, onHitCallback);
183 }
184 }
185 };
186
187 /**
188 * Creates a new timeline track div element
189 * @constructor
190 * @extends {HTMLDivElement}
191 */
192 TimelineSliceTrack = cr.ui.define('div');
193
194 TimelineSliceTrack.prototype = {
195 __proto__: HTMLDivElement.prototype,
196
197 decorate: function() {
198 this.className = 'timeline-slice-track';
199 this.slices_ = null;
200
201 this.headingDiv_ = document.createElement('div');
202 this.headingDiv_.className = 'timeline-slice-track-title';
203 this.appendChild(this.headingDiv_);
204
205 this.canvasContainer_ = document.createElement('div');
206 this.canvasContainer_.className = 'timeline-slice-track-canvas-container';
207 this.appendChild(this.canvasContainer_);
208 this.canvas_ = document.createElement('canvas');
209 this.canvas_.className = 'timeline-slice-track-canvas';
210 this.canvasContainer_.appendChild(this.canvas_);
211
212 this.ctx_ = this.canvas_.getContext('2d');
213 },
214
215 set heading(text) {
216 this.headingDiv_.textContent = text;
217 },
218
219 set tooltip(text) {
220 this.headingDiv_.title = text;
221 },
222
223 set slices(slices) {
224 this.slices_ = slices;
225 this.invalidate();
226 },
227
228 set viewport(v) {
229 this.viewport_ = v;
230 this.invalidate();
231 },
232
233 invalidate: function() {
234 if (this.parentNode)
235 this.parentNode.invalidate();
236 },
237
238 get firstCanvas() {
239 return this.canvas_;
240 },
241
242 onResize: function() {
243 this.canvas_.width = this.canvasContainer_.clientWidth;
244 this.canvas_.height = this.canvasContainer_.clientHeight;
245 this.invalidate();
246 },
247
248 redraw: function() {
249 if (!this.viewport_)
250 return;
251 var ctx = this.ctx_;
252 var canvasW = this.canvas_.width;
253 var canvasH = this.canvas_.height;
254
255 ctx.clearRect(0, 0, canvasW, canvasH);
256
257 // culling...
258 var vp = this.viewport_;
259 var pixWidth = vp.xViewVectorToWorld(1);
260 var viewLWorld = vp.xViewToWorld(0);
261 var viewRWorld = vp.xViewToWorld(canvasW);
262
263 // Draw grid without a transform because the scale
264 // affects line width.
265 if (vp.gridEnabled) {
266 var x = vp.gridTimebase;
267 ctx.beginPath();
268 while (x < viewRWorld) {
269 if (x >= viewLWorld) {
270 // Do conversion to viewspace here rather than on
271 // x to avoid precision issues.
272 var vx = vp.xWorldToView(x);
273 ctx.moveTo(vx, 0);
274 ctx.lineTo(vx, canvasH);
275 }
276 x += vp.gridStep;
277 }
278 ctx.strokeStyle = 'rgba(255,0,0,0.25)';
279 ctx.stroke();
280 }
281
282 // begin rendering in world space
283 ctx.save();
284 vp.applyTransformToCanavs(ctx);
285
286 // tracks
287 var tr = new gpu.FastRectRenderer(ctx, viewLWorld, 2 * pixWidth,
288 2 * pixWidth, viewRWorld, pallette);
289 tr.setYandH(0, canvasH);
290 var slices = this.slices_;
291 for (var i = 0; i < slices.length; ++i) {
292 var slice = slices[i];
293 var x = slice.start;
294 // Less than 0.001 causes short events to disappear when zoomed in.
295 var w = Math.max(slice.duration, 0.001);
296 var colorId;
297 colorId = slice.selected ?
298 slice.colorId + selectedIdBoost :
299 slice.colorId;
300
301 if (w < pixWidth)
302 w = pixWidth;
303 if (slice.duration > 0) {
304 tr.fillRect(x, w, colorId);
305 } else {
306 // Instant: draw a triangle. If zoomed too far, collapse
307 // into the FastRectRenderer.
308 if (pixWidth > 0.001) {
309 tr.fillRect(x, pixWidth, colorId);
310 } else {
311 ctx.fillStyle = pallette[colorId];
312 ctx.beginPath();
313 ctx.moveTo(x - (4 * pixWidth), canvasH);
314 ctx.lineTo(x, 0);
315 ctx.lineTo(x + (4 * pixWidth), canvasH);
316 ctx.closePath();
317 ctx.fill();
318 }
319 }
320 }
321 tr.flush();
322 ctx.restore();
323
324 // labels
325 ctx.textAlign = 'center';
326 ctx.textBaseline = 'top';
327 ctx.font = '10px sans-serif';
328 ctx.strokeStyle = 'rgb(0,0,0)';
329 ctx.fillStyle = 'rgb(0,0,0)';
330 var quickDiscardThresshold = pixWidth * 20; // dont render until 20px wide
331 for (var i = 0; i < slices.length; ++i) {
332 var slice = slices[i];
333 if (slice.duration > quickDiscardThresshold) {
334 var title = slice.title;
335 if (slice.didNotFinish) {
336 title += ' (Did Not Finish)';
337 }
338 function labelWidth() {
339 return quickMeasureText(ctx, title) + 2;
340 }
341 function labelWidthWorld() {
342 return pixWidth * labelWidth();
343 }
344 var elided = false;
345 while (labelWidthWorld() > slice.duration) {
346 title = title.substring(0, title.length * 0.75);
347 elided = true;
348 }
349 if (elided && title.length > 3)
350 title = title.substring(0, title.length - 3) + '...';
351 var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration);
352 ctx.fillText(title, cX, 2.5, labelWidthWorld());
353 }
354 }
355 },
356
357 /**
358 * Picks a slice, if any, at a given location.
359 * @param {number} wX X location to search at, in worldspace.
360 * @param {number} wY Y location to search at, in offset space.
361 * offset space.
362 * @param {function():*} onHitCallback Callback to call with the slice,
363 * if one is found.
364 * @return {boolean} true if a slice was found, otherwise false.
365 */
366 pick: function(wX, wY, onHitCallback) {
367 var clientRect = this.getBoundingClientRect();
368 if (wY < clientRect.top || wY >= clientRect.bottom)
369 return false;
370 var x = gpu.findLowIndexInSortedIntervals(this.slices_,
371 function(x) { return x.start; },
372 function(x) { return x.duration; },
373 wX);
374 if (x >= 0 && x < this.slices_.length) {
375 onHitCallback('slice', this, this.slices_[x]);
376 return true;
377 }
378 return false;
379 },
380
381 /**
382 * Finds slices intersecting the given interval.
383 * @param {number} loWX Lower X bound of the interval to search, in
384 * worldspace.
385 * @param {number} hiWX Upper X bound of the interval to search, in
386 * worldspace.
387 * @param {number} loY Lower Y bound of the interval to search, in
388 * offset space.
389 * @param {number} hiY Upper Y bound of the interval to search, in
390 * offset space.
391 * @param {function():*} onHitCallback Function to call for each slice
392 * intersecting the interval.
393 */
394 pickRange: function(loWX, hiWX, loY, hiY, onHitCallback) {
395 var clientRect = this.getBoundingClientRect();
396 var a = Math.max(loY, clientRect.top);
397 var b = Math.min(hiY, clientRect.bottom);
398 if (a > b)
399 return;
400
401 var that = this;
402 function onPickHit(slice) {
403 onHitCallback('slice', that, slice);
404 }
405 gpu.iterateOverIntersectingIntervals(this.slices_,
406 function(x) { return x.start; },
407 function(x) { return x.duration; },
408 loWX, hiWX,
409 onPickHit);
410 },
411
412 /**
413 * Find the index for the given slice.
414 * @return {index} Index of the given slice, or undefined.
415 * @private
416 */
417 indexOfSlice_: function(slice) {
418 var index = gpu.findLowIndexInSortedArray(this.slices_,
419 function(x) { return x.start; },
420 slice.start);
421 while (index < this.slices_.length &&
422 slice.start == this.slices_[index].start &&
423 slice.colorId != this.slices_[index].colorId) {
424 index++;
425 }
426 return index < this.slices_.length ? index : undefined;
427 },
428
429 /**
430 * Return the next slice, if any, after the given slice.
431 * @param {slice} The previous slice.
432 * @return {slice} The next slice, or undefined.
433 * @private
434 */
435 pickNext: function(slice) {
436 var index = this.indexOfSlice_(slice);
437 if (index != undefined) {
438 if (index < this.slices_.length - 1)
439 index++;
440 else
441 index = undefined;
442 }
443 return index != undefined ? this.slices_[index] : undefined;
444 },
445
446 /**
447 * Return the previous slice, if any, before the given slice.
448 * @param {slice} A slice.
449 * @return {slice} The previous slice, or undefined.
450 */
451 pickPrevious: function(slice) {
452 var index = this.indexOfSlice_(slice);
453 if (index == 0)
454 return undefined;
455 else if ((index != undefined) && (index > 0))
456 index--;
457 return index != undefined ? this.slices_[index] : undefined;
458 },
459
460 };
461
462 return {
463 TimelineSliceTrack: TimelineSliceTrack,
464 TimelineThreadTrack: TimelineThreadTrack
465 };
466 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/gpu_internals/timeline_model.js ('k') | chrome/browser/resources/gpu_internals/timeline_view.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698