am 99cf71b0: am 83536cee: Merge change I1179efbb into eclair-mr2

Merge commit '99cf71b0856e00c1ae9631db91702349cceb9bf8'

* commit '99cf71b0856e00c1ae9631db91702349cceb9bf8':
  Fix multi-lock ordering issues in GLSurfaceView
This commit is contained in:
Jack Palevich
2009-11-10 18:11:04 -08:00
committed by Android Git Automerger

View File

@ -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) {
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) {
stopEglLocked();
mWaitingForSurface = true;
sGLThreadManager.notifyAll();
}
} else {
if (!mHaveEgl) {
if (sGLThreadManager.tryAcquireEglSurface(this)) {
mHaveEgl = true;
mEglHelper.start();
mRequestRender = true;
needStart = true;
}
}
}
// 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", "waiting tid=" + getId());
}
sGLThreadManager.wait();
}
} // end of synchronized(sGLThreadManager)
/*
* Handle queued events
*/
if (eventsWaiting) {
Runnable r;
while ((r = getEvent()) != null) {
r.run();
}
if (mPaused) {
stopEgl();
needStart = true;
}
while(true) {
if (!mHasSurface) {
if (!mWaitingForSurface) {
stopEgl();
mWaitingForSurface = true;
notifyAll();
}
} else {
boolean shouldHaveEgl = sGLThreadManager.shouldHaveEgl(this);
if (mHaveEgl && (!shouldHaveEgl)) {
stopEgl();
} else if ((!mHaveEgl) && shouldHaveEgl) {
startEgl();
needStart = true;
}
if (isDone()) {
return;
}
if (!needToWait()) {
break;
}
if (LOG_THREADS) {
Log.i("GLThread", "needToWait tid=" + getId());
}
wait();
}
if (mDone) {
break;
}
changed = mSizeChanged;
w = mWidth;
h = mHeight;
mSizeChanged = false;
mRequestRender = false;
if (mHasSurface && mWaitingForSurface) {
changed = true;
mWaitingForSurface = false;
notifyAll();
}
// 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) {
public synchronized void threadExiting(GLThread thread) {
if (LOG_THREADS) {
Log.i("GLThread", "exiting tid=" + thread.getId());
}
thread.mDone = true;
if (mEglOwner == thread) {
mEglOwner = null;
}
notifyAll();
}
/*
* 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;
} else {
synchronized(this) {
return thread == mMostRecentGLThread || mMostRecentGLThread == null;
}
}
return false;
}
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;
public synchronized void releaseEglSurface(GLThread thread) {
if (mEglOwner == thread) {
mEglOwner = null;
}
GLThread oldThread = null;
synchronized(this) {
oldThread = mMostRecentGLThread;
mMostRecentGLThread = thread;
}
if (oldThread != null && ! mMultipleGLESContextsAllowed) {
synchronized(oldThread) {
oldThread.notifyAll();
}
}
synchronized(this) {
while ((! mMultipleGLESContextsAllowed)
&& mGLContextCount > 0) {
wait();
}
mGLContextCount++;
}
}
public synchronized void end(GLThread thread) {
mGLContextCount--;
notifyAll();
if (mMostRecentGLThread == thread) {
mMostRecentGLThread = null;
}
}
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;