• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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