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