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