Home | History | Annotate | Download | only in camera
      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 {
     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 
     67     public SurfaceTextureRenderer(SurfaceTexture tex,
     68             Handler handler, FrameDrawer renderer) {
     69         mEglHandler = handler;
     70         mFrameDrawer = renderer;
     71 
     72         initialize(tex);
     73     }
     74 
     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      */
     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 
    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 
    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 
    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 
    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