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

Commit 904bdb11 authored by Alec Mouri's avatar Alec Mouri Committed by Android (Google) Code Review
Browse files

Merge changes Ia4dcc893,Id33b11ce,Id56307db,Ie4e28985,I9e6ebf9d into main

* changes:
  Helper script for running TransactionFlinger CUJs
  Add moving layer CUJ for caching
  Add workload that draws a blur region over an animating background
  Add workload that toggles blur behind
  Add common infrastructure for defining a Scene
parents d6f7c8f2 bece2b9a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -33,6 +33,9 @@
            </intent-filter>
        </activity>

        <activity android:name=".activities.BlurOnOffActivity" />
        <activity android:name=".activities.BlurRegionActivity" />
        <activity android:name=".activities.MovingLayerActivity" />
        <activity android:name=".activities.TrivialActivity" />

    </application>
+109 −0
Original line number Diff line number Diff line
buffers {
  size_kb: 524288
  fill_policy: DISCARD
}
buffers {
  size_kb: 8192
  fill_policy: DISCARD
}
data_sources {
  config {
    name: "linux.ftrace"
    ftrace_config {
      ftrace_events: "sched/sched_process_exit"
      ftrace_events: "sched/sched_process_free"
      ftrace_events: "task/task_newtask"
      ftrace_events: "task/task_rename"
      ftrace_events: "sched/sched_switch"
      ftrace_events: "power/suspend_resume"
      ftrace_events: "sched/sched_blocked_reason"
      ftrace_events: "sched/sched_wakeup"
      ftrace_events: "sched/sched_wakeup_new"
      ftrace_events: "sched/sched_waking"
      ftrace_events: "sched/sched_process_exit"
      ftrace_events: "sched/sched_process_free"
      ftrace_events: "task/task_newtask"
      ftrace_events: "task/task_rename"
      ftrace_events: "power/cpu_frequency"
      ftrace_events: "power/cpu_idle"
      ftrace_events: "power/suspend_resume"
      ftrace_events: "power/gpu_frequency"
      ftrace_events: "gpu_mem/gpu_mem_total"
      ftrace_events: "regulator/regulator_set_voltage"
      ftrace_events: "regulator/regulator_set_voltage_complete"
      ftrace_events: "power/clock_enable"
      ftrace_events: "power/clock_disable"
      ftrace_events: "power/clock_set_rate"
      ftrace_events: "power/suspend_resume"
      ftrace_events: "ftrace/print"
      atrace_categories: "adb"
      atrace_categories: "aidl"
      atrace_categories: "am"
      atrace_categories: "audio"
      atrace_categories: "binder_driver"
      atrace_categories: "binder_lock"
      atrace_categories: "bionic"
      atrace_categories: "camera"
      atrace_categories: "dalvik"
      atrace_categories: "database"
      atrace_categories: "gfx"
      atrace_categories: "hal"
      atrace_categories: "input"
      atrace_categories: "network"
      atrace_categories: "nnapi"
      atrace_categories: "pm"
      atrace_categories: "power"
      atrace_categories: "res"
      atrace_categories: "rro"
      atrace_categories: "rs"
      atrace_categories: "sm"
      atrace_categories: "ss"
      atrace_categories: "vibrator"
      atrace_categories: "video"
      atrace_categories: "view"
      atrace_categories: "webview"
      atrace_categories: "wm"
      atrace_apps: "*"
      symbolize_ksyms: true
      disable_generic_events: true
    }
  }
}
data_sources {
  config {
    name: "linux.process_stats"
    process_stats_config {
      scan_all_processes_on_start: true
    }
  }
}
data_sources {
  config {
    name: "linux.sys_stats"
    sys_stats_config {
      stat_period_ms: 250
      stat_counters: STAT_CPU_TIMES
      stat_counters: STAT_FORK_COUNT
      cpufreq_period_ms: 250
    }
  }
}
data_sources {
  config {
    name: "android.gpu.memory"
  }
}
data_sources {
  config {
    name: "android.power"
    android_power_config {
      battery_poll_ms: 500
      collect_power_rails: true
    }
  }
}
data_sources {
  config {
    name: "android.surfaceflinger.frametimeline"
  }
}
 No newline at end of file
+92 −0
Original line number Diff line number Diff line
# Copyright 2025 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.

test_name=''
seconds=0
dump_powerhal=false
use_perfetto=false
use_simpleperf=false

print_usage() {
  printf 'Usage:\n    smoke.sh -n TEST_NAME -d [OPTIONAL SECONDS] \n\n'
  printf -- '-p: Enable power dumps. Only if the test duration is finite. \n\n'
  printf -- '-t: Enable perfetto tracing. Only if the test duration is finite. Only works if you run in the same directory as config.pbtx. \n\n'
  printf -- '-s: Enable simpleperf collection for surfaceflinger. Only if the test duration is finite. \n\n'
}

while getopts 'd::n:pst' flag; do
  case "${flag}" in
    d) seconds="${OPTARG}";;
    n) test_name="${OPTARG}";;
    p) dump_powerhal=true;;
    s) use_simpleperf=true;;
    t) use_perfetto=true;;
    *) print_usage
       exit 1 ;;
  esac
done

if [ -z "$test_name" ]; then
    print_usage
    exit 1
fi

if [[ ! "$seconds" =~ ^[0-9]+$ ]]; then
  echo "Seconds was not a positive integer -- defaulting to 0"
  seconds='0'
fi

echo "Launching test: $test_name"

adb shell am start -n com.android.test.transactionflinger/com.android.test.transactionflinger.activities.$test_name

if (( "$seconds" > 0 )); then
    echo "Running test for $seconds seconds"
    if (( "$seconds" < 10 )); then
        echo "Note that running a test for less than 10 seconds may not produce great power data"
    fi

    start_time=$(date +%s)
    if "$dump_powerhal" ; then
         adb shell dumpsys android.hardware.power.stats.IPowerStats/default | grep -A100 "energy meter" > before_energy.txt
         echo "Dumped initial power stats into before_energy.txt"
    fi

    if "$use_simpleperf" ; then
        $(${ANDROID_BUILD_TOP}/system/extras/simpleperf/scripts/app_profiler.py -np surfaceflinger -r "--duration ${seconds} -g --post-unwind=yes" ; ${ANDROID_BUILD_TOP}/system/extras/simpleperf/scripts/pprof_proto_generator.py -i perf.data) &
    fi

    if "$use_perfetto" ; then
        config="$(< config.pbtx)"
        config+=" duration_ms: ${seconds}000"
        echo ${config} | adb shell -t perfetto -c - --txt -o /data/misc/perfetto-traces/trace.pftrace
        adb pull /data/misc/perfetto-traces/trace.pftrace
    else
        sleep $seconds
    fi

    end_time=$(date +%s)
    elapsed=$(( end_time - start_time ))
    if "$dump_powerhal" ; then
        adb shell dumpsys android.hardware.power.stats.IPowerStats/default | grep -A100 "energy meter" > after_energy.txt
        echo "Dumped final power stats into after_energy.txt after $elapsed seconds"
    fi

    echo "Running for a few extra seconds before stopping"
    sleep 5
    echo "Stopping test: $test_name"
    adb shell am force-stop com.android.test.transactionflinger
fi

+7 −1
Original line number Diff line number Diff line
@@ -26,6 +26,9 @@ import android.widget.BaseExpandableListAdapter
import android.widget.ExpandableListView
import android.widget.TextView
import androidx.activity.ComponentActivity
import com.android.test.transactionflinger.activities.BlurOnOffActivity
import com.android.test.transactionflinger.activities.BlurRegionActivity
import com.android.test.transactionflinger.activities.MovingLayerActivity
import com.android.test.transactionflinger.activities.TrivialActivity
import kotlin.reflect.KClass

@@ -40,7 +43,10 @@ data class DemoGroup(val groupName: String, val demos: List<Demo>)
private val AllDemos = listOf(
    DemoGroup(
        "Workloads", listOf(
            Demo("TrivialActivity", TrivialActivity::class)
            Demo("TrivialActivity", TrivialActivity::class),
            Demo("BlurOnOffActivity", BlurOnOffActivity::class),
            Demo("BlurRegionActivity", BlurRegionActivity::class),
            Demo("MovingLayerActivity", MovingLayerActivity::class)
        )
    )
)
+228 −0
Original line number Diff line number Diff line
/*
 * Copyright 2025 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.test.transactionflinger

import android.graphics.Color
import android.graphics.ColorSpace
import android.graphics.HardwareBufferRenderer
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RenderNode
import android.hardware.HardwareBuffer
import android.view.Choreographer
import android.view.SurfaceControl
import java.util.concurrent.CompletableFuture

/**
 * A scene of SurfaceControls!
 */
class Scene {

    private val children = mutableListOf<Scene>()
    private var drawFunctor: (Scene.(Choreographer.FrameData, Int, Int) -> RenderNode?)? = null
    private var propertiesFunctor: (Scene.(Choreographer.FrameData) -> Unit)? = null

    /** Radius of a blur applied to content behind this scene */
    var backgroundBlurRadius = 0

    /** Region behind which to blur */
    var blurRegion = BlurRegion(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)

    /** Location of the top-left position in the x-direction of this scene, on a range of [0, 1] */
    var x = 0.0

    /** Location of the top-left position in the y-direction of this scene, on a range of [0, 1] */
    var y = 0.0

    /** Width of the scene normalized on [0, 1] */
    var width = 1.0

    /** Height of the scene normalized on [0, 1] */
    var height = 1.0

    var startTime = 0L
        private set

    val surfaceControl: SurfaceControl =
        SurfaceControl.Builder().setName("scene").setHidden(true).build()

    fun properties(functor: Scene.(Choreographer.FrameData) -> Unit) {
        propertiesFunctor = functor
    }

    /**
     * Adds a child scene
     */
    fun scene(init: Scene.() -> Unit): Scene {
        val scene = Scene()
        scene.init()
        children.add(scene)
        return scene
    }

    fun externalScene(sceneFunctor: () -> Scene): Scene {
        val scene = sceneFunctor()
        children.add(scene)
        return scene
    }

    fun content(draw: Scene.(Choreographer.FrameData, Int, Int) -> RenderNode?) {
        drawFunctor = draw
    }

    fun drawAndSubmit(data: Choreographer.FrameData, width: Int, height: Int) {
        val transaction = SurfaceControl.Transaction()
        onDraw(data, transaction, width, height).get()
        synchronized(transaction) {
            transaction.apply()
        }
    }

    private fun getBlurRegion(width: Int, height: Int): FloatArray {
        return floatArrayOf(
            blurRegion.blurRadius.toFloat(),
            blurRegion.alpha.toFloat(),
            (blurRegion.left * width).toFloat(),
            (blurRegion.top * height).toFloat(),
            (blurRegion.right * width).toFloat(),
            (blurRegion.bottom * height).toFloat(),
            blurRegion.cornerRadiusTL.toFloat(),
            blurRegion.cornerRadiusTR.toFloat(),
            blurRegion.cornerRadiusBL.toFloat(),
            blurRegion.cornerRadiusBR.toFloat()
        )
    }


    private fun onDraw(
        data: Choreographer.FrameData,
        transaction: SurfaceControl.Transaction,
        parentWidth: Int,
        parentHeight: Int
    ): CompletableFuture<Void> {
        val physicalX = (parentWidth * x).toFloat()
        val physicalY = (parentHeight * y).toFloat()
        if (startTime == 0L) {
            startTime = data.preferredFrameTimeline.deadlineNanos
            synchronized(transaction) {
                transaction.setPosition(surfaceControl, physicalX, physicalY)
                transaction.setVisibility(surfaceControl, true)
                for (child in children) {
                    transaction.reparent(child.surfaceControl, surfaceControl)
                }
            }
        }

        propertiesFunctor?.let {
            it.invoke(this@Scene, data)
            synchronized(transaction) {
                transaction.setBackgroundBlurRadius(surfaceControl, backgroundBlurRadius)
                transaction.setBlurRegions(
                    surfaceControl,
                    arrayOf(getBlurRegion(parentWidth, parentHeight))
                )
                transaction.setPosition(surfaceControl, physicalX, physicalY)
            }
        }

        val physicalWidth = (parentWidth * width).toInt()
        val physicalHeight = (parentHeight * height).toInt()
        val futuresList: MutableList<CompletableFuture<Void>> = mutableListOf()

        drawFunctor?.invoke(this@Scene, data, physicalWidth, physicalHeight)?.let { node ->
            val drawFuture = CompletableFuture<Void>()
            futuresList.add(drawFuture)
            val buffer = HardwareBuffer.create(
                physicalWidth, physicalHeight, HardwareBuffer.RGBA_8888, 1,
                HardwareBuffer.USAGE_COMPOSER_OVERLAY or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
                        or HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
            )

            val renderer = HardwareBufferRenderer(buffer)
            renderer.setContentRoot(node)
            renderer.obtainRenderRequest()
                .setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB))
                .draw(
                    Runnable::run
                ) {
                    // We could instead wrap the rendering result into a payload that we then
                    // dispatch to the main thread, but a lock is easier to write :)
                    synchronized(transaction) {
                        transaction.setBuffer(surfaceControl, buffer, it.fence).setVisibility(
                            surfaceControl, true
                        )
                    }
                    drawFuture.complete(null)
                }
        }


        futuresList.addAll(children.asSequence()
            .map { it.onDraw(data, transaction, physicalWidth, physicalHeight) }
            .toList())

        return CompletableFuture<Void>.allOf(*futuresList.toTypedArray())
    }

    fun drawColor(color: Int, data: Choreographer.FrameData, width: Int, height: Int): RenderNode? {
        if (startTime < data.preferredFrameTimeline.deadlineNanos) {
            return null
        }
        val renderNode = RenderNode("cogsapp")
        renderNode.setPosition(Rect(0, 0, width, height))
        val paint = Paint()
        paint.color = color
        renderNode.beginRecording(width, height).drawPaint(paint)
        renderNode.endRecording()
        return renderNode
    }

    data class BlurRegion(
        val blurRadius: Double, val alpha: Double, val left: Double,
        val top: Double, val right: Double, val bottom: Double, val cornerRadiusTL: Double,
        val cornerRadiusTR: Double, val cornerRadiusBL: Double, val cornerRadiusBR: Double
    )
}

/**
 * Creates a root level Scene.
 * Oh no, a DSL.
 */
fun scene(init: Scene.() -> Unit): Scene {
    val scene = Scene()
    scene.init()
    return scene
}

fun checkerboardScene(rows: Int, columns: Int): Scene {
    return scene {
        for (row in 0 until rows) {
            for (column in 0 until columns) {
                scene {
                    content { data, width, height ->
                        val color = if ((row + column) % 2 == 0) Color.BLACK else Color.WHITE
                        drawColor(color, data, width, height)
                    }
                    x = row.toDouble() / rows
                    y = column.toDouble() / columns
                    width = 1.0 / rows
                    height = 1.0 / columns
                }
            }
        }
    }
}
 No newline at end of file
Loading