blob: 69021d40b0bdad61ee037f203789a4c20697a672 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm.traces.common.layers
import com.android.server.wm.traces.common.CrossPlatform
import com.android.server.wm.traces.common.RectF
/**
* Represents a single Layer trace entry.
*
* This is a generic object that is reused by both Flicker and Winscope and cannot access internal
* Java/Android functionality
*/
class LayerTraceEntry(
override val elapsedTimestamp: Long,
override val clockTimestamp: Long?,
override val hwcBlob: String,
override val where: String,
override val displays: Array<Display>,
override val vSyncId: Long,
_rootLayers: Array<Layer>,
) : BaseLayerTraceEntry() {
override val timestamp =
CrossPlatform.timestamp.from(
systemUptimeNanos = elapsedTimestamp,
unixNanos = clockTimestamp
)
override val flattenedLayers: Array<Layer> = fillFlattenedLayers(_rootLayers)
private fun fillFlattenedLayers(rootLayers: Array<Layer>): Array<Layer> {
val layers = mutableListOf<Layer>()
val roots = rootLayers.fillOcclusionState().toMutableList()
while (roots.isNotEmpty()) {
val layer = roots.removeAt(0)
layers.add(layer)
roots.addAll(layer.children)
}
return layers.toTypedArray()
}
private fun Array<Layer>.topDownTraversal(): List<Layer> {
return this.sortedBy { it.z }.flatMap { it.topDownTraversal() }
}
private fun Layer.topDownTraversal(): List<Layer> {
val traverseList = mutableListOf(this)
this.children
.sortedBy { it.z }
.forEach { childLayer -> traverseList.addAll(childLayer.topDownTraversal()) }
return traverseList
}
private fun Array<Layer>.fillOcclusionState(): Array<Layer> {
val traversalList = topDownTraversal().reversed()
val opaqueLayers = mutableListOf<Layer>()
val transparentLayers = mutableListOf<Layer>()
traversalList.forEach { layer ->
val visible = layer.isVisible
val displaySize =
displays
.firstOrNull { it.layerStackId == layer.stackId }
?.layerStackSpace
?.toRectF()
?: RectF.EMPTY
if (visible) {
val occludedBy =
opaqueLayers
.filter {
it.stackId == layer.stackId &&
it.contains(layer, displaySize) &&
(!it.hasRoundedCorners || (layer.cornerRadius == it.cornerRadius))
}
.toTypedArray()
layer.addOccludedBy(occludedBy)
val partiallyOccludedBy =
opaqueLayers
.filter {
it.stackId == layer.stackId &&
it.overlaps(layer, displaySize) &&
it !in layer.occludedBy
}
.toTypedArray()
layer.addPartiallyOccludedBy(partiallyOccludedBy)
val coveredBy =
transparentLayers
.filter { it.stackId == layer.stackId && it.overlaps(layer, displaySize) }
.toTypedArray()
layer.addCoveredBy(coveredBy)
if (layer.isOpaque) {
opaqueLayers.add(layer)
} else {
transparentLayers.add(layer)
}
}
}
return this
}
}