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