• 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.common.flicker.extractors
18 
19 import android.tools.common.Timestamp
20 import android.tools.common.Timestamps
21 import android.tools.common.io.Reader
22 import android.tools.common.traces.surfaceflinger.Display
23 import android.tools.common.traces.surfaceflinger.LayerTraceEntry
24 import android.tools.common.traces.wm.Transition
25 import kotlin.math.abs
26 
27 object Utils {
interpolateStartTimestampFromTransitionnull28     fun interpolateStartTimestampFromTransition(transition: Transition, reader: Reader): Timestamp {
29         val wmTrace = reader.readWmTrace() ?: error("Missing WM trace")
30         val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace")
31         val transactionsTrace =
32             reader.readTransactionsTrace() ?: error("Missing transactions trace")
33 
34         val lastWmEntryBeforeTransitionCreated = wmTrace.getEntryAt(transition.createTime)
35         val elapsedNanos = lastWmEntryBeforeTransitionCreated.timestamp.elapsedNanos
36         val unixNanos = lastWmEntryBeforeTransitionCreated.timestamp.unixNanos
37 
38         val startTransactionAppliedTimestamp =
39             transition.getStartTransaction(transactionsTrace)?.let {
40                 layersTrace.getEntryForTransaction(it).timestamp
41             }
42 
43         // If we don't have a startTransactionAppliedTimestamp it's likely because the start
44         // transaction was merged into another transaction so we can't match the id, so we need to
45         // fallback on the send time reported on the WM side.
46         val systemUptimeNanos =
47             startTransactionAppliedTimestamp?.systemUptimeNanos
48                 ?: transition.createTime.systemUptimeNanos
49 
50         return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos)
51     }
52 
interpolateFinishTimestampFromTransitionnull53     fun interpolateFinishTimestampFromTransition(
54         transition: Transition,
55         reader: Reader
56     ): Timestamp {
57         val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace")
58         val wmTrace = reader.readWmTrace() ?: error("Missing WM trace")
59         val transactionsTrace =
60             reader.readTransactionsTrace() ?: error("Missing transactions trace")
61 
62         // There is a delay between when we flag that transition as finished with the CUJ tags
63         // and when it is actually finished on the SF side. We try and account for that by
64         // checking when the finish transaction is actually applied.
65         // TODO: Figure out how to get the vSyncId that the Jank tracker actually gets to avoid
66         //       relying on the transition and have a common end point.
67         val finishTransactionAppliedTimestamp =
68             transition.getFinishTransaction(transactionsTrace)?.let {
69                 layersTrace.getEntryForTransaction(it).timestamp
70             }
71 
72         val elapsedNanos: Long
73         val systemUptimeNanos: Long
74         val unixNanos: Long
75         val sfEntryAtTransitionFinished: LayerTraceEntry
76         if (finishTransactionAppliedTimestamp == null) {
77             // If we don't have a finishTransactionAppliedTimestamp it's likely because the finish
78             // transaction was merged into another transaction so we can't match the id, so we need
79             // to fallback on the finish time reported on the WM side.
80             val wmEntryAtTransitionFinished =
81                 wmTrace.entries.firstOrNull { it.timestamp >= transition.finishTime }
82 
83             elapsedNanos =
84                 wmEntryAtTransitionFinished?.timestamp?.elapsedNanos
85                     ?: transition.finishTime.elapsedNanos
86 
87             unixNanos =
88                 if (wmEntryAtTransitionFinished != null) {
89                     wmEntryAtTransitionFinished.timestamp.unixNanos
90                 } else {
91                     require(wmTrace.entries.isNotEmpty()) { "WM trace should not be empty!" }
92                     val closestWmEntry =
93                         wmTrace.entries.minByOrNull {
94                             abs(it.timestamp.elapsedNanos - transition.finishTime.elapsedNanos)
95                         }
96                             ?: error("WM entry was unexpectedly empty!")
97                     val offset =
98                         closestWmEntry.timestamp.unixNanos - closestWmEntry.timestamp.elapsedNanos
99                     transition.finishTime.elapsedNanos + offset
100                 }
101 
102             sfEntryAtTransitionFinished =
103                 layersTrace.entries.firstOrNull { it.timestamp.unixNanos >= unixNanos }
104                     ?: error("No SF entry for finish timestamp")
105             systemUptimeNanos = sfEntryAtTransitionFinished.timestamp.systemUptimeNanos
106         } else {
107             elapsedNanos =
108                 wmTrace.entries
109                     .first { it.timestamp >= finishTransactionAppliedTimestamp }
110                     .timestamp
111                     .elapsedNanos
112             systemUptimeNanos =
113                 layersTrace
114                     .getEntryAt(finishTransactionAppliedTimestamp)
115                     .timestamp
116                     .systemUptimeNanos
117             unixNanos =
118                 layersTrace.getEntryAt(finishTransactionAppliedTimestamp).timestamp.unixNanos
119         }
120 
121         return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos)
122     }
123 
getOnDisplayFornull124     fun getOnDisplayFor(layerTraceEntry: LayerTraceEntry): Display {
125         val displays = layerTraceEntry.displays.filter { !it.isVirtual }
126         require(displays.isNotEmpty()) { "Failed to get a display for provided entry" }
127         val onDisplays = displays.filter { it.isOn }
128         require(onDisplays.isNotEmpty()) { "No on displays found for entry" }
129         require(onDisplays.size == 1) { "More than one on display found!" }
130         return onDisplays.first()
131     }
132 }
133