1/* 2 * Copyright (C) 2024 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 {assertDefined} from 'common/assert_utils'; 18import {DuplicateLayerIds, MissingLayerIds} from 'messaging/user_warnings'; 19import {perfetto} from 'protos/surfaceflinger/latest/static'; 20import {android} from 'protos/surfaceflinger/udc/static'; 21import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node'; 22import { 23 LazyPropertiesStrategyType, 24 PropertiesProvider, 25} from 'trace/tree_node/properties_provider'; 26import {PropertiesProviderBuilder} from 'trace/tree_node/properties_provider_builder'; 27import {PropertyTreeBuilderFromProto} from 'trace/tree_node/property_tree_builder_from_proto'; 28import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 29import {COMMON_OPERATIONS} from './common_operations'; 30import {RectsComputation} from './computations/rects_computation'; 31import {VisibilityPropertiesComputation} from './computations/visibility_properties_computation'; 32import {ZOrderPathsComputation} from './computations/z_order_paths_computation'; 33import {DENYLIST_PROPERTIES} from './denylist_properties'; 34import {EAGER_PROPERTIES} from './eager_properties'; 35import {HierarchyTreeBuilderSf} from './hierarchy_tree_builder_sf'; 36import {ParserSurfaceFlinger as LegacyParserSurfaceFlinger} from './legacy/parser_surface_flinger'; 37import {ParserSurfaceFlinger as PerfettoParserSurfaceFlinger} from './perfetto/parser_surface_flinger'; 38 39export class EntryHierarchyTreeFactory { 40 makeEntryHierarchyTree( 41 entryProto: EntryType, 42 layerProtos: LayerType[], 43 ParserSurfaceFlinger: ParserSurfaceFlinger, 44 ): HierarchyTreeNode { 45 const excludesCompositionState = 46 entryProto?.excludesCompositionState ?? true; 47 const addExcludesCompositionState = excludesCompositionState 48 ? COMMON_OPERATIONS.AddExcludesCompositionStateTrue 49 : COMMON_OPERATIONS.AddExcludesCompositionStateFalse; 50 51 const processed = new Map<number, number>(); 52 let missingLayerIds = false; 53 54 const layers = layerProtos.reduce( 55 (allLayerProps: PropertiesProvider[], layer: LayerType) => { 56 if (layer.id === null || layer.id === undefined) { 57 missingLayerIds = true; 58 return allLayerProps; 59 } 60 const duplicateCount = processed.get(assertDefined(layer.id)) ?? 0; 61 processed.set(assertDefined(layer.id), duplicateCount + 1); 62 const eagerProperties = this.makeEagerPropertiesTree( 63 layer, 64 duplicateCount, 65 ); 66 const lazyPropertiesStrategy = this.makeLayerLazyPropertiesStrategy( 67 layer, 68 duplicateCount, 69 ); 70 71 const layerProps = new PropertiesProviderBuilder() 72 .setEagerProperties(eagerProperties) 73 .setLazyPropertiesStrategy(lazyPropertiesStrategy) 74 .setCommonOperations([ 75 ParserSurfaceFlinger.Operations.SetFormattersLayer, 76 ParserSurfaceFlinger.Operations.TranslateIntDefLayer, 77 ]) 78 .setEagerOperations([ 79 ParserSurfaceFlinger.Operations.AddDefaultsLayerEager, 80 COMMON_OPERATIONS.AddCompositionType, 81 COMMON_OPERATIONS.UpdateTransforms, 82 COMMON_OPERATIONS.AddVerboseFlags, 83 addExcludesCompositionState, 84 ]) 85 .setLazyOperations([ 86 ParserSurfaceFlinger.Operations.AddDefaultsLayerLazy, 87 ]) 88 .build(); 89 allLayerProps.push(layerProps); 90 return allLayerProps; 91 }, 92 [] as PropertiesProvider[], 93 ); 94 95 const entry = new PropertiesProviderBuilder() 96 .setEagerProperties(this.makeEntryEagerPropertiesTree(entryProto)) 97 .setLazyPropertiesStrategy( 98 this.makeEntryLazyPropertiesStrategy(entryProto), 99 ) 100 .setCommonOperations([ 101 COMMON_OPERATIONS.AddDisplayProperties, 102 ParserSurfaceFlinger.Operations.SetFormattersEntry, 103 ParserSurfaceFlinger.Operations.TranslateIntDefEntry, 104 ]) 105 .setEagerOperations([ 106 ParserSurfaceFlinger.Operations.AddDefaultsEntryEager, 107 ]) 108 .setLazyOperations([ParserSurfaceFlinger.Operations.AddDefaultsEntryLazy]) 109 .build(); 110 111 const tree = new HierarchyTreeBuilderSf() 112 .setRoot(entry) 113 .setChildren(layers) 114 .setComputations([ 115 new ZOrderPathsComputation(), 116 new VisibilityPropertiesComputation(), 117 new RectsComputation(), 118 ]) 119 .build(); 120 121 if (missingLayerIds) { 122 tree.addWarning(new MissingLayerIds()); 123 } 124 const duplicateIds = Array.from(processed.keys()).filter( 125 (layerId) => assertDefined(processed.get(layerId)) > 1, 126 ); 127 if (duplicateIds.length > 0) { 128 tree.addWarning(new DuplicateLayerIds(duplicateIds)); 129 } 130 131 return tree; 132 } 133 134 private makeEagerPropertiesTree( 135 layer: LayerType, 136 duplicateCount: number, 137 ): PropertyTreeNode { 138 const denyList: string[] = []; 139 let obj = layer; 140 do { 141 Object.getOwnPropertyNames(obj).forEach((it) => { 142 if (!EAGER_PROPERTIES.includes(it)) denyList.push(it); 143 }); 144 obj = Object.getPrototypeOf(obj); 145 } while (obj); 146 147 return new PropertyTreeBuilderFromProto() 148 .setData(layer) 149 .setRootId(assertDefined(layer.id)) 150 .setRootName(assertDefined(layer.name)) 151 .setDenyList(denyList) 152 .setDuplicateCount(duplicateCount) 153 .build(); 154 } 155 156 private makeEntryEagerPropertiesTree(entry: EntryType): PropertyTreeNode { 157 const denyList: string[] = []; 158 let obj = entry; 159 do { 160 Object.getOwnPropertyNames(obj).forEach((it) => { 161 if (it !== 'displays') denyList.push(it); 162 }); 163 obj = Object.getPrototypeOf(obj); 164 } while (obj); 165 166 return new PropertyTreeBuilderFromProto() 167 .setData(entry) 168 .setRootId('LayerTraceEntry') 169 .setRootName('root') 170 .setDenyList(denyList) 171 .build(); 172 } 173 174 private makeLayerLazyPropertiesStrategy( 175 layer: LayerType, 176 duplicateCount: number, 177 ): LazyPropertiesStrategyType { 178 return async () => { 179 return new PropertyTreeBuilderFromProto() 180 .setData(layer) 181 .setRootId(assertDefined(layer.id)) 182 .setRootName(assertDefined(layer.name)) 183 .setDenyList(EAGER_PROPERTIES.concat(DENYLIST_PROPERTIES)) 184 .setDuplicateCount(duplicateCount) 185 .build(); 186 }; 187 } 188 189 private makeEntryLazyPropertiesStrategy( 190 entry: EntryType, 191 ): LazyPropertiesStrategyType { 192 return async () => { 193 return new PropertyTreeBuilderFromProto() 194 .setData(entry) 195 .setRootId('LayerTraceEntry') 196 .setRootName('root') 197 .setDenyList(DENYLIST_PROPERTIES) 198 .build(); 199 }; 200 } 201} 202 203type EntryType = 204 | android.surfaceflinger.ILayersTraceProto 205 | perfetto.protos.ILayersSnapshotProto; 206 207type LayerType = 208 | android.surfaceflinger.ILayerProto 209 | perfetto.protos.ILayerProto; 210 211type ParserSurfaceFlinger = 212 | typeof PerfettoParserSurfaceFlinger 213 | typeof LegacyParserSurfaceFlinger; 214