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 import android.util.Log; 22 23 import javax.microedition.khronos.egl.EGL10; 24 import javax.microedition.khronos.egl.EGLConfig; 25 import javax.microedition.khronos.egl.EGLContext; 26 import javax.microedition.khronos.egl.EGLDisplay; 27 import javax.microedition.khronos.egl.EGLSurface; 28 import javax.microedition.khronos.opengles.GL10; 29 30 public class SurfaceTextureRenderer { 31 32 public interface FrameDrawer { 33 public void onDrawFrame(GL10 gl); 34 } 35 36 private static final String TAG = "CAM_" + SurfaceTextureRenderer.class.getSimpleName(); 37 private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 38 39 private EGLConfig mEglConfig; 40 private EGLDisplay mEglDisplay; 41 private EGLContext mEglContext; 42 private EGLSurface mEglSurface; 43 private EGL10 mEgl; 44 private GL10 mGl; 45 46 private Handler mEglHandler; 47 private FrameDrawer mFrameDrawer; 48 49 private Object mRenderLock = new Object(); 50 private Runnable mRenderTask = new Runnable() { 51 @Override 52 public void run() { 53 synchronized (mRenderLock) { 54 if (mEglDisplay != null && mEglSurface != null) { 55 mFrameDrawer.onDrawFrame(mGl); 56 mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); 57 } 58 mRenderLock.notifyAll(); 59 } 60 } 61 }; 62 63 public class RenderThread extends Thread { 64 private Boolean mRenderStopped = false; 65 66 @Override 67 public void run() { 68 while (true) { 69 synchronized (mRenderStopped) { 70 if (mRenderStopped) return; 71 } 72 draw(true); 73 } 74 } 75 76 public void stopRender() { 77 synchronized (mRenderStopped) { 78 mRenderStopped = true; 79 } 80 } 81 } 82 83 public SurfaceTextureRenderer(SurfaceTexture tex, 84 Handler handler, FrameDrawer renderer) { 85 mEglHandler = handler; 86 mFrameDrawer = renderer; 87 88 initialize(tex); 89 } 90 91 public RenderThread createRenderThread() { 92 return new RenderThread(); 93 } 94 95 public void release() { 96 mEglHandler.post(new Runnable() { 97 @Override 98 public void run() { 99 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 100 mEgl.eglDestroyContext(mEglDisplay, mEglContext); 101 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, 102 EGL10.EGL_NO_CONTEXT); 103 mEgl.eglTerminate(mEglDisplay); 104 mEglSurface = null; 105 mEglContext = null; 106 mEglDisplay = null; 107 } 108 }); 109 } 110 111 /** 112 * Posts a render request to the GL thread. 113 * @param sync set <code>true</code> if the caller needs it to be 114 * a synchronous call. 115 */ 116 public void draw(boolean sync) { 117 synchronized (mRenderLock) { 118 mEglHandler.post(mRenderTask); 119 if (sync) { 120 try { 121 mRenderLock.wait(); 122 } catch (InterruptedException ex) { 123 Log.v(TAG, "RenderLock.wait() interrupted"); 124 } 125 } 126 } 127 } 128 129 private void initialize(final SurfaceTexture target) { 130 mEglHandler.post(new Runnable() { 131 @Override 132 public void run() { 133 mEgl = (EGL10) EGLContext.getEGL(); 134 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 135 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 136 throw new RuntimeException("eglGetDisplay failed"); 137 } 138 int[] version = new int[2]; 139 if (!mEgl.eglInitialize(mEglDisplay, version)) { 140 throw new RuntimeException("eglInitialize failed"); 141 } else { 142 Log.v(TAG, "EGL version: " + version[0] + '.' + version[1]); 143 } 144 int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; 145 mEglConfig = chooseConfig(mEgl, mEglDisplay); 146 mEglContext = mEgl.eglCreateContext( 147 mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, attribList); 148 149 if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { 150 throw new RuntimeException("failed to createContext"); 151 } 152 mEglSurface = mEgl.eglCreateWindowSurface( 153 mEglDisplay, mEglConfig, target, null); 154 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 155 throw new RuntimeException("failed to createWindowSurface"); 156 } 157 158 if (!mEgl.eglMakeCurrent( 159 mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 160 throw new RuntimeException("failed to eglMakeCurrent"); 161 } 162 163 mGl = (GL10) mEglContext.getGL(); 164 } 165 }); 166 waitDone(); 167 } 168 169 private void waitDone() { 170 final Object lock = new Object(); 171 synchronized (lock) { 172 mEglHandler.post(new Runnable() { 173 @Override 174 public void run() { 175 synchronized (lock) { 176 lock.notifyAll(); 177 } 178 } 179 }); 180 try { 181 lock.wait(); 182 } catch (InterruptedException ex) { 183 Log.v(TAG, "waitDone() interrupted"); 184 } 185 } 186 } 187 188 private static void checkEglError(String prompt, EGL10 egl) { 189 int error; 190 while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { 191 Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); 192 } 193 } 194 195 private static final int EGL_OPENGL_ES2_BIT = 4; 196 private static final int[] CONFIG_SPEC = new int[] { 197 EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 198 EGL10.EGL_RED_SIZE, 8, 199 EGL10.EGL_GREEN_SIZE, 8, 200 EGL10.EGL_BLUE_SIZE, 8, 201 EGL10.EGL_ALPHA_SIZE, 0, 202 EGL10.EGL_DEPTH_SIZE, 0, 203 EGL10.EGL_STENCIL_SIZE, 0, 204 EGL10.EGL_NONE 205 }; 206 207 private static EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 208 int[] numConfig = new int[1]; 209 if (!egl.eglChooseConfig(display, CONFIG_SPEC, null, 0, numConfig)) { 210 throw new IllegalArgumentException("eglChooseConfig failed"); 211 } 212 213 int numConfigs = numConfig[0]; 214 if (numConfigs <= 0) { 215 throw new IllegalArgumentException("No configs match configSpec"); 216 } 217 218 EGLConfig[] configs = new EGLConfig[numConfigs]; 219 if (!egl.eglChooseConfig( 220 display, CONFIG_SPEC, configs, numConfigs, numConfig)) { 221 throw new IllegalArgumentException("eglChooseConfig#2 failed"); 222 } 223 224 return configs[0]; 225 } 226 } 227