summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java185
1 files changed, 157 insertions, 28 deletions
diff --git a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
index 48d050ce4391..bb0d30af42ee 100644
--- a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
+++ b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
@@ -22,9 +22,9 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.Manifest;
-import android.app.compat.CompatChanges;
import android.hardware.display.DisplayManager;
import android.os.Looper;
import android.support.test.uiautomator.UiDevice;
@@ -34,6 +34,7 @@ import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.WindowManager;
import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ActivityScenario;
@@ -52,7 +53,7 @@ import java.util.concurrent.TimeUnit;
public class AttachedChoreographerTest {
private static final String TAG = "AttachedChoreographerTest";
private static final long DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE_CHANGEID = 170503758;
- private static final long THRESHOLD_MS = 10;
+ private static final long THRESHOLD_MS = 4;
private static final int FRAME_ITERATIONS = 21;
private static final int CALLBACK_MISSED_THRESHOLD = 2;
@@ -66,7 +67,7 @@ public class AttachedChoreographerTest {
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private Choreographer mChoreographer;
- private boolean mIsFirstCallback = true;
+ private long mExpectedPresentTime;
private int mCallbackMissedCounter = 0;
@Before
@@ -74,6 +75,13 @@ public class AttachedChoreographerTest {
mScenario = ActivityScenario.launch(GraphicsActivity.class);
mScenario.moveToState(Lifecycle.State.CREATED);
mScenario.onActivity(activity -> {
+ // Lock the display frame rate. This will not allow SurfaceFlinger to use the frame rate
+ // override feature that throttles down the global choreographer for this test.
+ float refreshRate = activity.getDisplay().getMode().getRefreshRate();
+ WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
+ attrs.preferredRefreshRate = refreshRate;
+ activity.getWindow().setAttributes(attrs);
+
mSurfaceView = activity.findViewById(R.id.surface);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@@ -95,6 +103,12 @@ public class AttachedChoreographerTest {
});
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ // TODO(b/290634611): clean this up once SF new front end is enabled by default
+ boolean sfNewFeEnabled = uiDevice.executeShellCommand("dumpsys SurfaceFlinger")
+ .indexOf("SurfaceFlinger New Frontend Enabled:true") != -1;
+ assumeTrue(sfNewFeEnabled);
+
uiDevice.wakeUp();
uiDevice.executeShellCommand("wm dismiss-keyguard");
mScenario.moveToState(Lifecycle.State.RESUMED);
@@ -112,18 +126,16 @@ public class AttachedChoreographerTest {
mDisplayManager.setRefreshRateSwitchingType(
DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
- boolean changeIsEnabled =
- CompatChanges.isChangeEnabled(
- DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE_CHANGEID);
- Log.i(TAG, "DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE_CHANGE_ID is "
- + (changeIsEnabled ? "enabled" : "disabled"));
});
}
@After
public void tearDown() {
- mDisplayManager.setRefreshRateSwitchingType(mInitialMatchContentFrameRate);
- mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false);
+ if (mDisplayManager != null) {
+ mDisplayManager.setRefreshRateSwitchingType(mInitialMatchContentFrameRate);
+ mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false);
+ }
+
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
}
@@ -407,18 +419,141 @@ public class AttachedChoreographerTest {
}
}
+ @Test
+ public void testChoreographerAttachedAfterSetFrameRate() {
+ Log.i(TAG, "adyabr: starting testChoreographerAttachedAfterSetFrameRate");
+
+ class TransactionGenerator implements SurfaceControl.TransactionCommittedListener {
+ private SurfaceControl mSc;
+ private int mFrame;
+ private static final int NUM_FRAMES = 50;
+ private final CountDownLatch mCompleteLatch = new CountDownLatch(1);
+
+ TransactionGenerator(SurfaceControl sc) {
+ mSc = sc;
+
+ }
+
+ @Override
+ public void onTransactionCommitted() {
+ mFrame++;
+ if (mFrame >= NUM_FRAMES) {
+ mCompleteLatch.countDown();
+ return;
+ }
+
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ transaction.setAlpha(mSc, 1.0f / mFrame)
+ .addTransactionCommittedListener(Runnable::run, this)
+ .apply();
+
+ }
+
+ public void startAndWaitForCompletion() {
+ onTransactionCommitted();
+ if (waitForCountDown(mCompleteLatch, /* timeoutInSeconds */ 10L)) {
+ fail("failed to send new transactions");
+ }
+ }
+ }
+
+
+ for (int divisor : new int[]{2, 3}) {
+ CountDownLatch choreographerLatch = new CountDownLatch(1);
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ float displayRefreshRate = activity.getDisplay().getMode().getRefreshRate();
+ float fps = displayRefreshRate / divisor;
+ long callbackDurationMs = Math.round(1000 / fps);
+ mCallbackMissedCounter = 0;
+ transaction.setFrameRate(sc, fps, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
+ .apply();
+
+
+ new TransactionGenerator(sc).startAndWaitForCompletion();
+
+ Choreographer choreographer = sc.getChoreographer();
+ verifyVsyncCallbacks(choreographer, callbackDurationMs,
+ choreographerLatch, FRAME_ITERATIONS);
+ });
+ // wait for the previous callbacks to finish before moving to the next divisor
+ if (waitForCountDown(choreographerLatch, /* timeoutInSeconds */ 5L)) {
+ fail("Test not finished in 5 Seconds");
+ }
+ }
+ }
+
+ @Test
+ public void testChoreographerNonDivisorFixedSourceRefreshRate() {
+ CountDownLatch continueLatch = new CountDownLatch(1);
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ Choreographer choreographer = sc.getChoreographer();
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ float displayRefreshRate = activity.getDisplay().getMode().getRefreshRate();
+ float fps = 61.7f; // hopefully not a divisor
+ long callbackDurationMs = Math.round(1000 / displayRefreshRate);
+ mCallbackMissedCounter = 0;
+ transaction.setFrameRate(sc, fps, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
+ .addTransactionCommittedListener(Runnable::run,
+ () -> verifyVsyncCallbacks(choreographer,
+ callbackDurationMs, continueLatch, FRAME_ITERATIONS))
+ .apply();
+ });
+ // wait for the previous callbacks to finish before moving to the next divisor
+ if (waitForCountDown(continueLatch, /* timeoutInSeconds */ 5L)) {
+ fail("Test not finished in 5 Seconds");
+ }
+ }
+
+ @Test
+ public void testChoreographerNonDivisorRefreshRate() {
+ CountDownLatch continueLatch = new CountDownLatch(1);
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ Choreographer choreographer = sc.getChoreographer();
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ float displayRefreshRate = activity.getDisplay().getMode().getRefreshRate();
+ float fps = 61.7f; // hopefully not a divisor
+ float expectedFps = displayRefreshRate / Math.round(displayRefreshRate / fps);
+ long callbackDurationMs = Math.round(1000 / expectedFps);
+ mCallbackMissedCounter = 0;
+ transaction.setFrameRate(sc, fps, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
+ .addTransactionCommittedListener(Runnable::run,
+ () -> verifyVsyncCallbacks(choreographer,
+ callbackDurationMs, continueLatch, FRAME_ITERATIONS))
+ .apply();
+ });
+ // wait for the previous callbacks to finish before moving to the next divisor
+ if (waitForCountDown(continueLatch, /* timeoutInSeconds */ 5L)) {
+ fail("Test not finished in 5 Seconds");
+ }
+ }
+
private void verifyVsyncCallbacks(Choreographer choreographer, long callbackDurationMs,
CountDownLatch continueLatch, int frameCount) {
- long callbackRequestedTimeNs = System.nanoTime();
choreographer.postVsyncCallback(frameData -> {
+ long expectedPresentTime =
+ frameData.getPreferredFrameTimeline().getExpectedPresentationTimeNanos();
if (frameCount > 0) {
- if (!mIsFirstCallback) {
- // Skip the first callback as it takes 1 frame
- // to reflect the new refresh rate
- long callbackDurationDiffMs = getCallbackDurationDiffInMs(
- frameData.getFrameTimeNanos(),
- callbackRequestedTimeNs, callbackDurationMs);
- if (callbackDurationDiffMs < 0 || callbackDurationDiffMs > THRESHOLD_MS) {
+ if (mExpectedPresentTime > 0) {
+ long callbackDurationDiffMs =
+ TimeUnit.NANOSECONDS.toMillis(
+ expectedPresentTime - mExpectedPresentTime);
+ if (callbackDurationDiffMs < 0
+ || Math.abs(callbackDurationMs - callbackDurationDiffMs)
+ > THRESHOLD_MS) {
mCallbackMissedCounter++;
Log.e(TAG, "Frame #" + Math.abs(frameCount - FRAME_ITERATIONS)
+ " vsync callback failed, expected callback in "
@@ -427,25 +562,19 @@ public class AttachedChoreographerTest {
+ " but actual duration difference is " + callbackDurationDiffMs);
}
}
- mIsFirstCallback = false;
+ mExpectedPresentTime = expectedPresentTime;
verifyVsyncCallbacks(choreographer, callbackDurationMs,
continueLatch, frameCount - 1);
} else {
- assertTrue("Missed timeline for " + mCallbackMissedCounter + " callbacks, while "
- + CALLBACK_MISSED_THRESHOLD + " missed callbacks are allowed",
+ assertTrue("Missed timeline for " + mCallbackMissedCounter
+ + " callbacks, while " + CALLBACK_MISSED_THRESHOLD
+ + " missed callbacks are allowed",
mCallbackMissedCounter <= CALLBACK_MISSED_THRESHOLD);
continueLatch.countDown();
}
});
}
- private long getCallbackDurationDiffInMs(long callbackTimeNs, long requestedTimeNs,
- long expectedCallbackMs) {
- long actualTimeMs = TimeUnit.NANOSECONDS.toMillis(callbackTimeNs)
- - TimeUnit.NANOSECONDS.toMillis(requestedTimeNs);
- return Math.abs(expectedCallbackMs - actualTimeMs);
- }
-
private boolean waitForCountDown(CountDownLatch countDownLatch, long timeoutInSeconds) {
try {
return !countDownLatch.await(timeoutInSeconds, TimeUnit.SECONDS);