diff options
author | Jack Palevich <jackpal@google.com> | 2009-11-10 18:06:05 -0800 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2009-11-10 18:06:05 -0800 |
commit | 99cf71b0856e00c1ae9631db91702349cceb9bf8 (patch) | |
tree | a00a753b096e951cce2bf9c01bc168b4ea768489 /opengl/java/android | |
parent | fbac9bdfd40b0676a914e9dae7bdd26829087cae (diff) | |
parent | 83536cee2b2b5445c5ba0b22531c23890b82da41 (diff) | |
download | frameworks_base-99cf71b0856e00c1ae9631db91702349cceb9bf8.zip frameworks_base-99cf71b0856e00c1ae9631db91702349cceb9bf8.tar.gz frameworks_base-99cf71b0856e00c1ae9631db91702349cceb9bf8.tar.bz2 |
am 83536cee: Merge change I1179efbb into eclair-mr2
Merge commit '83536cee2b2b5445c5ba0b22531c23890b82da41' into eclair-mr2-plus-aosp
* commit '83536cee2b2b5445c5ba0b22531c23890b82da41':
Fix multi-lock ordering issues in GLSurfaceView
Diffstat (limited to 'opengl/java/android')
-rw-r--r-- | opengl/java/android/opengl/GLSurfaceView.java | 297 |
1 files changed, 158 insertions, 139 deletions
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index bb90ef8..8cc0f6f 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -955,6 +955,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * 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. + * */ class GLThread extends Thread { GLThread(Renderer renderer) { @@ -974,51 +977,31 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback Log.i("GLThread", "starting tid=" + getId()); } - /* - * When the android framework launches a second instance of - * an activity, the new instance's onCreate() method may be - * called before the first instance returns from onDestroy(). - * - * This semaphore ensures that only one instance at a time - * accesses EGL. - */ try { guardedRun(); } catch (InterruptedException e) { // fall thru and exit normally } finally { - synchronized(this) { - if (LOG_THREADS) { - Log.i("GLThread", "exiting tid=" + getId()); - } - mDone = true; - notifyAll(); - } + sGLThreadManager.threadExiting(this); } } - private void startEgl() throws InterruptedException { - if (! mHaveEgl) { - mHaveEgl = true; - sGLThreadManager.start(this); - mEglHelper.start(); - } - } - - private void stopEgl() { + /* + * This private method should only be called inside a + * synchronized(sGLThreadManager) block. + */ + private void stopEglLocked() { if (mHaveEgl) { mHaveEgl = false; mEglHelper.destroySurface(); mEglHelper.finish(); - sGLThreadManager.end(this); + sGLThreadManager.releaseEglSurface(this); } } private void guardedRun() throws InterruptedException { mEglHelper = new EglHelper(); try { - startEgl(); - GL10 gl = null; boolean tellRendererSurfaceCreated = true; boolean tellRendererSurfaceChanged = true; @@ -1027,63 +1010,97 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * This is our main activity thread's loop, we go until * asked to quit. */ - while (!mDone) { - + while (!isDone()) { /* * Update the asynchronous state (window size) */ - int w, h; - boolean changed; + int w = 0; + int h = 0; + boolean changed = false; boolean needStart = false; - synchronized (this) { - Runnable r; - while ((r = getEvent()) != null) { - r.run(); - } - if (mPaused) { - stopEgl(); - needStart = true; - } - while(true) { + boolean eventsWaiting = false; + + synchronized (sGLThreadManager) { + while (true) { + // Manage acquiring and releasing the SurfaceView + // surface and the EGL surface. + if (mPaused) { + stopEglLocked(); + } if (!mHasSurface) { if (!mWaitingForSurface) { - stopEgl(); + stopEglLocked(); mWaitingForSurface = true; - notifyAll(); + sGLThreadManager.notifyAll(); } } else { - boolean shouldHaveEgl = sGLThreadManager.shouldHaveEgl(this); - if (mHaveEgl && (!shouldHaveEgl)) { - stopEgl(); - } else if ((!mHaveEgl) && shouldHaveEgl) { - startEgl(); - needStart = true; + if (!mHaveEgl) { + if (sGLThreadManager.tryAcquireEglSurface(this)) { + mHaveEgl = true; + mEglHelper.start(); + mRequestRender = true; + needStart = true; + } } } - if (!needToWait()) { + + // Check if we need to wait. If not, update any state + // that needs to be updated, copy any state that + // needs to be copied, and use "break" to exit the + // wait loop. + + if (mDone) { + return; + } + + if (mEventsWaiting) { + eventsWaiting = true; + mEventsWaiting = false; + break; + } + + if ( (! mPaused) && mHasSurface && mHaveEgl + && (mWidth > 0) && (mHeight > 0) + && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY)) + ) { + changed = mSizeChanged; + w = mWidth; + h = mHeight; + mSizeChanged = false; + mRequestRender = false; + if (mHasSurface && mWaitingForSurface) { + changed = true; + mWaitingForSurface = false; + sGLThreadManager.notifyAll(); + } break; } + + // By design, this is the only place where we wait(). + if (LOG_THREADS) { - Log.i("GLThread", "needToWait tid=" + getId()); + Log.i("GLThread", "waiting tid=" + getId()); } - wait(); - } - if (mDone) { - break; + sGLThreadManager.wait(); } - changed = mSizeChanged; - w = mWidth; - h = mHeight; - mSizeChanged = false; - mRequestRender = false; - if (mHasSurface && mWaitingForSurface) { - changed = true; - mWaitingForSurface = false; - notifyAll(); + } // end of synchronized(sGLThreadManager) + + /* + * Handle queued events + */ + if (eventsWaiting) { + Runnable r; + while ((r = getEvent()) != null) { + r.run(); + if (isDone()) { + return; + } } + // Go back and see if we need to wait to render. + continue; } + if (needStart) { - startEgl(); tellRendererSurfaceCreated = true; changed = true; } @@ -1115,71 +1132,63 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback /* * clean-up everything... */ - stopEgl(); + synchronized (sGLThreadManager) { + stopEglLocked(); + } } } - private boolean needToWait() { - if (mDone) { - return false; + private boolean isDone() { + synchronized (sGLThreadManager) { + return mDone; } - - if (mPaused || (! mHasSurface) || (! mHaveEgl)) { - return true; - } - - if ((mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) { - return false; - } - - return true; } public void setRenderMode(int renderMode) { if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) { throw new IllegalArgumentException("renderMode"); } - synchronized(this) { + synchronized(sGLThreadManager) { mRenderMode = renderMode; if (renderMode == RENDERMODE_CONTINUOUSLY) { - notifyAll(); + sGLThreadManager.notifyAll(); } } } public int getRenderMode() { - synchronized(this) { + synchronized(sGLThreadManager) { return mRenderMode; } } public void requestRender() { - synchronized(this) { + synchronized(sGLThreadManager) { mRequestRender = true; - notifyAll(); + sGLThreadManager.notifyAll(); } } public void surfaceCreated() { - synchronized(this) { + synchronized(sGLThreadManager) { if (LOG_THREADS) { Log.i("GLThread", "surfaceCreated tid=" + getId()); } mHasSurface = true; - notifyAll(); + sGLThreadManager.notifyAll(); } } public void surfaceDestroyed() { - synchronized(this) { + synchronized(sGLThreadManager) { if (LOG_THREADS) { Log.i("GLThread", "surfaceDestroyed tid=" + getId()); } mHasSurface = false; - notifyAll(); + sGLThreadManager.notifyAll(); while(!mWaitingForSurface && isAlive() && ! mDone) { try { - wait(); + sGLThreadManager.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } @@ -1188,35 +1197,35 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } public void onPause() { - synchronized (this) { + synchronized (sGLThreadManager) { mPaused = true; - notifyAll(); + sGLThreadManager.notifyAll(); } } public void onResume() { - synchronized (this) { + synchronized (sGLThreadManager) { mPaused = false; mRequestRender = true; - notifyAll(); + sGLThreadManager.notifyAll(); } } public void onWindowResize(int w, int h) { - synchronized (this) { + synchronized (sGLThreadManager) { mWidth = w; mHeight = h; mSizeChanged = true; - notifyAll(); + sGLThreadManager.notifyAll(); } } public void requestExitAndWait() { // don't call this from GLThread thread or it is a guaranteed // deadlock! - synchronized(this) { + synchronized(sGLThreadManager) { mDone = true; - notifyAll(); + sGLThreadManager.notifyAll(); } try { join(); @@ -1232,6 +1241,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback public void queueEvent(Runnable r) { synchronized(this) { mEventQueue.add(r); + synchronized(sGLThreadManager) { + mEventsWaiting = true; + sGLThreadManager.notifyAll(); + } } } @@ -1245,6 +1258,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback return null; } + // Once the thread is started, all accesses to the following member + // variables are protected by the sGLThreadManager monitor private boolean mDone; private boolean mPaused; private boolean mHasSurface; @@ -1254,6 +1269,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private int mHeight; private int mRenderMode; private boolean mRequestRender; + private boolean mEventsWaiting; + // End of member variables protected by the sGLThreadManager monitor. + private Renderer mRenderer; private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); private EglHelper mEglHelper; @@ -1300,58 +1318,46 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } private static class GLThreadManager { - public boolean shouldHaveEgl(GLThread thread) { - if (mMultipleGLESContextsAllowed) { - return true; - } else { - synchronized(this) { - return thread == mMostRecentGLThread || mMostRecentGLThread == null; - } - } - } - public void start(GLThread thread) throws InterruptedException { - if (! mGLESVersionCheckComplete) { - mGLESVersion = SystemProperties.getInt( - "ro.opengles.version", - ConfigurationInfo.GL_ES_VERSION_UNDEFINED); - if (mGLESVersion >= kGLES_20) { - mMultipleGLESContextsAllowed = true; - } - mGLESVersionCheckComplete = true; - } - - GLThread oldThread = null; - synchronized(this) { - oldThread = mMostRecentGLThread; - mMostRecentGLThread = thread; + public synchronized void threadExiting(GLThread thread) { + if (LOG_THREADS) { + Log.i("GLThread", "exiting tid=" + thread.getId()); } - if (oldThread != null && ! mMultipleGLESContextsAllowed) { - synchronized(oldThread) { - oldThread.notifyAll(); - } + thread.mDone = true; + if (mEglOwner == thread) { + mEglOwner = null; } + notifyAll(); + } - synchronized(this) { - while ((! mMultipleGLESContextsAllowed) - && mGLContextCount > 0) { - wait(); - } - - mGLContextCount++; + /* + * Tries to acquire the right to use an EGL + * surface. Does not block. + * @return true if the right to use an EGL surface was acquired. + */ + public synchronized boolean tryAcquireEglSurface(GLThread thread) { + if (mEglOwner == thread || mEglOwner == null) { + mEglOwner = thread; + notifyAll(); + return true; + } + checkGLESVersion(); + if (mMultipleGLESContextsAllowed) { + return true; } + return false; } - public synchronized void end(GLThread thread) { - mGLContextCount--; - notifyAll(); - if (mMostRecentGLThread == thread) { - mMostRecentGLThread = null; + public synchronized void releaseEglSurface(GLThread thread) { + if (mEglOwner == thread) { + mEglOwner = null; } + notifyAll(); } public synchronized void checkGLDriver(GL10 gl) { if (! mGLESDriverCheckComplete) { + checkGLESVersion(); if (mGLESVersion < kGLES_20) { String renderer = gl.glGetString(GL10.GL_RENDERER); mMultipleGLESContextsAllowed = @@ -1362,16 +1368,29 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } } + private void checkGLESVersion() { + if (! mGLESVersionCheckComplete) { + mGLESVersion = SystemProperties.getInt( + "ro.opengles.version", + ConfigurationInfo.GL_ES_VERSION_UNDEFINED); + if (mGLESVersion >= kGLES_20) { + mMultipleGLESContextsAllowed = true; + } + mGLESVersionCheckComplete = true; + } + + } + private boolean mGLESVersionCheckComplete; private int mGLESVersion; - private GLThread mMostRecentGLThread; private boolean mGLESDriverCheckComplete; private boolean mMultipleGLESContextsAllowed; private int mGLContextCount; private static final int kGLES_20 = 0x20000; private static final String kMSM7K_RENDERER_PREFIX = "Q3Dimension MSM7500 "; - }; + private GLThread mEglOwner; + } private static final GLThreadManager sGLThreadManager = new GLThreadManager(); private boolean mSizeChanged = true; |