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