Add GlAppSwitchTest
- switches between two Gl-using apps and forces recreating GlContext
- replica island is used for one GL app.
- if something goes wrong and rendering is blocked, watchdog timer
in each app will fire
- fixed a bug in GlPlanetsActivity as the original code freed vertex /
index buffer and did not create after pause resume
: Buffers are just kept to reduce resume time for this test
Change-Id: Ie9f81f97f2746740c9bc37fe000a998bc4e90701
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index c0be204..c31fbcb 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -71,6 +71,7 @@
TestDeviceSetup \
CtsDelegatingAccessibilityService \
CtsDeviceAdmin \
+ replicaisland \
SignatureTest \
ApiDemos \
ApiDemosReferenceTest \
diff --git a/tests/tests/openglperf/Android.mk b/tests/tests/openglperf/Android.mk
index 0663c73..e510aa7 100644
--- a/tests/tests/openglperf/Android.mk
+++ b/tests/tests/openglperf/Android.mk
@@ -27,8 +27,8 @@
LOCAL_PACKAGE_NAME := CtsOpenGlPerfTestCases
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
+LOCAL_INSTRUMENTATION_FOR := replicaisland
+
LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
-
diff --git a/tests/tests/openglperf/AndroidManifest.xml b/tests/tests/openglperf/AndroidManifest.xml
index 95a7cd1..8e48eb0 100644
--- a/tests/tests/openglperf/AndroidManifest.xml
+++ b/tests/tests/openglperf/AndroidManifest.xml
@@ -13,16 +13,24 @@
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="com.android.cts.openglperf"
android:versionCode="1"
android:versionName="1.0" >
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
+ <uses-permission android:name="android.permission.GET_TASKS" />
+ <uses-permission android:name="android.permission.REORDER_TASKS" />
+ <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
+
+ <!-- Two activities are used -->
+ <instrumentation
+ android:targetPackage="replicaisland"
+ android:name="android.test.InstrumentationTestRunner" />
<instrumentation
android:targetPackage="com.android.cts.openglperf"
android:name="android.test.InstrumentationTestRunner" />
+
<application
android:label="@string/app_name" >
<uses-library android:name="android.test.runner" />
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/GlAppSwitchTest.java b/tests/tests/openglperf/src/android/openglperf/cts/GlAppSwitchTest.java
new file mode 100644
index 0000000..2d7df8a
--- /dev/null
+++ b/tests/tests/openglperf/src/android/openglperf/cts/GlAppSwitchTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012 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.openglperf.cts;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.util.List;
+
+/**
+ * Tests OpenGl rendering after task switching to other GL-using application
+ * This test needs to control two applications and task switch between them to
+ * force losing and reclaiming GL context
+ */
+public class GlAppSwitchTest extends
+ ActivityInstrumentationTestCase2<GlPlanetsActivity> {
+
+ private static final String REPLICA_ISLAND_PACKAGE = "com.replica.replicaisland";
+ private static final String REPLICA_ISLAND_ACTIVITY = "AndouKun";
+ private static final long TASK_SWITCH_SLOW_WAIT_TIME_MS = 15 * 1000;
+ private static final int NUMBER_OF_TASK_SWITCHES_SLOW = 10;
+ private static final long TASK_SWITCH_FAST_WAIT_TIME_MS = 2000;
+ private static final int NUMBER_OF_TASK_SWITCHES_FAST = 50;
+ private static final String ERROR_REPLICA_ISLAND_DEAD = "replica island not running";
+ private static final int MAX_RUNNING_TASKS = 1000;
+
+ private ActivityManager mActivityManager;
+
+ private int mTaskIdSelf;
+ private int mTaskIdReplica;
+
+ public GlAppSwitchTest() {
+ super(GlPlanetsActivity.class);
+ }
+
+ public void testGlActivitySwitchingFast() throws InterruptedException {
+ runTaskSwitchTest(TASK_SWITCH_FAST_WAIT_TIME_MS, NUMBER_OF_TASK_SWITCHES_FAST);
+ // wait for more time at the last run to allow watch dog timer in replica island to kick
+ Thread.sleep(TASK_SWITCH_SLOW_WAIT_TIME_MS);
+ assertTrue(ERROR_REPLICA_ISLAND_DEAD, isReplicaIslandRunning());
+ }
+
+ public void testGlActivitySwitchingSlow() throws InterruptedException {
+ runTaskSwitchTest(TASK_SWITCH_SLOW_WAIT_TIME_MS, NUMBER_OF_TASK_SWITCHES_SLOW);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Instrumentation instrument = getInstrumentation();
+ Context context = instrument.getContext();
+
+ mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+
+ Intent intentPlanets = new Intent();
+ intentPlanets.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_FRAMES, 0); //runs forever
+ intentPlanets.putExtra(GlPlanetsActivity.INTENT_EXTRA_NUM_PLANETS, 4); // max load
+ setActivityIntent(intentPlanets);
+ Activity activity = getActivity();
+ instrument.waitForIdleSync();
+ mTaskIdSelf = activity.getTaskId();
+ // wait further to render some frames
+ Thread.sleep(1000);
+ // terminate if it is already running
+ terminateReplicaIsland();
+
+ Intent intentIsland = new Intent(Intent.ACTION_MAIN);
+ intentIsland.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intentIsland.addCategory(Intent.CATEGORY_LAUNCHER);
+ intentIsland.setComponent(new ComponentName(REPLICA_ISLAND_PACKAGE,
+ REPLICA_ISLAND_PACKAGE + "." + REPLICA_ISLAND_ACTIVITY));
+ context.startActivity(intentIsland);
+ // wait to render some frames
+ Thread.sleep(5000);
+
+ setReplicaIslandTask();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ showOrHideReplicaIsland(false);
+ terminateReplicaIsland();
+ super.tearDown();
+ }
+
+ /**
+ * run foreground / background task switching between replica island and GlPlanetsActivity.
+ * @param waitTimeMs time to wait after each task switching
+ * @param numberOfSwitches number of task switches to run
+ * @throws InterruptedException
+ */
+ private void runTaskSwitchTest(long waitTimeMs, int numberOfSwitches)
+ throws InterruptedException {
+ boolean replicaForeground = false;
+ assertTrue(ERROR_REPLICA_ISLAND_DEAD, isReplicaIslandRunning());
+ for (int i = 0; i < numberOfSwitches; i++ ) {
+ showOrHideReplicaIsland(replicaForeground);
+ replicaForeground = !replicaForeground;
+ Thread.sleep(waitTimeMs);
+ assertTrue(ERROR_REPLICA_ISLAND_DEAD, isReplicaIslandRunning());
+ }
+ }
+
+ private void setReplicaIslandTask() {
+ boolean foundReplica = false;
+ List<ActivityManager.RunningTaskInfo> tasks =
+ mActivityManager.getRunningTasks(MAX_RUNNING_TASKS);
+ for (ActivityManager.RunningTaskInfo info : tasks) {
+ String packageName = info.baseActivity.getPackageName();
+ if (packageName.contentEquals(REPLICA_ISLAND_PACKAGE)) {
+ foundReplica = true;
+ mTaskIdReplica = info.id;
+ break;
+ }
+ }
+ assertTrue("cannot find replica island running", foundReplica);
+ }
+
+ private boolean isReplicaIslandRunning() {
+ boolean foundReplica = false;
+ List<ActivityManager.RunningTaskInfo> tasks =
+ mActivityManager.getRunningTasks(MAX_RUNNING_TASKS);
+ for (ActivityManager.RunningTaskInfo info : tasks) {
+ if (info.id == mTaskIdReplica) {
+ foundReplica = true;
+ break;
+ }
+ }
+ return foundReplica;
+ }
+
+ /**
+ * send replica island to foreground (when show = true) or background
+ * requires both replica island and GlPlanetsActivity to be alive.
+ * @param show
+ */
+ private void showOrHideReplicaIsland(boolean show) {
+ mActivityManager.moveTaskToFront(show ? mTaskIdReplica : mTaskIdSelf, 0);
+ }
+
+ /**
+ * this API works only when the replica island is in background.
+ */
+ private void terminateReplicaIsland() {
+ mActivityManager.killBackgroundProcesses(REPLICA_ISLAND_PACKAGE);
+ getInstrumentation().waitForIdleSync();
+ }
+}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
index 59a7782..5b90089 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsRenderer.java
@@ -45,6 +45,7 @@
private final Context mContext;
private final PlanetsRenderingParam mParam;
private final RenderCompletionListener mListener;
+ private final RenderingWatchDog mWatchDog;
private final Sphere[] mSpheres;
private final int mNumSpheres;
@@ -94,10 +95,11 @@
* @param listener
*/
public PlanetsRenderer(Context context, PlanetsRenderingParam param,
- RenderCompletionListener listener) {
+ RenderCompletionListener listener, RenderingWatchDog watchDog) {
resetTimer();
mContext = context;
mParam = param;
+ mWatchDog = watchDog;
mNumSpheres = mParam.mNumPlanets + 1; // 1 for sun
mNumIndices = mNumSpheres * mParam.mNumIndicesPerVertex;
mSpheres = new Sphere[mNumSpheres];
@@ -136,12 +138,14 @@
}
public void onDrawFrame(GL10 glUnused) {
+ mWatchDog.reset();
long currentTime = System.currentTimeMillis();
mFrameCount++;
if ((mFrameCount % FPS_DISPLAY_INTERVAL == 0) && (mFrameCount != 0)) {
float fps = (((float) FPS_DISPLAY_INTERVAL)
/ ((float) (currentTime - mLastFPSTime)) * 1000.0f);
+ // FPS is not correct if activity is paused/resumed.
Log.i(TAG, "FPS " + fps);
mLastFPSTime = currentTime;
}
@@ -370,11 +374,11 @@
checkGlError("glGenBuffers Vertex");
for (int i = 0; i < mNumSpheres; i++) {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVboVertices[i]);
+ checkGlError("glBindBuffer Vertex");
FloatBuffer vertices = mSpheres[i].getVertices();
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertices.limit()
* Sphere.FLOAT_SIZE, vertices, GLES20.GL_STATIC_DRAW);
checkGlError("glBufferData Vertex");
- mSpheres[i].releaseVertexBuffer(); // release memory
}
}
if (mParam.mUseVboForIndices) {
@@ -393,7 +397,6 @@
checkGlError("glBufferData Index");
}
- mSpheres[i].releaseIndexBuffer(); // release memory
}
}
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
index 526f706..2bfd001 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/PlanetsSurfaceView.java
@@ -20,23 +20,27 @@
import android.opengl.GLSurfaceView;
class PlanetsSurfaceView extends GLSurfaceView {
+ private final long RENDERING_TIMEOUT = 1900; // in msec, close to 2 secs
+ private final RenderingWatchDog mWatchDog = new RenderingWatchDog(RENDERING_TIMEOUT);
public PlanetsSurfaceView(Context context, PlanetsRenderingParam param,
RenderCompletionListener listener) {
super(context);
setEGLContextClientVersion(2);
- setRenderer(new PlanetsRenderer(context, param, listener));
+ setRenderer(new PlanetsRenderer(context, param, listener, mWatchDog));
}
@Override
public void onPause() {
+ mWatchDog.stop();
super.onPause();
setRenderMode(RENDERMODE_WHEN_DIRTY);
}
@Override
public void onResume() {
+ mWatchDog.start();
setRenderMode(RENDERMODE_CONTINUOUSLY);
super.onResume();
}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/RenderingWatchDog.java b/tests/tests/openglperf/src/android/openglperf/cts/RenderingWatchDog.java
new file mode 100644
index 0000000..4872af2
--- /dev/null
+++ b/tests/tests/openglperf/src/android/openglperf/cts/RenderingWatchDog.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 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.openglperf.cts;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import android.util.Log;
+
+import junit.framework.Assert;
+
+/**
+ * class for checking if rendering function is alive or not.
+ * panic if watch-dog is not reset over certain amount of time
+ */
+public class RenderingWatchDog implements Runnable {
+ private static final String TAG = "RenderingWatchDog";
+ private Thread mThread;
+ private Semaphore mSemaphore;
+ private volatile boolean mStopRequested;
+ private final long mTimeoutInMilliSecs;
+
+ public RenderingWatchDog(long timeoutInMilliSecs) {
+ mTimeoutInMilliSecs = timeoutInMilliSecs;
+ }
+
+ /** start watch-dog */
+ public void start() {
+ Log.i(TAG, "start");
+ mStopRequested = false;
+ mSemaphore = new Semaphore(0);
+ mThread = new Thread(this);
+ mThread.start();
+ }
+
+ /** stop watch-dog */
+ public void stop() {
+ Log.i(TAG, "stop");
+ if (mThread == null) {
+ return; // already finished
+ }
+ mStopRequested = true;
+ mSemaphore.release();
+ try {
+ mThread.join();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ mThread = null;
+ mSemaphore = null;
+ }
+
+ /** resets watch-dog, thus prevent it from panic */
+ public void reset() {
+ if (!mStopRequested) { // stop requested, but rendering still on-going
+ mSemaphore.release();
+ }
+ }
+
+ @Override
+ public void run() {
+ while (!mStopRequested) {
+ try {
+ Assert.assertTrue("Watchdog timed-out",
+ mSemaphore.tryAcquire(mTimeoutInMilliSecs, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ // this thread will not be interrupted,
+ // but if it happens, just check the exit condition.
+ }
+ }
+ }
+}
diff --git a/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java b/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java
index e8876dd..026ba97 100644
--- a/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java
+++ b/tests/tests/openglperf/src/android/openglperf/cts/Sphere.java
@@ -147,14 +147,7 @@
public int getTotalIndices() {
return mTotalIndices;
}
- /** once VBO buffer is generated, model data can be dropped */
- public void releaseVertexBuffer() {
- mVertices = null;
- }
- public void releaseIndexBuffer() {
- mVertices = null;
- }
private int max(int[] array) {
int max = array[0];