• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright 2017, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {transform, nanos_to_string, get_visible_chip} from './transform.js'
18
19// Layer flags
20const FLAG_HIDDEN = 0x01;
21const FLAG_OPAQUE = 0x02;
22const FLAG_SECURE = 0x80;
23
24var RELATIVE_Z_CHIP = {short: 'RelZ',
25    long: "Is relative Z-ordered to another surface",
26    class: 'warn'};
27var RELATIVE_Z_PARENT_CHIP = {short: 'RelZParent',
28    long: "Something is relative Z-ordered to this surface",
29    class: 'warn'};
30var MISSING_LAYER = {short: 'MissingLayer',
31    long: "This layer was referenced from the parent, but not present in the trace",
32    class: 'error'};
33
34function transform_layer(layer, {parentBounds, parentHidden}) {
35
36  function get_size(layer) {
37    var size = layer.size || {w: 0, h: 0};
38    return {
39      left: 0,
40      right: size.w,
41      top: 0,
42      bottom: size.h
43    };
44  }
45
46  function get_crop(layer) {
47    var crop = layer.crop || {left: 0, top: 0, right: 0 , bottom:0};
48    return {
49      left: crop.left || 0,
50      right: crop.right  || 0,
51      top: crop.top || 0,
52      bottom: crop.bottom || 0
53    };
54  }
55
56  function intersect(bounds, crop) {
57    return {
58      left: Math.max(crop.left, bounds.left),
59      right: Math.min(crop.right, bounds.right),
60      top: Math.max(crop.top, bounds.top),
61      bottom: Math.min(crop.bottom, bounds.bottom),
62    };
63  }
64
65  function is_empty_rect(rect) {
66    var right = rect.right || 0;
67    var left = rect.left || 0;
68    var top = rect.top || 0;
69    var bottom = rect.bottom || 0;
70
71    return (right - left) <= 0 || (bottom - top) <= 0;
72  }
73
74  function get_cropped_bounds(layer, parentBounds) {
75    var size = get_size(layer);
76    var crop = get_crop(layer);
77    if (!is_empty_rect(size) && !is_empty_rect(crop)) {
78      return intersect(size, crop);
79    }
80    if (!is_empty_rect(size)) {
81      return size;
82    }
83    if (!is_empty_rect(crop)) {
84      return crop;
85    }
86    return parentBounds || { left: 0, right: 0, top: 0, bottom: 0 };
87  }
88
89  function offset_to(bounds, x, y) {
90    return {
91      right: bounds.right - (bounds.left - x),
92      bottom: bounds.bottom - (bounds.top - y),
93      left: x,
94      top: y,
95    };
96  }
97
98  function transform_bounds(layer, parentBounds) {
99    var result = layer.bounds || get_cropped_bounds(layer, parentBounds);
100    var tx = (layer.position) ? layer.position.x || 0 : 0;
101    var ty = (layer.position) ? layer.position.y || 0 : 0;
102    result = offset_to(result, 0, 0);
103    result.label = layer.name;
104    result.transform = layer.transform;
105    result.transform.tx = tx;
106    result.transform.ty = ty;
107    return result;
108  }
109
110  function is_opaque(layer) {
111    return layer.color == undefined || (layer.color.a || 0) > 0;
112  }
113
114  function is_empty(region) {
115    return region == undefined ||
116        region.rect == undefined ||
117        region.rect.length == 0 ||
118        region.rect.every(function(r) { return is_empty_rect(r) } );
119  }
120
121  /**
122   * Checks if the layer is visible on screen according to its type,
123   * active buffer content, alpha and visible regions.
124   *
125   * @param {layer} layer
126   * @returns if the layer is visible on screen or not
127   */
128  function is_visible(layer) {
129    var visible = (layer.activeBuffer || layer.type === 'ColorLayer')
130                  && !hidden && is_opaque(layer);
131    visible &= !is_empty(layer.visibleRegion);
132    return visible;
133  }
134
135  function postprocess_flags(layer) {
136    if (!layer.flags) return;
137    var verboseFlags = [];
138    if (layer.flags & FLAG_HIDDEN) {
139      verboseFlags.push("HIDDEN");
140    }
141    if (layer.flags & FLAG_OPAQUE) {
142      verboseFlags.push("OPAQUE");
143    }
144    if (layer.flags & FLAG_SECURE) {
145      verboseFlags.push("SECURE");
146    }
147
148    layer.flags = verboseFlags.join('|') + " (" + layer.flags + ")";
149  }
150
151  var chips = [];
152  var rect = transform_bounds(layer, parentBounds);
153  var hidden = (layer.flags & FLAG_HIDDEN) != 0 || parentHidden;
154  var visible = is_visible(layer);
155  if (visible) {
156    chips.push(get_visible_chip());
157  } else {
158    rect = undefined;
159  }
160
161  var bounds = undefined;
162  if (layer.name.startsWith("Display Root#0") && layer.sourceBounds) {
163    bounds = {width: layer.sourceBounds.right, height: layer.sourceBounds.bottom};
164  }
165
166  if ((layer.zOrderRelativeOf || -1) !== -1) {
167    chips.push(RELATIVE_Z_CHIP);
168  }
169  if (layer.zOrderRelativeParentOf !== undefined) {
170    chips.push(RELATIVE_Z_PARENT_CHIP);
171  }
172  if (layer.missing) {
173    chips.push(MISSING_LAYER);
174  }
175
176  var transform_layer_with_parent_hidden =
177      (layer) => transform_layer(layer, {parentBounds: rect, parentHidden: hidden});
178
179  postprocess_flags(layer);
180
181  return transform({
182    obj: layer,
183    kind: 'layer',
184    name: layer.name,
185    children: [
186      [layer.resolvedChildren, transform_layer_with_parent_hidden],
187    ],
188    rect,
189    bounds,
190    highlight: rect,
191    chips,
192    visible,
193  });
194}
195
196function missingLayer(childId) {
197  return {
198    name: "layer #" + childId,
199    missing: true,
200    zOrderRelativeOf: -1,
201    transform: {dsdx:1, dtdx:0, dsdy:0, dtdy:1},
202  }
203}
204
205function transform_layers(layers) {
206  var idToItem = {};
207  var isChild = {}
208
209  var layersList = layers.layers || [];
210
211  layersList.forEach((e) => {
212    idToItem[e.id] = e;
213  });
214  layersList.forEach((e) => {
215    e.resolvedChildren = [];
216    if (Array.isArray(e.children)) {
217      e.resolvedChildren = e.children.map(
218          (childId) => idToItem[childId] || missingLayer(childId));
219      e.children.forEach((childId) => {
220        isChild[childId] = true;
221      });
222    }
223    if ((e.zOrderRelativeOf || -1) !== -1) {
224      idToItem[e.zOrderRelativeOf].zOrderRelativeParentOf = e.id;
225    }
226  });
227
228  var roots = layersList.filter((e) => !isChild[e.id]);
229
230  function foreachTree(nodes, fun) {
231    nodes.forEach((n) => {
232      fun(n);
233      foreachTree(n.children, fun);
234    });
235  }
236
237  var idToTransformed = {};
238  var transformed_roots = roots.map((r) =>
239    transform_layer(r, {parentBounds: {left: 0, right: 0, top: 0, bottom: 0},
240      parentHidden: false}));
241
242  foreachTree(transformed_roots, (n) => {
243    idToTransformed[n.obj.id] = n;
244  });
245  var flattened = [];
246  layersList.forEach((e) => {
247    flattened.push(idToTransformed[e.id]);
248  });
249
250  return transform({
251    obj: {},
252    kind: 'layers',
253    name: 'layers',
254    children: [
255      [transformed_roots, (c) => c],
256    ],
257    rects_transform (r) {
258      var res = [];
259      flattened.forEach((l) => {
260        if (l.rect) {
261          res.push(l.rect);
262        }
263      });
264      return res.reverse();
265    },
266    flattened,
267  });
268}
269
270function transform_layers_entry(entry) {
271  return transform({
272    obj: entry,
273    kind: 'entry',
274    name: nanos_to_string(entry.elapsedRealtimeNanos) + " - " + entry.where,
275    children: [
276      [[entry.layers], transform_layers],
277    ],
278    timestamp: entry.elapsedRealtimeNanos,
279    stableId: 'entry',
280  });
281}
282
283function transform_layers_trace(entries) {
284  var r = transform({
285    obj: entries,
286    kind: 'layerstrace',
287    name: 'layerstrace',
288    children: [
289      [entries.entry, transform_layers_entry],
290    ],
291  });
292
293  return r;
294}
295
296export {transform_layers, transform_layers_trace};
297