blob: ee557f88b99f3d1cc15630d36ce92657403cdda2 [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
Alan Newberger5db8e202014-09-30 14:59:32 -070047 private volatile boolean mDrawPending = false;
48
Sascha Haeberling58501152014-01-06 11:02:35 -080049 private final Handler mEglHandler;
50 private final FrameDrawer mFrameDrawer;
Angus Kong77f16152013-06-05 17:18:49 -070051
Sascha Haeberling58501152014-01-06 11:02:35 -080052 private final Object mRenderLock = new Object();
53 private final Runnable mRenderTask = new Runnable() {
Angus Kong77f16152013-06-05 17:18:49 -070054 @Override
55 public void run() {
56 synchronized (mRenderLock) {
Sascha Haeberlingeb605b42013-11-06 14:03:17 -080057 if (mEglDisplay != null && mEglSurface != null) {
58 mFrameDrawer.onDrawFrame(mGl);
59 mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
Alan Newberger5db8e202014-09-30 14:59:32 -070060 mDrawPending = false;
Sascha Haeberlingeb605b42013-11-06 14:03:17 -080061 }
Angus Kong77f16152013-06-05 17:18:49 -070062 mRenderLock.notifyAll();
63 }
64 }
65 };
66
Angus Kong77f16152013-06-05 17:18:49 -070067 public SurfaceTextureRenderer(SurfaceTexture tex,
68 Handler handler, FrameDrawer renderer) {
69 mEglHandler = handler;
70 mFrameDrawer = renderer;
71
72 initialize(tex);
73 }
74
Angus Kong77f16152013-06-05 17:18:49 -070075 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) {
Alan Newberger5db8e202014-09-30 14:59:32 -070098 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 }
Angus Kong77f16152013-06-05 17:18:49 -0700107 }
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}