Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 56468ec9 authored by Omar Miatello's avatar Omar Miatello
Browse files

MM: Add performance tracing to MotionValueCollection

Adds `systrace` markers to the main animation loop within
`MotionValueCollection` to improve performance analysis and debugging.

This change introduces a new static flag,
`MotionValueCollection.isTraceEnabled`, which can be toggled to globally
enable or disable tracing for all instances.

When enabled, each frame processed within the `withFrameNanos` block
will be wrapped in a trace section. This trace includes valuable
metadata:
- The duration of the frame's computation in microseconds.
- The number of currently unstable (actively animating) computations.
- The total number of managed computations.

Test: Manually tested in the demo app
Bug: 392535471
Flag: com.android.systemui.scene_container
Change-Id: Icfc7a8338b257e03195438df03aef9393b2cbe49
parent f2dc6fb4
Loading
Loading
Loading
Loading
+73 −18
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import androidx.compose.runtime.mutableStateSetOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.util.trace
import androidx.compose.ui.util.traceValue
import com.android.mechanics.MotionValue.Companion.StableThresholdSpatial
import com.android.mechanics.debug.DebugInspector
import com.android.mechanics.debug.FrameData
@@ -38,6 +40,8 @@ import com.android.mechanics.spec.SegmentKey
import com.android.mechanics.spec.SemanticKey
import com.android.mechanics.spring.SpringState
import java.util.concurrent.atomic.AtomicInteger
import kotlin.time.Duration
import kotlin.time.measureTime
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.first
@@ -77,6 +81,36 @@ class MotionValueCollection(
        }
    }

    /**
     * Conditionally wraps the execution of a [block] in a performance trace.
     *
     * The primary advantage of this helper is lazy evaluation. The trace message from
     * [onTraceStart] is not computed and no `try-finally` block is entered unless tracing is
     * [enabled]. This helps to avoid performance penalties in production builds where tracing is
     * often turned off.
     *
     * @param enabled A boolean flag to enable or disable tracing.
     * @param onTraceStart A lambda that returns the trace section name. Only invoked if [enabled]
     *   is true.
     * @param onTraceEnd A lambda that executes after the block has finished. Only invoked if
     *   [enabled] is true.
     * @param block The code block to be executed and traced.
     */
    private inline fun trace(
        enabled: Boolean,
        onTraceStart: () -> String,
        onTraceEnd: (Duration) -> Unit = {},
        block: () -> Unit,
    ) {
        if (enabled) {
            val duration = measureTime { trace(sectionName = onTraceStart(), block = block) }

            onTraceEnd(duration)
        } else {
            block()
        }
    }

    /**
     * Keeps the all created [ManagedMotionValue]'s animated output running.
     *
@@ -110,6 +144,22 @@ class MotionValueCollection(
                    withFrameNanos { frameTimeNanos ->
                        frameCount++

                        trace(
                            enabled = isTraceEnabled,
                            onTraceStart = {
                                val prefix = "MotionValueCollection($label)"
                                val unstable = managedComputations.count { !it.isStable }
                                val all = managedComputations.size
                                traceValue("$prefix:unstable", unstable.toLong())
                                traceValue("$prefix:all", all.toLong())

                                "$prefix withFrameNanos f:$frameCount ($unstable/$all)"
                            },
                            onTraceEnd = {
                                val prefix = "MotionValueCollection($label)"
                                traceValue("$prefix:duration", it.inWholeMicroseconds)
                            },
                        ) {
                            lastFrameTimeNanos = currentAnimationTimeNanos
                            lastInput = currentInput
                            lastDirection = currentDirection
@@ -133,6 +183,7 @@ class MotionValueCollection(
                                }
                            }
                        }
                    }

                    isAnimatingUninterrupted = scheduleNextFrame
                    if (scheduleNextFrame) {
@@ -212,6 +263,10 @@ class MotionValueCollection(
        managedComputations.remove(toDispose)
        toDispose.onDeactivate()
    }

    companion object {
        var isTraceEnabled: Boolean = false
    }
}

internal class ManagedMotionComputation(