1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera; 18 19 import android.graphics.SurfaceTexture; 20 import android.os.Handler; 21 22 import com.android.camera.debug.Log; 23 24 import javax.microedition.khronos.egl.EGL10; 25 import javax.microedition.khronos.egl.EGLConfig; 26 import javax.microedition.khronos.egl.EGLContext; 27 import javax.microedition.khronos.egl.EGLDisplay; 28 import javax.microedition.khronos.egl.EGLSurface; 29 import javax.microedition.khronos.opengles.GL10; 30 31 public class SurfaceTextureRenderer { 32 33 public interface FrameDrawer { onDrawFrame(GL10 gl)34 public void onDrawFrame(GL10 gl); 35 } 36 37 private static final Log.Tag TAG = new Log.Tag("SurfTexRenderer"); 38 private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 39 40 private EGLConfig mEglConfig; 41 private EGLDisplay mEglDisplay; 42 private EGLContext mEglContext; 43 private EGLSurface mEglSurface; 44 private EGL10 mEgl; 45 private GL10 mGl; 46 47 private volatile boolean mDrawPending = false; 48 49 private final Handler mEglHandler; 50 private final FrameDrawer mFrameDrawer; 51 52 private final Object mRenderLock = new Object(); 53 private final Runnable mRenderTask = new Runnable() { 54 @Override 55 public void run() { 56 synchronized (mRenderLock) { 57 if (mEglDisplay != null && mEglSurface != null) { 58 mFrameDrawer.onDrawFrame(mGl); 59 mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); 60 mDrawPending = false; 61 } 62 mRenderLock.notifyAll(); 63 } 64 } 65 }; 66 SurfaceTextureRenderer(SurfaceTexture tex, Handler handler, FrameDrawer renderer)67 public SurfaceTextureRenderer(SurfaceTexture tex, 68 Handler handler, FrameDrawer renderer) { 69 mEglHandler = handler; 70 mFrameDrawer = renderer; 71 72 initialize(tex); 73 } 74 release()75 public void release() { 76 mEglHandler.post(new Runnable() { 77 @Override 78 public void run() { 79 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 80 mEgl.eglDestroyContext(mEglDisplay, mEglContext); 81 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, 82 EGL10.EGL_NO_CONTEXT); 83 mEgl.eglTerminate(mEglDisplay); 84 mEglSurface = null; 85 mEglContext = null; 86 mEglDisplay = null; 87 } 88 }); 89 } 90 91 /** 92 * Posts a render request to the GL thread. 93 * @param sync set <code>true</code> if the caller needs it to be 94 * a synchronous call. 95 */ draw(boolean sync)96 public void draw(boolean sync) { 97 synchronized (mRenderLock) { 98 if (!mDrawPending) { 99 mEglHandler.post(mRenderTask); 100 mDrawPending = true; 101 if (sync) { 102 try { 103 mRenderLock.wait(); 104 } catch (InterruptedException ex) { 105 Log.v(TAG, "RenderLock.wait() interrupted"); 106 } 107 } 108 } 109 } 110 } 111 initialize(final SurfaceTexture target)112 private void initialize(final SurfaceTexture target) { 113 mEglHandler.post(new Runnable() { 114 @Override 115 public void run() { 116 mEgl = (EGL10) EGLContext.getEGL(); 117 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 118 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 119 throw new RuntimeException("eglGetDisplay failed"); 120 } 121 int[] version = new int[2]; 122 if (!mEgl.eglInitialize(mEglDisplay, version)) { 123 throw new RuntimeException("eglInitialize failed"); 124 } else { 125 Log.v(TAG, "EGL version: " + version[0] + '.' + version[1]); 126 } 127 int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; 128 mEglConfig = chooseConfig(mEgl, mEglDisplay); 129 mEglContext = mEgl.eglCreateContext( 130 mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, attribList); 131 132 if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { 133 throw new RuntimeException("failed to createContext"); 134 } 135 mEglSurface = mEgl.eglCreateWindowSurface( 136 mEglDisplay, mEglConfig, target, null); 137 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 138 throw new RuntimeException("failed to createWindowSurface"); 139 } 140 141 if (!mEgl.eglMakeCurrent( 142 mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 143 throw new RuntimeException("failed to eglMakeCurrent"); 144 } 145 146 mGl = (GL10) mEglContext.getGL(); 147 } 148 }); 149 waitDone(); 150 } 151 waitDone()152 private void waitDone() { 153 final Object lock = new Object(); 154 synchronized (lock) { 155 mEglHandler.post(new Runnable() { 156 @Override 157 public void run() { 158 synchronized (lock) { 159 lock.notifyAll(); 160 } 161 } 162 }); 163 try { 164 lock.wait(); 165 } catch (InterruptedException ex) { 166 Log.v(TAG, "waitDone() interrupted"); 167 } 168 } 169 } 170 checkEglError(String prompt, EGL10 egl)171 private static void checkEglError(String prompt, EGL10 egl) { 172 int error; 173 while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { 174 Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); 175 } 176 } 177 178 private static final int EGL_OPENGL_ES2_BIT = 4; 179 private static final int[] CONFIG_SPEC = new int[] { 180 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 181 EGL10.EGL_RED_SIZE, 8, 182 EGL10.EGL_GREEN_SIZE, 8, 183 EGL10.EGL_BLUE_SIZE, 8, 184 EGL10.EGL_ALPHA_SIZE, 0, 185 EGL10.EGL_DEPTH_SIZE, 0, 186 EGL10.EGL_STENCIL_SIZE, 0, 187 EGL10.EGL_NONE 188 }; 189 chooseConfig(EGL10 egl, EGLDisplay display)190 private static EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 191 int[] numConfig = new int[1]; 192 if (!egl.eglChooseConfig(display, CONFIG_SPEC, null, 0, numConfig)) { 193 throw new IllegalArgumentException("eglChooseConfig failed"); 194 } 195 196 int numConfigs = numConfig[0]; 197 if (numConfigs <= 0) { 198 throw new IllegalArgumentException("No configs match configSpec"); 199 } 200 201 EGLConfig[] configs = new EGLConfig[numConfigs]; 202 if (!egl.eglChooseConfig( 203 display, CONFIG_SPEC, configs, numConfigs, numConfig)) { 204 throw new IllegalArgumentException("eglChooseConfig#2 failed"); 205 } 206 207 return configs[0]; 208 } 209 } 210