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