1<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="stylesheet" href="/tracing/ui/extras/chrome/cc/layer_picker.css">
9
10<link rel="import" href="/tracing/extras/chrome/cc/constants.html">
11<link rel="import" href="/tracing/extras/chrome/cc/layer_tree_host_impl.html">
12<link rel="import" href="/tracing/extras/chrome/cc/util.html">
13<link rel="import" href="/tracing/ui/analysis/generic_object_view.html">
14<link rel="import" href="/tracing/model/event.html">
15<link rel="import" href="/tracing/ui/base/drag_handle.html">
16<link rel="import" href="/tracing/ui/base/list_view.html">
17<link rel="import" href="/tracing/ui/base/dom_helpers.html">
18<link rel="import" href="/tracing/ui/extras/chrome/cc/selection.html">
19
20<script>
21'use strict';
22
23tr.exportTo('tr.ui.e.chrome.cc', function() {
24  var constants = tr.e.cc.constants;
25  var bytesToRoundedMegabytes = tr.e.cc.bytesToRoundedMegabytes;
26  var RENDER_PASS_QUADS =
27      Math.max(constants.ACTIVE_TREE, constants.PENDING_TREE) + 1;
28
29  /**
30   * @constructor
31   */
32  var LayerPicker = tr.ui.b.define('tr-ui-e-chrome-cc-layer-picker');
33
34  LayerPicker.prototype = {
35    __proto__: HTMLUnknownElement.prototype,
36
37    decorate: function() {
38      this.lthi_ = undefined;
39      this.controls_ = document.createElement('top-controls');
40      this.renderPassQuads_ = false;
41
42
43      this.itemList_ = new tr.ui.b.ListView();
44      this.appendChild(this.controls_);
45
46      this.appendChild(this.itemList_);
47
48      this.itemList_.addEventListener(
49          'selection-changed', this.onItemSelectionChanged_.bind(this));
50
51      this.controls_.appendChild(tr.ui.b.createSelector(
52          this, 'whichTree',
53          'layerPicker.whichTree', constants.ACTIVE_TREE,
54          [{label: 'Active tree', value: constants.ACTIVE_TREE},
55           {label: 'Pending tree', value: constants.PENDING_TREE},
56           {label: 'Render pass quads', value: RENDER_PASS_QUADS}]));
57
58      this.showPureTransformLayers_ = false;
59      var showPureTransformLayers = tr.ui.b.createCheckBox(
60          this, 'showPureTransformLayers',
61          'layerPicker.showPureTransformLayers', false,
62          'Transform layers');
63      showPureTransformLayers.classList.add('show-transform-layers');
64      showPureTransformLayers.title =
65          'When checked, pure transform layers are shown';
66      this.controls_.appendChild(showPureTransformLayers);
67    },
68
69    get lthiSnapshot() {
70      return this.lthiSnapshot_;
71    },
72
73    set lthiSnapshot(lthiSnapshot) {
74      this.lthiSnapshot_ = lthiSnapshot;
75      this.updateContents_();
76    },
77
78    get whichTree() {
79      return this.renderPassQuads_ ? constants.ACTIVE_TREE : this.whichTree_;
80    },
81
82    set whichTree(whichTree) {
83      this.whichTree_ = whichTree;
84      this.renderPassQuads_ = (whichTree == RENDER_PASS_QUADS);
85      this.updateContents_();
86      tr.b.dispatchSimpleEvent(this, 'selection-change', false);
87    },
88
89    get layerTreeImpl() {
90      if (this.lthiSnapshot === undefined)
91        return undefined;
92      return this.lthiSnapshot.getTree(this.whichTree);
93    },
94
95    get isRenderPassQuads() {
96      return this.renderPassQuads_;
97    },
98
99    get showPureTransformLayers() {
100      return this.showPureTransformLayers_;
101    },
102
103    set showPureTransformLayers(show) {
104      if (this.showPureTransformLayers_ === show)
105        return;
106      this.showPureTransformLayers_ = show;
107      this.updateContents_();
108    },
109
110    getRenderPassInfos_: function() {
111      if (!this.lthiSnapshot_)
112        return [];
113
114      var renderPassInfo = [];
115      if (!this.lthiSnapshot_.args.frame ||
116          !this.lthiSnapshot_.args.frame.renderPasses)
117        return renderPassInfo;
118
119      var renderPasses = this.lthiSnapshot_.args.frame.renderPasses;
120      for (var i = 0; i < renderPasses.length; ++i) {
121        var info = {renderPass: renderPasses[i],
122                     depth: 0,
123                     id: i,
124                     name: 'cc::RenderPass'};
125        renderPassInfo.push(info);
126      }
127      return renderPassInfo;
128    },
129
130    getLayerInfos_: function() {
131      if (!this.lthiSnapshot_)
132        return [];
133
134      var tree = this.lthiSnapshot_.getTree(this.whichTree_);
135      if (!tree)
136        return [];
137
138      var layerInfos = [];
139
140      var showPureTransformLayers = this.showPureTransformLayers_;
141
142      function isPureTransformLayer(layer) {
143        if (layer.args.compositingReasons &&
144            layer.args.compositingReasons.length != 1 &&
145            layer.args.compositingReasons[0] != 'No reasons given')
146          return false;
147
148        if (layer.args.drawsContent)
149          return false;
150
151        return true;
152      }
153      var visitedLayers = {};
154      function visitLayer(layer, depth, isMask, isReplica) {
155        if (visitedLayers[layer.layerId])
156          return;
157        visitedLayers[layer.layerId] = true;
158        var info = {layer: layer,
159          depth: depth};
160
161        if (layer.args.drawsContent)
162          info.name = layer.objectInstance.name;
163        else
164          info.name = 'cc::LayerImpl';
165
166        if (layer.usingGpuRasterization)
167          info.name += ' (G)';
168
169        info.isMaskLayer = isMask;
170        info.replicaLayer = isReplica;
171
172        if (showPureTransformLayers || !isPureTransformLayer(layer))
173          layerInfos.push(info);
174
175      };
176      tree.iterLayers(visitLayer);
177      return layerInfos;
178    },
179
180    updateContents_: function() {
181      if (this.renderPassQuads_)
182        this.updateRenderPassContents_();
183      else
184        this.updateLayerContents_();
185    },
186
187    updateRenderPassContents_: function() {
188      this.itemList_.clear();
189
190      var selectedRenderPassId;
191      if (this.selection_ && this.selection_.associatedRenderPassId)
192        selectedRenderPassId = this.selection_.associatedRenderPassId;
193
194      var renderPassInfos = this.getRenderPassInfos_();
195      renderPassInfos.forEach(function(renderPassInfo) {
196        var renderPass = renderPassInfo.renderPass;
197        var id = renderPassInfo.id;
198
199        var item = this.createElementWithDepth_(renderPassInfo.depth);
200        var labelEl = item.appendChild(tr.ui.b.createSpan());
201
202        labelEl.textContent = renderPassInfo.name + ' ' + id;
203        item.renderPass = renderPass;
204        item.renderPassId = id;
205        this.itemList_.appendChild(item);
206
207        if (id == selectedRenderPassId) {
208          renderPass.selectionState =
209              tr.model.SelectionState.SELECTED;
210        }
211      }, this);
212    },
213
214    updateLayerContents_: function() {
215      this.changingItemSelection_ = true;
216      try {
217        this.itemList_.clear();
218
219        var selectedLayerId;
220        if (this.selection_ && this.selection_.associatedLayerId)
221          selectedLayerId = this.selection_.associatedLayerId;
222
223        var layerInfos = this.getLayerInfos_();
224        layerInfos.forEach(function(layerInfo) {
225          var layer = layerInfo.layer;
226          var id = layer.layerId;
227
228          var item = this.createElementWithDepth_(layerInfo.depth);
229          var labelEl = item.appendChild(tr.ui.b.createSpan());
230
231          labelEl.textContent = layerInfo.name + ' ' + id;
232
233          var notesEl = item.appendChild(tr.ui.b.createSpan());
234          if (layerInfo.isMaskLayer)
235            notesEl.textContent += '(mask)';
236          if (layerInfo.isReplicaLayer)
237            notesEl.textContent += '(replica)';
238
239          if (layer.gpuMemoryUsageInBytes !== undefined) {
240            var rounded = bytesToRoundedMegabytes(layer.gpuMemoryUsageInBytes);
241            if (rounded !== 0)
242              notesEl.textContent += ' (' + rounded + ' MB)';
243          }
244
245          item.layer = layer;
246          this.itemList_.appendChild(item);
247
248          if (layer.layerId == selectedLayerId) {
249            layer.selectionState = tr.model.SelectionState.SELECTED;
250            item.selected = true;
251          }
252        }, this);
253      } finally {
254        this.changingItemSelection_ = false;
255      }
256    },
257
258    createElementWithDepth_: function(depth) {
259      var item = document.createElement('div');
260
261      var indentEl = item.appendChild(tr.ui.b.createSpan());
262      indentEl.style.whiteSpace = 'pre';
263      for (var i = 0; i < depth; i++)
264        indentEl.textContent = indentEl.textContent + ' ';
265      return item;
266    },
267
268    onItemSelectionChanged_: function(e) {
269      if (this.changingItemSelection_)
270        return;
271      if (this.renderPassQuads_)
272        this.onRenderPassSelected_(e);
273      else
274        this.onLayerSelected_(e);
275      tr.b.dispatchSimpleEvent(this, 'selection-change', false);
276    },
277
278    onRenderPassSelected_: function(e) {
279      var selectedRenderPass;
280      var selectedRenderPassId;
281      if (this.itemList_.selectedElement) {
282        selectedRenderPass = this.itemList_.selectedElement.renderPass;
283        selectedRenderPassId =
284            this.itemList_.selectedElement.renderPassId;
285      }
286
287      if (selectedRenderPass) {
288        this.selection_ = new tr.ui.e.chrome.cc.RenderPassSelection(
289            selectedRenderPass, selectedRenderPassId);
290      } else {
291        this.selection_ = undefined;
292      }
293    },
294
295    onLayerSelected_: function(e) {
296      var selectedLayer;
297      if (this.itemList_.selectedElement)
298        selectedLayer = this.itemList_.selectedElement.layer;
299
300      if (selectedLayer)
301        this.selection_ = new tr.ui.e.chrome.cc.LayerSelection(selectedLayer);
302      else
303        this.selection_ = undefined;
304    },
305
306    get selection() {
307      return this.selection_;
308    },
309
310    set selection(selection) {
311      if (this.selection_ == selection)
312        return;
313      this.selection_ = selection;
314      this.updateContents_();
315    }
316  };
317
318  return {
319    LayerPicker: LayerPicker
320  };
321});
322</script>
323