BlockingGLTextureView.java revision d8732610ff48dc57063559d55ea3c84775e30e41
1d8732610ff48dc57063559d55ea3c84775e30e41John Reck/*
2d8732610ff48dc57063559d55ea3c84775e30e41John Reck * Copyright (C) 2013 The Android Open Source Project
3d8732610ff48dc57063559d55ea3c84775e30e41John Reck *
4d8732610ff48dc57063559d55ea3c84775e30e41John Reck * Licensed under the Apache License, Version 2.0 (the "License");
5d8732610ff48dc57063559d55ea3c84775e30e41John Reck * you may not use this file except in compliance with the License.
6d8732610ff48dc57063559d55ea3c84775e30e41John Reck * You may obtain a copy of the License at
7d8732610ff48dc57063559d55ea3c84775e30e41John Reck *
8d8732610ff48dc57063559d55ea3c84775e30e41John Reck *      http://www.apache.org/licenses/LICENSE-2.0
9d8732610ff48dc57063559d55ea3c84775e30e41John Reck *
10d8732610ff48dc57063559d55ea3c84775e30e41John Reck * Unless required by applicable law or agreed to in writing, software
11d8732610ff48dc57063559d55ea3c84775e30e41John Reck * distributed under the License is distributed on an "AS IS" BASIS,
12d8732610ff48dc57063559d55ea3c84775e30e41John Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d8732610ff48dc57063559d55ea3c84775e30e41John Reck * See the License for the specific language governing permissions and
14d8732610ff48dc57063559d55ea3c84775e30e41John Reck * limitations under the License.
15d8732610ff48dc57063559d55ea3c84775e30e41John Reck */
16d8732610ff48dc57063559d55ea3c84775e30e41John Reck
17d8732610ff48dc57063559d55ea3c84775e30e41John Reckpackage com.android.photos.views;
18d8732610ff48dc57063559d55ea3c84775e30e41John Reck
19d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.annotation.SuppressLint;
20d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.content.Context;
21d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.graphics.SurfaceTexture;
22d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.opengl.GLSurfaceView.Renderer;
23d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.opengl.GLUtils;
24d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.util.Log;
25d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.view.TextureView;
26d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport android.view.TextureView.SurfaceTextureListener;
27d8732610ff48dc57063559d55ea3c84775e30e41John Reck
28d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport javax.microedition.khronos.egl.EGL10;
29d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport javax.microedition.khronos.egl.EGLConfig;
30d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport javax.microedition.khronos.egl.EGLContext;
31d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport javax.microedition.khronos.egl.EGLDisplay;
32d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport javax.microedition.khronos.egl.EGLSurface;
33d8732610ff48dc57063559d55ea3c84775e30e41John Reckimport javax.microedition.khronos.opengles.GL10;
34d8732610ff48dc57063559d55ea3c84775e30e41John Reck
35d8732610ff48dc57063559d55ea3c84775e30e41John Reck
36d8732610ff48dc57063559d55ea3c84775e30e41John Reckpublic class BlockingGLTextureView extends TextureView
37d8732610ff48dc57063559d55ea3c84775e30e41John Reck        implements SurfaceTextureListener {
38d8732610ff48dc57063559d55ea3c84775e30e41John Reck
39d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private RenderThread mRenderThread;
40d8732610ff48dc57063559d55ea3c84775e30e41John Reck
41d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public BlockingGLTextureView(Context context) {
42d8732610ff48dc57063559d55ea3c84775e30e41John Reck        super(context);
43d8732610ff48dc57063559d55ea3c84775e30e41John Reck        setSurfaceTextureListener(this);
44d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
45d8732610ff48dc57063559d55ea3c84775e30e41John Reck
46d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void setRenderer(Renderer renderer) {
47d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (mRenderThread != null) {
48d8732610ff48dc57063559d55ea3c84775e30e41John Reck            throw new IllegalArgumentException("Renderer already set");
49d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
50d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mRenderThread = new RenderThread(renderer);
51d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
52d8732610ff48dc57063559d55ea3c84775e30e41John Reck
53d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void render() {
54d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mRenderThread.render();
55d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
56d8732610ff48dc57063559d55ea3c84775e30e41John Reck
57d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void destroy() {
58d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (mRenderThread != null) {
59d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mRenderThread.finish();
60d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mRenderThread = null;
61d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
62d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
63d8732610ff48dc57063559d55ea3c84775e30e41John Reck
64d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
65d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
66d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int height) {
67d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mRenderThread.setSurface(surface);
68d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mRenderThread.setSize(width, height);
69d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
70d8732610ff48dc57063559d55ea3c84775e30e41John Reck
71d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
72d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
73d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int height) {
74d8732610ff48dc57063559d55ea3c84775e30e41John Reck        mRenderThread.setSize(width, height);
75d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
76d8732610ff48dc57063559d55ea3c84775e30e41John Reck
77d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
78d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
79d8732610ff48dc57063559d55ea3c84775e30e41John Reck        if (mRenderThread != null) {
80d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mRenderThread.setSurface(null);
81d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
82d8732610ff48dc57063559d55ea3c84775e30e41John Reck        return false;
83d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
84d8732610ff48dc57063559d55ea3c84775e30e41John Reck
85d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
86d8732610ff48dc57063559d55ea3c84775e30e41John Reck    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
87d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
88d8732610ff48dc57063559d55ea3c84775e30e41John Reck
89d8732610ff48dc57063559d55ea3c84775e30e41John Reck    @Override
90d8732610ff48dc57063559d55ea3c84775e30e41John Reck    protected void finalize() throws Throwable {
91d8732610ff48dc57063559d55ea3c84775e30e41John Reck        try {
92d8732610ff48dc57063559d55ea3c84775e30e41John Reck            destroy();
93d8732610ff48dc57063559d55ea3c84775e30e41John Reck        } catch (Throwable t) {}
94d8732610ff48dc57063559d55ea3c84775e30e41John Reck        super.finalize();
95d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
96d8732610ff48dc57063559d55ea3c84775e30e41John Reck
97d8732610ff48dc57063559d55ea3c84775e30e41John Reck    /**
98d8732610ff48dc57063559d55ea3c84775e30e41John Reck     * An EGL helper class.
99d8732610ff48dc57063559d55ea3c84775e30e41John Reck     */
100d8732610ff48dc57063559d55ea3c84775e30e41John Reck
101d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static class EglHelper {
102d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
103d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private static final int EGL_OPENGL_ES2_BIT = 4;
104d8732610ff48dc57063559d55ea3c84775e30e41John Reck
105d8732610ff48dc57063559d55ea3c84775e30e41John Reck        EGL10 mEgl;
106d8732610ff48dc57063559d55ea3c84775e30e41John Reck        EGLDisplay mEglDisplay;
107d8732610ff48dc57063559d55ea3c84775e30e41John Reck        EGLSurface mEglSurface;
108d8732610ff48dc57063559d55ea3c84775e30e41John Reck        EGLConfig mEglConfig;
109d8732610ff48dc57063559d55ea3c84775e30e41John Reck        EGLContext mEglContext;
110d8732610ff48dc57063559d55ea3c84775e30e41John Reck
111d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private EGLConfig chooseEglConfig() {
112d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int[] configsCount = new int[1];
113d8732610ff48dc57063559d55ea3c84775e30e41John Reck            EGLConfig[] configs = new EGLConfig[1];
114d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int[] configSpec = getConfig();
115d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
116d8732610ff48dc57063559d55ea3c84775e30e41John Reck                throw new IllegalArgumentException("eglChooseConfig failed " +
117d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
118d8732610ff48dc57063559d55ea3c84775e30e41John Reck            } else if (configsCount[0] > 0) {
119d8732610ff48dc57063559d55ea3c84775e30e41John Reck                return configs[0];
120d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
121d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return null;
122d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
123d8732610ff48dc57063559d55ea3c84775e30e41John Reck
124d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private static int[] getConfig() {
125d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return new int[] {
126d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
127d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    EGL10.EGL_RED_SIZE, 8,
128d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    EGL10.EGL_GREEN_SIZE, 8,
129d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    EGL10.EGL_BLUE_SIZE, 8,
130d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    EGL10.EGL_ALPHA_SIZE, 8,
131d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    EGL10.EGL_DEPTH_SIZE, 0,
132d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    EGL10.EGL_STENCIL_SIZE, 0,
133d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    EGL10.EGL_NONE
134d8732610ff48dc57063559d55ea3c84775e30e41John Reck            };
135d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
136d8732610ff48dc57063559d55ea3c84775e30e41John Reck
137d8732610ff48dc57063559d55ea3c84775e30e41John Reck        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
138d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
139d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
140d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
141d8732610ff48dc57063559d55ea3c84775e30e41John Reck
142d8732610ff48dc57063559d55ea3c84775e30e41John Reck        /**
143d8732610ff48dc57063559d55ea3c84775e30e41John Reck         * Initialize EGL for a given configuration spec.
144d8732610ff48dc57063559d55ea3c84775e30e41John Reck         */
145d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void start() {
146d8732610ff48dc57063559d55ea3c84775e30e41John Reck            /*
147d8732610ff48dc57063559d55ea3c84775e30e41John Reck             * Get an EGL instance
148d8732610ff48dc57063559d55ea3c84775e30e41John Reck             */
149d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mEgl = (EGL10) EGLContext.getEGL();
150d8732610ff48dc57063559d55ea3c84775e30e41John Reck
151d8732610ff48dc57063559d55ea3c84775e30e41John Reck            /*
152d8732610ff48dc57063559d55ea3c84775e30e41John Reck             * Get to the default display.
153d8732610ff48dc57063559d55ea3c84775e30e41John Reck             */
154d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
155d8732610ff48dc57063559d55ea3c84775e30e41John Reck
156d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
157d8732610ff48dc57063559d55ea3c84775e30e41John Reck                throw new RuntimeException("eglGetDisplay failed");
158d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
159d8732610ff48dc57063559d55ea3c84775e30e41John Reck
160d8732610ff48dc57063559d55ea3c84775e30e41John Reck            /*
161d8732610ff48dc57063559d55ea3c84775e30e41John Reck             * We can now initialize EGL for that display
162d8732610ff48dc57063559d55ea3c84775e30e41John Reck             */
163d8732610ff48dc57063559d55ea3c84775e30e41John Reck            int[] version = new int[2];
164d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if(!mEgl.eglInitialize(mEglDisplay, version)) {
165d8732610ff48dc57063559d55ea3c84775e30e41John Reck                throw new RuntimeException("eglInitialize failed");
166d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
167d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mEglConfig = chooseEglConfig();
168d8732610ff48dc57063559d55ea3c84775e30e41John Reck
169d8732610ff48dc57063559d55ea3c84775e30e41John Reck            /*
170d8732610ff48dc57063559d55ea3c84775e30e41John Reck            * Create an EGL context. We want to do this as rarely as we can, because an
171d8732610ff48dc57063559d55ea3c84775e30e41John Reck            * EGL context is a somewhat heavy object.
172d8732610ff48dc57063559d55ea3c84775e30e41John Reck            */
173d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
174d8732610ff48dc57063559d55ea3c84775e30e41John Reck
175d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
176d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEglContext = null;
177d8732610ff48dc57063559d55ea3c84775e30e41John Reck                throwEglException("createContext");
178d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
179d8732610ff48dc57063559d55ea3c84775e30e41John Reck
180d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mEglSurface = null;
181d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
182d8732610ff48dc57063559d55ea3c84775e30e41John Reck
183d8732610ff48dc57063559d55ea3c84775e30e41John Reck        /**
184d8732610ff48dc57063559d55ea3c84775e30e41John Reck         * Create an egl surface for the current SurfaceTexture surface. If a surface
185d8732610ff48dc57063559d55ea3c84775e30e41John Reck         * already exists, destroy it before creating the new surface.
186d8732610ff48dc57063559d55ea3c84775e30e41John Reck         *
187d8732610ff48dc57063559d55ea3c84775e30e41John Reck         * @return true if the surface was created successfully.
188d8732610ff48dc57063559d55ea3c84775e30e41John Reck         */
189d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public boolean createSurface(SurfaceTexture surface) {
190d8732610ff48dc57063559d55ea3c84775e30e41John Reck            /*
191d8732610ff48dc57063559d55ea3c84775e30e41John Reck             * Check preconditions.
192d8732610ff48dc57063559d55ea3c84775e30e41John Reck             */
193d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mEgl == null) {
194d8732610ff48dc57063559d55ea3c84775e30e41John Reck                throw new RuntimeException("egl not initialized");
195d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
196d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mEglDisplay == null) {
197d8732610ff48dc57063559d55ea3c84775e30e41John Reck                throw new RuntimeException("eglDisplay not initialized");
198d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
199d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mEglConfig == null) {
200d8732610ff48dc57063559d55ea3c84775e30e41John Reck                throw new RuntimeException("mEglConfig not initialized");
201d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
202d8732610ff48dc57063559d55ea3c84775e30e41John Reck
203d8732610ff48dc57063559d55ea3c84775e30e41John Reck            /*
204d8732610ff48dc57063559d55ea3c84775e30e41John Reck             *  The window size has changed, so we need to create a new
205d8732610ff48dc57063559d55ea3c84775e30e41John Reck             *  surface.
206d8732610ff48dc57063559d55ea3c84775e30e41John Reck             */
207d8732610ff48dc57063559d55ea3c84775e30e41John Reck            destroySurfaceImp();
208d8732610ff48dc57063559d55ea3c84775e30e41John Reck
209d8732610ff48dc57063559d55ea3c84775e30e41John Reck            /*
210d8732610ff48dc57063559d55ea3c84775e30e41John Reck             * Create an EGL surface we can render into.
211d8732610ff48dc57063559d55ea3c84775e30e41John Reck             */
212d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (surface != null) {
213d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surface, null);
214d8732610ff48dc57063559d55ea3c84775e30e41John Reck            } else {
215d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEglSurface = null;
216d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
217d8732610ff48dc57063559d55ea3c84775e30e41John Reck
218d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
219d8732610ff48dc57063559d55ea3c84775e30e41John Reck                int error = mEgl.eglGetError();
220d8732610ff48dc57063559d55ea3c84775e30e41John Reck                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
221d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
222d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
223d8732610ff48dc57063559d55ea3c84775e30e41John Reck                return false;
224d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
225d8732610ff48dc57063559d55ea3c84775e30e41John Reck
226d8732610ff48dc57063559d55ea3c84775e30e41John Reck            /*
227d8732610ff48dc57063559d55ea3c84775e30e41John Reck             * Before we can issue GL commands, we need to make sure
228d8732610ff48dc57063559d55ea3c84775e30e41John Reck             * the context is current and bound to a surface.
229d8732610ff48dc57063559d55ea3c84775e30e41John Reck             */
230d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
231d8732610ff48dc57063559d55ea3c84775e30e41John Reck                /*
232d8732610ff48dc57063559d55ea3c84775e30e41John Reck                 * Could not make the context current, probably because the underlying
233d8732610ff48dc57063559d55ea3c84775e30e41John Reck                 * SurfaceView surface has been destroyed.
234d8732610ff48dc57063559d55ea3c84775e30e41John Reck                 */
235d8732610ff48dc57063559d55ea3c84775e30e41John Reck                logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
236d8732610ff48dc57063559d55ea3c84775e30e41John Reck                return false;
237d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
238d8732610ff48dc57063559d55ea3c84775e30e41John Reck
239d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return true;
240d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
241d8732610ff48dc57063559d55ea3c84775e30e41John Reck
242d8732610ff48dc57063559d55ea3c84775e30e41John Reck        /**
243d8732610ff48dc57063559d55ea3c84775e30e41John Reck         * Create a GL object for the current EGL context.
244d8732610ff48dc57063559d55ea3c84775e30e41John Reck         */
245d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public GL10 createGL() {
246d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return (GL10) mEglContext.getGL();
247d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
248d8732610ff48dc57063559d55ea3c84775e30e41John Reck
249d8732610ff48dc57063559d55ea3c84775e30e41John Reck        /**
250d8732610ff48dc57063559d55ea3c84775e30e41John Reck         * Display the current render surface.
251d8732610ff48dc57063559d55ea3c84775e30e41John Reck         * @return the EGL error code from eglSwapBuffers.
252d8732610ff48dc57063559d55ea3c84775e30e41John Reck         */
253d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public int swap() {
254d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
255d8732610ff48dc57063559d55ea3c84775e30e41John Reck                return mEgl.eglGetError();
256d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
257d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return EGL10.EGL_SUCCESS;
258d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
259d8732610ff48dc57063559d55ea3c84775e30e41John Reck
260d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void destroySurface() {
261d8732610ff48dc57063559d55ea3c84775e30e41John Reck            destroySurfaceImp();
262d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
263d8732610ff48dc57063559d55ea3c84775e30e41John Reck
264d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private void destroySurfaceImp() {
265d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
266d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
267d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        EGL10.EGL_NO_SURFACE,
268d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        EGL10.EGL_NO_CONTEXT);
269d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
270d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEglSurface = null;
271d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
272d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
273d8732610ff48dc57063559d55ea3c84775e30e41John Reck
274d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void finish() {
275d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mEglContext != null) {
276d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEgl.eglDestroyContext(mEglDisplay, mEglContext);
277d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEglContext = null;
278d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
279d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mEglDisplay != null) {
280d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEgl.eglTerminate(mEglDisplay);
281d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEglDisplay = null;
282d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
283d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
284d8732610ff48dc57063559d55ea3c84775e30e41John Reck
285d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private void throwEglException(String function) {
286d8732610ff48dc57063559d55ea3c84775e30e41John Reck            throwEglException(function, mEgl.eglGetError());
287d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
288d8732610ff48dc57063559d55ea3c84775e30e41John Reck
289d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public static void throwEglException(String function, int error) {
290d8732610ff48dc57063559d55ea3c84775e30e41John Reck            String message = formatEglError(function, error);
291d8732610ff48dc57063559d55ea3c84775e30e41John Reck            throw new RuntimeException(message);
292d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
293d8732610ff48dc57063559d55ea3c84775e30e41John Reck
294d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public static void logEglErrorAsWarning(String tag, String function, int error) {
295d8732610ff48dc57063559d55ea3c84775e30e41John Reck            Log.w(tag, formatEglError(function, error));
296d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
297d8732610ff48dc57063559d55ea3c84775e30e41John Reck
298d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public static String formatEglError(String function, int error) {
299d8732610ff48dc57063559d55ea3c84775e30e41John Reck            return function + " failed: " + error;
300d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
301d8732610ff48dc57063559d55ea3c84775e30e41John Reck
302d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
303d8732610ff48dc57063559d55ea3c84775e30e41John Reck
304d8732610ff48dc57063559d55ea3c84775e30e41John Reck    private static class RenderThread extends Thread {
305d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private static final int INVALID = -1;
306d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private static final int RENDER = 1;
307d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private static final int CHANGE_SURFACE = 2;
308d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private static final int RESIZE_SURFACE = 3;
309d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private static final int FINISH = 4;
310d8732610ff48dc57063559d55ea3c84775e30e41John Reck
311d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private EglHelper mEglHelper = new EglHelper();
312d8732610ff48dc57063559d55ea3c84775e30e41John Reck
313d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private Object mLock = new Object();
314d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private int mExecMsgId = INVALID;
315d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private SurfaceTexture mSurface;
316d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private Renderer mRenderer;
317d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private int mWidth, mHeight;
318d8732610ff48dc57063559d55ea3c84775e30e41John Reck
319d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private boolean mFinished = false;
320d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private GL10 mGL;
321d8732610ff48dc57063559d55ea3c84775e30e41John Reck
322d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public RenderThread(Renderer renderer) {
323d8732610ff48dc57063559d55ea3c84775e30e41John Reck            super("RenderThread");
324d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mRenderer = renderer;
325d8732610ff48dc57063559d55ea3c84775e30e41John Reck            start();
326d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
327d8732610ff48dc57063559d55ea3c84775e30e41John Reck
328d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private void checkRenderer() {
329d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mRenderer == null) {
330d8732610ff48dc57063559d55ea3c84775e30e41John Reck                throw new IllegalArgumentException("Renderer is null!");
331d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
332d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
333d8732610ff48dc57063559d55ea3c84775e30e41John Reck
334d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private void checkSurface() {
335d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mSurface == null) {
336d8732610ff48dc57063559d55ea3c84775e30e41John Reck                throw new IllegalArgumentException("surface is null!");
337d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
338d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
339d8732610ff48dc57063559d55ea3c84775e30e41John Reck
340d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void setSurface(SurfaceTexture surface) {
341d8732610ff48dc57063559d55ea3c84775e30e41John Reck            // If the surface is null we're being torn down, don't need a
342d8732610ff48dc57063559d55ea3c84775e30e41John Reck            // renderer then
343d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (surface != null) {
344d8732610ff48dc57063559d55ea3c84775e30e41John Reck                checkRenderer();
345d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
346d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mSurface = surface;
347d8732610ff48dc57063559d55ea3c84775e30e41John Reck            exec(CHANGE_SURFACE);
348d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
349d8732610ff48dc57063559d55ea3c84775e30e41John Reck
350d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void setSize(int width, int height) {
351d8732610ff48dc57063559d55ea3c84775e30e41John Reck            checkRenderer();
352d8732610ff48dc57063559d55ea3c84775e30e41John Reck            checkSurface();
353d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mWidth = width;
354d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mHeight = height;
355d8732610ff48dc57063559d55ea3c84775e30e41John Reck            exec(RESIZE_SURFACE);
356d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
357d8732610ff48dc57063559d55ea3c84775e30e41John Reck
358d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void render() {
359d8732610ff48dc57063559d55ea3c84775e30e41John Reck            checkRenderer();
360d8732610ff48dc57063559d55ea3c84775e30e41John Reck            if (mSurface != null) {
361d8732610ff48dc57063559d55ea3c84775e30e41John Reck                exec(RENDER);
362d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mSurface.updateTexImage();
363d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
364d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
365d8732610ff48dc57063559d55ea3c84775e30e41John Reck
366d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void finish() {
367d8732610ff48dc57063559d55ea3c84775e30e41John Reck            mSurface = null;
368d8732610ff48dc57063559d55ea3c84775e30e41John Reck            exec(FINISH);
369d8732610ff48dc57063559d55ea3c84775e30e41John Reck            try {
370d8732610ff48dc57063559d55ea3c84775e30e41John Reck                join();
371d8732610ff48dc57063559d55ea3c84775e30e41John Reck            } catch (InterruptedException e) {}
372d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
373d8732610ff48dc57063559d55ea3c84775e30e41John Reck
374d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private void exec(int msgid) {
375d8732610ff48dc57063559d55ea3c84775e30e41John Reck            synchronized (mLock) {
376d8732610ff48dc57063559d55ea3c84775e30e41John Reck                if (mExecMsgId != INVALID) {
377d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    throw new IllegalArgumentException("Message already set - multithreaded access?");
378d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
379d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mExecMsgId = msgid;
380d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mLock.notify();
381d8732610ff48dc57063559d55ea3c84775e30e41John Reck                try {
382d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    mLock.wait();
383d8732610ff48dc57063559d55ea3c84775e30e41John Reck                } catch (InterruptedException e) {}
384d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
385d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
386d8732610ff48dc57063559d55ea3c84775e30e41John Reck
387d8732610ff48dc57063559d55ea3c84775e30e41John Reck        private void handleMessageLocked(int what) {
388d8732610ff48dc57063559d55ea3c84775e30e41John Reck            switch (what) {
389d8732610ff48dc57063559d55ea3c84775e30e41John Reck            case CHANGE_SURFACE:
390d8732610ff48dc57063559d55ea3c84775e30e41John Reck                if (mEglHelper.createSurface(mSurface)) {
391d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    mGL = mEglHelper.createGL();
392d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    mRenderer.onSurfaceCreated(mGL, mEglHelper.mEglConfig);
393d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
394d8732610ff48dc57063559d55ea3c84775e30e41John Reck                break;
395d8732610ff48dc57063559d55ea3c84775e30e41John Reck            case RESIZE_SURFACE:
396d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mRenderer.onSurfaceChanged(mGL, mWidth, mHeight);
397d8732610ff48dc57063559d55ea3c84775e30e41John Reck                break;
398d8732610ff48dc57063559d55ea3c84775e30e41John Reck            case RENDER:
399d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mRenderer.onDrawFrame(mGL);
400d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEglHelper.swap();
401d8732610ff48dc57063559d55ea3c84775e30e41John Reck                break;
402d8732610ff48dc57063559d55ea3c84775e30e41John Reck            case FINISH:
403d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEglHelper.destroySurface();
404d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEglHelper.finish();
405d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mFinished = true;
406d8732610ff48dc57063559d55ea3c84775e30e41John Reck                break;
407d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
408d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
409d8732610ff48dc57063559d55ea3c84775e30e41John Reck
410d8732610ff48dc57063559d55ea3c84775e30e41John Reck        @Override
411d8732610ff48dc57063559d55ea3c84775e30e41John Reck        public void run() {
412d8732610ff48dc57063559d55ea3c84775e30e41John Reck            synchronized (mLock) {
413d8732610ff48dc57063559d55ea3c84775e30e41John Reck                mEglHelper.start();
414d8732610ff48dc57063559d55ea3c84775e30e41John Reck                while (!mFinished) {
415d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    while (mExecMsgId == INVALID) {
416d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        try {
417d8732610ff48dc57063559d55ea3c84775e30e41John Reck                            mLock.wait();
418d8732610ff48dc57063559d55ea3c84775e30e41John Reck                        } catch (InterruptedException e) {}
419d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    }
420d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    handleMessageLocked(mExecMsgId);
421d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    mExecMsgId = INVALID;
422d8732610ff48dc57063559d55ea3c84775e30e41John Reck                    mLock.notify();
423d8732610ff48dc57063559d55ea3c84775e30e41John Reck                }
424d8732610ff48dc57063559d55ea3c84775e30e41John Reck            }
425d8732610ff48dc57063559d55ea3c84775e30e41John Reck        }
426d8732610ff48dc57063559d55ea3c84775e30e41John Reck    }
427d8732610ff48dc57063559d55ea3c84775e30e41John Reck}
428