Merge "Performance test for MotionPredictor"

This commit is contained in:
TreeHugger Robot
2023-01-14 05:13:39 +00:00
committed by Android (Google) Code Review
3 changed files with 150 additions and 4 deletions

View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2023 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.input
import android.content.Context
import android.content.res.Resources
import android.os.SystemProperties
import android.perftests.utils.PerfStatusReporter
import android.view.InputDevice
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_DOWN
import android.view.MotionEvent.ACTION_MOVE
import android.view.MotionEvent.PointerCoords
import android.view.MotionEvent.PointerProperties
import android.view.MotionPredictor
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.filters.LargeTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import java.time.Duration
private fun getStylusMotionEvent(
eventTime: Duration,
action: Int,
x: Float,
y: Float,
): MotionEvent{
val pointerCount = 1
val properties = arrayOfNulls<MotionEvent.PointerProperties>(pointerCount)
val coords = arrayOfNulls<MotionEvent.PointerCoords>(pointerCount)
for (i in 0 until pointerCount) {
properties[i] = PointerProperties()
properties[i]!!.id = i
properties[i]!!.toolType = MotionEvent.TOOL_TYPE_STYLUS
coords[i] = PointerCoords()
coords[i]!!.x = x
coords[i]!!.y = y
}
return MotionEvent.obtain(/*downTime=*/0, eventTime.toMillis(), action, properties.size,
properties, coords, /*metaState=*/0, /*buttonState=*/0,
/*xPrecision=*/0f, /*yPrecision=*/0f, /*deviceId=*/0, /*edgeFlags=*/0,
InputDevice.SOURCE_STYLUS, /*flags=*/0)
}
private fun getPredictionContext(offset: Duration, enablePrediction: Boolean): Context {
val context = mock(Context::class.java)
val resources: Resources = mock(Resources::class.java)
`when`(context.getResources()).thenReturn(resources)
`when`(resources.getInteger(
com.android.internal.R.integer.config_motionPredictionOffsetNanos)).thenReturn(
offset.toNanos().toInt())
`when`(resources.getBoolean(
com.android.internal.R.bool.config_enableMotionPrediction)).thenReturn(enablePrediction)
return context
}
@RunWith(AndroidJUnit4::class)
@LargeTest
class MotionPredictorBenchmark {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
@get:Rule
val perfStatusReporter = PerfStatusReporter()
private val initialPropertyValue =
SystemProperties.get("persist.input.enable_motion_prediction")
private var eventTime = Duration.ofMillis(1)
@Before
fun setUp() {
instrumentation.uiAutomation.executeShellCommand(
"setprop persist.input.enable_motion_prediction true")
}
@After
fun tearDown() {
instrumentation.uiAutomation.executeShellCommand(
"setprop persist.input.enable_motion_prediction $initialPropertyValue")
}
/**
* In a typical usage, app will send the event to the predictor and then call .predict to draw
* a prediction. In a loop, we keep sending MOVE and then calling .predict to simulate this.
*/
@Test
fun timeRecordAndPredict() {
val offset = Duration.ofMillis(1)
val predictor = MotionPredictor(getPredictionContext(offset, /*enablePrediction=*/true))
// ACTION_DOWN t=0 x=0 y=0
predictor.record(getStylusMotionEvent(eventTime, ACTION_DOWN, /*x=*/0f, /*y=*/0f))
val state = perfStatusReporter.getBenchmarkState()
while (state.keepRunning()) {
eventTime += Duration.ofMillis(1)
// Send MOVE event and then call .predict
val moveEvent = getStylusMotionEvent(eventTime, ACTION_MOVE, /*x=*/1f, /*y=*/2f)
predictor.record(moveEvent)
val predictionTime = eventTime + Duration.ofMillis(2)
val predicted = predictor.predict(predictionTime.toNanos())
assertEquals(1, predicted.size)
assertEquals((predictionTime + offset).toMillis(), predicted[0].eventTime)
}
}
/**
* The creation of the predictor should happen infrequently. However, we still want to be
* mindful of the load times.
*/
@Test
fun timeCreatePredictor() {
val context = getPredictionContext(
/*offset=*/Duration.ofMillis(1), /*enablePrediction=*/true)
val state = perfStatusReporter.getBenchmarkState()
while (state.keepRunning()) {
MotionPredictor(context)
}
}
}

View File

@@ -0,0 +1,3 @@
include platform/frameworks/base:/INPUT_OWNERS
# Bug component: 136048

View File

@@ -31,15 +31,14 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import java.time.Duration
@@ -49,7 +48,6 @@ private fun getStylusMotionEvent(
x: Float,
y: Float,
): MotionEvent{
// One-time: send a DOWN event
val pointerCount = 1
val properties = arrayOfNulls<MotionEvent.PointerProperties>(pointerCount)
val coords = arrayOfNulls<MotionEvent.PointerCoords>(pointerCount)