GLTextureViewActivity.java revision 80429c458506485904715180d10584092a5cd082
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.test.hwui;
18
19import android.animation.ObjectAnimator;
20import android.animation.ValueAnimator;
21import android.app.Activity;
22import android.graphics.SurfaceTexture;
23import android.opengl.GLES20;
24import android.os.Bundle;
25import android.util.Log;
26import android.view.Gravity;
27import android.view.TextureView;
28import android.view.View;
29import android.widget.FrameLayout;
30
31import javax.microedition.khronos.egl.EGL10;
32import javax.microedition.khronos.egl.EGL11;
33import javax.microedition.khronos.egl.EGLConfig;
34import javax.microedition.khronos.egl.EGLContext;
35import javax.microedition.khronos.egl.EGLDisplay;
36import javax.microedition.khronos.egl.EGLSurface;
37import javax.microedition.khronos.opengles.GL;
38
39@SuppressWarnings({"UnusedDeclaration"})
40public class GLTextureViewActivity extends Activity implements TextureView.SurfaceTextureListener {
41    private RenderThread mRenderThread;
42    private TextureView mTextureView;
43
44    @Override
45    protected void onCreate(Bundle savedInstanceState) {
46        super.onCreate(savedInstanceState);
47
48        mTextureView = new TextureView(this);
49        mTextureView.setSurfaceTextureListener(this);
50
51        setContentView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
52    }
53
54    @Override
55    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
56        mRenderThread = new RenderThread(surface);
57        mRenderThread.start();
58
59        mTextureView.setCameraDistance(5000);
60
61        ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
62        animator.setRepeatMode(ObjectAnimator.REVERSE);
63        animator.setRepeatCount(ObjectAnimator.INFINITE);
64        animator.setDuration(4000);
65        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
66            @Override
67            public void onAnimationUpdate(ValueAnimator animation) {
68                ((View) mTextureView.getParent()).invalidate();
69            }
70        });
71        animator.start();
72    }
73
74    @Override
75    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
76    }
77
78    @Override
79    public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
80        mRenderThread.finish();
81        try {
82            mRenderThread.join();
83        } catch (InterruptedException e) {
84            Log.e(RenderThread.LOG_TAG, "Could not wait for render thread");
85        }
86    }
87
88    @Override
89    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
90    }
91
92    private static class RenderThread extends Thread {
93        private static final String LOG_TAG = "GLTextureView";
94
95        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
96        static final int EGL_OPENGL_ES2_BIT = 4;
97
98        private volatile boolean mFinished;
99
100        private SurfaceTexture mSurface;
101
102        private EGL10 mEgl;
103        private EGLDisplay mEglDisplay;
104        private EGLConfig mEglConfig;
105        private EGLContext mEglContext;
106        private EGLSurface mEglSurface;
107        private GL mGL;
108
109        RenderThread(SurfaceTexture surface) {
110            mSurface = surface;
111        }
112
113        @Override
114        public void run() {
115            initGL();
116
117            float red = 1.0f;
118            while (!mFinished) {
119                checkCurrent();
120
121                Log.d(LOG_TAG, "Rendering frame");
122
123                GLES20.glClearColor(red, 0.0f, 0.0f, 1.0f);
124                checkGlError();
125
126                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
127                checkGlError();
128
129                if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
130                    throw new RuntimeException("Cannot swap buffers");
131                }
132                checkEglError();
133
134                try {
135                    Thread.sleep(20);
136                } catch (InterruptedException e) {
137                    // Ignore
138                }
139
140                red += 0.021f;
141                if (red > 1.0f) red = 0.0f;
142            }
143
144            finishGL();
145        }
146
147        private void checkEglError() {
148            int error = mEgl.eglGetError();
149            if (error != EGL10.EGL_SUCCESS) {
150                Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error));
151            }
152        }
153
154        private void checkGlError() {
155            int error = GLES20.glGetError();
156            if (error != GLES20.GL_NO_ERROR) {
157                Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
158            }
159        }
160
161        private void finishGL() {
162            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
163            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
164        }
165
166        private void checkCurrent() {
167            if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
168                    !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
169                if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
170                    throw new RuntimeException("eglMakeCurrent failed "
171                            + getEGLErrorString(mEgl.eglGetError()));
172                }
173            }
174        }
175
176        private void initGL() {
177            mEgl = (EGL10) EGLContext.getEGL();
178
179            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
180            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
181                throw new RuntimeException("eglGetDisplay failed "
182                        + getEGLErrorString(mEgl.eglGetError()));
183            }
184
185            int[] version = new int[2];
186            if (!mEgl.eglInitialize(mEglDisplay, version)) {
187                throw new RuntimeException("eglInitialize failed " +
188                        getEGLErrorString(mEgl.eglGetError()));
189            }
190
191            mEglConfig = chooseEglConfig();
192            if (mEglConfig == null) {
193                throw new RuntimeException("eglConfig not initialized");
194            }
195
196            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
197
198            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
199
200            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
201                int error = mEgl.eglGetError();
202                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
203                    Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
204                    return;
205                }
206                throw new RuntimeException("createWindowSurface failed "
207                        + getEGLErrorString(error));
208            }
209
210            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
211                throw new RuntimeException("eglMakeCurrent failed "
212                        + getEGLErrorString(mEgl.eglGetError()));
213            }
214
215            mGL = mEglContext.getGL();
216        }
217
218
219        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
220            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
221            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
222        }
223
224        private EGLConfig chooseEglConfig() {
225            int[] configsCount = new int[1];
226            EGLConfig[] configs = new EGLConfig[1];
227            int[] configSpec = getConfig();
228            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
229                throw new IllegalArgumentException("eglChooseConfig failed " +
230                        getEGLErrorString(mEgl.eglGetError()));
231            } else if (configsCount[0] > 0) {
232                return configs[0];
233            }
234            return null;
235        }
236
237        private int[] getConfig() {
238            return new int[] {
239                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
240                    EGL10.EGL_RED_SIZE, 8,
241                    EGL10.EGL_GREEN_SIZE, 8,
242                    EGL10.EGL_BLUE_SIZE, 8,
243                    EGL10.EGL_ALPHA_SIZE, 8,
244                    EGL10.EGL_DEPTH_SIZE, 0,
245                    EGL10.EGL_STENCIL_SIZE, 0,
246                    EGL10.EGL_NONE
247            };
248        }
249
250        static String getEGLErrorString(int error) {
251            switch (error) {
252                case EGL10.EGL_SUCCESS:
253                    return "EGL_SUCCESS";
254                case EGL10.EGL_NOT_INITIALIZED:
255                    return "EGL_NOT_INITIALIZED";
256                case EGL10.EGL_BAD_ACCESS:
257                    return "EGL_BAD_ACCESS";
258                case EGL10.EGL_BAD_ALLOC:
259                    return "EGL_BAD_ALLOC";
260                case EGL10.EGL_BAD_ATTRIBUTE:
261                    return "EGL_BAD_ATTRIBUTE";
262                case EGL10.EGL_BAD_CONFIG:
263                    return "EGL_BAD_CONFIG";
264                case EGL10.EGL_BAD_CONTEXT:
265                    return "EGL_BAD_CONTEXT";
266                case EGL10.EGL_BAD_CURRENT_SURFACE:
267                    return "EGL_BAD_CURRENT_SURFACE";
268                case EGL10.EGL_BAD_DISPLAY:
269                    return "EGL_BAD_DISPLAY";
270                case EGL10.EGL_BAD_MATCH:
271                    return "EGL_BAD_MATCH";
272                case EGL10.EGL_BAD_NATIVE_PIXMAP:
273                    return "EGL_BAD_NATIVE_PIXMAP";
274                case EGL10.EGL_BAD_NATIVE_WINDOW:
275                    return "EGL_BAD_NATIVE_WINDOW";
276                case EGL10.EGL_BAD_PARAMETER:
277                    return "EGL_BAD_PARAMETER";
278                case EGL10.EGL_BAD_SURFACE:
279                    return "EGL_BAD_SURFACE";
280                case EGL11.EGL_CONTEXT_LOST:
281                    return "EGL_CONTEXT_LOST";
282                default:
283                    return "0x" + Integer.toHexString(error);
284            }
285        }
286
287        void finish() {
288            mFinished = true;
289        }
290    }
291}
292