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