EGL Config Test
Bug 3184998
Get the EGL configurations that have the EGL_WINDOW_BIT enabled and try
to create a GLSurfaceView and draw a red or blue triangle on it...
if any of them crash then fail!
Change-Id: I88a13b268c136b0c24bed7f761632ee5ca5c8724
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 8cf1de2..7eaafb3 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -931,6 +931,8 @@
<activity android:name="android.opengl.cts.OpenGlEsVersionStubActivity"/>
+ <activity android:name="android.opengl.cts.EglConfigStubActivity"/>
+
<activity android:name="android.preference.cts.PreferenceStubActivity">
<meta-data android:name="android.preference"
android:resource="@xml/preferences_from_intent" />
diff --git a/tests/src/android/opengl/cts/EglConfigGLSurfaceView.java b/tests/src/android/opengl/cts/EglConfigGLSurfaceView.java
new file mode 100644
index 0000000..03e8d94
--- /dev/null
+++ b/tests/src/android/opengl/cts/EglConfigGLSurfaceView.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2011 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.opengl.cts;
+
+import android.content.Context;
+import android.opengl.GLES20;
+import android.opengl.GLSurfaceView;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * {@link GLSurfaceView} that uses the EGL configuration specified. Draws a couple frames of a red
+ * triangle and then calls the callback.
+ */
+public class EglConfigGLSurfaceView extends GLSurfaceView {
+
+ private static final String TAG = EglConfigGLSurfaceView.class.getName();
+
+ private final int mConfigId;
+
+ private final Runnable mCallback;
+
+ public EglConfigGLSurfaceView(Context context, int configId, int contextClientVersion,
+ Runnable callback) {
+ super(context);
+ mConfigId = configId;
+ mCallback = callback;
+ setEGLConfigChooser(new ConfigChooser());
+ setEGLContextClientVersion(contextClientVersion);
+ setRenderer(contextClientVersion == 1
+ ? new Renderer()
+ : new Renderer20());
+ }
+
+ private class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] attributeList = new int[] {
+ EGL10.EGL_CONFIG_ID, mConfigId,
+ EGL10.EGL_NONE
+ };
+
+ EGLConfig[] configs = new EGLConfig[1];
+ if (egl.eglChooseConfig(display, attributeList, configs, 1, new int[] {1})) {
+ // Print out the configuration since we may crash...
+ printConfig(egl, display, configs[0]);
+ return configs[0];
+ } else {
+ throw new IllegalStateException("Could not get EGL config...");
+ }
+ }
+ }
+
+ private class Renderer implements GLSurfaceView.Renderer {
+
+ private int mNumFrames;
+
+ private FloatBuffer mFloatBuffer;
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ float[] triangleVertices = {
+ 0.0f, 1.0f, -1.0f,
+ -1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f
+ };
+
+ ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleVertices.length * 4);
+ byteBuffer.order(ByteOrder.nativeOrder());
+
+ mFloatBuffer = ByteBuffer.allocateDirect(triangleVertices.length * 4)
+ .order(ByteOrder.nativeOrder())
+ .asFloatBuffer();
+ mFloatBuffer.put(triangleVertices).position(0);
+
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+ }
+
+ public void onDrawFrame(GL10 gl) {
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
+ gl.glColor4f(1.0f, 0, 0, 0);
+ gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFloatBuffer);
+ gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 9);
+
+ if (++mNumFrames == 10) {
+ post(mCallback);
+ }
+ }
+
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ gl.glViewport(0, 0, width, height);
+ }
+ }
+
+ private class Renderer20 implements GLSurfaceView.Renderer {
+
+ private FloatBuffer mFloatBuffer;
+
+ private final String mVertexShader =
+ "attribute vec4 aPosition;\n" +
+ "void main() {\n" +
+ " gl_Position = aPosition;\n" +
+ "}\n";
+
+ private final String mFragmentShader =
+ "void main() {\n" +
+ " gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);\n" +
+ "}\n";
+
+ private int mProgram;
+
+ private int maPositionHandle;
+
+ private int mNumFrames;
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ mProgram = createProgram(mVertexShader, mFragmentShader);
+ if (mProgram == 0) {
+ throw new RuntimeException("Could not create program");
+ }
+
+ maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
+ checkGlError("glGetAttribLocation aPosition");
+ if (maPositionHandle == -1) {
+ throw new RuntimeException("Could not get attrib location for aPosition");
+ }
+
+ float[] triangleVertices = {
+ 0.0f, 1.0f, 0.0f,
+ -1.0f, -1.0f, 0.0f,
+ 1.0f, -1.0f, 0.0f
+ };
+
+ mFloatBuffer = ByteBuffer.allocateDirect(triangleVertices.length * 4)
+ .order(ByteOrder.nativeOrder())
+ .asFloatBuffer();
+ mFloatBuffer.put(triangleVertices).position(0);
+ }
+
+ private int createProgram(String vertexSource, String fragmentSource) {
+ int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+ if (vertexShader == 0) {
+ return 0;
+ }
+
+ int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+ if (pixelShader == 0) {
+ return 0;
+ }
+
+ int program = GLES20.glCreateProgram();
+ if (program != 0) {
+ GLES20.glAttachShader(program, vertexShader);
+ checkGlError("glAttachShader");
+ GLES20.glAttachShader(program, pixelShader);
+ checkGlError("glAttachShader");
+ GLES20.glLinkProgram(program);
+ int[] linkStatus = new int[1];
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+ if (linkStatus[0] != GLES20.GL_TRUE) {
+ Log.e(TAG, "Could not link program: ");
+ Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+ GLES20.glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ return program;
+ }
+
+ private int loadShader(int shaderType, String source) {
+ int shader = GLES20.glCreateShader(shaderType);
+ if (shader != 0) {
+ GLES20.glShaderSource(shader, source);
+ GLES20.glCompileShader(shader);
+ int[] compiled = new int[1];
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+ if (compiled[0] == 0) {
+ Log.e(TAG, "Could not compile shader " + shaderType + ":");
+ Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
+ GLES20.glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ return shader;
+ }
+
+ private void checkGlError(String op) {
+ int error;
+ while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+ Log.e(TAG, op + ": glError " + error);
+ throw new RuntimeException(op + ": glError " + error);
+ }
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ GLES20.glClearColor(0, 0, 0, 1);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ GLES20.glUseProgram(mProgram);
+
+ GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
+ 0, mFloatBuffer);
+ checkGlError("glVertexAttribPointer maPosition");
+
+ GLES20.glEnableVertexAttribArray(maPositionHandle);
+ checkGlError("glEnableVertexAttribArray maPositionHandle");
+
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
+ checkGlError("glDrawArrays");
+
+ if (++mNumFrames == 10) {
+ post(mCallback);
+ }
+ }
+
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ GLES20.glViewport(0, 0, width, height);
+ }
+ }
+
+ /** Ripped from the NDK sample GL2JNIView class. */
+ private static void printConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig config) {
+ int[] attributes = {
+ EGL10.EGL_BUFFER_SIZE,
+ EGL10.EGL_ALPHA_SIZE,
+ EGL10.EGL_BLUE_SIZE,
+ EGL10.EGL_GREEN_SIZE,
+ EGL10.EGL_RED_SIZE,
+ EGL10.EGL_DEPTH_SIZE,
+ EGL10.EGL_STENCIL_SIZE,
+ EGL10.EGL_CONFIG_CAVEAT,
+ EGL10.EGL_CONFIG_ID,
+ EGL10.EGL_LEVEL,
+ EGL10.EGL_MAX_PBUFFER_HEIGHT,
+ EGL10.EGL_MAX_PBUFFER_PIXELS,
+ EGL10.EGL_MAX_PBUFFER_WIDTH,
+ EGL10.EGL_NATIVE_RENDERABLE,
+ EGL10.EGL_NATIVE_VISUAL_ID,
+ EGL10.EGL_NATIVE_VISUAL_TYPE,
+ 0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+ EGL10.EGL_SAMPLES,
+ EGL10.EGL_SAMPLE_BUFFERS,
+ EGL10.EGL_SURFACE_TYPE,
+ EGL10.EGL_TRANSPARENT_TYPE,
+ EGL10.EGL_TRANSPARENT_RED_VALUE,
+ EGL10.EGL_TRANSPARENT_GREEN_VALUE,
+ EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+ 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
+ 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+ 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
+ 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
+ EGL10.EGL_LUMINANCE_SIZE,
+ EGL10.EGL_ALPHA_MASK_SIZE,
+ EGL10.EGL_COLOR_BUFFER_TYPE,
+ EGL10.EGL_RENDERABLE_TYPE,
+ 0x3042 // EGL10.EGL_CONFORMANT
+ };
+ String[] names = {
+ "EGL_BUFFER_SIZE",
+ "EGL_ALPHA_SIZE",
+ "EGL_BLUE_SIZE",
+ "EGL_GREEN_SIZE",
+ "EGL_RED_SIZE",
+ "EGL_DEPTH_SIZE",
+ "EGL_STENCIL_SIZE",
+ "EGL_CONFIG_CAVEAT",
+ "EGL_CONFIG_ID",
+ "EGL_LEVEL",
+ "EGL_MAX_PBUFFER_HEIGHT",
+ "EGL_MAX_PBUFFER_PIXELS",
+ "EGL_MAX_PBUFFER_WIDTH",
+ "EGL_NATIVE_RENDERABLE",
+ "EGL_NATIVE_VISUAL_ID",
+ "EGL_NATIVE_VISUAL_TYPE",
+ "EGL_PRESERVED_RESOURCES",
+ "EGL_SAMPLES",
+ "EGL_SAMPLE_BUFFERS",
+ "EGL_SURFACE_TYPE",
+ "EGL_TRANSPARENT_TYPE",
+ "EGL_TRANSPARENT_RED_VALUE",
+ "EGL_TRANSPARENT_GREEN_VALUE",
+ "EGL_TRANSPARENT_BLUE_VALUE",
+ "EGL_BIND_TO_TEXTURE_RGB",
+ "EGL_BIND_TO_TEXTURE_RGBA",
+ "EGL_MIN_SWAP_INTERVAL",
+ "EGL_MAX_SWAP_INTERVAL",
+ "EGL_LUMINANCE_SIZE",
+ "EGL_ALPHA_MASK_SIZE",
+ "EGL_COLOR_BUFFER_TYPE",
+ "EGL_RENDERABLE_TYPE",
+ "EGL_CONFORMANT"
+ };
+ int[] value = new int[1];
+ for (int i = 0; i < attributes.length; i++) {
+ int attribute = attributes[i];
+ String name = names[i];
+ if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
+ Log.w(TAG, String.format(" %s: %d\n", name, value[0]));
+ } else {
+ // Log.w(TAG, String.format(" %s: failed\n", name));
+ while (egl.eglGetError() != EGL10.EGL_SUCCESS);
+ }
+ }
+ }
+}
diff --git a/tests/src/android/opengl/cts/EglConfigStubActivity.java b/tests/src/android/opengl/cts/EglConfigStubActivity.java
new file mode 100644
index 0000000..ab1a6d0
--- /dev/null
+++ b/tests/src/android/opengl/cts/EglConfigStubActivity.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 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.opengl.cts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * {@link Activity} with a {@link GLSurfaceView} that chooses a specific configuration.
+ */
+public class EglConfigStubActivity extends Activity {
+
+ public static final String CONFIG_ID_EXTRA = "eglConfigId";
+
+ public static final String CONTEXT_CLIENT_VERSION_EXTRA = "eglContextClientVersion";
+
+ private EglConfigGLSurfaceView mView;
+
+ private CountDownLatch mFinishedDrawing;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ int configId = getConfigId();
+ int contextClientVersion = getContextClientVersion();
+ setTitle("EGL Config Id: " + configId + " Client Version: " + contextClientVersion);
+
+ mFinishedDrawing = new CountDownLatch(1);
+ mView = new EglConfigGLSurfaceView(this, configId, contextClientVersion, new Runnable() {
+ @Override
+ public void run() {
+ mFinishedDrawing.countDown();
+ }
+ });
+ setContentView(mView);
+ }
+
+ private int getConfigId() {
+ Intent intent = getIntent();
+ if (intent != null) {
+ return intent.getIntExtra(CONFIG_ID_EXTRA, 0);
+ } else {
+ return 0;
+ }
+ }
+
+ private int getContextClientVersion() {
+ Intent intent = getIntent();
+ if (intent != null) {
+ return intent.getIntExtra(CONTEXT_CLIENT_VERSION_EXTRA, 0);
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mView.onPause();
+ }
+
+ public void waitToFinishDrawing() throws InterruptedException {
+ if (!mFinishedDrawing.await(3, TimeUnit.SECONDS)) {
+ throw new IllegalStateException("Coudn't finish drawing frames!");
+ }
+ }
+}
diff --git a/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java b/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java
new file mode 100644
index 0000000..2f918db
--- /dev/null
+++ b/tests/tests/graphics/src/android/opengl/cts/EglConfigTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 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.opengl.cts;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.test.ActivityInstrumentationTestCase2;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+
+/**
+ * Test that gets a list of EGL configurations and tries to use each one in a GLSurfaceView.
+ */
+public class EglConfigTest extends ActivityInstrumentationTestCase2<EglConfigStubActivity> {
+
+ private static final int EGL_OPENGL_ES_BIT = 0x1;
+
+ private static final int EGL_OPENGL_ES2_BIT = 0x4;
+
+ private Instrumentation mInstrumentation;
+
+ public EglConfigTest() {
+ super("com.android.cts.stub", EglConfigStubActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mInstrumentation = getInstrumentation();
+ }
+
+ public void testEglConfigs() throws Exception {
+ int[] configIds = getEglConfigIds(EGL_OPENGL_ES_BIT);
+ int[] configIds2 = getEglConfigIds(EGL_OPENGL_ES2_BIT);
+ assertTrue(configIds.length + configIds2.length > 0);
+ runConfigTests(configIds, 1);
+ runConfigTests(configIds2, 2);
+ }
+
+ private void runConfigTests(int[] configIds, int contextClientVersion)
+ throws InterruptedException {
+ for (int configId : configIds) {
+ Bundle extras = new Bundle();
+ extras.putInt(EglConfigStubActivity.CONFIG_ID_EXTRA, configId);
+ extras.putInt(EglConfigStubActivity.CONTEXT_CLIENT_VERSION_EXTRA, contextClientVersion);
+ EglConfigStubActivity activity = launchActivity("com.android.cts.stub",
+ EglConfigStubActivity.class, extras);
+ activity.waitToFinishDrawing();
+ activity.finish();
+ mInstrumentation.waitForIdleSync();
+ }
+ }
+
+ private static int[] getEglConfigIds(int renderableType) {
+ EGL10 egl = (EGL10) EGLContext.getEGL();
+ EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ int[] numConfigs = new int[1];
+
+ int[] attributeList = new int[] {
+ EGL10.EGL_RENDERABLE_TYPE, renderableType,
+
+ // Avoid configs like RGBA0008 which crash even though they have the window bit set.
+ EGL10.EGL_RED_SIZE, 1,
+ EGL10.EGL_GREEN_SIZE, 1,
+ EGL10.EGL_BLUE_SIZE, 1,
+
+ EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
+ EGL10.EGL_NONE
+ };
+
+ if (egl.eglInitialize(display, null)) {
+ try {
+ if (egl.eglChooseConfig(display, attributeList, null, 0, numConfigs)) {
+ EGLConfig[] configs = new EGLConfig[numConfigs[0]];
+ if (egl.eglChooseConfig(display, attributeList, configs, configs.length,
+ numConfigs)) {
+ int[] configIds = new int[numConfigs[0]];
+ for (int i = 0; i < numConfigs[0]; i++) {
+ int[] value = new int[1];
+ if (egl.eglGetConfigAttrib(display, configs[i], EGL10.EGL_CONFIG_ID,
+ value)) {
+ configIds[i] = value[0];
+ } else {
+ throw new IllegalStateException("Couldn't call eglGetConfigAttrib");
+ }
+ }
+ return configIds;
+ } else {
+ throw new IllegalStateException("Couldn't call eglChooseConfig (1)");
+ }
+ } else {
+ throw new IllegalStateException("Couldn't call eglChooseConfig (2)");
+ }
+ } finally {
+ egl.eglTerminate(display);
+ }
+ } else {
+ throw new IllegalStateException("Couldn't initialize EGL.");
+ }
+ }
+}