GLTextureViewActivity.java revision cf559377b750271472aa0a717bf3b7d34abc0b39
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_SURFACE_TYPE = 0x3033;
97        static final int EGL_SWAP_BEHAVIOR_PRESERVED_BIT = 0x0400;
98        static final int EGL_OPENGL_ES2_BIT = 4;
99
100        private volatile boolean mFinished;
101
102        private SurfaceTexture mSurface;
103
104        private EGL10 mEgl;
105        private EGLDisplay mEglDisplay;
106        private EGLConfig mEglConfig;
107        private EGLContext mEglContext;
108        private EGLSurface mEglSurface;
109        private GL mGL;
110
111        RenderThread(SurfaceTexture surface) {
112            mSurface = surface;
113        }
114
115        @Override
116        public void run() {
117            initGL();
118
119            float red = 1.0f;
120            while (!mFinished) {
121                checkCurrent();
122
123                Log.d(LOG_TAG, "Rendering frame");
124
125                GLES20.glClearColor(red, 0.0f, 0.0f, 1.0f);
126                checkGlError();
127
128                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
129                checkGlError();
130
131                if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
132                    throw new RuntimeException("Cannot swap buffers");
133                }
134                checkEglError();
135
136                try {
137                    Thread.sleep(20);
138                } catch (InterruptedException e) {
139                    // Ignore
140                }
141
142                red += 0.021f;
143                if (red > 1.0f) red = 0.0f;
144            }
145
146            finishGL();
147        }
148
149        private void checkEglError() {
150            int error = mEgl.eglGetError();
151            if (error != EGL10.EGL_SUCCESS) {
152                Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error));
153            }
154        }
155
156        private void checkGlError() {
157            int error = GLES20.glGetError();
158            if (error != GLES20.GL_NO_ERROR) {
159                Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
160            }
161        }
162
163        private void finishGL() {
164            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
165            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
166        }
167
168        private void checkCurrent() {
169            if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
170                    !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
171                if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
172                    throw new RuntimeException("eglMakeCurrent failed "
173                            + getEGLErrorString(mEgl.eglGetError()));
174                }
175            }
176        }
177
178        private void initGL() {
179            mEgl = (EGL10) EGLContext.getEGL();
180
181            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
182            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
183                throw new RuntimeException("eglGetDisplay failed "
184                        + getEGLErrorString(mEgl.eglGetError()));
185            }
186
187            int[] version = new int[2];
188            if (!mEgl.eglInitialize(mEglDisplay, version)) {
189                throw new RuntimeException("eglInitialize failed " +
190                        getEGLErrorString(mEgl.eglGetError()));
191            }
192
193            mEglConfig = chooseEglConfig();
194            if (mEglConfig == null) {
195                throw new RuntimeException("eglConfig not initialized");
196            }
197
198            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
199
200            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);
201
202            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
203                int error = mEgl.eglGetError();
204                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
205                    Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
206                    return;
207                }
208                throw new RuntimeException("createWindowSurface failed "
209                        + getEGLErrorString(error));
210            }
211
212            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
213                throw new RuntimeException("eglMakeCurrent failed "
214                        + getEGLErrorString(mEgl.eglGetError()));
215            }
216
217            mGL = mEglContext.getGL();
218        }
219
220
221        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
222            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
223            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
224        }
225
226        private EGLConfig chooseEglConfig() {
227            int[] configsCount = new int[1];
228            EGLConfig[] configs = new EGLConfig[1];
229            int[] configSpec = getConfig();
230            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
231                throw new IllegalArgumentException("eglChooseConfig failed " +
232                        getEGLErrorString(mEgl.eglGetError()));
233            } else if (configsCount[0] > 0) {
234                return configs[0];
235            }
236            return null;
237        }
238
239        private int[] getConfig() {
240            return new int[] {
241                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
242                    EGL10.EGL_RED_SIZE, 8,
243                    EGL10.EGL_GREEN_SIZE, 8,
244                    EGL10.EGL_BLUE_SIZE, 8,
245                    EGL10.EGL_ALPHA_SIZE, 8,
246                    EGL10.EGL_DEPTH_SIZE, 0,
247                    EGL10.EGL_STENCIL_SIZE, 0,
248                    EGL10.EGL_NONE
249            };
250        }
251
252        static String getEGLErrorString(int error) {
253            switch (error) {
254                case EGL10.EGL_SUCCESS:
255                    return "EGL_SUCCESS";
256                case EGL10.EGL_NOT_INITIALIZED:
257                    return "EGL_NOT_INITIALIZED";
258                case EGL10.EGL_BAD_ACCESS:
259                    return "EGL_BAD_ACCESS";
260                case EGL10.EGL_BAD_ALLOC:
261                    return "EGL_BAD_ALLOC";
262                case EGL10.EGL_BAD_ATTRIBUTE:
263                    return "EGL_BAD_ATTRIBUTE";
264                case EGL10.EGL_BAD_CONFIG:
265                    return "EGL_BAD_CONFIG";
266                case EGL10.EGL_BAD_CONTEXT:
267                    return "EGL_BAD_CONTEXT";
268                case EGL10.EGL_BAD_CURRENT_SURFACE:
269                    return "EGL_BAD_CURRENT_SURFACE";
270                case EGL10.EGL_BAD_DISPLAY:
271                    return "EGL_BAD_DISPLAY";
272                case EGL10.EGL_BAD_MATCH:
273                    return "EGL_BAD_MATCH";
274                case EGL10.EGL_BAD_NATIVE_PIXMAP:
275                    return "EGL_BAD_NATIVE_PIXMAP";
276                case EGL10.EGL_BAD_NATIVE_WINDOW:
277                    return "EGL_BAD_NATIVE_WINDOW";
278                case EGL10.EGL_BAD_PARAMETER:
279                    return "EGL_BAD_PARAMETER";
280                case EGL10.EGL_BAD_SURFACE:
281                    return "EGL_BAD_SURFACE";
282                case EGL11.EGL_CONTEXT_LOST:
283                    return "EGL_CONTEXT_LOST";
284                default:
285                    return "0x" + Integer.toHexString(error);
286            }
287        }
288
289        void finish() {
290            mFinished = true;
291        }
292    }
293}
294