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.parsers.perfetto
18 
19 import android.graphics.Color
20 import android.graphics.Rect
21 import android.graphics.RectF
22 import android.graphics.Region
23 import android.tools.Timestamp
24 import android.tools.datatypes.ActiveBuffer
25 import android.tools.datatypes.Matrix33
26 import android.tools.datatypes.Size
27 import android.tools.datatypes.emptyColor
28 import android.tools.parsers.AbstractTraceParser
29 import android.tools.traces.surfaceflinger.Display
30 import android.tools.traces.surfaceflinger.HwcCompositionType
31 import android.tools.traces.surfaceflinger.Layer
32 import android.tools.traces.surfaceflinger.LayerTraceEntry
33 import android.tools.traces.surfaceflinger.LayerTraceEntryBuilder
34 import android.tools.traces.surfaceflinger.LayersTrace
35 import android.tools.traces.surfaceflinger.Transform
36 import android.tools.traces.surfaceflinger.Transform.Companion.isFlagClear
37 import android.tools.traces.surfaceflinger.Transform.Companion.isFlagSet
38 import android.tools.withCache
39 import android.tools.withTracing
40 
41 /** Parser for [LayersTrace] */
42 class LayersTraceParser(
43     private val ignoreLayersStackMatchNoDisplay: Boolean = true,
44     private val ignoreLayersInVirtualDisplay: Boolean = true,
45     private val orphanLayerCallback: ((Layer) -> Boolean)? = null,
46 ) : AbstractTraceParser<TraceProcessorSession, LayerTraceEntry, LayerTraceEntry, LayersTrace>() {
47 
48     override val traceName = "Layers trace (SF)"
49 
50     override fun createTrace(entries: Collection<LayerTraceEntry>): LayersTrace {
51         return LayersTrace(entries)
52     }
53 
54     override fun doDecodeByteArray(bytes: ByteArray): TraceProcessorSession {
55         error("This parser can only read from perfetto trace processor")
56     }
57 
58     override fun shouldParseEntry(entry: LayerTraceEntry) = true
59 
60     override fun getEntries(input: TraceProcessorSession): List<LayerTraceEntry> {
61         val realToMonotonicTimeOffsetNs =
62             queryRealToMonotonicTimeOffsetNs(input, "surfaceflinger_layers_snapshot")
63 
64         return input.query(getSqlQuerySnapshots()) { snapshotsRows ->
65             val traceEntries = mutableListOf<LayerTraceEntry>()
66             val snapshotGroups = snapshotsRows.groupBy { it["snapshot_id"] }
67 
68             for (snapshotId in 0L until snapshotGroups.size) {
69                 withTracing("query + build entry") {
70                     val layerRows =
71                         withTracing("query layer rows") {
72                             input.query(getSqlQueryLayers(snapshotId)) { it }
73                         }
74                     withTracing("build entry") {
75                         val snapshotRows = snapshotGroups[snapshotId]!!
76                         val entry =
77                             buildTraceEntry(snapshotRows, layerRows, realToMonotonicTimeOffsetNs)
78                         traceEntries.add(entry)
79                     }
80                 }
81             }
82 
83             traceEntries
84         }
85     }
86 
87     override fun getTimestamp(entry: LayerTraceEntry): Timestamp = entry.timestamp
88 
89     override fun onBeforeParse(input: TraceProcessorSession) {}
90 
91     override fun doParseEntry(entry: LayerTraceEntry) = entry
92 
93     private fun buildTraceEntry(
94         snapshotRows: List<Row>,
95         layersRows: List<Row>,
96         realToMonotonicTimeOffsetNs: Long
97     ): LayerTraceEntry {
98         val snapshotArgs = Args.build(snapshotRows)
99         val displays = snapshotArgs.getChildren("displays")?.map { newDisplay(it) } ?: emptyList()
100         val excludesCompositionState =
101             snapshotArgs.getChild("excludes_composition_state")?.getBoolean() ?: false
102 
103         val idAndLayers =
104             layersRows
105                 .groupBy { it["layer_id"].toString() }
106                 .map { (layerId, layerRows) ->
107                     Pair(layerId, newLayer(Args.build(layerRows), excludesCompositionState))
108                 }
109                 .toMutableList()
110         idAndLayers.sortBy { it.first.toLong() }
111 
112         val layers = idAndLayers.map { it.second }
113 
114         return LayerTraceEntryBuilder()
115             .setElapsedTimestamp(snapshotArgs.getChild("elapsed_realtime_nanos")?.getLong() ?: 0L)
116             .setRealToElapsedTimeOffsetNs(realToMonotonicTimeOffsetNs)
117             .setLayers(layers)
118             .setDisplays(displays)
119             .setVSyncId(snapshotArgs.getChild("vsync_id")?.getLong() ?: 0L)
120             .setHwcBlob(snapshotArgs.getChild("hwc_blob")?.getString() ?: "")
121             .setWhere(snapshotArgs.getChild("where")?.getString() ?: "")
122             .setOrphanLayerCallback(orphanLayerCallback)
123             .ignoreLayersStackMatchNoDisplay(ignoreLayersStackMatchNoDisplay)
124             .ignoreVirtualDisplay(ignoreLayersInVirtualDisplay)
125             .build()
126     }
127 
128     companion object {
129         private fun getSqlQuerySnapshots(): String {
130             return """
131                 SELECT
132                     sfs.id AS snapshot_id,
133                     sfs.ts as ts,
134                     args.key as key,
135                     args.display_value as value,
136                     args.value_type as value_type
137                 FROM surfaceflinger_layers_snapshot AS sfs
138                 INNER JOIN args ON sfs.arg_set_id = args.arg_set_id;
139             """
140                 .trimIndent()
141         }
142 
143         private fun getSqlQueryLayers(snapshotId: Long): String {
144             return """
145                 SELECT
146                     sfl.snapshot_id,
147                     sfl.id as layer_id,
148                     args.key as key,
149                     args.display_value as value,
150                     args.value_type
151                 FROM
152                     surfaceflinger_layer as sfl
153                 INNER JOIN args ON sfl.arg_set_id = args.arg_set_id
154                 WHERE snapshot_id = $snapshotId;
155             """
156                 .trimIndent()
157         }
158 
159         private fun newLayer(layer: Args, excludesCompositionState: Boolean): Layer {
160             // Differentiate between the cases when there's no HWC data on
161             // the trace, and when the visible region is actually empty
162             val activeBuffer = newActiveBuffer(layer.getChild("active_buffer"))
163             val visibleRegion = newRegion(layer.getChild("visible_region")) ?: Region()
164             val crop = newCropRect(layer.getChild("crop"))
165             return Layer.from(
166                 layer.getChild("name")?.getString() ?: "",
167                 layer.getChild("id")?.getInt() ?: 0,
168                 layer.getChild("parent")?.getInt() ?: 0,
169                 layer.getChild("z")?.getInt() ?: 0,
170                 visibleRegion,
171                 activeBuffer,
172                 layer.getChild("flags")?.getInt() ?: 0,
173                 newRectF(layer.getChild("bounds")),
174                 newColor(layer.getChild("color")),
175                 layer.getChild("is_opaque")?.getBoolean() ?: false,
176                 layer.getChild("shadow_radius")?.getFloat() ?: 0f,
177                 layer.getChild("corner_radius")?.getFloat() ?: 0f,
178                 newRectF(layer.getChild("screen_bounds")),
179                 newTransform(layer.getChild("transform"), position = layer.getChild("position")),
180                 layer.getChild("curr_frame")?.getLong() ?: -1,
181                 layer.getChild("effective_scaling_mode")?.getInt() ?: 0,
182                 newTransform(layer.getChild("buffer_transform"), position = null),
183                 newHwcCompositionType(layer.getChild("hwc_composition_type")),
184                 layer.getChild("background_blur_radius")?.getInt() ?: 0,
185                 crop,
186                 layer.getChild("is_relative_of")?.getBoolean() ?: false,
187                 layer.getChild("z_order_relative_of")?.getInt() ?: 0,
188                 layer.getChild("layer_stack")?.getInt() ?: 0,
189                 excludesCompositionState
190             )
191         }
192 
193         private fun newDisplay(display: Args): Display {
194             return Display.from(
195                 display.getChild("id")?.getLong() ?: 0L,
196                 display.getChild("name")?.getString() ?: "",
197                 display.getChild("layer_stack")?.getInt() ?: 0,
198                 newSize(display.getChild("size")),
199                 newRect(display.getChild("layer_stack_space_rect")),
200                 newTransform(display.getChild("transform"), position = null),
201                 display.getChild("is_virtual")?.getBoolean() ?: false,
202                 display.getChild("dpi_x")?.getFloat()?.toDouble() ?: 0.0,
203                 display.getChild("dpi_y")?.getFloat()?.toDouble() ?: 0.0,
204             )
205         }
206 
207         private fun newRectF(rectf: Args?): RectF {
208             if (rectf == null) {
209                 return RectF()
210             }
211             return RectF(
212                 /* left */ rectf.getChild("left")?.getFloat() ?: 0f,
213                 /* top */ rectf.getChild("top")?.getFloat() ?: 0f,
214                 /* right */ rectf.getChild("right")?.getFloat() ?: 0f,
215                 /* bottom */ rectf.getChild("bottom")?.getFloat() ?: 0f
216             )
217         }
218 
219         private fun newSize(sizeArgs: Args?): Size {
220             if (sizeArgs == null) {
221                 return Size.EMPTY
222             }
223             return Size.from(
224                 sizeArgs.getChild("w")?.getInt() ?: 0,
225                 sizeArgs.getChild("h")?.getInt() ?: 0
226             )
227         }
228 
229         private fun newColor(color: Args?): Color {
230             if (color == null) {
231                 return emptyColor()
232             }
233             return withCache {
234                 Color.valueOf(
235                     color.getChild("r")?.getFloat() ?: 0f,
236                     color.getChild("g")?.getFloat() ?: 0f,
237                     color.getChild("b")?.getFloat() ?: 0f,
238                     color.getChild("a")?.getFloat() ?: 0f
239                 )
240             }
241         }
242 
243         private fun newActiveBuffer(buffer: Args?): ActiveBuffer {
244             if (buffer == null) {
245                 return ActiveBuffer.EMPTY
246             }
247             return ActiveBuffer.from(
248                 buffer.getChild("width")?.getInt() ?: 0,
249                 buffer.getChild("height")?.getInt() ?: 0,
250                 buffer.getChild("stride")?.getInt() ?: 0,
251                 buffer.getChild("format")?.getInt() ?: 0
252             )
253         }
254 
255         private fun newHwcCompositionType(value: Args?): HwcCompositionType {
256             if (value == null || !value.isString()) {
257                 return HwcCompositionType.HWC_TYPE_UNRECOGNIZED
258             }
259             return HwcCompositionType.valueOf(value.getString())
260         }
261 
262         private fun newCropRect(crop: Args?): RectF? {
263             if (crop == null) {
264                 return RectF()
265             }
266 
267             val right = crop.getChild("right")?.getInt() ?: 0
268             val left = crop.getChild("left")?.getInt() ?: 0
269             val bottom = crop.getChild("bottom")?.getInt() ?: 0
270             val top = crop.getChild("top")?.getInt() ?: 0
271 
272             // crop (0,0) (-1,-1) means no crop
273             if (right == -1 && left == 0 && bottom == -1 && top == 0) {
274                 return null
275             }
276 
277             return RectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
278         }
279 
280         private fun newRegion(region: Args?): Region? {
281             if (region == null) {
282                 return null
283             }
284             val result = Region()
285             val rects = region.getChildren("rect")?.map { newRect(it) } ?: emptyList()
286             rects.forEach { rect -> result.op(rect, Region.Op.UNION) }
287             return result
288         }
289 
290         private fun newRect(rect: Args?): Rect =
291             Rect(
292                 rect?.getChild("left")?.getInt() ?: 0,
293                 rect?.getChild("top")?.getInt() ?: 0,
294                 rect?.getChild("right")?.getInt() ?: 0,
295                 rect?.getChild("bottom")?.getInt() ?: 0
296             )
297 
298         private fun newTransform(transform: Args?, position: Args?) =
299             Transform.from(transform?.getChild("type")?.getInt(), getMatrix(transform, position))
300 
301         private fun getMatrix(transform: Args?, position: Args?): Matrix33 {
302             val x = position?.getChild("x")?.getFloat() ?: 0f
303             val y = position?.getChild("y")?.getFloat() ?: 0f
304 
305             if (
306                 transform == null ||
307                     Transform.isSimpleTransform(transform.getChild("type")?.getInt())
308             ) {
309                 return transform?.getChild("type")?.getInt().getDefaultTransform(x, y)
310             }
311 
312             return Matrix33.from(
313                 transform.getChild("dsdx")?.getFloat() ?: 0f,
314                 transform.getChild("dtdx")?.getFloat() ?: 0f,
315                 x,
316                 transform.getChild("dsdy")?.getFloat() ?: 0f,
317                 transform.getChild("dtdy")?.getFloat() ?: 0f,
318                 y
319             )
320         }
321 
322         private fun Int?.getDefaultTransform(x: Float, y: Float): Matrix33 {
323             return when {
324                 // IDENTITY
325                 this == null -> Matrix33.identity(x, y)
326                 // // ROT_270 = ROT_90|FLIP_H|FLIP_V
327                 isFlagSet(Transform.ROT_90_VAL or Transform.FLIP_V_VAL or Transform.FLIP_H_VAL) ->
328                     Matrix33.rot270(x, y)
329                 // ROT_180 = FLIP_H|FLIP_V
330                 isFlagSet(Transform.FLIP_V_VAL or Transform.FLIP_H_VAL) -> Matrix33.rot180(x, y)
331                 // ROT_90
332                 isFlagSet(Transform.ROT_90_VAL) -> Matrix33.rot90(x, y)
333                 // IDENTITY
334                 isFlagClear(Transform.SCALE_VAL or Transform.ROTATE_VAL) -> Matrix33.identity(x, y)
335                 else -> throw IllegalStateException("Unknown transform type $this")
336             }
337         }
338     }
339 }
340