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