/*
|
* 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];
|
}
|
}
|