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'
18import { fill_occlusion_state, fill_inherited_state } from './sf_visibility.js';
19
20var RELATIVE_Z_CHIP = {short: 'RelZ',
21    long: "Is relative Z-ordered to another surface",
22    class: 'warn'};
23var RELATIVE_Z_PARENT_CHIP = {short: 'RelZParent',
24    long: "Something is relative Z-ordered to this surface",
25    class: 'warn'};
26var MISSING_LAYER = {short: 'MissingLayer',
27    long: "This layer was referenced from the parent, but not present in the trace",
28    class: 'error'};
29var GPU_CHIP = {short: 'GPU',
30    long: "This layer was composed on the GPU",
31    class: 'gpu'};
32var HWC_CHIP = {short: 'HWC',
33    long: "This layer was composed by Hardware Composer",
34    class: 'hwc'};
35
36function transform_layer(layer) {
37  function offset_to(bounds, x, y) {
38    return {
39      right: bounds.right - (bounds.left - x),
40      bottom: bounds.bottom - (bounds.top - y),
41      left: x,
42      top: y,
43    };
44  }
45
46  function get_rect(layer) {
47    var result = layer.bounds;
48    var tx = layer.position ? layer.position.x || 0 : 0;
49    var ty = layer.position ? layer.position.y || 0 : 0;
50    result = offset_to(result, 0, 0);
51    result.label = layer.name;
52    result.transform = layer.transform;
53    result.transform.tx = tx;
54    result.transform.ty = ty;
55    return result;
56  }
57
58  function add_hwc_composition_type_chip(layer) {
59      if (layer.hwcCompositionType === "CLIENT") {
60          chips.push(GPU_CHIP);
61      } else if (layer.hwcCompositionType === "DEVICE" || layer.hwcCompositionType === "SOLID_COLOR") {
62          chips.push(HWC_CHIP);
63      }
64  }
65
66  var chips = [];
67  if (layer.visible) {
68    chips.push(get_visible_chip());
69  }
70  if ((layer.zOrderRelativeOf || -1) !== -1) {
71    chips.push(RELATIVE_Z_CHIP);
72  }
73  if (layer.zOrderRelativeParentOf !== undefined) {
74    chips.push(RELATIVE_Z_PARENT_CHIP);
75  }
76  if (layer.missing) {
77    chips.push(MISSING_LAYER);
78  }
79  add_hwc_composition_type_chip(layer);
80  const rect = layer.visible ? get_rect(layer) : undefined;
81
82  return transform({
83    obj: layer,
84    kind: '',
85    name: layer.id + ": " + layer.name,
86    children: [[layer.resolvedChildren, transform_layer]],
87    rect,
88    undefined /* bounds */,
89    highlight: rect,
90    chips,
91    visible: layer.visible,
92  });
93}
94
95function missingLayer(childId) {
96  return {
97    name: "layer #" + childId,
98    missing: true,
99    zOrderRelativeOf: -1,
100    transform: {dsdx:1, dtdx:0, dsdy:0, dtdy:1},
101  }
102}
103
104function transform_layers(includesCompositionState, layers) {
105  var idToItem = {};
106  var isChild = {}
107
108  var layersList = layers.layers || [];
109
110  layersList.forEach((e) => {
111    idToItem[e.id] = e;
112  });
113  layersList.forEach((e) => {
114    e.resolvedChildren = [];
115    if (Array.isArray(e.children)) {
116      e.resolvedChildren = e.children.map(
117          (childId) => idToItem[childId] || missingLayer(childId));
118      e.children.forEach((childId) => {
119        isChild[childId] = true;
120      });
121    }
122    if ((e.zOrderRelativeOf || -1) !== -1) {
123      idToItem[e.zOrderRelativeOf].zOrderRelativeParentOf = e.id;
124    }
125  });
126
127  var roots = layersList.filter((e) => !isChild[e.id]);
128  fill_inherited_state(idToItem, roots);
129  fill_occlusion_state(idToItem, roots, includesCompositionState);
130  function foreachTree(nodes, fun) {
131    nodes.forEach((n) => {
132      fun(n);
133      foreachTree(n.children, fun);
134    });
135  }
136
137  var idToTransformed = {};
138  var transformed_roots = roots.map((r) =>
139    transform_layer(r, {parentBounds: {left: 0, right: 0, top: 0, bottom: 0},
140      parentHidden: null}));
141
142  foreachTree(transformed_roots, (n) => {
143    idToTransformed[n.obj.id] = n;
144  });
145  var flattened = [];
146  layersList.forEach((e) => {
147    flattened.push(idToTransformed[e.id]);
148  });
149
150  return transform({
151    obj: {},
152    kind: 'layers',
153    name: 'layers',
154    children: [
155      [transformed_roots, (c) => c],
156    ],
157    rects_transform (r) {
158      var res = [];
159      flattened.forEach((l) => {
160        if (l.rect) {
161          res.push(l.rect);
162        }
163      });
164      return res.reverse();
165    },
166    flattened,
167  });
168}
169
170function transform_layers_entry(entry) {
171  const includesCompositionState = !entry.excludesCompositionState;
172  return transform({
173    obj: entry,
174    kind: 'entry',
175    name: nanos_to_string(entry.elapsedRealtimeNanos) + " - " + entry.where,
176    children: [
177      [[entry.layers], (layer) => transform_layers(includesCompositionState, layer)],
178    ],
179    timestamp: entry.elapsedRealtimeNanos,
180    stableId: 'entry',
181  });
182}
183
184function transform_layers_trace(entries) {
185  var r = transform({
186    obj: entries,
187    kind: 'layerstrace',
188    name: 'layerstrace',
189    children: [
190      [entries.entry, transform_layers_entry],
191    ],
192  });
193
194  return r;
195}
196
197export {transform_layers, transform_layers_trace};
198