Allows to render with an OpenGL context inside a TextureView.

Change-Id: I59453f7fc3997f0502a1c5d325d37fed376fabc7
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index c5c3f70..6285880 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -37,6 +37,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity
+                android:name="GLTextureViewActivity"
+                android:label="_TextureViewGL">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
         
         <activity
                 android:name="BitmapMeshActivity"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
new file mode 100644
index 0000000..7f97098
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
@@ -0,0 +1,274 @@
+/*
+ * 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 com.android.test.hwui;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.graphics.SurfaceTexture;
+import android.opengl.GLES20;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.TextureView;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class GLTextureViewActivity extends Activity implements TextureView.SurfaceTextureListener {
+    private RenderThread mRenderThread;
+    private TextureView mTextureView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mTextureView = new TextureView(this);
+        mTextureView.setSurfaceTextureListener(this);
+
+        setContentView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mRenderThread.finish();
+    }
+
+    @Override
+    public void onSurfaceTextureAvailable(SurfaceTexture surface) {
+        mRenderThread = new RenderThread(surface);
+        mRenderThread.start();
+
+        mTextureView.setCameraDistance(5000);
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
+        animator.setRepeatMode(ObjectAnimator.REVERSE);
+        animator.setRepeatCount(ObjectAnimator.INFINITE);
+        animator.setDuration(4000);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                ((View) mTextureView.getParent()).invalidate();
+            }
+        });
+        animator.start();
+    }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+    }
+
+    private static class RenderThread extends Thread {
+        private static final String LOG_TAG = "GLTextureView";
+
+        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+        static final int EGL_SURFACE_TYPE = 0x3033;
+        static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
+        static final int EGL_OPENGL_ES2_BIT = 4;
+
+        private volatile boolean mFinished;
+
+        private SurfaceTexture mSurface;
+        
+        private EGL10 mEgl;
+        private EGLDisplay mEglDisplay;
+        private EGLConfig mEglConfig;
+        private EGLContext mEglContext;
+        private EGLSurface mEglSurface;
+        private GL mGL;
+
+        RenderThread(SurfaceTexture surface) {
+            mSurface = surface;
+        }
+
+        @Override
+        public void run() {
+            initGL();
+
+            float red = 0.0f;
+            while (!mFinished) {
+                checkCurrent();
+
+                GLES20.glClearColor(red, 0.0f, 0.0f, 1.0f);
+                int error = GLES20.glGetError();
+                if (error != GLES20.GL_NO_ERROR) {
+                    Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
+                }
+
+                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+                error = GLES20.glGetError();
+                if (error != GLES20.GL_NO_ERROR) {
+                    Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
+                }
+
+                if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
+                    throw new RuntimeException("Cannot swap buffers");
+                }
+                
+                try {
+                    Thread.sleep(20);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+
+                red += 0.021f;
+                if (red > 1.0f) red = 0.0f;
+            }
+
+            finishGL();
+        }
+
+        private void finishGL() {
+            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+        }
+
+        private void checkCurrent() {
+            if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
+                    !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
+                if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+                    throw new RuntimeException("eglMakeCurrent failed "
+                            + getEGLErrorString(mEgl.eglGetError()));
+                }
+            }
+        }
+        
+        private void initGL() {
+            mEgl = (EGL10) EGLContext.getEGL();
+
+            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+                throw new RuntimeException("eglGetDisplay failed "
+                        + getEGLErrorString(mEgl.eglGetError()));
+            }
+            
+            int[] version = new int[2];
+            if (!mEgl.eglInitialize(mEglDisplay, version)) {
+                throw new RuntimeException("eglInitialize failed " +
+                        getEGLErrorString(mEgl.eglGetError()));
+            }
+
+            mEglConfig = chooseEglConfig();
+            if (mEglConfig == null) {
+                throw new RuntimeException("eglConfig not initialized");
+            }
+            
+            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
+
+            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
+
+            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+                int error = mEgl.eglGetError();
+                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
+                    Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+                    return;
+                }
+                throw new RuntimeException("createWindowSurface failed "
+                        + getEGLErrorString(error));
+            }
+
+            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+                throw new RuntimeException("eglMakeCurrent failed "
+                        + getEGLErrorString(mEgl.eglGetError()));
+            }
+
+            mGL = mEglContext.getGL();
+        }
+        
+
+        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
+            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
+            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);            
+        }
+
+        private EGLConfig chooseEglConfig() {
+            int[] configsCount = new int[1];
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] configSpec = getConfig();
+            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
+                throw new IllegalArgumentException("eglChooseConfig failed " +
+                        getEGLErrorString(mEgl.eglGetError()));
+            } else if (configsCount[0] > 0) {
+                return configs[0];
+            }
+            return null;
+        }
+        
+        private int[] getConfig() {
+            return new int[] {
+                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                    EGL10.EGL_RED_SIZE, 8,
+                    EGL10.EGL_GREEN_SIZE, 8,
+                    EGL10.EGL_BLUE_SIZE, 8,
+                    EGL10.EGL_ALPHA_SIZE, 8,
+                    EGL10.EGL_DEPTH_SIZE, 0,
+                    EGL10.EGL_STENCIL_SIZE, 0,
+                    EGL10.EGL_NONE
+            };
+        }
+
+        static String getEGLErrorString(int error) {
+            switch (error) {
+                case EGL10.EGL_SUCCESS:
+                    return "EGL_SUCCESS";
+                case EGL10.EGL_NOT_INITIALIZED:
+                    return "EGL_NOT_INITIALIZED";
+                case EGL10.EGL_BAD_ACCESS:
+                    return "EGL_BAD_ACCESS";
+                case EGL10.EGL_BAD_ALLOC:
+                    return "EGL_BAD_ALLOC";
+                case EGL10.EGL_BAD_ATTRIBUTE:
+                    return "EGL_BAD_ATTRIBUTE";
+                case EGL10.EGL_BAD_CONFIG:
+                    return "EGL_BAD_CONFIG";
+                case EGL10.EGL_BAD_CONTEXT:
+                    return "EGL_BAD_CONTEXT";
+                case EGL10.EGL_BAD_CURRENT_SURFACE:
+                    return "EGL_BAD_CURRENT_SURFACE";
+                case EGL10.EGL_BAD_DISPLAY:
+                    return "EGL_BAD_DISPLAY";
+                case EGL10.EGL_BAD_MATCH:
+                    return "EGL_BAD_MATCH";
+                case EGL10.EGL_BAD_NATIVE_PIXMAP:
+                    return "EGL_BAD_NATIVE_PIXMAP";
+                case EGL10.EGL_BAD_NATIVE_WINDOW:
+                    return "EGL_BAD_NATIVE_WINDOW";
+                case EGL10.EGL_BAD_PARAMETER:
+                    return "EGL_BAD_PARAMETER";
+                case EGL10.EGL_BAD_SURFACE:
+                    return "EGL_BAD_SURFACE";
+                case EGL11.EGL_CONTEXT_LOST:
+                    return "EGL_CONTEXT_LOST";
+                default:
+                    return "0x" + Integer.toHexString(error);
+            }
+        }
+
+        void finish() {
+            mFinished = true;
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
index 4726672..2feda57 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
@@ -84,4 +84,9 @@
         animator.setDuration(4000);
         animator.start();
     }
+
+    @Override
+    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+        // Ignored, the Camera does all the work for us
+    }
 }