• 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
17// eslint-disable-next-line camelcase
18import {transform, nanos_to_string, get_visible_chip} from './transform.js';
19// eslint-disable-next-line camelcase
20import {fill_occlusion_state, fill_inherited_state} from './sf_visibility.js';
21import {getSimplifiedLayerName} from './utils/names';
22import ObjectFormatter from './flickerlib/ObjectFormatter'
23
24const RELATIVE_Z_CHIP = {
25  short: 'RelZ',
26  long: 'Is relative Z-ordered to another surface',
27  class: 'warn',
28};
29const RELATIVE_Z_PARENT_CHIP = {
30  short: 'RelZParent',
31  long: 'Something is relative Z-ordered to this surface',
32  class: 'warn',
33};
34const MISSING_LAYER = {
35  short: 'MissingLayer',
36  long:
37    'This layer was referenced from the parent, but not present in the trace',
38  class: 'error',
39};
40const GPU_CHIP = {
41  short: 'GPU',
42  long: 'This layer was composed on the GPU',
43  class: 'gpu',
44};
45const HWC_CHIP = {
46  short: 'HWC',
47  long: 'This layer was composed by Hardware Composer',
48  class: 'hwc',
49};
50
51function transformLayer(layer) {
52  function offsetTo(bounds, x, y) {
53    return {
54      right: bounds.right - (bounds.left - x),
55      bottom: bounds.bottom - (bounds.top - y),
56      left: x,
57      top: y,
58    };
59  }
60
61  function getRect(layer) {
62    let result = layer.bounds;
63    const tx = layer.position ? layer.position.x || 0 : 0;
64    const ty = layer.position ? layer.position.y || 0 : 0;
65    result.label = layer.name;
66    result.transform = layer.transform;
67    result.transform.tx = tx;
68    result.transform.ty = ty;
69    return result;
70  }
71
72  function addHwcCompositionTypeChip(layer) {
73    if (layer.hwcCompositionType === 'CLIENT') {
74      chips.push(GPU_CHIP);
75    } else if (layer.hwcCompositionType === 'DEVICE' ||
76        layer.hwcCompositionType === 'SOLID_COLOR') {
77      chips.push(HWC_CHIP);
78    }
79  }
80
81  const chips = [];
82  if (layer.visible) {
83    chips.push(get_visible_chip());
84  }
85  if ((layer.zOrderRelativeOf || -1) !== -1) {
86    chips.push(RELATIVE_Z_CHIP);
87  }
88  if (layer.zOrderRelativeParentOf !== undefined) {
89    chips.push(RELATIVE_Z_PARENT_CHIP);
90  }
91  if (layer.missing) {
92    chips.push(MISSING_LAYER);
93  }
94  addHwcCompositionTypeChip(layer);
95
96  const rect = layer.visible && layer.bounds !== null ?
97      getRect(layer) : undefined;
98
99  const simplifiedLayerName = getSimplifiedLayerName(layer.name);
100  const shortName = simplifiedLayerName ?
101      layer.id + ': ' + simplifiedLayerName : undefined;
102
103  const transformedLayer = transform({
104    obj: ObjectFormatter.format(layer),
105    kind: '',
106    name: layer.id + ': ' + layer.name,
107    shortName,
108    children: [[layer.resolvedChildren, transformLayer]],
109    rect,
110    undefined /* bounds */,
111    highlight: rect,
112    chips,
113    visible: layer.visible,
114    freeze: false,
115  });
116
117  // NOTE: Temporary until refactored to use flickerlib
118  transformedLayer.invisibleDueTo = layer.invisibleDueTo;
119  transformedLayer.occludedBy = layer.occludedBy;
120  transformedLayer.partiallyOccludedBy = layer.partiallyOccludedBy;
121  transformedLayer.coveredBy = layer.coveredBy;
122
123  return Object.freeze(transformedLayer);
124}
125
126function missingLayer(childId) {
127  return {
128    name: 'layer #' + childId,
129    missing: true,
130    zOrderRelativeOf: -1,
131    transform: {dsdx: 1, dtdx: 0, dsdy: 0, dtdy: 1},
132  };
133}
134
135function transformLayers(includesCompositionState, layers) {
136  const idToItem = {};
137  const isChild = {};
138
139  const layersList = layers.layers || [];
140
141  layersList.forEach((e) => {
142    idToItem[e.id] = e;
143  });
144  layersList.forEach((e) => {
145    e.resolvedChildren = [];
146    if (Array.isArray(e.children)) {
147      e.resolvedChildren = e.children.map(
148          (childId) => idToItem[childId] || missingLayer(childId));
149      e.children.forEach((childId) => {
150        isChild[childId] = true;
151      });
152    }
153    // We don't clean up relatives when the relative parent is removed, so it
154    // may be inconsistent
155    if ((e.zOrderRelativeOf || -1) !== -1 && (idToItem[e.zOrderRelativeOf])) {
156      idToItem[e.zOrderRelativeOf].zOrderRelativeParentOf = e.id;
157    }
158  });
159
160  const roots = layersList.filter((e) => !isChild[e.id]);
161  fill_inherited_state(idToItem, roots);
162
163  // Backwards compatibility check
164  const occlusionDetectionCompatible = roots[0].bounds !== null;
165  if (occlusionDetectionCompatible) {
166    fill_occlusion_state(idToItem, roots, includesCompositionState);
167  }
168  function foreachTree(nodes, fun) {
169    nodes.forEach((n) => {
170      fun(n);
171      foreachTree(n.children, fun);
172    });
173  }
174
175  const idToTransformed = {};
176  const transformedRoots = roots.map((r) =>
177    transformLayer(r, {
178      parentBounds: {left: 0, right: 0, top: 0, bottom: 0},
179      parentHidden: null,
180    }));
181
182  foreachTree(transformedRoots, (n) => {
183    idToTransformed[n.obj.id] = n;
184  });
185  const flattened = [];
186  layersList.forEach((e) => {
187    flattened.push(idToTransformed[e.id]);
188  });
189
190  return transform({
191    obj: {},
192    kind: 'layers',
193    name: 'layers',
194    children: [
195      [transformedRoots, (c) => c],
196    ],
197    rectsTransform(r) {
198      const res = [];
199      flattened.forEach((l) => {
200        if (l.rect) {
201          res.push(l.rect);
202        }
203      });
204      return res.reverse();
205    },
206    flattened,
207  });
208}
209
210function transformLayersEntry(entry) {
211  const includesCompositionState = !entry.excludesCompositionState;
212  return transform({
213    obj: entry,
214    kind: 'entry',
215    name: nanos_to_string(entry.elapsedRealtimeNanos) + ' - ' + entry.where,
216    children: [
217      [
218        [entry.layers],
219        (layer) => transformLayers(includesCompositionState, layer),
220      ],
221    ],
222    timestamp: entry.elapsedRealtimeNanos,
223    stableId: 'entry',
224  });
225}
226
227function transformLayersTrace(entries) {
228  const r = transform({
229    obj: entries,
230    kind: 'layerstrace',
231    name: 'layerstrace',
232    children: [
233      [entries.entry, transformLayersEntry],
234    ],
235  });
236
237  return r;
238}
239
240export {transformLayers, transformLayersTrace};
241