1 /* 2 * 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.device.traces.parsers.surfaceflinger 18 19 import android.surfaceflinger.Common 20 import android.surfaceflinger.Display 21 import android.surfaceflinger.Layers 22 import android.surfaceflinger.Layerstrace 23 import android.tools.common.Timestamp 24 import android.tools.common.Timestamps 25 import android.tools.common.datatypes.ActiveBuffer 26 import android.tools.common.datatypes.Color 27 import android.tools.common.datatypes.Matrix33 28 import android.tools.common.datatypes.Rect 29 import android.tools.common.datatypes.RectF 30 import android.tools.common.datatypes.Region 31 import android.tools.common.datatypes.Size 32 import android.tools.common.parsers.AbstractTraceParser 33 import android.tools.common.traces.surfaceflinger.HwcCompositionType 34 import android.tools.common.traces.surfaceflinger.Layer 35 import android.tools.common.traces.surfaceflinger.LayerTraceEntry 36 import android.tools.common.traces.surfaceflinger.LayerTraceEntryBuilder 37 import android.tools.common.traces.surfaceflinger.LayersTrace 38 import android.tools.common.traces.surfaceflinger.Transform 39 import android.tools.common.traces.surfaceflinger.Transform.Companion.isFlagClear 40 import android.tools.common.traces.surfaceflinger.Transform.Companion.isFlagSet 41 42 /** Parser for [LayersTrace] objects containing traces or state dumps */ 43 class LayersTraceParser( 44 private val ignoreLayersStackMatchNoDisplay: Boolean = true, 45 private val ignoreLayersInVirtualDisplay: Boolean = true, 46 private val legacyTrace: Boolean = false, 47 private val orphanLayerCallback: ((Layer) -> Boolean)? = null, 48 ) : 49 AbstractTraceParser< 50 Layerstrace.LayersTraceFileProto, Layerstrace.LayersTraceProto, LayerTraceEntry, LayersTrace 51 >() { 52 private var realToElapsedTimeOffsetNanos = 0L 53 54 override val traceName: String = "Layers Trace" 55 doDecodeByteArraynull56 override fun doDecodeByteArray(bytes: ByteArray): Layerstrace.LayersTraceFileProto = 57 Layerstrace.LayersTraceFileProto.parseFrom(bytes) 58 59 override fun createTrace(entries: List<LayerTraceEntry>): LayersTrace = 60 LayersTrace(entries.toTypedArray()) 61 62 override fun getEntries( 63 input: Layerstrace.LayersTraceFileProto 64 ): List<Layerstrace.LayersTraceProto> = input.entryList 65 66 override fun getTimestamp(entry: Layerstrace.LayersTraceProto): Timestamp { 67 require(legacyTrace || realToElapsedTimeOffsetNanos != 0L) 68 return Timestamps.from( 69 systemUptimeNanos = entry.elapsedRealtimeNanos, 70 unixNanos = entry.elapsedRealtimeNanos + realToElapsedTimeOffsetNanos 71 ) 72 } 73 onBeforeParsenull74 override fun onBeforeParse(input: Layerstrace.LayersTraceFileProto) { 75 realToElapsedTimeOffsetNanos = input.realToElapsedTimeOffsetNanos 76 } 77 doParseEntrynull78 override fun doParseEntry(entry: Layerstrace.LayersTraceProto): LayerTraceEntry { 79 val layers = entry.layers.layersList.map { newLayer(it) }.toTypedArray() 80 val displays = entry.displaysList.map { newDisplay(it) }.toTypedArray() 81 val builder = 82 LayerTraceEntryBuilder() 83 .setElapsedTimestamp(entry.elapsedRealtimeNanos.toString()) 84 .setLayers(layers) 85 .setDisplays(displays) 86 .setVSyncId(entry.vsyncId.toString()) 87 .setHwcBlob(entry.hwcBlob) 88 .setWhere(entry.where) 89 .setRealToElapsedTimeOffsetNs(realToElapsedTimeOffsetNanos.toString()) 90 .setOrphanLayerCallback(orphanLayerCallback) 91 .ignoreLayersStackMatchNoDisplay(ignoreLayersStackMatchNoDisplay) 92 .ignoreVirtualDisplay(ignoreLayersInVirtualDisplay) 93 return builder.build() 94 } 95 96 companion object { newLayernull97 private fun newLayer( 98 proto: Layers.LayerProto, 99 excludeCompositionState: Boolean = false 100 ): Layer { 101 // Differentiate between the cases when there's no HWC data on 102 // the trace, and when the visible region is actually empty 103 val activeBuffer = proto.activeBuffer.toBuffer() 104 val visibleRegion = proto.visibleRegion.toRegion() ?: Region.EMPTY 105 val crop = proto.crop?.toCropRect() 106 return Layer.from( 107 proto.name ?: "", 108 proto.id, 109 proto.parent, 110 proto.z, 111 visibleRegion, 112 activeBuffer, 113 proto.flags, 114 proto.bounds?.toRectF() ?: RectF.EMPTY, 115 proto.color.toColor(), 116 proto.isOpaque, 117 proto.shadowRadius, 118 proto.cornerRadius, 119 proto.type ?: "", 120 proto.screenBounds?.toRectF() ?: RectF.EMPTY, 121 createTransformFromProto(proto.transform, proto.position), 122 proto.sourceBounds?.toRectF() ?: RectF.EMPTY, 123 proto.currFrame, 124 proto.effectiveScalingMode, 125 createTransformFromProto(proto.bufferTransform, position = null), 126 toHwcCompositionType(proto.hwcCompositionType), 127 proto.hwcCrop.toRectF() ?: RectF.EMPTY, 128 proto.hwcFrame.toRect(), 129 proto.backgroundBlurRadius, 130 crop, 131 proto.isRelativeOf, 132 proto.zOrderRelativeOf, 133 proto.layerStack, 134 createTransformFromProto(proto.transform, position = proto.requestedPosition), 135 proto.requestedColor.toColor(), 136 proto.cornerRadiusCrop?.toRectF() ?: RectF.EMPTY, 137 createTransformFromProto(proto.inputWindowInfo?.transform, position = null), 138 proto.inputWindowInfo?.touchableRegion?.toRegion(), 139 excludeCompositionState 140 ) 141 } 142 newDisplaynull143 private fun newDisplay( 144 proto: Display.DisplayProto 145 ): android.tools.common.traces.surfaceflinger.Display { 146 return android.tools.common.traces.surfaceflinger.Display.from( 147 "${proto.id}", 148 proto.name, 149 proto.layerStack, 150 proto.size.toSize(), 151 proto.layerStackSpaceRect.toRect(), 152 createTransformFromProto(proto.transform, position = null), 153 proto.isVirtual 154 ) 155 } 156 toRectFnull157 private fun Layers.FloatRectProto?.toRectF(): RectF? { 158 return this?.let { RectF.from(left = left, top = top, right = right, bottom = bottom) } 159 } 160 toSizenull161 private fun Common.SizeProto?.toSize(): Size { 162 return this?.let { Size.from(this.w, this.h) } ?: Size.EMPTY 163 } 164 toColornull165 private fun Common.ColorProto?.toColor(): Color { 166 return this?.let { Color.from(r, g, b, a) } ?: Color.EMPTY 167 } 168 toBuffernull169 private fun Layers.ActiveBufferProto?.toBuffer(): ActiveBuffer { 170 return this?.let { ActiveBuffer.from(width, height, stride, format) } 171 ?: ActiveBuffer.EMPTY 172 } 173 toHwcCompositionTypenull174 private fun toHwcCompositionType(value: Layers.HwcCompositionType): HwcCompositionType { 175 return when (value) { 176 Layers.HwcCompositionType.INVALID -> HwcCompositionType.INVALID 177 Layers.HwcCompositionType.CLIENT -> HwcCompositionType.CLIENT 178 Layers.HwcCompositionType.DEVICE -> HwcCompositionType.DEVICE 179 Layers.HwcCompositionType.SOLID_COLOR -> HwcCompositionType.SOLID_COLOR 180 Layers.HwcCompositionType.CURSOR -> HwcCompositionType.CURSOR 181 Layers.HwcCompositionType.SIDEBAND -> HwcCompositionType.SIDEBAND 182 else -> HwcCompositionType.UNRECOGNIZED 183 } 184 } 185 toCropRectnull186 private fun Common.RectProto?.toCropRect(): Rect? { 187 return when { 188 this == null -> Rect.EMPTY 189 // crop (0,0) (-1,-1) means no crop 190 right == -1 && left == 0 && bottom == -1 && top == 0 -> null 191 (right - left) <= 0 || (bottom - top) <= 0 -> Rect.EMPTY 192 else -> Rect.from(left, top, right, bottom) 193 } 194 } 195 196 /** 197 * Extracts [Rect] from [Common.RegionProto] by returning a rect that encompasses all the 198 * rectangles making up the region. 199 */ toRegionnull200 private fun Common.RegionProto?.toRegion(): Region? { 201 return this?.let { 202 val rectArray = this.rectList.map { it.toRect() }.toTypedArray() 203 return Region(rectArray) 204 } 205 } 206 toRectnull207 private fun Common.RectProto?.toRect(): Rect = 208 Rect.from(this?.left ?: 0, this?.top ?: 0, this?.right ?: 0, this?.bottom ?: 0) 209 210 fun createTransformFromProto( 211 transform: Common.TransformProto?, 212 position: Layers.PositionProto? 213 ) = Transform.from(transform?.type, getMatrix(transform, position)) 214 215 private fun getMatrix( 216 transform: Common.TransformProto?, 217 position: Layers.PositionProto? 218 ): Matrix33 { 219 val x = position?.x ?: 0f 220 val y = position?.y ?: 0f 221 222 return when { 223 transform == null || Transform.isSimpleTransform(transform.type) -> 224 transform?.type.getDefaultTransform(x, y) 225 else -> 226 Matrix33.from( 227 transform.dsdx, 228 transform.dtdx, 229 x, 230 transform.dsdy, 231 transform.dtdy, 232 y 233 ) 234 } 235 } 236 getDefaultTransformnull237 private fun Int?.getDefaultTransform(x: Float, y: Float): Matrix33 { 238 return when { 239 // IDENTITY 240 this == null -> Matrix33.identity(x, y) 241 // // ROT_270 = ROT_90|FLIP_H|FLIP_V 242 isFlagSet(Transform.ROT_90_VAL or Transform.FLIP_V_VAL or Transform.FLIP_H_VAL) -> 243 Matrix33.rot270(x, y) 244 // ROT_180 = FLIP_H|FLIP_V 245 isFlagSet(Transform.FLIP_V_VAL or Transform.FLIP_H_VAL) -> Matrix33.rot180(x, y) 246 // ROT_90 247 isFlagSet(Transform.ROT_90_VAL) -> Matrix33.rot90(x, y) 248 // IDENTITY 249 isFlagClear(Transform.SCALE_VAL or Transform.ROTATE_VAL) -> Matrix33.identity(x, y) 250 else -> throw IllegalStateException("Unknown transform type $this") 251 } 252 } 253 } 254 } 255