summaryrefslogtreecommitdiffstats
path: root/opengl/java
diff options
context:
space:
mode:
Diffstat (limited to 'opengl/java')
-rw-r--r--opengl/java/android/opengl/GLSurfaceView.java263
1 files changed, 181 insertions, 82 deletions
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 695d061..9ca57ba 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -18,7 +18,6 @@ package android.opengl;
import java.io.Writer;
import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
@@ -145,6 +144,7 @@ import android.view.SurfaceView;
*
*/
public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+ private final static boolean LOG_THREADS = false;
/**
* The renderer only renders
* when the surface is created, or when {@link #requestRender} is called.
@@ -942,6 +942,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) {
@@ -952,38 +955,40 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
mRequestRender = true;
mRenderMode = RENDERMODE_CONTINUOUSLY;
mRenderer = renderer;
- setName("GLThread");
}
@Override
public void run() {
- /*
- * 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.
- */
+ setName("GLThread " + getId());
+ if (LOG_THREADS) {
+ Log.i("GLThread", "starting tid=" + getId());
+ }
+
try {
- try {
- sEglSemaphore.acquire();
- } catch (InterruptedException e) {
- return;
- }
guardedRun();
} catch (InterruptedException e) {
// fall thru and exit normally
} finally {
- sEglSemaphore.release();
+ sGLThreadManager.threadExiting(this);
+ }
+ }
+
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglLocked() {
+ if (mHaveEgl) {
+ mHaveEgl = false;
+ mEglHelper.destroySurface();
+ mEglHelper.finish();
+ sGLThreadManager.releaseEglSurface(this);
}
}
private void guardedRun() throws InterruptedException {
mEglHelper = new EglHelper();
try {
- mEglHelper.start();
-
GL10 gl = null;
boolean tellRendererSurfaceCreated = true;
boolean tellRendererSurfaceChanged = true;
@@ -992,49 +997,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) {
- mEglHelper.destroySurface();
- mEglHelper.finish();
- needStart = true;
- }
- while (needToWait()) {
+ 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) {
- mEglHelper.destroySurface();
+ stopEglLocked();
mWaitingForSurface = true;
- notify();
+ sGLThreadManager.notifyAll();
+ }
+ } else {
+ if (!mHaveEgl) {
+ if (sGLThreadManager.tryAcquireEglSurface(this)) {
+ mHaveEgl = true;
+ mEglHelper.start();
+ mRequestRender = true;
+ needStart = true;
+ }
}
}
- wait();
- }
- if (mDone) {
- break;
+
+ // 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();
}
- changed = mSizeChanged;
- w = mWidth;
- h = mHeight;
- mSizeChanged = false;
- mRequestRender = false;
- if (mHasSurface && mWaitingForSurface) {
- changed = true;
- mWaitingForSurface = false;
+ } // 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) {
- mEglHelper.start();
tellRendererSurfaceCreated = true;
changed = true;
}
@@ -1065,66 +1118,63 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
/*
* clean-up everything...
*/
- mEglHelper.destroySurface();
- mEglHelper.finish();
+ synchronized (sGLThreadManager) {
+ stopEglLocked();
+ }
}
}
- private boolean needToWait() {
- if (mDone) {
- return false;
- }
-
- if (mPaused || (! mHasSurface)) {
- return true;
- }
-
- if ((mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
- return false;
+ private boolean isDone() {
+ synchronized (sGLThreadManager) {
+ return mDone;
}
-
- 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) {
- notify();
+ sGLThreadManager.notifyAll();
}
}
}
public int getRenderMode() {
- synchronized(this) {
+ synchronized(sGLThreadManager) {
return mRenderMode;
}
}
public void requestRender() {
- synchronized(this) {
+ synchronized(sGLThreadManager) {
mRequestRender = true;
- notify();
+ sGLThreadManager.notifyAll();
}
}
public void surfaceCreated() {
- synchronized(this) {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceCreated tid=" + getId());
+ }
mHasSurface = true;
- notify();
+ sGLThreadManager.notifyAll();
}
}
public void surfaceDestroyed() {
- synchronized(this) {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceDestroyed tid=" + getId());
+ }
mHasSurface = false;
- notify();
- while(!mWaitingForSurface && isAlive()) {
+ sGLThreadManager.notifyAll();
+ while(!mWaitingForSurface && isAlive() && ! mDone) {
try {
- wait();
+ sGLThreadManager.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
@@ -1133,33 +1183,35 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
public void onPause() {
- synchronized (this) {
+ synchronized (sGLThreadManager) {
mPaused = true;
+ sGLThreadManager.notifyAll();
}
}
public void onResume() {
- synchronized (this) {
+ synchronized (sGLThreadManager) {
mPaused = false;
- notify();
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
}
}
public void onWindowResize(int w, int h) {
- synchronized (this) {
+ synchronized (sGLThreadManager) {
mWidth = w;
mHeight = h;
mSizeChanged = true;
- notify();
+ sGLThreadManager.notifyAll();
}
}
public void requestExitAndWait() {
// don't call this from GLThread thread or it is a guaranteed
// deadlock!
- synchronized(this) {
+ synchronized(sGLThreadManager) {
mDone = true;
- notify();
+ sGLThreadManager.notifyAll();
}
try {
join();
@@ -1175,6 +1227,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();
+ }
}
}
@@ -1188,14 +1244,20 @@ 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;
private boolean mWaitingForSurface;
+ private boolean mHaveEgl;
private int mWidth;
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;
@@ -1241,7 +1303,44 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
}
- private static final Semaphore sEglSemaphore = new Semaphore(1);
+ private static class GLThreadManager {
+
+ 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 once 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;
+ }
+ return false;
+ }
+
+ public synchronized void releaseEglSurface(GLThread thread) {
+ if (mEglOwner == thread) {
+ mEglOwner = null;
+ }
+ notifyAll();
+ }
+
+ private GLThread mEglOwner;
+ }
+
+ private static final GLThreadManager sGLThreadManager = new GLThreadManager();
private boolean mSizeChanged = true;
private GLThread mGLThread;