Home | History | Annotate | Download | only in cc
      1 // Copyright (c) 2013 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 'use strict';
      6 
      7 /**
      8  * @fileoverview Graphical view of  LayerTreeImpl, with controls for
      9  * type of layer content shown and info bar for content-loading warnings.
     10  */
     11 
     12 base.requireStylesheet('cc.layer_tree_quad_stack_viewer');
     13 
     14 base.require('base.properties');
     15 base.require('base.raf');
     16 base.require('cc.constants');
     17 base.require('cc.picture');
     18 base.require('ui.quad_stack_viewer');
     19 base.require('ui.info_bar');
     20 
     21 
     22 base.exportTo('cc', function() {
     23   var constants = {
     24     // The ratio of quad stack pixels to world pixels before CSS scaling.
     25     QUAD_STACK_SCALE: 0.5
     26   };
     27 
     28   /**
     29    * @constructor
     30    */
     31   var LayerTreeQuadStackViewer = ui.define('layer-tree-quad-stack-viewer');
     32 
     33   LayerTreeQuadStackViewer.prototype = {
     34     __proto__: HTMLDivElement.prototype,
     35 
     36     decorate: function() {
     37       this.pictureAsCanvas_ = {}; // Maps picture.guid to PictureAsCanvas.
     38       this.quads_ = [];
     39       this.messages_ = [];
     40       this.controls_ = document.createElement('top-controls');
     41       this.infoBar_ = new ui.InfoBar();
     42       this.quadStackViewer_ = new ui.QuadStackViewer();
     43 
     44       this.appendChild(this.controls_);
     45       this.appendChild(this.infoBar_);
     46       this.appendChild(this.quadStackViewer_);
     47 
     48       var scaleSelector = ui.createSelector(
     49           this.quadStackViewer_, 'scale',
     50           'layerViewer.scale', 0.375,
     51           [{label: '6.25%', value: 0.0625},
     52            {label: '12.5%', value: 0.125},
     53            {label: '25%', value: 0.25},
     54            {label: '37.5%', value: 0.375},
     55            {label: '50%', value: 0.5},
     56            {label: '75%', value: 0.75},
     57            {label: '100%', value: 1},
     58            {label: '200%', value: 2}
     59           ]);
     60       this.controls_.appendChild(scaleSelector);
     61 
     62       var showOtherLayersCheckbox = ui.createCheckBox(
     63           this, 'showOtherLayers',
     64           'layerViewer.showOtherLayers', true,
     65           'Show other layers');
     66       this.controls_.appendChild(showOtherLayersCheckbox);
     67 
     68       var showInvalidationsCheckbox = ui.createCheckBox(
     69           this, 'showInvalidations',
     70           'layerViewer.showInvalidations', true,
     71           'Show invalidations');
     72       this.controls_.appendChild(showInvalidationsCheckbox);
     73 
     74       var showContentsCheckbox = ui.createCheckBox(
     75           this, 'showContents',
     76           'layerViewer.showContents', true,
     77           'Show contents');
     78       this.controls_.appendChild(showContentsCheckbox);
     79     },
     80 
     81     get layerTreeImpl() {
     82       return this.layerTreeImpl_;
     83     },
     84 
     85     set layerTreeImpl(layerTreeImpl) {
     86       // FIXME(pdr): We may want to clear pictureAsCanvas_ here to save memory
     87       //             at the cost of performance. Note that pictureAsCanvas_ will
     88       //             be cleared when this is destructed, but this view might
     89       //             live for several layerTreeImpls.
     90       this.layerTreeImpl_ = layerTreeImpl;
     91       this.selection = null;
     92       this.updateContents_();
     93     },
     94 
     95     get showOtherLayers() {
     96       return this.showOtherLayers_;
     97     },
     98 
     99     set showOtherLayers(show) {
    100       this.showOtherLayers_ = show;
    101       this.updateContents_();
    102     },
    103 
    104     get showContents() {
    105       return this.showContents_;
    106     },
    107 
    108     set showContents(show) {
    109       this.showContents_ = show;
    110       this.updateContents_();
    111     },
    112 
    113     get showInvalidations() {
    114       return this.showInvalidations_;
    115     },
    116 
    117     set showInvalidations(show) {
    118       this.showInvalidations_ = show;
    119       this.updateContents_();
    120     },
    121 
    122     get selection() {
    123       return this.selection_;
    124     },
    125 
    126     set selection(selection) {
    127       base.setPropertyAndDispatchChange(this, 'selection', selection);
    128       this.updateContents_();
    129     },
    130 
    131     scheduleUpdateContents_: function() {
    132       if (this.updateContentsPending_)
    133         return;
    134       this.updateContentsPending_ = true;
    135       base.requestAnimationFrameInThisFrameIfPossible(
    136           this.updateContents_, this);
    137     },
    138 
    139     updateContents_: function() {
    140       if (!this.layerTreeImpl_)
    141         return;
    142 
    143       if (this.pictureLoadingComplete_())
    144         this.generateQuads_();
    145     },
    146 
    147     pictureLoadingComplete_: function() {
    148       // Figure out if we can draw the quads yet. While we're at it, figure out
    149       // if we have any warnings we need to show.
    150       var layers = this.layers;
    151       var messages = [];
    152       if (this.showContents) {
    153         var hasPendingRasterizeImage = false;
    154         var firstPictureError = undefined;
    155         var hasMissingLayerRect = false;
    156         var hasUnresolvedPictureRef = false;
    157         for (var i = 0; i < layers.length; i++) {
    158           var layer = layers[i];
    159           for (var ir = layer.pictures.length - 1; ir >= 0; ir--) {
    160             var picture = layer.pictures[ir];
    161 
    162             if (picture.idRef) {
    163               hasUnresolvedPictureRef = true;
    164               continue;
    165             }
    166             if (!picture.layerRect) {
    167               hasMissingLayerRect = true;
    168               continue;
    169             }
    170 
    171             var pictureAsCanvas = this.pictureAsCanvas_[picture.guid];
    172             if (!pictureAsCanvas) {
    173               hasPendingRasterizeImage = true;
    174               this.pictureAsCanvas_[picture.guid] =
    175                   cc.PictureAsCanvas.Pending(this);
    176               picture.rasterize(
    177                   {stopIndex: undefined},
    178                   function(pictureAsCanvas) {
    179                     var picture_ = pictureAsCanvas.picture;
    180                     this.pictureAsCanvas_[picture_.guid] = pictureAsCanvas;
    181                     this.scheduleUpdateContents_();
    182                   }.bind(this));
    183               continue;
    184             }
    185             if (pictureAsCanvas.isPending()) {
    186               hasPendingRasterizeImage = true;
    187               continue;
    188             }
    189             if (pictureAsCanvas.error) {
    190               if (!firstPictureError)
    191                 firstPictureError = pictureAsCanvas.error;
    192               break;
    193             }
    194           }
    195         }
    196         if (hasPendingRasterizeImage)
    197           return false;
    198 
    199         if (hasUnresolvedPictureRef) {
    200           messages.push({
    201             header: 'Missing picture',
    202             details: 'Your trace didnt have pictures for every layer. ' +
    203                 'Old chrome versions had this problem'});
    204         }
    205         if (hasMissingLayerRect) {
    206           messages.push({
    207             header: 'Missing layer rect',
    208             details: 'Your trace may be corrupt or from a very old ' +
    209                 'Chrome revision.'});
    210         }
    211         if (firstPictureError) {
    212           messages.push({
    213             header: 'Cannot rasterize',
    214             details: firstPictureError});
    215         }
    216       }
    217       this.messages_ = messages;
    218       return true;
    219     },
    220 
    221     get selectedLayer() {
    222       if (this.selection) {
    223         var selectedLayerId = this.selection.associatedLayerId;
    224         return this.layerTreeImpl_.findLayerWithId(selectedLayerId);
    225       }
    226     },
    227 
    228     get layers() {
    229       var layers = this.layerTreeImpl.renderSurfaceLayerList;
    230       if (!this.showOtherLayers) {
    231         var selectedLayer = this.selectedLayer;
    232         if (selectedLayer)
    233           layers = [selectedLayer];
    234       }
    235       return layers;
    236     },
    237 
    238     appendImageQuads_: function(layer, layerQuad) {
    239       // Generate image quads for the layer
    240       for (var ir = layer.pictures.length - 1; ir >= 0; ir--) {
    241         var picture = layer.pictures[ir];
    242         if (!picture.layerRect)
    243           continue;
    244 
    245         var unitRect = picture.layerRect.asUVRectInside(layer.bounds);
    246         var iq = layerQuad.projectUnitRect(unitRect);
    247 
    248         var pictureAsCanvas = this.pictureAsCanvas_[picture.guid];
    249         if (this.showContents && pictureAsCanvas.canvas)
    250           iq.canvas = pictureAsCanvas.canvas;
    251         else
    252           iq.canvas = undefined;
    253 
    254         iq.stackingGroupId = layerQuad.stackingGroupId;
    255         this.quads_.push(iq);
    256       }
    257     },
    258 
    259     appendInvalidationQuads_: function(layer, layerQuad) {
    260       // Generate the invalidation rect quads.
    261       for (var ir = 0; ir < layer.invalidation.rects.length; ir++) {
    262         var rect = layer.invalidation.rects[ir];
    263         var unitRect = rect.asUVRectInside(layer.bounds);
    264         var iq = layerQuad.projectUnitRect(unitRect);
    265         iq.backgroundColor = 'rgba(255, 0, 0, 0.1)';
    266         iq.borderColor = 'rgba(255, 0, 0, 1)';
    267         iq.stackingGroupId = layerQuad.stackingGroupId;
    268         this.quads_.push(iq);
    269       }
    270     },
    271 
    272     appendSelectionQuads_: function(layer, layerQuad) {
    273       var selection = this.selection;
    274       var quad = this.layerTreeImpl_.whichTree == constants.ACTIVE_TREE ?
    275           selection.quadIfActive : selection.quadIfPending;
    276       if (!quad)
    277         return [];
    278 
    279       var colorId = tracing.getStringColorId(selection.title);
    280       colorId += tracing.getColorPaletteHighlightIdBoost();
    281 
    282       var color = base.Color.fromString(tracing.getColorPalette()[colorId]);
    283 
    284       var quadForDrawing = quad.clone();
    285       quadForDrawing.backgroundColor = color.withAlpha(0.5).toString();
    286       quadForDrawing.borderColor = color.withAlpha(1.0).darken().toString();
    287       quadForDrawing.stackingGroupId = layerQuad.stackingGroupId;
    288       return [quadForDrawing];
    289     },
    290 
    291     generateQuads_: function() {
    292       this.updateContentsPending_ = false;
    293 
    294       // Generate the quads for the view.
    295       var layers = this.layers;
    296       this.quads_ = [];
    297       for (var i = 0; i < layers.length; i++) {
    298         var layer = layers[i];
    299 
    300         var layerQuad = layer.layerQuad.clone();
    301         layerQuad.borderColor = 'rgba(0,0,0,0.75)';
    302         layerQuad.stackingGroupId = i;
    303         if (this.showOtherLayers && this.selectedLayer == layer)
    304           layerQuad.upperBorderColor = 'rgb(156,189,45)';
    305 
    306         this.appendImageQuads_(layer, layerQuad);
    307 
    308         if (this.showInvalidations) {
    309           this.appendInvalidationQuads_(layer, layerQuad);
    310         }
    311 
    312         // Push the layer quad last.
    313         this.quads_.push(layerQuad);
    314 
    315         if (this.selectedLayer === layer) {
    316           this.appendSelectionQuads_(layer, layerQuad);
    317         }
    318       }
    319       var lthi = this.layerTreeImpl_.layerTreeHostImpl;
    320       var lthiInstance = lthi.objectInstance;
    321       var worldViewportRect = base.Rect.FromXYWH(0, 0,
    322           lthi.deviceViewportSize.width, lthi.deviceViewportSize.height);
    323       this.quadStackViewer_.quadStack.initialize(
    324           lthiInstance.allLayersBBox.asRect(), worldViewportRect);
    325 
    326       this.quadStackViewer_.quadStack.quads = this.quads_;
    327 
    328       this.updateInfoBar_(this.messages_);
    329     },
    330 
    331     updateInfoBar_: function(infoBarMessages) {
    332       if (infoBarMessages.length) {
    333         this.infoBar_.removeAllButtons();
    334         this.infoBar_.message = 'Some problems were encountered...';
    335         this.infoBar_.addButton('More info...', function() {
    336           var overlay = new ui.Overlay();
    337           overlay.textContent = '';
    338           infoBarMessages.forEach(function(message) {
    339             var title = document.createElement('h3');
    340             title.textContent = message.header;
    341 
    342             var details = document.createElement('div');
    343             details.textContent = message.details;
    344 
    345             overlay.appendChild(title);
    346             overlay.appendChild(details);
    347           });
    348           overlay.visible = true;
    349           overlay.obeyCloseEvents = true;
    350         });
    351         this.infoBar_.visible = true;
    352       } else {
    353         this.infoBar_.removeAllButtons();
    354         this.infoBar_.message = '';
    355         this.infoBar_.visible = false;
    356       }
    357     }
    358   };
    359 
    360   return {
    361     LayerTreeQuadStackViewer: LayerTreeQuadStackViewer
    362   };
    363 });
    364