3 Copyright (c) 2013 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
8 <link rel="import" href="/cc/picture.html">
9 <link rel="import" href="/cc/render_pass.html">
10 <link rel="import" href="/cc/tile.html">
11 <link rel="import" href="/cc/debug_colors.html">
12 <link rel="import" href="/cc/util.html">
13 <link rel="import" href="/tvcm/color.html">
14 <link rel="import" href="/tvcm/properties.html">
15 <link rel="import" href="/tvcm/raf.html">
16 <link rel="import" href="/tvcm/quad.html">
17 <link rel="import" href="/tvcm/range.html">
18 <link rel="import" href="/tvcm/ui/quad_stack_view.html">
19 <link rel="import" href="/tvcm/ui/info_bar.html">
22 layer-tree-quad-stack-view {
26 layer-tree-quad-stack-view > top-controls {
27 -webkit-flex: 0 0 auto;
28 background-image: -webkit-gradient(linear,
32 border-bottom: 1px solid #8e8e8e;
33 border-top: 1px solid white;
42 layer-tree-quad-stack-view > top-controls input[type='checkbox'] {
46 layer-tree-quad-stack-view > .what-rasterized {
49 text-decoration: underline;
55 layer-tree-quad-stack-view > #input-event {
56 content: url('../images/input-event.png');
62 <template id='layer-tree-quad-stack-view-template'>
63 <img id='input-event'/>
70 * @fileoverview Graphical view of LayerTreeImpl, with controls for
71 * type of layer content shown and info bar for content-loading warnings.
73 tvcm.exportTo('cc', function() {
75 var THIS_DOC = document.currentScript.ownerDocument;
76 var TILE_HEATMAP_TYPE = {};
77 TILE_HEATMAP_TYPE.NONE = 'none';
78 TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY = 'scheduledPriority';
79 TILE_HEATMAP_TYPE.DISTANCE_TO_VISIBLE = 'distanceToVisible';
80 TILE_HEATMAP_TYPE.USING_GPU_MEMORY = 'usingGpuMemory';
82 function createTileRectsSelectorBaseOptions() {
83 return [{label: 'None', value: 'none'},
84 {label: 'Coverage Rects', value: 'coverage'}];
87 var bytesToRoundedMegabytes = cc.bytesToRoundedMegabytes;
93 var LayerTreeQuadStackView = tvcm.ui.define('layer-tree-quad-stack-view');
95 LayerTreeQuadStackView.prototype = {
96 __proto__: HTMLDivElement.prototype,
98 decorate: function() {
99 this.isRenderPassQuads_ = false;
100 this.pictureAsImageData_ = {}; // Maps picture.guid to PictureAsImageData.
102 this.controls_ = document.createElement('top-controls');
103 this.infoBar_ = new tvcm.ui.InfoBar();
104 this.quadStackView_ = new tvcm.ui.QuadStackView();
105 this.quadStackView_.addEventListener(
106 'selectionchange', this.onQuadStackViewSelectionChange_.bind(this));
107 this.extraHighlightsByLayerId_ = undefined;
108 this.inputEventImageData_ = undefined;
110 var m = tvcm.ui.MOUSE_SELECTOR_MODE;
111 var mms = this.quadStackView_.mouseModeSelector;
112 mms.settingsKey = 'cc.layerTreeQuadStackView.mouseModeSelector';
113 mms.setKeyCodeForMode(m.SELECTION, 'Z'.charCodeAt(0));
114 mms.setKeyCodeForMode(m.PANSCAN, 'X'.charCodeAt(0));
115 mms.setKeyCodeForMode(m.ZOOM, 'C'.charCodeAt(0));
116 mms.setKeyCodeForMode(m.ROTATE, 'V'.charCodeAt(0));
118 var node = tvcm.instantiateTemplate(
119 '#layer-tree-quad-stack-view-template', THIS_DOC);
120 this.appendChild(node);
121 this.appendChild(this.controls_);
122 this.appendChild(this.infoBar_);
123 this.appendChild(this.quadStackView_);
125 this.tileRectsSelector_ = tvcm.ui.createSelector(
126 this, 'howToShowTiles',
127 'layerView.howToShowTiles', 'none',
128 createTileRectsSelectorBaseOptions());
129 this.controls_.appendChild(this.tileRectsSelector_);
131 var tileHeatmapText = tvcm.ui.createSpan({
132 textContent: 'Tile heatmap:'
134 this.controls_.appendChild(tileHeatmapText);
136 var tileHeatmapSelector = tvcm.ui.createSelector(
137 this, 'tileHeatmapType',
138 'layerView.tileHeatmapType', TILE_HEATMAP_TYPE.NONE,
140 value: TILE_HEATMAP_TYPE.NONE},
141 {label: 'Scheduled Priority',
142 value: TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY},
143 {label: 'Distance to Visible',
144 value: TILE_HEATMAP_TYPE.DISTANCE_TO_VISIBLE},
145 {label: 'Is using GPU memory',
146 value: TILE_HEATMAP_TYPE.USING_GPU_MEMORY}
148 this.controls_.appendChild(tileHeatmapSelector);
150 var showOtherLayersCheckbox = tvcm.ui.createCheckBox(
151 this, 'showOtherLayers',
152 'layerView.showOtherLayers', true,
153 'Other layers/passes');
154 showOtherLayersCheckbox.title =
155 'When checked, show all layers, selected or not.';
156 this.controls_.appendChild(showOtherLayersCheckbox);
158 var showInvalidationsCheckbox = tvcm.ui.createCheckBox(
159 this, 'showInvalidations',
160 'layerView.showInvalidations', true,
162 showInvalidationsCheckbox.title =
163 'When checked, compositing invalidations are highlighted in red';
164 this.controls_.appendChild(showInvalidationsCheckbox);
166 var showUnrecordedRegionCheckbox = tvcm.ui.createCheckBox(
167 this, 'showUnrecordedRegion',
168 'layerView.showUnrecordedRegion', true,
170 showUnrecordedRegionCheckbox.title =
171 'When checked, unrecorded areas are highlighted in yellow';
172 this.controls_.appendChild(showUnrecordedRegionCheckbox);
174 var showBottlenecksCheckbox = tvcm.ui.createCheckBox(
175 this, 'showBottlenecks',
176 'layerView.showBottlenecks', true,
178 showBottlenecksCheckbox.title =
179 'When checked, scroll bottlenecks are highlighted';
180 this.controls_.appendChild(showBottlenecksCheckbox);
182 var showLayoutRectsCheckbox = tvcm.ui.createCheckBox(
183 this, 'showLayoutRects',
184 'layerView.showLayoutRects', false,
186 showLayoutRectsCheckbox.title =
187 'When checked, shows rects for regions where layout happened';
188 this.controls_.appendChild(showLayoutRectsCheckbox);
190 var showContentsCheckbox = tvcm.ui.createCheckBox(
191 this, 'showContents',
192 'layerView.showContents', true,
194 showContentsCheckbox.title =
195 'When checked, show the rendered contents inside the layer outlines';
196 this.controls_.appendChild(showContentsCheckbox);
198 var showAnimationBoundsCheckbox = tvcm.ui.createCheckBox(
199 this, 'showAnimationBounds',
200 'layerView.showAnimationBounds', false,
202 showAnimationBoundsCheckbox.title = 'When checked, show a border around' +
203 ' a layer showing the extent of its animation.';
204 this.controls_.appendChild(showAnimationBoundsCheckbox);
206 var showInputEventsCheckbox = tvcm.ui.createCheckBox(
207 this, 'showInputEvents',
208 'layerView.showInputEvents', true,
210 showInputEventsCheckbox.title = 'When checked, input events are ' +
211 'displayed as circles.';
212 this.controls_.appendChild(showInputEventsCheckbox);
214 this.whatRasterizedLink_ = document.createElement('a');
215 this.whatRasterizedLink_.classList.add('what-rasterized');
216 this.whatRasterizedLink_.textContent = 'What rasterized?';
217 this.whatRasterizedLink_.addEventListener(
218 'click', this.onWhatRasterizedLinkClicked_.bind(this));
219 this.appendChild(this.whatRasterizedLink_);
222 get layerTreeImpl() {
223 return this.layerTreeImpl_;
226 set isRenderPassQuads(newValue) {
227 this.isRenderPassQuads_ = newValue;
230 set layerTreeImpl(layerTreeImpl) {
231 if (this.layerTreeImpl_ === layerTreeImpl)
234 // FIXME(pdr): We may want to clear pictureAsImageData_ here to save
235 // memory at the cost of performance. Note that
236 // pictureAsImageData_ will be cleared when this is
237 // destructed, but this view might live for several
239 this.layerTreeImpl_ = layerTreeImpl;
240 this.selection = undefined;
243 get extraHighlightsByLayerId() {
244 return this.extraHighlightsByLayerId_;
247 set extraHighlightsByLayerId(extraHighlightsByLayerId) {
248 this.extraHighlightsByLayerId_ = extraHighlightsByLayerId;
249 this.scheduleUpdateContents_();
252 get showOtherLayers() {
253 return this.showOtherLayers_;
256 set showOtherLayers(show) {
257 this.showOtherLayers_ = show;
258 this.updateContents_();
261 get showAnimationBounds() {
262 return this.showAnimationBounds_;
265 set showAnimationBounds(show) {
266 this.showAnimationBounds_ = show;
267 this.updateContents_();
270 get showInputEvents() {
271 return this.showInputEvents_;
274 set showInputEvents(show) {
275 this.showInputEvents_ = show;
276 this.updateContents_();
280 return this.showContents_;
283 set showContents(show) {
284 this.showContents_ = show;
285 this.updateContents_();
288 get showInvalidations() {
289 return this.showInvalidations_;
292 set showInvalidations(show) {
293 this.showInvalidations_ = show;
294 this.updateContents_();
297 get showUnrecordedRegion() {
298 return this.showUnrecordedRegion_;
301 set showUnrecordedRegion(show) {
302 this.showUnrecordedRegion_ = show;
303 this.updateContents_();
306 get showBottlenecks() {
307 return this.showBottlenecks_;
310 set showBottlenecks(show) {
311 this.showBottlenecks_ = show;
312 this.updateContents_();
315 get showLayoutRects() {
316 return this.showLayoutRects_;
319 set showLayoutRects(show) {
320 this.showLayoutRects_ = show;
321 this.updateContents_();
324 get howToShowTiles() {
325 return this.howToShowTiles_;
328 set howToShowTiles(val) {
329 // Make sure val is something we expect.
332 (val === 'coverage') ||
333 !isNaN(parseFloat(val)));
335 this.howToShowTiles_ = val;
336 this.updateContents_();
339 get tileHeatmapType() {
340 return this.tileHeatmapType_;
343 set tileHeatmapType(val) {
344 this.tileHeatmapType_ = val;
345 this.updateContents_();
349 return this.selection_;
352 set selection(selection) {
353 if (this.selection === selection)
355 this.selection_ = selection;
356 tvcm.dispatchSimpleEvent(this, 'selection-change');
357 this.updateContents_();
360 regenerateContent: function() {
361 this.updateTilesSelector_();
362 this.updateContents_();
365 loadDataForImageElement_: function(image, callback) {
366 var imageContent = window.getComputedStyle(image).content;
367 image.src = imageContent.replace(/url\((.*)\)/, '$1');
368 image.onload = function() {
369 var canvas = document.createElement('canvas');
370 var ctx = canvas.getContext('2d');
371 canvas.width = image.width;
372 canvas.height = image.height;
373 ctx.drawImage(image, 0, 0);
374 var imageData = ctx.getImageData(
375 0, 0, canvas.width, canvas.height);
380 onQuadStackViewSelectionChange_: function(e) {
381 var selectableQuads = e.quads.filter(function(q) {
382 return q.selectionToSetIfClicked !== undefined;
384 if (selectableQuads.length == 0) {
385 this.selection = undefined;
389 // Sort the quads low to high on stackingGroupId.
390 selectableQuads.sort(function(x, y) {
391 var z = x.stackingGroupId - y.stackingGroupId;
394 return x.selectionToSetIfClicked.specicifity -
395 y.selectionToSetIfClicked.specicifity;
398 // TODO(nduca): Support selecting N things at once.
399 var quadToSelect = selectableQuads[selectableQuads.length - 1];
400 this.selection = quadToSelect.selectionToSetIfClicked;
403 scheduleUpdateContents_: function() {
404 if (this.updateContentsPending_)
406 this.updateContentsPending_ = true;
407 tvcm.requestAnimationFrameInThisFrameIfPossible(
408 this.updateContents_, this);
411 updateContents_: function() {
412 if (!this.layerTreeImpl_) {
413 this.quadStackView_.headerText = 'No tree';
414 this.quadStackView_.quads = [];
419 var status = this.computePictureLoadingStatus_();
420 if (!status.picturesComplete)
423 var lthi = this.layerTreeImpl_.layerTreeHostImpl;
424 var lthiInstance = lthi.objectInstance;
425 var worldViewportRect = tvcm.Rect.fromXYWH(
427 lthi.deviceViewportSize.width, lthi.deviceViewportSize.height);
428 this.quadStackView_.deviceRect = worldViewportRect;
429 if (this.isRenderPassQuads_)
430 this.quadStackView_.quads = this.generateRenderPassQuads();
432 this.quadStackView_.quads = this.generateLayerQuads();
434 this.updateWhatRasterizedLinkState_();
437 if (lthi.tilesHaveGpuMemoryUsageInfo) {
438 var thisTreeUsageInBytes = this.layerTreeImpl_.gpuMemoryUsageInBytes;
439 var otherTreeUsageInBytes = lthi.gpuMemoryUsageInBytes -
440 thisTreeUsageInBytes;
441 message += bytesToRoundedMegabytes(thisTreeUsageInBytes) +
443 if (otherTreeUsageInBytes) {
445 bytesToRoundedMegabytes(otherTreeUsageInBytes) +
446 'MB on the other tree';
449 if (this.layerTreeImpl_) {
450 var thisTreeUsageInBytes = this.layerTreeImpl_.gpuMemoryUsageInBytes;
451 message += bytesToRoundedMegabytes(thisTreeUsageInBytes) +
454 if (this.layerTreeImpl_.otherTree) {
455 // Older Chromes don't report enough data to know how much memory
456 // is being used across both trees. We know the memory consumed by
457 // each tree, but there is resource sharing *between the trees* so
458 // we can't simply sum up the per-tree costs. We need either the total
459 // plus one tree, to guess the unique on the other tree, etc. Newer
460 // chromes report memory per tile, which allows LTHI to compute the
461 // total tile memory usage, letting us figure things out properly.
462 message += ', ???MB on other tree. ';
467 if (lthi.args.tileManagerBasicState) {
468 var tmgs = lthi.args.tileManagerBasicState.globalState;
469 message += ' (softMax=' +
470 bytesToRoundedMegabytes(tmgs.softMemoryLimitInBytes) +
472 bytesToRoundedMegabytes(tmgs.hardMemoryLimitInBytes) + 'MB, ' +
473 tmgs.memoryLimitPolicy + ')';
476 // Old Chromes do not have a globalState on the LTHI dump.
477 // But they do issue a DidManage event wiht the globalstate. Find that
478 // event so that we show some global state.
479 var thread = lthi.snapshottedOnThread;
480 var didManageTilesSlices = thread.sliceGroup.slices.filter(function(s) {
481 if (s.category !== 'cc')
483 if (s.title !== 'DidManage')
489 didManageTilesSlices.sort(function(x, y) {
490 return x.end - y.end;
492 if (didManageTilesSlices.length > 0) {
493 var newest = didManageTilesSlices[didManageTilesSlices.length - 1];
494 var tmgs = newest.args.state.global_state;
495 message += ' (softMax=' +
496 bytesToRoundedMegabytes(tmgs.soft_memory_limit_in_bytes) +
498 bytesToRoundedMegabytes(tmgs.hard_memory_limit_in_bytes) + 'MB, ' +
499 tmgs.memory_limit_policy + ')';
503 if (this.layerTreeImpl_.otherTree)
504 message += ' (Another tree exists)';
508 this.quadStackView_.headerText = message;
510 this.quadStackView_.headerText = undefined;
512 this.updateInfoBar_(status.messages);
515 updateTilesSelector_: function() {
516 var data = createTileRectsSelectorBaseOptions();
518 if (this.layerTreeImpl_) {
519 // First get all of the scales information from LTHI.
520 var lthi = this.layerTreeImpl_.layerTreeHostImpl;
521 var scaleNames = lthi.getContentsScaleNames();
522 for (var scale in scaleNames) {
524 label: 'Scale ' + scale + ' (' + scaleNames[scale] + ')',
530 // Then create a new selector and replace the old one.
531 var new_selector = tvcm.ui.createSelector(
532 this, 'howToShowTiles',
533 'layerView.howToShowTiles', 'none',
535 this.controls_.replaceChild(new_selector, this.tileRectsSelector_);
536 this.tileRectsSelector_ = new_selector;
539 computePictureLoadingStatus_: function() {
540 // Figure out if we can draw the quads yet. While we're at it, figure out
541 // if we have any warnings we need to show.
542 var layers = this.layers;
545 picturesComplete: true
547 if (this.showContents) {
548 var hasPendingRasterizeImage = false;
549 var firstPictureError = undefined;
550 var hasMissingLayerRect = false;
551 var hasUnresolvedPictureRef = false;
552 for (var i = 0; i < layers.length; i++) {
553 var layer = layers[i];
554 for (var ir = 0; ir < layer.pictures.length; ++ir) {
555 var picture = layer.pictures[ir];
558 hasUnresolvedPictureRef = true;
561 if (!picture.layerRect) {
562 hasMissingLayerRect = true;
566 var pictureAsImageData = this.pictureAsImageData_[picture.guid];
567 if (!pictureAsImageData) {
568 hasPendingRasterizeImage = true;
569 this.pictureAsImageData_[picture.guid] =
570 cc.PictureAsImageData.Pending(this);
572 {stopIndex: undefined},
573 function(pictureImageData) {
574 var picture_ = pictureImageData.picture;
575 this.pictureAsImageData_[picture_.guid] = pictureImageData;
576 this.scheduleUpdateContents_();
580 if (pictureAsImageData.isPending()) {
581 hasPendingRasterizeImage = true;
584 if (pictureAsImageData.error) {
585 if (!firstPictureError)
586 firstPictureError = pictureAsImageData.error;
591 if (hasPendingRasterizeImage) {
592 status.picturesComplete = false;
594 if (hasUnresolvedPictureRef) {
595 status.messages.push({
596 header: 'Missing picture',
597 details: 'Your trace didnt have pictures for every layer. ' +
598 'Old chrome versions had this problem'});
600 if (hasMissingLayerRect) {
601 status.messages.push({
602 header: 'Missing layer rect',
603 details: 'Your trace may be corrupt or from a very old ' +
604 'Chrome revision.'});
606 if (firstPictureError) {
607 status.messages.push({
608 header: 'Cannot rasterize',
609 details: firstPictureError});
613 if (this.showInputEvents && this.layerTreeImpl.tracedInputLatencies &&
614 this.inputEventImageData_ === undefined) {
615 var image = this.querySelector('#input-event');
617 this.loadDataForImageElement_(image, function(imageData) {
618 this.inputEventImageData_ = imageData;
619 this.updateContentsPending_ = false;
620 this.scheduleUpdateContents_();
623 status.picturesComplete = false;
628 get selectedRenderPass() {
630 return this.selection.renderPass_;
633 get selectedLayer() {
634 if (this.selection) {
635 var selectedLayerId = this.selection.associatedLayerId;
636 return this.layerTreeImpl_.findLayerWithId(selectedLayerId);
642 this.layerTreeImpl.layerTreeHostImpl.args.frame.renderPasses;
643 if (!this.showOtherLayers) {
644 var selectedRenderPass = this.selectedRenderPass;
645 if (selectedRenderPass)
646 renderPasses = [selectedRenderPass];
652 var layers = this.layerTreeImpl.renderSurfaceLayerList;
653 if (!this.showOtherLayers) {
654 var selectedLayer = this.selectedLayer;
656 layers = [selectedLayer];
661 appendImageQuads_: function(quads, layer, layerQuad) {
662 // Generate image quads for the layer
663 for (var ir = 0; ir < layer.pictures.length; ++ir) {
664 var picture = layer.pictures[ir];
665 if (!picture.layerRect)
668 var unitRect = picture.layerRect.asUVRectInside(layer.bounds);
669 var iq = layerQuad.projectUnitRect(unitRect);
671 var pictureData = this.pictureAsImageData_[picture.guid];
672 if (this.showContents && pictureData && pictureData.imageData) {
673 iq.imageData = pictureData.imageData;
674 iq.borderColor = 'rgba(0,0,0,0)';
676 iq.imageData = undefined;
679 iq.stackingGroupId = layerQuad.stackingGroupId;
684 appendAnimationQuads_: function(quads, layer, layerQuad) {
685 if (!layer.animationBoundsRect)
688 var rect = layer.animationBoundsRect;
689 var abq = tvcm.Quad.fromRect(rect);
691 abq.backgroundColor = 'rgba(164,191,48,0.5)';
692 abq.borderColor = 'rgba(205,255,0,0.75)';
693 abq.borderWidth = 3.0;
694 abq.stackingGroupId = layerQuad.stackingGroupId;
695 abq.selectionToSetIfClicked = new cc.AnimationRectSelection(
700 appendInvalidationQuads_: function(quads, layer, layerQuad) {
701 if (layer.layerTreeImpl.hasSourceFrameBeenDrawnBefore)
704 // Generate the invalidation rect quads.
705 for (var ir = 0; ir < layer.invalidation.rects.length; ir++) {
706 var rect = layer.invalidation.rects[ir];
707 var unitRect = rect.asUVRectInside(layer.bounds);
708 var iq = layerQuad.projectUnitRect(unitRect);
709 iq.backgroundColor = 'rgba(0, 255, 0, 0.1)';
710 iq.borderColor = 'rgba(0, 255, 0, 1)';
711 iq.stackingGroupId = layerQuad.stackingGroupId;
712 iq.selectionToSetIfClicked = new cc.LayerRectSelection(
713 layer, 'Invalidation rect', rect, rect);
718 appendUnrecordedRegionQuads_: function(quads, layer, layerQuad) {
719 // Generate the unrecorded region quads.
720 for (var ir = 0; ir < layer.unrecordedRegion.rects.length; ir++) {
721 var rect = layer.unrecordedRegion.rects[ir];
722 var unitRect = rect.asUVRectInside(layer.bounds);
723 var iq = layerQuad.projectUnitRect(unitRect);
724 iq.backgroundColor = 'rgba(240, 230, 140, 0.3)';
725 iq.borderColor = 'rgba(240, 230, 140, 1)';
726 iq.stackingGroupId = layerQuad.stackingGroupId;
727 iq.selectionToSetIfClicked = new cc.LayerRectSelection(
728 layer, 'Unrecorded area', rect, rect);
733 appendBottleneckQuads_: function(quads, layer, layerQuad, stackingGroupId) {
734 function processRegion(region, label, borderColor) {
735 var backgroundColor = borderColor.clone();
736 backgroundColor.a = 0.4 * (borderColor.a || 1.0);
738 if (!region || !region.rects)
741 for (var ir = 0; ir < region.rects.length; ir++) {
742 var rect = region.rects[ir];
743 var unitRect = rect.asUVRectInside(layer.bounds);
744 var iq = layerQuad.projectUnitRect(unitRect);
745 iq.backgroundColor = backgroundColor.toString();
746 iq.borderColor = borderColor.toString();
747 iq.borderWidth = 4.0;
748 iq.stackingGroupId = stackingGroupId;
749 iq.selectionToSetIfClicked = new cc.LayerRectSelection(
750 layer, label, rect, rect);
755 processRegion(layer.touchEventHandlerRegion, 'Touch listener',
756 tvcm.Color.fromString('rgb(228, 226, 27)'));
757 processRegion(layer.wheelEventHandlerRegion, 'Wheel listener',
758 tvcm.Color.fromString('rgb(176, 205, 29)'));
759 processRegion(layer.nonFastScrollableRegion, 'Repaints on scroll',
760 tvcm.Color.fromString('rgb(213, 134, 32)'));
763 appendTileCoverageRectQuads_: function(
764 quads, layer, layerQuad, heatmapType) {
765 if (!layer.tileCoverageRects)
769 for (var ct = 0; ct < layer.tileCoverageRects.length; ++ct) {
770 var tile = layer.tileCoverageRects[ct].tile;
771 if (tile !== undefined)
775 var lthi = this.layerTreeImpl_.layerTreeHostImpl;
777 this.getMinMaxForHeatmap_(lthi.activeTiles, heatmapType);
779 this.computeHeatmapColors_(tiles, minMax, heatmapType);
782 for (var ct = 0; ct < layer.tileCoverageRects.length; ++ct) {
783 var rect = layer.tileCoverageRects[ct].geometryRect;
784 rect = rect.scale(1.0 / layer.geometryContentsScale);
786 var tile = layer.tileCoverageRects[ct].tile;
788 var unitRect = rect.asUVRectInside(layer.bounds);
789 var quad = layerQuad.projectUnitRect(unitRect);
791 quad.backgroundColor = 'rgba(0, 0, 0, 0)';
792 quad.stackingGroupId = layerQuad.stackingGroupId;
793 var type = cc.tileTypes.missing;
795 type = tile.getTypeForLayer(layer);
796 quad.backgroundColor = heatmapResult[heatIndex].color;
800 quad.borderColor = cc.tileBorder[type].color;
801 quad.borderWidth = cc.tileBorder[type].width;
804 label = 'coverageRect';
806 label = 'checkerboard coverageRect';
807 quad.selectionToSetIfClicked = new cc.LayerRectSelection(
808 layer, label, rect, layer.tileCoverageRects[ct]);
814 appendLayoutRectQuads_: function(quads, layer, layerQuad) {
815 if (!layer.layoutRects) {
819 for (var ct = 0; ct < layer.layoutRects.length; ++ct) {
820 var rect = layer.layoutRects[ct].geometryRect;
821 rect = rect.scale(1.0 / layer.geometryContentsScale);
823 var unitRect = rect.asUVRectInside(layer.bounds);
824 var quad = layerQuad.projectUnitRect(unitRect);
826 quad.backgroundColor = 'rgba(0, 0, 0, 0)';
827 quad.stackingGroupId = layerQuad.stackingGroupId;
829 quad.borderColor = 'rgba(0, 0, 200, 0.7)';
830 quad.borderWidth = 2;
832 label = 'Layout rect';
833 quad.selectionToSetIfClicked = new cc.LayerRectSelection(
840 getValueForHeatmap_: function(tile, heatmapType) {
841 if (heatmapType == TILE_HEATMAP_TYPE.SCHEDULED_PRIORITY) {
842 return tile.scheduledPriority == 0 ?
844 tile.scheduledPriority;
845 } else if (heatmapType == TILE_HEATMAP_TYPE.DISTANCE_TO_VISIBLE) {
846 return Math.min(3000, tile.distanceToVisible);
847 } else if (heatmapType == TILE_HEATMAP_TYPE.USING_GPU_MEMORY) {
848 if (tile.isSolidColor)
850 return tile.isUsingGpuMemory ? 0 : 1;
854 getMinMaxForHeatmap_: function(tiles, heatmapType) {
855 var range = new tvcm.Range();
856 if (heatmapType == TILE_HEATMAP_TYPE.USING_GPU_MEMORY) {
862 for (var i = 0; i < tiles.length; ++i) {
863 var value = this.getValueForHeatmap_(tiles[i], heatmapType);
864 if (value === undefined)
866 range.addValue(value);
868 if (range.range === 0)
873 computeHeatmapColors_: function(tiles, minMax, heatmapType) {
874 var min = minMax.min;
875 var max = minMax.max;
877 var color = function(value) {
878 var hue = 120 * (1 - (value - min) / (max - min));
881 return 'hsla(' + hue + ', 100%, 50%, 0.5)';
885 for (var i = 0; i < tiles.length; ++i) {
887 var value = this.getValueForHeatmap_(tile, heatmapType);
890 color: value !== undefined ? color(value) : undefined
898 appendTilesWithScaleQuads_: function(
899 quads, layer, layerQuad, scale, heatmapType) {
900 var lthi = this.layerTreeImpl_.layerTreeHostImpl;
903 for (var i = 0; i < lthi.activeTiles.length; ++i) {
904 var tile = lthi.activeTiles[i];
906 if (Math.abs(tile.contentsScale - scale) > 1e-6)
909 // TODO(vmpstr): Make the stiching of tiles and layers a part of
910 // tile construction (issue 346)
911 if (layer.layerId != tile.layerId)
918 this.getMinMaxForHeatmap_(lthi.activeTiles, heatmapType);
920 this.computeHeatmapColors_(tiles, minMax, heatmapType);
922 for (var i = 0; i < tiles.length; ++i) {
924 var rect = tile.layerRect;
927 var unitRect = rect.asUVRectInside(layer.bounds);
928 var quad = layerQuad.projectUnitRect(unitRect);
930 quad.backgroundColor = 'rgba(0, 0, 0, 0)';
931 quad.stackingGroupId = layerQuad.stackingGroupId;
933 var type = tile.getTypeForLayer(layer);
934 quad.borderColor = cc.tileBorder[type].color;
935 quad.borderWidth = cc.tileBorder[type].width;
937 quad.backgroundColor = heatmapResult[i].color;
941 if (heatmapType !== TILE_HEATMAP_TYPE.NONE)
942 data[heatmapType] = heatmapResult[i].value;
943 quad.selectionToSetIfClicked = new cc.TileSelection(tile, data);
948 appendHighlightQuadsForLayer_: function(
949 quads, layer, layerQuad, highlights) {
950 highlights.forEach(function(highlight) {
951 var rect = highlight.rect;
953 var unitRect = rect.asUVRectInside(layer.bounds);
954 var quad = layerQuad.projectUnitRect(unitRect);
956 var colorId = tvcm.ui.getStringColorId(highlight.colorKey);
957 colorId += tvcm.ui.getColorPaletteHighlightIdBoost();
959 var color = tvcm.Color.fromString(tvcm.ui.getColorPalette()[colorId]);
961 var quadForDrawing = quad.clone();
962 quadForDrawing.backgroundColor = color.withAlpha(0.5).toString();
963 quadForDrawing.borderColor = color.withAlpha(1.0).darken().toString();
964 quadForDrawing.stackingGroupId = layerQuad.stackingGroupId;
965 quads.push(quadForDrawing);
970 generateRenderPassQuads: function() {
971 if (!this.layerTreeImpl.layerTreeHostImpl.args.frame)
973 var renderPasses = this.renderPasses;
978 for (var i = 0; i < renderPasses.length; ++i) {
979 var quadList = renderPasses[i].quadList;
980 for (var j = 0; j < quadList.length; ++j) {
981 var drawQuad = quadList[j];
982 var quad = drawQuad.rectAsTargetSpaceQuad.clone();
983 quad.borderColor = 'rgb(170, 204, 238)';
984 quad.borderWidth = 2;
985 quad.stackingGroupId = i;
992 generateLayerQuads: function() {
993 this.updateContentsPending_ = false;
995 // Generate the quads for the view.
996 var layers = this.layers;
998 var nextStackingGroupId = 0;
999 var alreadyVisitedLayerIds = {};
1002 var selectionHighlightsByLayerId;
1004 selectionHighlightsByLayerId = this.selection.highlightsByLayerId;
1006 selectionHighlightsByLayerId = {};
1008 var extraHighlightsByLayerId = this.extraHighlightsByLayerId || {};
1010 for (var i = 1; i <= layers.length; i++) {
1011 // Generate quads back-to-front.
1012 var layer = layers[layers.length - i];
1013 alreadyVisitedLayerIds[layer.layerId] = true;
1014 if (layer.objectInstance.name == 'cc::NinePatchLayerImpl')
1017 var layerQuad = layer.layerQuad.clone();
1018 if (layer.usingGpuRasterization) {
1019 var pixelRatio = window.devicePixelRatio || 1;
1020 layerQuad.borderWidth = 2.0 * pixelRatio;
1021 layerQuad.borderColor = 'rgba(154,205,50,0.75)';
1023 layerQuad.borderColor = 'rgba(0,0,0,0.75)';
1025 layerQuad.stackingGroupId = nextStackingGroupId++;
1026 layerQuad.selectionToSetIfClicked = new cc.LayerSelection(layer);
1027 layerQuad.layer = layer;
1028 if (this.showOtherLayers && this.selectedLayer == layer)
1029 layerQuad.upperBorderColor = 'rgb(156,189,45)';
1031 if (this.showAnimationBounds)
1032 this.appendAnimationQuads_(quads, layer, layerQuad);
1034 this.appendImageQuads_(quads, layer, layerQuad);
1035 quads.push(layerQuad);
1038 if (this.showInvalidations)
1039 this.appendInvalidationQuads_(quads, layer, layerQuad);
1040 if (this.showUnrecordedRegion)
1041 this.appendUnrecordedRegionQuads_(quads, layer, layerQuad);
1042 if (this.showBottlenecks)
1043 this.appendBottleneckQuads_(quads, layer, layerQuad,
1044 layerQuad.stackingGroupId);
1045 if (this.showLayoutRects)
1046 this.appendLayoutRectQuads_(quads, layer, layerQuad);
1048 if (this.howToShowTiles === 'coverage') {
1049 this.appendTileCoverageRectQuads_(
1050 quads, layer, layerQuad, this.tileHeatmapType);
1051 } else if (this.howToShowTiles !== 'none') {
1052 this.appendTilesWithScaleQuads_(
1053 quads, layer, layerQuad,
1054 this.howToShowTiles, this.tileHeatmapType);
1058 highlights = extraHighlightsByLayerId[layer.layerId];
1060 this.appendHighlightQuadsForLayer_(
1061 quads, layer, layerQuad, highlights)
1064 highlights = selectionHighlightsByLayerId[layer.layerId];
1066 this.appendHighlightQuadsForLayer_(
1067 quads, layer, layerQuad, highlights)
1071 this.layerTreeImpl.iterLayers(function(layer, depth, isMask, isReplica) {
1072 if (!this.showOtherLayers && this.selectedLayer != layer)
1074 if (alreadyVisitedLayerIds[layer.layerId])
1076 var layerQuad = layer.layerQuad;
1077 var stackingGroupId = nextStackingGroupId++;
1078 if (this.showBottlenecks)
1079 this.appendBottleneckQuads_(quads, layer, layerQuad, stackingGroupId);
1082 var tracedInputLatencies = this.layerTreeImpl.tracedInputLatencies;
1083 if (this.showInputEvents && tracedInputLatencies) {
1084 for (var i = 0; i < tracedInputLatencies.length; i++) {
1085 var coordinatesArray = tracedInputLatencies[i].args.data.coordinates;
1086 for (var j = 0; j < coordinatesArray.length; j++) {
1087 var inputQuad = tvcm.Quad.fromXYWH(
1088 coordinatesArray[j].x - 25,
1089 coordinatesArray[j].y - 25,
1092 inputQuad.borderColor = 'rgba(0, 0, 0, 0)';
1093 inputQuad.imageData = this.inputEventImageData_;
1094 quads.push(inputQuad);
1102 updateInfoBar_: function(infoBarMessages) {
1103 if (infoBarMessages.length) {
1104 this.infoBar_.removeAllButtons();
1105 this.infoBar_.message = 'Some problems were encountered...';
1106 this.infoBar_.addButton('More info...', function(e) {
1107 var overlay = new tvcm.ui.Overlay();
1108 overlay.textContent = '';
1109 infoBarMessages.forEach(function(message) {
1110 var title = document.createElement('h3');
1111 title.textContent = message.header;
1113 var details = document.createElement('div');
1114 details.textContent = message.details;
1116 overlay.appendChild(title);
1117 overlay.appendChild(details);
1119 overlay.visible = true;
1121 e.stopPropagation();
1124 this.infoBar_.visible = true;
1126 this.infoBar_.removeAllButtons();
1127 this.infoBar_.message = '';
1128 this.infoBar_.visible = false;
1132 getWhatRasterized_: function() {
1133 var lthi = this.layerTreeImpl_.layerTreeHostImpl;
1134 var renderProcess = lthi.objectInstance.parent;
1136 renderProcess.iterateAllEvents(function(event) {
1137 if (!(event instanceof tracing.trace_model.Slice))
1140 var tile = cc.getTileFromRasterTaskSlice(event);
1141 if (tile === undefined)
1144 if (tile.containingSnapshot == lthi)
1150 updateWhatRasterizedLinkState_: function() {
1151 var tasks = this.getWhatRasterized_();
1153 this.whatRasterizedLink_.textContent = tasks.length + ' raster tasks';
1154 this.whatRasterizedLink_.style.display = '';
1156 this.whatRasterizedLink_.textContent = '';
1157 this.whatRasterizedLink_.style.display = 'none';
1161 onWhatRasterizedLinkClicked_: function() {
1162 var tasks = this.getWhatRasterized_();
1163 var event = new tracing.RequestSelectionChangeEvent();
1164 event.selection = new tracing.Selection(tasks);
1165 this.dispatchEvent(event);
1170 LayerTreeQuadStackView: LayerTreeQuadStackView