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