Merge "DO NOT MERGE: Add SurfaceFlingerPerfTests" into tm-qpr-dev

This commit is contained in:
Vishnu Nair
2022-09-01 20:40:31 +00:00
committed by Android (Google) Code Review
8 changed files with 580 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
// Copyright (C) 2022 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
android_test {
name: "SurfaceFlingerPerfTests",
srcs: ["src/**/*.java"],
libs: ["android.test.runner.stubs"],
static_libs: [
"androidx.test.rules",
"androidx.test.ext.junit",
"androidx.annotation_annotation",
"apct-perftests-utils",
"collector-device-lib",
"platform-test-annotations",
],
test_suites: ["device-tests"],
data: [":perfetto_artifacts"],
platform_apis: true,
certificate: "platform",
}

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.perftests.surfaceflinger">
<!-- permission needed to write perfetto trace and read/write simpleperf report -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:label="SurfaceFlingerPerfTests">
<uses-library android:name="android.test.runner" />
<activity android:name="android.surfaceflinger.SurfaceFlingerTestActivity"
android:exported="true">
<intent-filter>
<action android:name="com.android.perftests.core.PERFTEST" />
</intent-filter>
</activity>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.perftests.surfaceflinger">
</instrumentation>
</manifest>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 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.
-->
<configuration description="Runs SurfaceFlingerPerfTests metric instrumentation.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-metric-instrumentation" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
<option name="test-file-name" value="SurfaceFlingerPerfTests.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
<option name="force-skip-system-props" value="true" />
<option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
<option name="run-command" value="cmd window dismiss-keyguard" />
<option name="run-command" value="cmd package compile -m speed android.perftests.surfaceflinger" />
</target_preparer>
<!-- Needed for pushing the trace config file -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
</target_preparer>
<!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
<option name="isolated-storage" value="false" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="package" value="android.perftests.surfaceflinger" />
<option name="hidden-api-checks" value="false"/>
<!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
<option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener,android.device.collectors.SimpleperfListener" />
<!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
<option name="instrumentation-arg" key="newRunListenerMode" value="true" />
<!-- PerfettoListener related arguments -->
<option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
<option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
<!-- SimpleperfListener related arguments -->
<option name="instrumentation-arg" key="report" value="true" />
<option name="instrumentation-arg" key="arguments" value="&quot;&quot;" />
<option name="instrumentation-arg" key="events_to_record" value="instructions,cpu-cycles,raw-l3d-cache-refill,sched:sched_waking" />
<option name="instrumentation-arg" key="processes_to_record" value="surfaceflinger" />
<option name="instrumentation-arg" key="symbols_to_report" value="&quot;android::SurfaceFlinger::commit(;android::SurfaceFlinger::composite(&quot;" />
<!-- ProcLoadListener related arguments -->
<!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
<option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
<option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
<option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
<option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="directory-keys" value="/data/local/tmp/SurfaceFlingerPerfTests" />
<!-- Needed for pulling the collected trace config on to the host -->
<option name="pull-pattern-keys" value="perfetto_file_path" />
<option name="pull-pattern-keys" value="simpleperf_file_path" />
</metrics_collector>
</configuration>

View File

@@ -0,0 +1 @@
include /services/core/java/com/android/server/wm/OWNERS

View File

@@ -0,0 +1,34 @@
## SurfaceFlinger performance tests
### Precondition
To reduce the variance of the test, if `perf-setup.sh` (platform_testing/scripts/perf-setup)
is available, it is better to use the following instructions to lock CPU and GPU frequencies.
```
m perf-setup
PERF_SETUP_PATH=/data/local/tmp/perf-setup.sh
adb push $OUT/$PERF_SETUP_PATH $PERF_SETUP_PATH
adb shell chmod +x $PERF_SETUP_PATH
adb shell $PERF_SETUP_PATH
```
### Example to run
Use `atest`
```
atest SurfaceFlingerPerfTests:SurfaceFlingerPerfTest -- \
--module-arg SurfaceFlingerPerfTests:instrumentation-arg:kill-bg:=true
```
Use `am instrument`
```
adb shell am instrument -w -r -e class android.surfaceflinger.SurfaceFlingerPerfTest \
-e kill-bg true \
com.android.perftests.surfaceflinger/androidx.test.runner.AndroidJUnitRunner
```
* `kill-bg` is optional.
Test arguments
- kill-bg
* boolean: Kill background process before running test.
- profiling-iterations
* int: Run the extra iterations with enabling method profiling.
- profiling-sampling
* int: The interval (0=trace each method, default is 10) of sample profiling in microseconds.

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 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 android.surfaceflinger;
import android.annotation.ColorInt;
import android.graphics.Canvas;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.hardware.HardwareBuffer;
import android.hardware.SyncFence;
import android.view.SurfaceControl;
import java.util.concurrent.ArrayBlockingQueue;
/**
* Allocates n amount of buffers to a SurfaceControl using a Queue implementation. Executes a
* releaseCallback so a buffer can be safely re-used.
*
* @hide
*/
public class BufferFlinger {
ArrayBlockingQueue<GraphicBuffer> mBufferQ;
public BufferFlinger(int numOfBuffers, @ColorInt int color) {
mBufferQ = new ArrayBlockingQueue<>(numOfBuffers);
while (numOfBuffers > 0) {
GraphicBuffer buffer = GraphicBuffer.create(500, 500,
PixelFormat.RGBA_8888,
GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
| GraphicBuffer.USAGE_SW_WRITE_RARELY);
Canvas canvas = buffer.lockCanvas();
canvas.drawColor(color);
buffer.unlockCanvasAndPost(canvas);
mBufferQ.add(buffer);
numOfBuffers--;
}
}
public void addBuffer(SurfaceControl.Transaction t, SurfaceControl surfaceControl)
throws InterruptedException {
GraphicBuffer buffer = mBufferQ.take();
t.setBuffer(surfaceControl,
HardwareBuffer.createFromGraphicBuffer(buffer),
null,
(SyncFence fence) -> {
releaseCallback(fence, buffer);
});
}
public void releaseCallback(SyncFence fence, GraphicBuffer buffer) {
if (fence != null) {
fence.awaitForever();
}
mBufferQ.add(buffer);
}
public void freeBuffers() {
for (GraphicBuffer buffer : mBufferQ) {
buffer.destroy();
}
}
}

View File

@@ -0,0 +1,228 @@
/*
* Copyright (C) 2022 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 android.surfaceflinger;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.view.SurfaceControl;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Random;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class SurfaceFlingerPerfTest {
protected ActivityScenarioRule<SurfaceFlingerTestActivity> mActivityRule =
new ActivityScenarioRule<>(SurfaceFlingerTestActivity.class);
protected PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
private SurfaceFlingerTestActivity mActivity;
static final int BUFFER_COUNT = 2;
@Rule
public final RuleChain mAllRules = RuleChain
.outerRule(mPerfStatusReporter)
.around(mActivityRule);
@Before
public void setup() {
mActivityRule.getScenario().onActivity(activity -> mActivity = activity);
}
@After
public void teardown() {
mSurfaceControls.forEach(SurfaceControl::release);
mByfferTrackers.forEach(BufferFlinger::freeBuffers);
}
private ArrayList<BufferFlinger> mByfferTrackers = new ArrayList<>();
private BufferFlinger createBufferTracker(int color) {
BufferFlinger bufferTracker = new BufferFlinger(BUFFER_COUNT, color);
mByfferTrackers.add(bufferTracker);
return bufferTracker;
}
private ArrayList<SurfaceControl> mSurfaceControls = new ArrayList<>();
private SurfaceControl createSurfaceControl() throws InterruptedException {
SurfaceControl sc = mActivity.createChildSurfaceControl();
mSurfaceControls.add(sc);
return sc;
}
@Test
public void singleBuffer() throws Exception {
SurfaceControl sc = createSurfaceControl();
BufferFlinger bufferTracker = createBufferTracker(Color.GREEN);
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
bufferTracker.addBuffer(t, sc);
t.show(sc);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
bufferTracker.addBuffer(t, sc);
t.apply();
}
}
static int getRandomColorComponent() {
return new Random().nextInt(155) + 100;
}
@Test
public void multipleBuffers() throws Exception {
final int MAX_BUFFERS = 10;
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (int i = 0; i < MAX_BUFFERS; i++) {
SurfaceControl sc = createSurfaceControl();
BufferFlinger bufferTracker = createBufferTracker(Color.argb(getRandomColorComponent(),
getRandomColorComponent(), getRandomColorComponent(),
getRandomColorComponent()));
bufferTracker.addBuffer(t, sc);
t.setPosition(sc, i * 10, i * 10);
t.show(sc);
}
t.apply(true);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
for (int i = 0; i < MAX_BUFFERS; i++) {
mByfferTrackers.get(i).addBuffer(t, mSurfaceControls.get(i));
}
t.apply();
}
}
@Test
public void multipleOpaqueBuffers() throws Exception {
final int MAX_BUFFERS = 10;
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (int i = 0; i < MAX_BUFFERS; i++) {
SurfaceControl sc = createSurfaceControl();
BufferFlinger bufferTracker = createBufferTracker(Color.rgb(getRandomColorComponent(),
getRandomColorComponent(), getRandomColorComponent()));
bufferTracker.addBuffer(t, sc);
t.setOpaque(sc, true);
t.setPosition(sc, i * 10, i * 10);
t.show(sc);
}
t.apply(true);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
for (int i = 0; i < MAX_BUFFERS; i++) {
mByfferTrackers.get(i).addBuffer(t, mSurfaceControls.get(i));
}
t.apply();
}
}
@Test
public void geometryChanges() throws Exception {
final int MAX_POSITION = 10;
final float MAX_SCALE = 2.0f;
SurfaceControl sc = createSurfaceControl();
BufferFlinger bufferTracker = createBufferTracker(Color.GREEN);
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
bufferTracker.addBuffer(t, sc);
t.show(sc).apply(true);
int step = 0;
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
step = ++step % MAX_POSITION;
t.setPosition(sc, step, step);
float scale = ((step * MAX_SCALE) / MAX_POSITION) + 0.5f;
t.setScale(sc, scale, scale);
t.apply();
}
}
@Test
public void geometryWithBufferChanges() throws Exception {
final int MAX_POSITION = 10;
SurfaceControl sc = createSurfaceControl();
BufferFlinger bufferTracker = createBufferTracker(Color.GREEN);
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
bufferTracker.addBuffer(t, sc);
t.show(sc).apply(true);
int step = 0;
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
step = ++step % MAX_POSITION;
t.setPosition(sc, step, step);
float scale = ((step * 2.0f) / MAX_POSITION) + 0.5f;
t.setScale(sc, scale, scale);
bufferTracker.addBuffer(t, sc);
t.apply();
}
}
@Test
public void addRemoveLayers() throws Exception {
SurfaceControl sc = createSurfaceControl();
BufferFlinger bufferTracker = createBufferTracker(Color.GREEN);
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
SurfaceControl childSurfaceControl = new SurfaceControl.Builder()
.setName("childLayer").setBLASTLayer().build();
bufferTracker.addBuffer(t, childSurfaceControl);
t.reparent(childSurfaceControl, sc);
t.apply();
t.remove(childSurfaceControl).apply();
}
}
@Test
public void displayScreenshot() throws Exception {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
Bitmap screenshot =
InstrumentationRegistry.getInstrumentation().getUiAutomation().takeScreenshot();
screenshot.recycle();
}
}
@Test
public void layerScreenshot() throws Exception {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
Bitmap screenshot =
InstrumentationRegistry.getInstrumentation().getUiAutomation().takeScreenshot(
mActivity.getWindow());
screenshot.recycle();
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2022 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 android.surfaceflinger;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import java.util.concurrent.CountDownLatch;
/**
* A simple activity used for testing, e.g. performance of activity switching, or as a base
* container of testing view.
*/
public class SurfaceFlingerTestActivity extends Activity {
public TestSurfaceView mTestSurfaceView;
SurfaceControl mSurfaceControl;
CountDownLatch mIsReady = new CountDownLatch(1);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mTestSurfaceView = new TestSurfaceView(this);
setContentView(mTestSurfaceView);
}
public SurfaceControl createChildSurfaceControl() throws InterruptedException {
return mTestSurfaceView.getChildSurfaceControlHelper();
}
public class TestSurfaceView extends SurfaceView {
public TestSurfaceView(Context context) {
super(context);
SurfaceHolder holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsReady.countDown();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
}
public SurfaceControl getChildSurfaceControlHelper() throws InterruptedException {
mIsReady.await();
SurfaceHolder holder = getHolder();
// check to see if surface is valid
if (holder.getSurface().isValid()) {
mSurfaceControl = getSurfaceControl();
}
return new SurfaceControl.Builder()
.setName("ChildSurfaceControl")
.setParent(mSurfaceControl)
.build();
}
}
}