Java side of OpenGL performance benchmark

Change-Id: I33f6be5b884487f90e755540f1b29acd63c575db
diff --git a/suite/pts/PtsBenchmarkingList.mk b/suite/pts/PtsBenchmarkingList.mk
index c106e1f..7c0ad47 100644
--- a/suite/pts/PtsBenchmarkingList.mk
+++ b/suite/pts/PtsBenchmarkingList.mk
@@ -23,7 +23,8 @@
     PtsDeviceDram \
     PtsDeviceSimpleCpu \
     PtsDeviceBrowserBench \
-    PtsDeviceVideoPerf
+    PtsDeviceVideoPerf \
+    PtsDeviceOpenGl
 
 PTS_SUPPORT_PACKAGES := \
     PtsDeviceTaskswitchingAppA \
diff --git a/suite/pts/deviceTests/opengl/Android.mk b/suite/pts/deviceTests/opengl/Android.mk
new file mode 100644
index 0000000..377562c
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ptsutil ctsutil ctstestrunner
+
+LOCAL_JNI_SHARED_LIBRARIES := libptsopengl_jni
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PtsDeviceOpenGl
+
+LOCAL_SDK_VERSION := 16
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/suite/pts/deviceTests/opengl/AndroidManifest.xml b/suite/pts/deviceTests/opengl/AndroidManifest.xml
new file mode 100644
index 0000000..a91766f
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.pts.opengl"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="16" />
+
+    <uses-feature
+        android:glEsVersion="0x00020000"
+        android:required="true" />
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />\
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <application android:allowBackup="false" >
+        <uses-library android:name="android.test.runner" />
+
+        <activity
+            android:name="com.android.pts.opengl.primitive.GLActivity"
+            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation
+        android:name="android.test.InstrumentationCtsTestRunner"
+        android:label="OpenGL ES 2.0 Benchmark"
+        android:targetPackage="com.android.pts.opengl" />
+
+</manifest>
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/jni/Android.mk b/suite/pts/deviceTests/opengl/jni/Android.mk
new file mode 100644
index 0000000..b872b02
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/Android.mk
@@ -0,0 +1,31 @@
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libptsopengl_jni
+
+LOCAL_MODULE_TAGS := optional
+
+# Get all cpp files but not hidden files
+LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
+          find . -name "*.cpp" -and -not -name ".*"))
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := libEGL libGLESv2 libandroid libutils
+
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/suite/pts/deviceTests/opengl/jni/GLNative.cpp b/suite/pts/deviceTests/opengl/jni/GLNative.cpp
new file mode 100644
index 0000000..48b87a5
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/jni/GLNative.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+#include <jni.h>
+
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_startBenchmark(
+        JNIEnv* env, jclass clazz, jint numFrames, jdoubleArray frameTimes) {
+    return true;
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupFullPipelineBenchmark(
+        JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupPixelOutputBenchmark(
+        JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupShaderPerfBenchmark(
+        JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_pts_opengl_primitive_GLActivity_setupContextSwitchBenchmark(
+        JNIEnv* env, jclass clazz, jobject surface, jint workload) {
+}
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/Benchmark.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/Benchmark.java
new file mode 100644
index 0000000..d61f45d
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/Benchmark.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 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 com.android.pts.opengl.primitive;
+
+/**
+ * Represents the different primitive benchmarks.
+ */
+public enum Benchmark {
+    FullPipeline,
+    PixelOutput,
+    ShaderPerf,
+    ContextSwitch;
+}
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLActivity.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLActivity.java
new file mode 100644
index 0000000..0caa576
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLActivity.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2013 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 com.android.pts.opengl.primitive;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.cts.util.WatchDog;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.Surface;
+
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+
+public class GLActivity extends Activity {
+
+    public final static String TAG = "GLActivity";
+    /**
+     * Holds the name of the benchmark to run.
+     */
+    public final static String INTENT_EXTRA_BENCHMARK_NAME = "benchmark_name";
+    /**
+     * Holds the number of milliseconds to wait before timing out.
+     */
+    public final static String INTENT_EXTRA_TIMEOUT = "timeout";
+    /**
+     * The minimum number of frames per second the device must achieve to pass.
+     */
+    public final static String INTENT_EXTRA_MIN_FPS = "min_fps";
+    /**
+     * The number of frames to render for each workload.
+     */
+    public final static String INTENT_EXTRA_NUM_FRAMES = "num_frames";
+
+    private Worker runner;
+    private volatile Exception mException;
+    private volatile Surface mSurface;
+
+    private Benchmark mBenchmark;
+    private int mTimeout;
+    private double mMinFps;
+    private int mNumFrames;
+    private volatile int mWorkload = 0;
+
+    @Override
+    public void onCreate(Bundle data) {
+        super.onCreate(data);
+        System.loadLibrary("ptsopengl_jni");
+        Intent intent = getIntent();
+        mBenchmark = Benchmark.valueOf(intent.getStringExtra(INTENT_EXTRA_BENCHMARK_NAME));
+        mTimeout = intent.getIntExtra(INTENT_EXTRA_TIMEOUT, 0);
+        mMinFps = intent.getDoubleExtra(INTENT_EXTRA_MIN_FPS, 0);
+        mNumFrames = intent.getIntExtra(INTENT_EXTRA_NUM_FRAMES, 0);
+
+        Log.i(TAG, "Benchmark: " + mBenchmark);
+        Log.i(TAG, "Time Out: " + mTimeout);
+        Log.i(TAG, "Min FPS: " + mMinFps);
+        Log.i(TAG, "Num Frames: " + mNumFrames);
+
+        SurfaceView surfaceView = new SurfaceView(this);
+        surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
+            @Override
+            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+                mSurface = holder.getSurface();
+            }
+
+            @Override
+            public void surfaceCreated(SurfaceHolder holder) {}
+
+            @Override
+            public void surfaceDestroyed(SurfaceHolder holder) {}
+        });
+        setContentView(surfaceView);
+    }
+
+    public int waitForCompletion() throws Exception {
+        // Creates, starts and waits for a worker to run the benchmark.
+        runner = new Worker();
+        runner.start();
+        runner.join();
+        if (mException != null) {
+            throw mException;
+        }
+        return mWorkload;
+    }
+
+    private static native void setupFullPipelineBenchmark(Surface surface, int workload);
+
+    private static native void setupPixelOutputBenchmark(Surface surface, int workload);
+
+    private static native void setupShaderPerfBenchmark(Surface surface, int workload);
+
+    private static native void setupContextSwitchBenchmark(Surface surface, int workload);
+
+    private static native boolean startBenchmark(int numFrames, double[] frameTimes);
+
+    /**
+     * This thread runs the benchmarks, freeing the UI thread.
+     */
+    private class Worker extends Thread {
+
+        private volatile boolean repeat = true;
+        private WatchDog watchDog;
+
+        @Override
+        public void run() {
+            // Creates a watchdog to ensure a iteration doesn't exceed the timeout.
+            watchDog = new WatchDog(mTimeout);
+            watchDog.start();
+            // Used to record the start and end time of the iteration.
+            double[] times = new double[2];
+            while (repeat) {
+                // The workload to use for this iteration.
+                int wl = mWorkload + 1;
+                // Setup the benchmark.
+                switch (mBenchmark) {
+                    case FullPipeline:
+                        setupFullPipelineBenchmark(mSurface, wl);
+                        break;
+                    case PixelOutput:
+                        setupPixelOutputBenchmark(mSurface, wl);
+                        break;
+                    case ShaderPerf:
+                        setupShaderPerfBenchmark(mSurface, wl);
+                        break;
+                    case ContextSwitch:
+                        setupContextSwitchBenchmark(mSurface, wl);
+                        break;
+                }
+                // Start benchmark.
+                if (!startBenchmark(mNumFrames, times)) {
+                    mException = new Exception("Could not run benchmark");
+                    repeat = false;
+                } else {
+                    // Calculate FPS.
+                    double totalTimeTaken = times[1] - times[0];
+                    double meanFps = mNumFrames * 1000.0f / totalTimeTaken;
+                    Log.i(TAG, "Workload: " + wl);
+                    Log.i(TAG, "MeanFPS: " + meanFps);
+                    if (meanFps >= mMinFps) {
+                        // Iteration passed, proceed to next one.
+                        mWorkload++;
+                    } else {
+                        // Iteration failed.
+                        repeat = false;
+                    }
+                }
+                // Resets the watchdog for the next iteration.
+                watchDog.reset();
+            }
+            watchDog.stop();
+        }
+
+    }
+}
diff --git a/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLBenchmark.java b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLBenchmark.java
new file mode 100644
index 0000000..ad3159f
--- /dev/null
+++ b/suite/pts/deviceTests/opengl/src/com/android/pts/opengl/primitive/GLBenchmark.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 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 com.android.pts.opengl.primitive;
+
+import android.content.Intent;
+
+import com.android.pts.util.PtsActivityInstrumentationTestCase2;
+import com.android.pts.util.ResultType;
+import com.android.pts.util.ResultUnit;
+
+/**
+ * Runs the Primitive OpenGL ES 2.0 Benchmarks.
+ */
+public class GLBenchmark extends PtsActivityInstrumentationTestCase2<GLActivity> {
+
+    private static final double MIN_FPS = 50;
+
+    public GLBenchmark() {
+        super(GLActivity.class);
+    }
+
+    /**
+     * Runs the full OpenGL ES 2.0 pipeline test.
+     *
+     * @throws Exception If the benchmark could not be run.
+     */
+    public void testFullPipeline() throws Exception {
+        runBenchmark(Benchmark.FullPipeline, 500, 10000);
+    }
+
+    /**
+     * Runs the pixel output test.
+     *
+     * @throws Exception If the benchmark could not be run.
+     */
+    public void testPixelOutput() throws Exception {
+        runBenchmark(Benchmark.PixelOutput, 500, 10000);
+    }
+
+    /**
+     * Runs the shader performance test.
+     *
+     * @throws Exception If the benchmark could not be run.
+     */
+    public void testShaderPerf() throws Exception {
+        // TODO(stuartscott): Not yet implemented
+        // runBenchmark(Benchmark.ShaderPerf, 500, 10000);
+    }
+
+    /**
+     * Runs the OpenGL context switch overhead test.
+     *
+     * @throws Exception If the benchmark could not be run.
+     */
+    public void testContextSwitch() throws Exception {
+        runBenchmark(Benchmark.ContextSwitch, 500, 10000);
+    }
+
+    /**
+     * Runs the specified test.
+     *
+     * @param benchmark An enum representing the benchmark to run.
+     * @param numFrames The number of frames to render.
+     * @param timeout The milliseconds to wait for an iteration of the benchmark before timing out.
+     * @throws Exception If the benchmark could not be run.
+     */
+    private void runBenchmark(Benchmark benchmark, int numFrames, int timeout) throws Exception {
+        String benchmarkName = benchmark.toString();
+        Intent intent = new Intent();
+        intent.putExtra(GLActivity.INTENT_EXTRA_BENCHMARK_NAME, benchmarkName);
+        intent.putExtra(GLActivity.INTENT_EXTRA_TIMEOUT, timeout);
+        intent.putExtra(GLActivity.INTENT_EXTRA_MIN_FPS, MIN_FPS);
+        intent.putExtra(GLActivity.INTENT_EXTRA_NUM_FRAMES, numFrames);
+
+        GLActivity activity = null;
+        setActivityIntent(intent);
+        try {
+            activity = getActivity();
+            int workload = activity.waitForCompletion();
+            // represents the maximum workload it can do whilst maintaining MIN_FPS.
+            getReportLog()
+                    .printSummary("Workload", workload, ResultType.HIGHER_BETTER, ResultUnit.SCORE);
+        } finally {
+            if (activity != null) {
+                activity.finish();
+            }
+        }
+    }
+}