Merge "Performance test for MotionPredictor"
This commit is contained in:
committed by
Android (Google) Code Review
commit
f98fc3d1fd
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
3
apct-tests/perftests/core/src/android/input/OWNERS
Normal file
3
apct-tests/perftests/core/src/android/input/OWNERS
Normal file
@@ -0,0 +1,3 @@
|
||||
include platform/frameworks/base:/INPUT_OWNERS
|
||||
|
||||
# Bug component: 136048
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user