Back to project page FxCameraApp.
The source code is released under:
MIT License
If you think the Android project FxCameraApp listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.af.experiments.FxCameraApp.ogles; /*from w w w. j ava 2s . co m*/ import android.app.ActivityManager; import android.content.Context; import android.graphics.SurfaceTexture; import android.opengl.GLDebugHelper; import android.opengl.GLSurfaceView; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.TextureView; import com.af.experiments.FxCameraApp.Utils.LogWriter; import javax.microedition.khronos.egl.*; import javax.microedition.khronos.opengles.GL; import javax.microedition.khronos.opengles.GL10; import java.io.Writer; import java.lang.ref.WeakReference; import java.util.ArrayList; import static android.opengl.GLSurfaceView.DEBUG_CHECK_GL_ERROR; import static android.opengl.GLSurfaceView.DEBUG_LOG_GL_CALLS; import static android.opengl.GLSurfaceView.RENDERMODE_CONTINUOUSLY; import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY; import static javax.microedition.khronos.egl.EGL10.*; import static javax.microedition.khronos.egl.EGL11.EGL_CONTEXT_LOST; public class GlTextureView extends TextureView implements TextureView.SurfaceTextureListener { private final static String TAG = "GLTextureView"; private final static boolean LOG_ATTACH_DETACH = false; private final static boolean LOG_THREADS = false; private final static boolean LOG_PAUSE_RESUME = false; private final static boolean LOG_SURFACE = false; private final static boolean LOG_RENDERER = false; private final static boolean LOG_RENDERER_DRAW_FRAME = false; private final static boolean LOG_EGL = false; private static int sGLESVersion; private SurfaceTextureListener mSurfaceTextureListener; public GlTextureView(final Context context) { super(context); init(context); } public GlTextureView(final Context context, final AttributeSet attrs) { super(context, attrs); init(context); } public GlTextureView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); init(context); } private void init(final Context context) { if (sGLESVersion == 0) { final ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); sGLESVersion = am.getDeviceConfigurationInfo().reqGlEsVersion; } super.setSurfaceTextureListener(this); } @Override protected void finalize() throws Throwable { try { if (mGLThread != null) { mGLThread.requestExitAndWait(); } } finally { super.finalize(); } } @Override public void setSurfaceTextureListener(final SurfaceTextureListener listener) { mSurfaceTextureListener = listener; } public void setGLWrapper(final GLSurfaceView.GLWrapper glWrapper) { mGLWrapper = glWrapper; } public void setDebugFlags(final int debugFlags) { mDebugFlags = debugFlags; } public int getDebugFlags() { return mDebugFlags; } public void setPreserveEGLContextOnPause(final boolean preserveOnPause) { mPreserveEGLContextOnPause = preserveOnPause; } public boolean getPreserveEGLContextOnPause() { return mPreserveEGLContextOnPause; } public void setRenderer(final GLSurfaceView.Renderer renderer) { checkRenderThreadState(); if (mEGLConfigChooser == null) { mEGLConfigChooser = new DefaultConfigChooser(true, mEGLContextClientVersion); } if (mEGLContextFactory == null) { mEGLContextFactory = new DefaultContextFactory(mEGLContextClientVersion); } if (mEGLWindowSurfaceFactory == null) { mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); } mRenderer = renderer; mGLThread = new GLThread(mThisWeakRef); mGLThread.start(); } public void setEGLContextFactory(final GLSurfaceView.EGLContextFactory factory) { checkRenderThreadState(); mEGLContextFactory = factory; } public void setEGLWindowSurfaceFactory(final GLSurfaceView.EGLWindowSurfaceFactory factory) { checkRenderThreadState(); mEGLWindowSurfaceFactory = factory; } public void setEGLConfigChooser(final GLSurfaceView.EGLConfigChooser configChooser) { checkRenderThreadState(); mEGLConfigChooser = configChooser; } public void setEGLConfigChooser(final boolean needDepth) { setEGLConfigChooser(new DefaultConfigChooser(needDepth, mEGLContextClientVersion)); } public void setEGLConfigChooser(final int redSize, final int greenSize, final int blueSize, final int alphaSize, final int depthSize, final int stencilSize) { setEGLConfigChooser(new DefaultConfigChooser(redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize, mEGLContextClientVersion)); } public void setEGLContextClientVersion(final int version) { checkRenderThreadState(); mEGLContextClientVersion = version; } public void setRenderMode(final int renderMode) { mGLThread.setRenderMode(renderMode); } public int getRenderMode() { return mGLThread.getRenderMode(); } public void requestRender() { mGLThread.requestRender(); } @Override public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) { mGLThread.surfaceCreated(); mGLThread.onWindowResize(width, height); if (mSurfaceTextureListener != null) { mSurfaceTextureListener.onSurfaceTextureAvailable(surface, width, height); } } @Override public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) { try { if (mSurfaceTextureListener != null) { mSurfaceTextureListener.onSurfaceTextureDestroyed(surface); } } finally { mGLThread.surfaceDestroyed(); } return true; } @Override public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) { mGLThread.onWindowResize(width, height); if (mSurfaceTextureListener != null) { mSurfaceTextureListener.onSurfaceTextureSizeChanged(surface, width, height); } } @Override public void onSurfaceTextureUpdated(final SurfaceTexture surface) { if (mSurfaceTextureListener != null) { mSurfaceTextureListener.onSurfaceTextureUpdated(surface); } } public void onPause() { mGLThread.onPause(); } public void onResume() { mGLThread.onResume(); } public void queueEvent(final Runnable r) { mGLThread.queueEvent(r); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (LOG_ATTACH_DETACH) { Log.d(TAG, "onAttachedToWindow reattach =" + mDetached); } if (mDetached && (mRenderer != null)) { int renderMode = RENDERMODE_CONTINUOUSLY; if (mGLThread != null) { renderMode = mGLThread.getRenderMode(); } mGLThread = new GLThread(mThisWeakRef); if (renderMode != RENDERMODE_CONTINUOUSLY) { mGLThread.setRenderMode(renderMode); } mGLThread.start(); } mDetached = false; } @Override protected void onDetachedFromWindow() { if (LOG_ATTACH_DETACH) { Log.d(TAG, "onDetachedFromWindow"); } if (mGLThread != null) { mGLThread.requestExitAndWait(); } mDetached = true; super.onDetachedFromWindow(); } private static class EglHelper { public EglHelper(final WeakReference<GlTextureView> glSurfaceViewWeakRef) { mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; } public void start() { if (LOG_EGL) { Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId()); } /* * Get an EGL instance */ mEgl = (EGL10) EGLContext.getEGL(); /* * Get to the default display. */ mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY); if (mEglDisplay == EGL_NO_DISPLAY) { throw new RuntimeException("eglGetDisplay failed"); } /* * We can now initialize EGL for that display */ int[] version = new int[2]; if(!mEgl.eglInitialize(mEglDisplay, version)) { throw new RuntimeException("eglInitialize failed"); } GlTextureView view = mGLSurfaceViewWeakRef.get(); if (view == null) { mEglConfig = null; mEglContext = null; } else { mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); /* * Create an EGL context. We want to do this as rarely as we can, because an * EGL context is a somewhat heavy object. */ mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); } if (mEglContext == null || mEglContext == EGL_NO_CONTEXT) { mEglContext = null; throwEglException("createContext"); } if (LOG_EGL) { Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId()); } mEglSurface = null; } public boolean createSurface() { if (LOG_EGL) { Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId()); } /* * Check preconditions. */ if (mEgl == null) { throw new RuntimeException("egl not initialized"); } if (mEglDisplay == null) { throw new RuntimeException("eglDisplay not initialized"); } if (mEglConfig == null) { throw new RuntimeException("mEglConfig not initialized"); } /* * The window size has changed, so we need to create a new * surface. */ destroySurfaceImp(); /* * Create an EGL surface we can render into. */ GlTextureView view = mGLSurfaceViewWeakRef.get(); if (view != null) { mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl, mEglDisplay, mEglConfig, view.getSurfaceTexture()); } else { mEglSurface = null; } if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) { final int error = mEgl.eglGetError(); if (error == EGL_BAD_NATIVE_WINDOW) { Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); } return false; } /* * Before we can issue GL commands, we need to make sure * the context is current and bound to a surface. */ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { /* * Could not make the context current, probably because the underlying * SurfaceView surface has been destroyed. */ logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); return false; } return true; } /** * Create a GL object for the current EGL context. * @return */ GL createGL() { GL gl = mEglContext.getGL(); final GlTextureView view = mGLSurfaceViewWeakRef.get(); if (view != null) { if (view.mGLWrapper != null) { gl = view.mGLWrapper.wrap(gl); } if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) { int configFlags = 0; Writer log = null; if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; } if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { log = new LogWriter(GlTextureView.TAG); } gl = GLDebugHelper.wrap(gl, configFlags, log); } } return gl; } /** * Display the current render surface. * @return the EGL error code from eglSwapBuffers. */ public int swap() { if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { return mEgl.eglGetError(); } return EGL_SUCCESS; } public void destroySurface() { if (LOG_EGL) { Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId()); } destroySurfaceImp(); } private void destroySurfaceImp() { if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) { mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); final GlTextureView view = mGLSurfaceViewWeakRef.get(); if (view != null) { view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); } mEglSurface = null; } } public void finish() { if (LOG_EGL) { Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId()); } if (mEglContext != null) { final GlTextureView view = mGLSurfaceViewWeakRef.get(); if (view != null) { view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext); } mEglContext = null; } if (mEglDisplay != null) { mEgl.eglTerminate(mEglDisplay); mEglDisplay = null; } } private void throwEglException(final String function) { throwEglException(function, mEgl.eglGetError()); } public static void throwEglException(final String function, final int error) { final String message = formatEglError(function, error); if (LOG_THREADS) { Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " " + message); } throw new RuntimeException(message); } public static void logEglErrorAsWarning(final String tag, final String function, final int error) { Log.w(tag, formatEglError(function, error)); } public static String formatEglError(final String function, final int error) { return function + " failed: " + EGLLogWrapper.getErrorString(error); } private WeakReference<GlTextureView> mGLSurfaceViewWeakRef; EGL10 mEgl; EGLDisplay mEglDisplay; EGLSurface mEglSurface; EGLConfig mEglConfig; EGLContext mEglContext; } /** * A generic GL Thread. Takes care of initializing EGL and GL. Delegates * to a Renderer instance to do the actual drawing. Can be configured to * render continuously or on request. * * All potentially blocking synchronization is done through the * sGLThreadManager object. This avoids multiple-lock ordering issues. * */ static class GLThread extends Thread { GLThread(final WeakReference<GlTextureView> glSurfaceViewWeakRef) { super(); mWidth = 0; mHeight = 0; mRequestRender = true; mRenderMode = RENDERMODE_CONTINUOUSLY; mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; } @Override public void run() { setName("GLThread " + getId()); if (LOG_THREADS) { Log.i("GLThread", "starting tid=" + getId()); } try { guardedRun(); } catch (final InterruptedException e) { // fall thru and exit normally } finally { sGLThreadManager.threadExiting(this); } } /* * This private method should only be called inside a * synchronized(sGLThreadManager) block. */ private void stopEglSurfaceLocked() { if (mHaveEglSurface) { mHaveEglSurface = false; mEglHelper.destroySurface(); } } /* * This private method should only be called inside a * synchronized(sGLThreadManager) block. */ private void stopEglContextLocked() { if (mHaveEglContext) { mEglHelper.finish(); mHaveEglContext = false; sGLThreadManager.releaseEglContextLocked(this); } } private void guardedRun() throws InterruptedException { mEglHelper = new EglHelper(mGLSurfaceViewWeakRef); mHaveEglContext = false; mHaveEglSurface = false; try { GL10 gl = null; boolean createEglContext = false; boolean createEglSurface = false; boolean createGlInterface = false; boolean lostEglContext = false; boolean sizeChanged = false; boolean wantRenderNotification = false; boolean doRenderNotification = false; boolean askedToReleaseEglContext = false; int w = 0; int h = 0; Runnable event = null; while (true) { synchronized (sGLThreadManager) { while (true) { if (mShouldExit) { return; } if (! mEventQueue.isEmpty()) { event = mEventQueue.remove(0); break; } // Update the pause state. boolean pausing = false; if (mPaused != mRequestPaused) { pausing = mRequestPaused; mPaused = mRequestPaused; sGLThreadManager.notifyAll(); if (LOG_PAUSE_RESUME) { Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId()); } } // Do we need to give up the EGL context? if (mShouldReleaseEglContext) { if (LOG_SURFACE) { Log.i("GLThread", "releasing EGL context because asked to tid=" + getId()); } stopEglSurfaceLocked(); stopEglContextLocked(); mShouldReleaseEglContext = false; askedToReleaseEglContext = true; } // Have we lost the EGL context? if (lostEglContext) { stopEglSurfaceLocked(); stopEglContextLocked(); lostEglContext = false; } // When pausing, release the EGL surface: if (pausing && mHaveEglSurface) { if (LOG_SURFACE) { Log.i("GLThread", "releasing EGL surface because paused tid=" + getId()); } stopEglSurfaceLocked(); } // When pausing, optionally release the EGL Context: if (pausing && mHaveEglContext) { final GlTextureView view = mGLSurfaceViewWeakRef.get(); final boolean preserveEglContextOnPause = view == null ? false : view.mPreserveEGLContextOnPause; if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) { stopEglContextLocked(); if (LOG_SURFACE) { Log.i("GLThread", "releasing EGL context because paused tid=" + getId()); } } } // When pausing, optionally terminate EGL: if (pausing) { if (sGLThreadManager.shouldTerminateEGLWhenPausing()) { mEglHelper.finish(); if (LOG_SURFACE) { Log.i("GLThread", "terminating EGL because paused tid=" + getId()); } } } // Have we lost the SurfaceView surface? if ((! mHasSurface) && (! mWaitingForSurface)) { if (LOG_SURFACE) { Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId()); } if (mHaveEglSurface) { stopEglSurfaceLocked(); } mWaitingForSurface = true; mSurfaceIsBad = false; sGLThreadManager.notifyAll(); } // Have we acquired the surface view surface? if (mHasSurface && mWaitingForSurface) { if (LOG_SURFACE) { Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId()); } mWaitingForSurface = false; sGLThreadManager.notifyAll(); } if (doRenderNotification) { if (LOG_SURFACE) { Log.i("GLThread", "sending render notification tid=" + getId()); } wantRenderNotification = false; doRenderNotification = false; mRenderComplete = true; sGLThreadManager.notifyAll(); } // Ready to draw? if (readyToDraw()) { // If we don't have an EGL context, try to acquire one. if (! mHaveEglContext) { if (askedToReleaseEglContext) { askedToReleaseEglContext = false; } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) { try { mEglHelper.start(); } catch (final RuntimeException t) { sGLThreadManager.releaseEglContextLocked(this); throw t; } mHaveEglContext = true; createEglContext = true; sGLThreadManager.notifyAll(); } } if (mHaveEglContext && !mHaveEglSurface) { mHaveEglSurface = true; createEglSurface = true; createGlInterface = true; sizeChanged = true; } if (mHaveEglSurface) { if (mSizeChanged) { sizeChanged = true; w = mWidth; h = mHeight; wantRenderNotification = true; if (LOG_SURFACE) { Log.i("GLThread", "noticing that we want render notification tid=" + getId()); } // Destroy and recreate the EGL surface. createEglSurface = true; mSizeChanged = false; } mRequestRender = false; sGLThreadManager.notifyAll(); break; } } // By design, this is the only place in a GLThread thread where we wait(). if (LOG_THREADS) { Log.i("GLThread", "waiting tid=" + getId() + " mHaveEglContext: " + mHaveEglContext + " mHaveEglSurface: " + mHaveEglSurface + " mPaused: " + mPaused + " mHasSurface: " + mHasSurface + " mSurfaceIsBad: " + mSurfaceIsBad + " mWaitingForSurface: " + mWaitingForSurface + " mWidth: " + mWidth + " mHeight: " + mHeight + " mRequestRender: " + mRequestRender + " mRenderMode: " + mRenderMode); } sGLThreadManager.wait(); } } // end of synchronized(sGLThreadManager) if (event != null) { event.run(); event = null; continue; } if (createEglSurface) { if (LOG_SURFACE) { Log.w("GLThread", "egl createSurface"); } if (!mEglHelper.createSurface()) { synchronized (sGLThreadManager) { mSurfaceIsBad = true; sGLThreadManager.notifyAll(); } continue; } createEglSurface = false; } if (createGlInterface) { gl = (GL10) mEglHelper.createGL(); sGLThreadManager.checkGLDriver(gl); createGlInterface = false; } if (createEglContext) { if (LOG_RENDERER) { Log.w("GLThread", "onSurfaceCreated"); } final GlTextureView view = mGLSurfaceViewWeakRef.get(); if (view != null) { view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); } createEglContext = false; } if (sizeChanged) { if (LOG_RENDERER) { Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")"); } final GlTextureView view = mGLSurfaceViewWeakRef.get(); if (view != null) { view.mRenderer.onSurfaceChanged(gl, w, h); } sizeChanged = false; } if (LOG_RENDERER_DRAW_FRAME) { Log.w("GLThread", "onDrawFrame tid=" + getId()); } { final GlTextureView view = mGLSurfaceViewWeakRef.get(); if (view != null) { view.mRenderer.onDrawFrame(gl); } } final int swapError = mEglHelper.swap(); switch (swapError) { case EGL_SUCCESS: break; case EGL_CONTEXT_LOST: if (LOG_SURFACE) { Log.i("GLThread", "egl context lost tid=" + getId()); } lostEglContext = true; break; default: // Other errors typically mean that the current surface is bad, // probably because the SurfaceView surface has been destroyed, // but we haven't been notified yet. // Log the error to help developers understand why rendering stopped. EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError); synchronized (sGLThreadManager) { mSurfaceIsBad = true; sGLThreadManager.notifyAll(); } break; } if (wantRenderNotification) { doRenderNotification = true; } } } finally { /* * clean-up everything... */ synchronized (sGLThreadManager) { stopEglSurfaceLocked(); stopEglContextLocked(); } } } public boolean ableToDraw() { return mHaveEglContext && mHaveEglSurface && readyToDraw(); } private boolean readyToDraw() { return (!mPaused) && mHasSurface && (!mSurfaceIsBad) && (mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY)); } public void setRenderMode(final int renderMode) { if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) { throw new IllegalArgumentException("renderMode"); } synchronized(sGLThreadManager) { mRenderMode = renderMode; sGLThreadManager.notifyAll(); } } public int getRenderMode() { synchronized(sGLThreadManager) { return mRenderMode; } } public void requestRender() { synchronized(sGLThreadManager) { mRequestRender = true; sGLThreadManager.notifyAll(); } } public void surfaceCreated() { synchronized(sGLThreadManager) { if (LOG_THREADS) { Log.i("GLThread", "surfaceCreated tid=" + getId()); } mHasSurface = true; sGLThreadManager.notifyAll(); while((mWaitingForSurface) && (!mExited)) { try { sGLThreadManager.wait(); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } } } } public void surfaceDestroyed() { synchronized(sGLThreadManager) { if (LOG_THREADS) { Log.i("GLThread", "surfaceDestroyed tid=" + getId()); } mHasSurface = false; sGLThreadManager.notifyAll(); while((!mWaitingForSurface) && (!mExited)) { try { sGLThreadManager.wait(); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } } } } public void onPause() { synchronized (sGLThreadManager) { if (LOG_PAUSE_RESUME) { Log.i("GLThread", "onPause tid=" + getId()); } mRequestPaused = true; sGLThreadManager.notifyAll(); while ((! mExited) && (! mPaused)) { if (LOG_PAUSE_RESUME) { Log.i("Main thread", "onPause waiting for mPaused."); } try { sGLThreadManager.wait(); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); } } } } public void onResume() { synchronized (sGLThreadManager) { if (LOG_PAUSE_RESUME) { Log.i("GLThread", "onResume tid=" + getId()); } mRequestPaused = false; mRequestRender = true; mRenderComplete = false; sGLThreadManager.notifyAll(); while ((! mExited) && mPaused && (!mRenderComplete)) { if (LOG_PAUSE_RESUME) { Log.i("Main thread", "onResume waiting for !mPaused."); } try { sGLThreadManager.wait(); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); } } } } public void onWindowResize(final int width, final int height) { synchronized (sGLThreadManager) { mWidth = width; mHeight = height; mSizeChanged = true; mRequestRender = true; mRenderComplete = false; sGLThreadManager.notifyAll(); // Wait for thread to react to resize and render a frame while (! mExited && !mPaused && !mRenderComplete && ableToDraw()) { if (LOG_SURFACE) { Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId()); } try { sGLThreadManager.wait(); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); } } } } public void requestExitAndWait() { // don't call this from GLThread thread or it is a guaranteed // deadlock! synchronized(sGLThreadManager) { mShouldExit = true; sGLThreadManager.notifyAll(); while (! mExited) { try { sGLThreadManager.wait(); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); } } } } public void requestReleaseEglContextLocked() { mShouldReleaseEglContext = true; sGLThreadManager.notifyAll(); } /** * Queue an "event" to be run on the GL rendering thread. * @param r the runnable to be run on the GL rendering thread. */ public void queueEvent(final Runnable r) { if (r == null) { throw new IllegalArgumentException("r must not be null"); } synchronized(sGLThreadManager) { mEventQueue.add(r); sGLThreadManager.notifyAll(); } } // Once the thread is started, all accesses to the following member // variables are protected by the sGLThreadManager monitor private boolean mShouldExit; private boolean mExited; private boolean mRequestPaused; private boolean mPaused; private boolean mHasSurface; private boolean mSurfaceIsBad; private boolean mWaitingForSurface; private boolean mHaveEglContext; private boolean mHaveEglSurface; private boolean mShouldReleaseEglContext; private int mWidth; private int mHeight; private int mRenderMode; private boolean mRequestRender; private boolean mRenderComplete; private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); private boolean mSizeChanged = true; // End of member variables protected by the sGLThreadManager monitor. private EglHelper mEglHelper; /** * Set once at thread construction time, nulled out when the parent view is garbage * called. This weak reference allows the GLSurfaceView to be garbage collected while * the GLThread is still alive. */ private WeakReference<GlTextureView> mGLSurfaceViewWeakRef; } private void checkRenderThreadState() { if (mGLThread != null) { throw new IllegalStateException("setRenderer has already been called for this instance."); } } private static class GLThreadManager { private static String TAG = "GLThreadManager"; public synchronized void threadExiting(final GLThread thread) { if (LOG_THREADS) { Log.i("GLThread", "exiting tid=" + thread.getId()); } thread.mExited = true; if (mEglOwner == thread) { mEglOwner = null; } notifyAll(); } /* * Tries once to acquire the right to use an EGL context. Does not * block. Requires that we are already in the sGLThreadManager monitor * when this is called. * @return true if the right to use an EGL context was acquired. */ public boolean tryAcquireEglContextLocked(final GLThread thread) { if (mEglOwner == thread || mEglOwner == null) { mEglOwner = thread; notifyAll(); return true; } checkGLESVersion(); if (mMultipleGLESContextsAllowed) { return true; } // Notify the owning thread that it should release the context. // TODO: implement a fairness policy. Currently // if the owning thread is drawing continuously it will just // reacquire the EGL context. if (mEglOwner != null) { mEglOwner.requestReleaseEglContextLocked(); } return false; } /* * Releases the EGL context. Requires that we are already in the * sGLThreadManager monitor when this is called. */ public void releaseEglContextLocked(final GLThread thread) { if (mEglOwner == thread) { mEglOwner = null; } notifyAll(); } public synchronized boolean shouldReleaseEGLContextWhenPausing() { // Release the EGL context when pausing even if // the hardware supports multiple EGL contexts. // Otherwise the device could run out of EGL contexts. return mLimitedGLESContexts; } public synchronized boolean shouldTerminateEGLWhenPausing() { checkGLESVersion(); return !mMultipleGLESContextsAllowed; } public synchronized void checkGLDriver(final GL10 gl) { if (!mGLESDriverCheckComplete) { checkGLESVersion(); final String renderer = gl.glGetString(GL10.GL_RENDERER); if (sGLESVersion < kGLES_20) { mMultipleGLESContextsAllowed = !renderer.startsWith(kMSM7K_RENDERER_PREFIX); notifyAll(); } mLimitedGLESContexts = !mMultipleGLESContextsAllowed || (Integer.parseInt(Build.VERSION.SDK) < JELLY_BEAN && renderer.startsWith(kADRENO)); if (LOG_SURFACE) { Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = " + mMultipleGLESContextsAllowed + " mLimitedGLESContexts = " + mLimitedGLESContexts); } mGLESDriverCheckComplete = true; } } private void checkGLESVersion() { if (!mGLESVersionCheckComplete) { if (sGLESVersion >= kGLES_20) { mMultipleGLESContextsAllowed = true; } if (LOG_SURFACE) { Log.w(TAG, "checkGLESVersion mGLESVersion =" + " " + sGLESVersion + " mMultipleGLESContextsAllowed = " + mMultipleGLESContextsAllowed); } mGLESVersionCheckComplete = true; } } /** * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides * support for hardware-accelerated views, therefore multiple EGL contexts are * supported on all Android 3.0+ EGL drivers. */ private boolean mGLESVersionCheckComplete; private boolean mGLESDriverCheckComplete; private boolean mMultipleGLESContextsAllowed; private boolean mLimitedGLESContexts; private static final int kGLES_20 = 0x20000; private static final String kMSM7K_RENDERER_PREFIX = "Q3Dimension MSM7500 "; private static final String kADRENO = "Adreno"; private static final int JELLY_BEAN = 16; private GLThread mEglOwner; } private static final GLThreadManager sGLThreadManager = new GLThreadManager(); private final WeakReference<GlTextureView> mThisWeakRef = new WeakReference<GlTextureView>(this); private GLThread mGLThread; private GLSurfaceView.Renderer mRenderer; private boolean mDetached; private GLSurfaceView.EGLConfigChooser mEGLConfigChooser; private GLSurfaceView.EGLContextFactory mEGLContextFactory; private GLSurfaceView.EGLWindowSurfaceFactory mEGLWindowSurfaceFactory; private GLSurfaceView.GLWrapper mGLWrapper; private int mDebugFlags; private int mEGLContextClientVersion; private boolean mPreserveEGLContextOnPause; }