| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.camera; |
| |
| import android.graphics.SurfaceTexture; |
| import android.os.Handler; |
| |
| import com.android.camera.debug.Log; |
| |
| import javax.microedition.khronos.egl.EGL10; |
| import javax.microedition.khronos.egl.EGLConfig; |
| import javax.microedition.khronos.egl.EGLContext; |
| import javax.microedition.khronos.egl.EGLDisplay; |
| import javax.microedition.khronos.egl.EGLSurface; |
| import javax.microedition.khronos.opengles.GL10; |
| |
| public class SurfaceTextureRenderer { |
| |
| public interface FrameDrawer { |
| public void onDrawFrame(GL10 gl); |
| } |
| |
| private static final Log.Tag TAG = new Log.Tag("SurfTexRenderer"); |
| private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; |
| |
| private EGLConfig mEglConfig; |
| private EGLDisplay mEglDisplay; |
| private EGLContext mEglContext; |
| private EGLSurface mEglSurface; |
| private EGL10 mEgl; |
| private GL10 mGl; |
| |
| private volatile boolean mDrawPending = false; |
| |
| private final Handler mEglHandler; |
| private final FrameDrawer mFrameDrawer; |
| |
| private final Object mRenderLock = new Object(); |
| private final Runnable mRenderTask = new Runnable() { |
| @Override |
| public void run() { |
| synchronized (mRenderLock) { |
| if (mEglDisplay != null && mEglSurface != null) { |
| mFrameDrawer.onDrawFrame(mGl); |
| mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); |
| mDrawPending = false; |
| } |
| mRenderLock.notifyAll(); |
| } |
| } |
| }; |
| |
| public SurfaceTextureRenderer(SurfaceTexture tex, |
| Handler handler, FrameDrawer renderer) { |
| mEglHandler = handler; |
| mFrameDrawer = renderer; |
| |
| initialize(tex); |
| } |
| |
| public void release() { |
| mEglHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| mEgl.eglDestroySurface(mEglDisplay, mEglSurface); |
| mEgl.eglDestroyContext(mEglDisplay, mEglContext); |
| mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, |
| EGL10.EGL_NO_CONTEXT); |
| mEgl.eglTerminate(mEglDisplay); |
| mEglSurface = null; |
| mEglContext = null; |
| mEglDisplay = null; |
| } |
| }); |
| } |
| |
| /** |
| * Posts a render request to the GL thread. |
| * @param sync set <code>true</code> if the caller needs it to be |
| * a synchronous call. |
| */ |
| public void draw(boolean sync) { |
| synchronized (mRenderLock) { |
| if (!mDrawPending) { |
| mEglHandler.post(mRenderTask); |
| mDrawPending = true; |
| if (sync) { |
| try { |
| mRenderLock.wait(); |
| } catch (InterruptedException ex) { |
| Log.v(TAG, "RenderLock.wait() interrupted"); |
| } |
| } |
| } |
| } |
| } |
| |
| private void initialize(final SurfaceTexture target) { |
| mEglHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| mEgl = (EGL10) EGLContext.getEGL(); |
| mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); |
| if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { |
| throw new RuntimeException("eglGetDisplay failed"); |
| } |
| int[] version = new int[2]; |
| if (!mEgl.eglInitialize(mEglDisplay, version)) { |
| throw new RuntimeException("eglInitialize failed"); |
| } else { |
| Log.v(TAG, "EGL version: " + version[0] + '.' + version[1]); |
| } |
| int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; |
| mEglConfig = chooseConfig(mEgl, mEglDisplay); |
| mEglContext = mEgl.eglCreateContext( |
| mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, attribList); |
| |
| if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { |
| throw new RuntimeException("failed to createContext"); |
| } |
| mEglSurface = mEgl.eglCreateWindowSurface( |
| mEglDisplay, mEglConfig, target, null); |
| if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { |
| throw new RuntimeException("failed to createWindowSurface"); |
| } |
| |
| if (!mEgl.eglMakeCurrent( |
| mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { |
| throw new RuntimeException("failed to eglMakeCurrent"); |
| } |
| |
| mGl = (GL10) mEglContext.getGL(); |
| } |
| }); |
| waitDone(); |
| } |
| |
| private void waitDone() { |
| final Object lock = new Object(); |
| synchronized (lock) { |
| mEglHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| synchronized (lock) { |
| lock.notifyAll(); |
| } |
| } |
| }); |
| try { |
| lock.wait(); |
| } catch (InterruptedException ex) { |
| Log.v(TAG, "waitDone() interrupted"); |
| } |
| } |
| } |
| |
| private static void checkEglError(String prompt, EGL10 egl) { |
| int error; |
| while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { |
| Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); |
| } |
| } |
| |
| private static final int EGL_OPENGL_ES2_BIT = 4; |
| private static final int[] CONFIG_SPEC = new int[] { |
| EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
| EGL10.EGL_RED_SIZE, 8, |
| EGL10.EGL_GREEN_SIZE, 8, |
| EGL10.EGL_BLUE_SIZE, 8, |
| EGL10.EGL_ALPHA_SIZE, 0, |
| EGL10.EGL_DEPTH_SIZE, 0, |
| EGL10.EGL_STENCIL_SIZE, 0, |
| EGL10.EGL_NONE |
| }; |
| |
| private static EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { |
| int[] numConfig = new int[1]; |
| if (!egl.eglChooseConfig(display, CONFIG_SPEC, null, 0, numConfig)) { |
| throw new IllegalArgumentException("eglChooseConfig failed"); |
| } |
| |
| int numConfigs = numConfig[0]; |
| if (numConfigs <= 0) { |
| throw new IllegalArgumentException("No configs match configSpec"); |
| } |
| |
| EGLConfig[] configs = new EGLConfig[numConfigs]; |
| if (!egl.eglChooseConfig( |
| display, CONFIG_SPEC, configs, numConfigs, numConfig)) { |
| throw new IllegalArgumentException("eglChooseConfig#2 failed"); |
| } |
| |
| return configs[0]; |
| } |
| } |