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
+ }
}