1 /* <lambda>null2 * Copyright (C) 2023 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 package android.tools.traces.surfaceflinger 18 19 import android.tools.Timestamp 20 21 /** Builder for LayerTraceEntries */ 22 class LayerTraceEntryBuilder { 23 private var elapsedTimestamp: Long = 0L 24 private var realTimestamp: Long? = null 25 private var orphanLayerCallback: ((Layer) -> Boolean)? = null 26 private val orphans = mutableListOf<Layer>() 27 private var layers: MutableMap<Int, Layer> = mutableMapOf() 28 private var ignoreVirtualDisplay = false 29 private var ignoreLayersStackMatchNoDisplay = false 30 private var timestamp: Timestamp? = null 31 private var displays: Collection<Display> = emptyList() 32 private var vSyncId: Long = 0L 33 private var hwcBlob: String = "" 34 private var where: String = "" 35 private var duplicateLayerCallback: ((Layer) -> Boolean) = { 36 error("Duplicate layer id found: ${it.id}") 37 } 38 39 fun setVSyncId(vSyncId: Long): LayerTraceEntryBuilder = apply { this.vSyncId = vSyncId } 40 41 fun setHwcBlob(hwcBlob: String): LayerTraceEntryBuilder = apply { this.hwcBlob = hwcBlob } 42 43 fun setWhere(where: String): LayerTraceEntryBuilder = apply { this.where = where } 44 45 fun setDisplays(displays: Collection<Display>): LayerTraceEntryBuilder = apply { 46 this.displays = displays 47 } 48 49 fun setElapsedTimestamp(timestamp: Long): LayerTraceEntryBuilder = apply { 50 this.elapsedTimestamp = timestamp 51 } 52 53 fun setRealToElapsedTimeOffsetNs(realToElapsedTimeOffsetNs: Long?): LayerTraceEntryBuilder = 54 apply { 55 this.realTimestamp = 56 if (realToElapsedTimeOffsetNs != null && realToElapsedTimeOffsetNs != 0L) { 57 realToElapsedTimeOffsetNs + elapsedTimestamp 58 } else { 59 null 60 } 61 } 62 63 fun setLayers(layers: Collection<Layer>): LayerTraceEntryBuilder = apply { 64 val result = mutableMapOf<Int, Layer>() 65 layers.forEach { layer -> 66 val id = layer.id 67 if (result.containsKey(id)) { 68 duplicateLayerCallback(layer) 69 } 70 result[id] = layer 71 } 72 73 this.layers = result 74 } 75 76 fun setOrphanLayerCallback(value: ((Layer) -> Boolean)?): LayerTraceEntryBuilder = apply { 77 this.orphanLayerCallback = value 78 } 79 80 fun setDuplicateLayerCallback(value: ((Layer) -> Boolean)): LayerTraceEntryBuilder = apply { 81 this.duplicateLayerCallback = value 82 } 83 84 private fun notifyOrphansLayers() { 85 val callback = this.orphanLayerCallback ?: return 86 87 // Fail if we find orphan layers. 88 orphans.forEach { orphan -> 89 // Workaround for b/141326137, ignore the existence of an orphan layer 90 if (callback.invoke(orphan)) { 91 return@forEach 92 } 93 throw RuntimeException( 94 ("Failed to parse layers trace. Found orphan layer with id = ${orphan.id}" + 95 " with parentId = ${orphan.parentId}") 96 ) 97 } 98 } 99 100 /** 101 * Update the parent layers or each trace 102 * 103 * @return root layer 104 */ 105 private fun updateParents() { 106 for (layer in layers.values) { 107 val parentId = layer.parentId 108 109 val parentLayer = layers[parentId] 110 if (parentLayer == null) { 111 orphans.add(layer) 112 continue 113 } 114 parentLayer.addChild(layer) 115 layer.parent = parentLayer 116 } 117 } 118 119 /** 120 * Update the parent layers or each trace 121 * 122 * @return root layer 123 */ 124 private fun updateRelZParents() { 125 for (layer in layers.values) { 126 val parentId = layer.zOrderRelativeOfId 127 128 val parentLayer = layers[parentId] 129 if (parentLayer == null) { 130 layer.zOrderRelativeParentOf = parentId 131 continue 132 } 133 layer.zOrderRelativeOf = parentLayer 134 } 135 } 136 137 private fun computeRootLayers(): Collection<Layer> { 138 updateParents() 139 updateRelZParents() 140 141 // Find all root layers (any sibling of the root layer is considered a root layer in the 142 // trace) 143 val rootLayers = mutableListOf<Layer>() 144 145 // Getting the first orphan works because when dumping the layers, the root layer comes 146 // first, and given that orphans are added in the same order as the layers are provided 147 // in the first orphan layer should be the root layer. 148 if (orphans.isNotEmpty()) { 149 val firstRoot = orphans.first() 150 orphans.remove(firstRoot) 151 rootLayers.add(firstRoot) 152 153 val remainingRoots = orphans.filter { it.parentId == firstRoot.parentId } 154 rootLayers.addAll(remainingRoots) 155 156 // Remove RootLayers from orphans 157 orphans.removeAll(rootLayers) 158 } 159 160 return rootLayers 161 } 162 163 private fun filterOutLayersInVirtualDisplays(roots: Collection<Layer>): Collection<Layer> { 164 val physicalDisplays = displays.filterNot { it.isVirtual }.map { it.layerStackId } 165 166 return roots.filter { physicalDisplays.contains(it.stackId) } 167 } 168 169 private fun filterOutVirtualDisplays(displays: Collection<Display>): Collection<Display> { 170 return displays.filterNot { it.isVirtual } 171 } 172 173 private fun filterOutLayersInOffDisplays(roots: Collection<Layer>): Collection<Layer> { 174 val offDisplays = displays.filter { it.isOff }.map { it.layerStackId } 175 176 // Negated filtering because legacy traces do not contain any displays, so we don't want to 177 // remove all traces since displays will be empty, so we won't have any on or off displays 178 return roots.filterNot { offDisplays.contains(it.stackId) } 179 } 180 181 private fun filterOutLayersStackMatchNoDisplay(roots: Collection<Layer>): Collection<Layer> { 182 val displayStacks = displays.map { it.layerStackId } 183 return roots.filter { displayStacks.contains(it.stackId) } 184 } 185 186 /** 187 * Defines if virtual displays and the layers belonging to virtual displays (e.g., Screen 188 * Recording) should be ignored while parsing the entry 189 * 190 * @param ignore If the layers from virtual displays should be ignored or not 191 */ 192 fun ignoreVirtualDisplay(ignore: Boolean): LayerTraceEntryBuilder = apply { 193 this.ignoreVirtualDisplay = ignore 194 } 195 196 /** 197 * Ignore layers whose stack ID doesn't match any display. This is the case, for example, when 198 * the device screen is off, or for layers that have not yet been removed after a display change 199 * (e.g., virtual screen recording display removed) 200 * 201 * @param ignore If the layers not matching any stack id should be removed or not 202 */ 203 fun ignoreLayersStackMatchNoDisplay(ignore: Boolean): LayerTraceEntryBuilder = apply { 204 this.ignoreLayersStackMatchNoDisplay = ignore 205 } 206 207 /** Constructs the layer hierarchy from a flattened list of layers. */ 208 fun build(): LayerTraceEntry { 209 val allRoots = computeRootLayers() 210 var filteredRoots = allRoots 211 var filteredDisplays = displays 212 213 if (ignoreLayersStackMatchNoDisplay) { 214 filteredRoots = filterOutLayersStackMatchNoDisplay(filteredRoots) 215 } 216 217 if (ignoreVirtualDisplay) { 218 filteredRoots = filterOutLayersInVirtualDisplays(filteredRoots) 219 filteredDisplays = filterOutVirtualDisplays(filteredDisplays) 220 } 221 222 filteredRoots = filterOutLayersInOffDisplays(filteredRoots) 223 224 // Fail if we find orphan layers. 225 notifyOrphansLayers() 226 227 return LayerTraceEntry( 228 elapsedTimestamp, 229 realTimestamp, 230 hwcBlob, 231 where, 232 filteredDisplays, 233 vSyncId, 234 filteredRoots, 235 ) 236 } 237 } 238