1104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/*
2104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Copyright (C) 2013 The Android Open Source Project
3104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
4104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * you may not use this file except in compliance with the License.
6104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * You may obtain a copy of the License at
7104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
8104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9104c45677660586026a7e74ef8c47d396403d50eMichael Jurka *
10104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Unless required by applicable law or agreed to in writing, software
11104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * See the License for the specific language governing permissions and
14104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * limitations under the License.
15104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
16104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
17104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapackage com.android.photos.views;
18104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
19104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.content.Context;
20104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.SurfaceTexture;
21104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.opengl.GLSurfaceView.Renderer;
22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.opengl.GLUtils;
23104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.Log;
24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.view.TextureView;
25104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.view.TextureView.SurfaceTextureListener;
26104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
27104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport javax.microedition.khronos.egl.EGL10;
28104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport javax.microedition.khronos.egl.EGLConfig;
29104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport javax.microedition.khronos.egl.EGLContext;
30104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport javax.microedition.khronos.egl.EGLDisplay;
31104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport javax.microedition.khronos.egl.EGLSurface;
32104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport javax.microedition.khronos.opengles.GL10;
33104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
34104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/**
35104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * A TextureView that supports blocking rendering for synchronous drawing
36104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */
37104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapublic class BlockingGLTextureView extends TextureView
38104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        implements SurfaceTextureListener {
39104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
40104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private RenderThread mRenderThread;
41104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
42104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public BlockingGLTextureView(Context context) {
43104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        super(context);
44104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        setSurfaceTextureListener(this);
45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
46104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
47104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void setRenderer(Renderer renderer) {
48104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mRenderThread != null) {
49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new IllegalArgumentException("Renderer already set");
50104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
51104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mRenderThread = new RenderThread(renderer);
52104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
53104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
54104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void render() {
55104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mRenderThread.render();
56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
57104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void destroy() {
59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mRenderThread != null) {
60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderThread.finish();
61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderThread = null;
62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int height) {
68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mRenderThread.setSurface(surface);
69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mRenderThread.setSize(width, height);
70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
71104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
73104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
74104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int height) {
75104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        mRenderThread.setSize(width, height);
76104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
77104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
78104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
79104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
80104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        if (mRenderThread != null) {
81104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderThread.setSurface(null);
82104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
83104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        return false;
84104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
85104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
86104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
87104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
88104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
89104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
90104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    @Override
91104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    protected void finalize() throws Throwable {
92104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        try {
93104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            destroy();
94104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        } catch (Throwable t) {
95104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // Ignore
96104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
97104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        super.finalize();
98104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
99104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
100104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    /**
101104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     * An EGL helper class.
102104c45677660586026a7e74ef8c47d396403d50eMichael Jurka     */
103104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
104104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static class EglHelper {
105104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
106104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private static final int EGL_OPENGL_ES2_BIT = 4;
107104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
108104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        EGL10 mEgl;
109104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        EGLDisplay mEglDisplay;
110104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        EGLSurface mEglSurface;
111104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        EGLConfig mEglConfig;
112104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        EGLContext mEglContext;
113104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
114104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private EGLConfig chooseEglConfig() {
115104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int[] configsCount = new int[1];
116104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            EGLConfig[] configs = new EGLConfig[1];
117104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int[] configSpec = getConfig();
118104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
119104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new IllegalArgumentException("eglChooseConfig failed " +
120104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
121104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else if (configsCount[0] > 0) {
122104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return configs[0];
123104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
124104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return null;
125104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
126104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
127104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private static int[] getConfig() {
128104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return new int[] {
129104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
130104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    EGL10.EGL_RED_SIZE, 8,
131104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    EGL10.EGL_GREEN_SIZE, 8,
132104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    EGL10.EGL_BLUE_SIZE, 8,
133104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    EGL10.EGL_ALPHA_SIZE, 8,
134104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    EGL10.EGL_DEPTH_SIZE, 0,
135104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    EGL10.EGL_STENCIL_SIZE, 0,
136104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    EGL10.EGL_NONE
137104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            };
138104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
139104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
140104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
141104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int[] attribList = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
142104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attribList);
143104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
144104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
145104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        /**
146104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         * Initialize EGL for a given configuration spec.
147104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         */
148104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void start() {
149104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            /*
150104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             * Get an EGL instance
151104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             */
152104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mEgl = (EGL10) EGLContext.getEGL();
153104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
154104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            /*
155104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             * Get to the default display.
156104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             */
157104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
158104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
159104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
160104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new RuntimeException("eglGetDisplay failed");
161104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
162104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
163104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            /*
164104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             * We can now initialize EGL for that display
165104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             */
166104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            int[] version = new int[2];
167104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (!mEgl.eglInitialize(mEglDisplay, version)) {
168104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new RuntimeException("eglInitialize failed");
169104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
170104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mEglConfig = chooseEglConfig();
171104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
172104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            /*
173104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            * Create an EGL context. We want to do this as rarely as we can, because an
174104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            * EGL context is a somewhat heavy object.
175104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            */
176104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
177104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
178104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
179104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEglContext = null;
180104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throwEglException("createContext");
181104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
182104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
183104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mEglSurface = null;
184104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
185104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
186104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        /**
187104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         * Create an egl surface for the current SurfaceTexture surface. If a surface
188104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         * already exists, destroy it before creating the new surface.
189104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         *
190104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         * @return true if the surface was created successfully.
191104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         */
192104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public boolean createSurface(SurfaceTexture surface) {
193104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            /*
194104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             * Check preconditions.
195104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             */
196104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mEgl == null) {
197104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new RuntimeException("egl not initialized");
198104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
199104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mEglDisplay == null) {
200104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new RuntimeException("eglDisplay not initialized");
201104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
202104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mEglConfig == null) {
203104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new RuntimeException("mEglConfig not initialized");
204104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
205104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
206104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            /*
207104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             *  The window size has changed, so we need to create a new
208104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             *  surface.
209104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             */
210104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            destroySurfaceImp();
211104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
212104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            /*
213104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             * Create an EGL surface we can render into.
214104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             */
215104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (surface != null) {
216104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surface, null);
217104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } else {
218104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEglSurface = null;
219104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
220104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
221104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
222104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                int error = mEgl.eglGetError();
223104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
224104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
225104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
226104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return false;
227104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
228104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
229104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            /*
230104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             * Before we can issue GL commands, we need to make sure
231104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             * the context is current and bound to a surface.
232104c45677660586026a7e74ef8c47d396403d50eMichael Jurka             */
233104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
234104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                /*
235104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                 * Could not make the context current, probably because the underlying
236104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                 * SurfaceView surface has been destroyed.
237104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                 */
238104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
239104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return false;
240104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
241104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
242104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return true;
243104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
244104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
245104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        /**
246104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         * Create a GL object for the current EGL context.
247104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         */
248104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public GL10 createGL() {
249104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return (GL10) mEglContext.getGL();
250104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
251104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
252104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        /**
253104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         * Display the current render surface.
254104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         * @return the EGL error code from eglSwapBuffers.
255104c45677660586026a7e74ef8c47d396403d50eMichael Jurka         */
256104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public int swap() {
257104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
258104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                return mEgl.eglGetError();
259104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
260104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return EGL10.EGL_SUCCESS;
261104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
262104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
263104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void destroySurface() {
264104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            destroySurfaceImp();
265104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
266104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
267104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private void destroySurfaceImp() {
268104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
269104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
270104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        EGL10.EGL_NO_SURFACE,
271104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        EGL10.EGL_NO_CONTEXT);
272104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
273104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEglSurface = null;
274104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
275104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
276104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
277104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void finish() {
278104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mEglContext != null) {
279104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEgl.eglDestroyContext(mEglDisplay, mEglContext);
280104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEglContext = null;
281104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
282104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mEglDisplay != null) {
283104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEgl.eglTerminate(mEglDisplay);
284104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEglDisplay = null;
285104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
286104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
287104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
288104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private void throwEglException(String function) {
289104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throwEglException(function, mEgl.eglGetError());
290104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
291104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
292104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public static void throwEglException(String function, int error) {
293104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            String message = formatEglError(function, error);
294104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            throw new RuntimeException(message);
295104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
296104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
297104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public static void logEglErrorAsWarning(String tag, String function, int error) {
298104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            Log.w(tag, formatEglError(function, error));
299104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
300104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
301104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public static String formatEglError(String function, int error) {
302104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            return function + " failed: " + error;
303104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
304104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
305104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
306104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
307104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    private static class RenderThread extends Thread {
308104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private static final int INVALID = -1;
309104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private static final int RENDER = 1;
310104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private static final int CHANGE_SURFACE = 2;
311104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private static final int RESIZE_SURFACE = 3;
312104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private static final int FINISH = 4;
313104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
314104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private EglHelper mEglHelper = new EglHelper();
315104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
316104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private Object mLock = new Object();
317104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private int mExecMsgId = INVALID;
318104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private SurfaceTexture mSurface;
319104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private Renderer mRenderer;
320104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private int mWidth, mHeight;
321104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
322104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private boolean mFinished = false;
323104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private GL10 mGL;
324104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
325104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public RenderThread(Renderer renderer) {
326104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            super("RenderThread");
327104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mRenderer = renderer;
328104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            start();
329104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
330104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
331104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private void checkRenderer() {
332104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mRenderer == null) {
333104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new IllegalArgumentException("Renderer is null!");
334104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
335104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
336104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
337104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private void checkSurface() {
338104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mSurface == null) {
339104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                throw new IllegalArgumentException("surface is null!");
340104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
341104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
342104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
343104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void setSurface(SurfaceTexture surface) {
344104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // If the surface is null we're being torn down, don't need a
345104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            // renderer then
346104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (surface != null) {
347104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                checkRenderer();
348104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
349104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mSurface = surface;
350104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            exec(CHANGE_SURFACE);
351104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
352104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
353104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void setSize(int width, int height) {
354104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkRenderer();
355104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkSurface();
356104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mWidth = width;
357104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mHeight = height;
358104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            exec(RESIZE_SURFACE);
359104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
360104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
361104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void render() {
362104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            checkRenderer();
363104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            if (mSurface != null) {
364104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                exec(RENDER);
365104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mSurface.updateTexImage();
366104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
367104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
368104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
369104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void finish() {
370104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            mSurface = null;
371104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            exec(FINISH);
372104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            try {
373104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                join();
374104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            } catch (InterruptedException e) {
375104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                // Ignore
376104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
377104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
378104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
379104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private void exec(int msgid) {
380104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            synchronized (mLock) {
381104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (mExecMsgId != INVALID) {
382104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    throw new IllegalArgumentException(
383104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            "Message already set - multithreaded access?");
384104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
385104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mExecMsgId = msgid;
386104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mLock.notify();
387104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                try {
388104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mLock.wait();
389104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                } catch (InterruptedException e) {
390104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    // Ignore
391104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
392104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
393104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
394104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
395104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        private void handleMessageLocked(int what) {
396104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            switch (what) {
397104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case CHANGE_SURFACE:
398104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                if (mEglHelper.createSurface(mSurface)) {
399104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mGL = mEglHelper.createGL();
400104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mRenderer.onSurfaceCreated(mGL, mEglHelper.mEglConfig);
401104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
402104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
403104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case RESIZE_SURFACE:
404104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mRenderer.onSurfaceChanged(mGL, mWidth, mHeight);
405104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
406104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case RENDER:
407104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mRenderer.onDrawFrame(mGL);
408104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEglHelper.swap();
409104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
410104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            case FINISH:
411104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEglHelper.destroySurface();
412104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEglHelper.finish();
413104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mFinished = true;
414104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                break;
415104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
416104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
417104c45677660586026a7e74ef8c47d396403d50eMichael Jurka
418104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        @Override
419104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        public void run() {
420104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            synchronized (mLock) {
421104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mEglHelper.start();
422104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                while (!mFinished) {
423104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    while (mExecMsgId == INVALID) {
424104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        try {
425104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            mLock.wait();
426104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        } catch (InterruptedException e) {
427104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                            // Ignore
428104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                        }
429104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    }
430104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    handleMessageLocked(mExecMsgId);
431104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mExecMsgId = INVALID;
432104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                    mLock.notify();
433104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                }
434104c45677660586026a7e74ef8c47d396403d50eMichael Jurka                mExecMsgId = FINISH;
435104c45677660586026a7e74ef8c47d396403d50eMichael Jurka            }
436104c45677660586026a7e74ef8c47d396403d50eMichael Jurka        }
437104c45677660586026a7e74ef8c47d396403d50eMichael Jurka    }
438104c45677660586026a7e74ef8c47d396403d50eMichael Jurka}
439