summaryrefslogtreecommitdiffstats
path: root/opengl/java/android/opengl/GLSurfaceView.java
diff options
context:
space:
mode:
Diffstat (limited to 'opengl/java/android/opengl/GLSurfaceView.java')
-rw-r--r--opengl/java/android/opengl/GLSurfaceView.java1059
1 files changed, 662 insertions, 397 deletions
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index dac3506..5a2e261 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -18,6 +18,7 @@ package android.opengl;
import java.io.Writer;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
@@ -29,13 +30,10 @@ import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Trace;
+import android.content.pm.ConfigurationInfo;
+import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.Choreographer;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -166,25 +164,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
private final static String TAG = "GLSurfaceView";
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 final static boolean TRACE_ENABLED = false;
-
- private final WeakReference<GLSurfaceView> mThisWeakRef =
- new WeakReference<GLSurfaceView>(this);
- private GLThread mGLThread;
- private Renderer mRenderer;
- private boolean mDetached;
- private EGLConfigChooser mEGLConfigChooser;
- private EGLContextFactory mEGLContextFactory;
- private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
- private GLWrapper mGLWrapper;
- private int mDebugFlags;
- private int mEGLContextClientVersion;
- private boolean mPreserveEGLContextOnPause;
-
/**
* The renderer only renders
* when the surface is created, or when {@link #requestRender} is called.
@@ -245,7 +229,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
if (mGLThread != null) {
// GLThread may still be running if this view was never
// attached to a window.
- mGLThread.quitSafely();
+ mGLThread.requestExitAndWait();
}
} finally {
super.finalize();
@@ -257,6 +241,13 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
// underlying surface is created and destroyed
SurfaceHolder holder = getHolder();
holder.addCallback(this);
+ // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
+ // this statement if back-porting to 2.2 or older:
+ // holder.setFormat(PixelFormat.RGB_565);
+ //
+ // setType is not needed for SDK 2.0 or newer. Uncomment this
+ // statement if back-porting this code to older SDKs.
+ // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
}
/**
@@ -355,16 +346,15 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
public void setRenderer(Renderer renderer) {
checkRenderThreadState();
if (mEGLConfigChooser == null) {
- mEGLConfigChooser = new SimpleEGLConfigChooser(true, mEGLContextClientVersion);
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true);
}
if (mEGLContextFactory == null) {
- mEGLContextFactory = new DefaultContextFactory(mEGLContextClientVersion);
+ mEGLContextFactory = new DefaultContextFactory();
}
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
mRenderer = renderer;
-
mGLThread = new GLThread(mThisWeakRef);
mGLThread.start();
}
@@ -430,7 +420,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* @param needDepth
*/
public void setEGLConfigChooser(boolean needDepth) {
- setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth, mEGLContextClientVersion));
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
}
/**
@@ -449,7 +439,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
int alphaSize, int depthSize, int stencilSize) {
setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
- blueSize, alphaSize, depthSize, stencilSize, mEGLContextClientVersion));
+ blueSize, alphaSize, depthSize, stencilSize));
}
/**
@@ -476,13 +466,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* If
* {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
* EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
- *
- * This method must be called before:
- * <ul>
- * <li>{@link #setEGLConfigChooser(boolean)}
- * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
- * </ul>
- *
* @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
*/
public void setEGLContextClientVersion(int version) {
@@ -507,13 +490,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* @see #RENDERMODE_WHEN_DIRTY
*/
public void setRenderMode(int renderMode) {
- switch (renderMode) {
- case RENDERMODE_WHEN_DIRTY:
- case RENDERMODE_CONTINUOUSLY:
- break;
- default:
- throw new IllegalArgumentException("renderMode");
- }
mGLThread.setRenderMode(renderMode);
}
@@ -605,13 +581,15 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
if (LOG_ATTACH_DETACH) {
Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
}
-
- final int renderMode = mGLThread != null ?
- mGLThread.mRenderMode : RENDERMODE_CONTINUOUSLY;
-
if (mDetached && (mRenderer != null)) {
+ int renderMode = RENDERMODE_CONTINUOUSLY;
+ if (mGLThread != null) {
+ renderMode = mGLThread.getRenderMode();
+ }
mGLThread = new GLThread(mThisWeakRef);
- mGLThread.setRenderMode(renderMode);
+ if (renderMode != RENDERMODE_CONTINUOUSLY) {
+ mGLThread.setRenderMode(renderMode);
+ }
mGLThread.start();
}
mDetached = false;
@@ -628,11 +606,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
Log.d(TAG, "onDetachedFromWindow");
}
if (mGLThread != null) {
- mGLThread.quitSafely();
- try {
- mGLThread.join();
- } catch (InterruptedException ex) {
- }
+ mGLThread.requestExitAndWait();
}
mDetached = true;
super.onDetachedFromWindow();
@@ -787,15 +761,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
}
- private static class DefaultContextFactory implements EGLContextFactory {
- private final int mEGLContextClientVersion;
-
- public DefaultContextFactory(int version) {
- mEGLContextClientVersion = version;
- }
+ private class DefaultContextFactory implements EGLContextFactory {
+ private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
- int[] attrib_list = {EGL14.EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
+ int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
EGL10.EGL_NONE };
return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
@@ -805,9 +775,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
public void destroyContext(EGL10 egl, EGLDisplay display,
EGLContext context) {
if (!egl.eglDestroyContext(display, context)) {
- Log.e(TAG, "display:" + display + " context: " + context);
+ Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
if (LOG_THREADS) {
- Log.d(TAG, "tid=" + Thread.currentThread().getId());
+ Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
}
EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
}
@@ -837,8 +807,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
try {
result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
} catch (IllegalArgumentException e) {
- // This exception indicates that the surfaceflinger surface
- // is not valid. This can happen if the surfaceflinger surface has
+ // This exception indicates that the surface flinger surface
+ // is not valid. This can happen if the surface flinger surface has
// been torn down, but the application has not yet been
// notified via SurfaceHolder.Callback.surfaceDestroyed.
// In theory the application should be notified first,
@@ -874,11 +844,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
}
- private static abstract class BaseConfigChooser
+ private abstract class BaseConfigChooser
implements EGLConfigChooser {
-
- public BaseConfigChooser(int[] configSpec, int version) {
- mConfigSpec = filterConfigSpec(configSpec, version);
+ public BaseConfigChooser(int[] configSpec) {
+ mConfigSpec = filterConfigSpec(configSpec);
}
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
@@ -912,8 +881,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
protected int[] mConfigSpec;
- private int[] filterConfigSpec(int[] configSpec, int version) {
- if (version != 2) {
+ private int[] filterConfigSpec(int[] configSpec) {
+ if (mEGLContextClientVersion != 2) {
return configSpec;
}
/* We know none of the subclasses define EGL_RENDERABLE_TYPE.
@@ -923,7 +892,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
int[] newConfigSpec = new int[len + 2];
System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
- newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT;
+ newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
newConfigSpec[len+1] = EGL10.EGL_NONE;
return newConfigSpec;
}
@@ -933,9 +902,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* Choose a configuration with exactly the specified r,g,b,a sizes,
* and at least the specified depth and stencil sizes.
*/
- private static class ComponentSizeChooser extends BaseConfigChooser {
+ private class ComponentSizeChooser extends BaseConfigChooser {
public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
- int alphaSize, int depthSize, int stencilSize, int version) {
+ int alphaSize, int depthSize, int stencilSize) {
super(new int[] {
EGL10.EGL_RED_SIZE, redSize,
EGL10.EGL_GREEN_SIZE, greenSize,
@@ -943,7 +912,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
EGL10.EGL_ALPHA_SIZE, alphaSize,
EGL10.EGL_DEPTH_SIZE, depthSize,
EGL10.EGL_STENCIL_SIZE, stencilSize,
- EGL10.EGL_NONE}, version);
+ EGL10.EGL_NONE});
mValue = new int[1];
mRedSize = redSize;
mGreenSize = greenSize;
@@ -951,7 +920,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
mAlphaSize = alphaSize;
mDepthSize = depthSize;
mStencilSize = stencilSize;
- }
+ }
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
@@ -962,10 +931,14 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
if ((d >= mDepthSize) && (s >= mStencilSize)) {
- int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
- int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
- int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
- int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
if ((r == mRedSize) && (g == mGreenSize)
&& (b == mBlueSize) && (a == mAlphaSize)) {
return config;
@@ -992,16 +965,16 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
protected int mAlphaSize;
protected int mDepthSize;
protected int mStencilSize;
- }
+ }
/**
* This class will choose a RGB_888 surface with
* or without a depth buffer.
*
*/
- private static class SimpleEGLConfigChooser extends ComponentSizeChooser {
- public SimpleEGLConfigChooser(boolean withDepthBuffer, int version) {
- super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0, version);
+ private class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer) {
+ super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
}
}
@@ -1018,9 +991,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* Initialize EGL for a given configuration spec.
* @param configSpec
*/
- public void initialize() {
+ public void start() {
if (LOG_EGL) {
- Log.d(TAG, "initialize() tid=" + Thread.currentThread().getId());
+ Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
}
/*
* Get an EGL instance
@@ -1061,7 +1034,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
throwEglException("createContext");
}
if (LOG_EGL) {
- Log.d(TAG, "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
+ Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
}
mEglSurface = null;
@@ -1075,7 +1048,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
*/
public boolean createSurface() {
if (LOG_EGL) {
- Log.d(TAG, "createSurface() tid=" + Thread.currentThread().getId());
+ Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
}
/*
* Check preconditions.
@@ -1110,7 +1083,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
- Log.e(TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+ Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
}
return false;
}
@@ -1124,9 +1097,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* Could not make the context current, probably because the underlying
* SurfaceView surface has been destroyed.
*/
- logEglErrorAsWarning(TAG, "eglMakeCurrent", mEgl.eglGetError());
- // we fall-through to "true" here because we do have a
- // valid EGLSurface at this point.
+ logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
+ return false;
}
return true;
@@ -1136,7 +1108,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
* Create a GL object for the current EGL context.
* @return
*/
- public GL createGL() {
+ GL createGL() {
+
GL gl = mEglContext.getGL();
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
@@ -1172,7 +1145,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
public void destroySurface() {
if (LOG_EGL) {
- Log.d(TAG, "destroySurface() tid=" + Thread.currentThread().getId());
+ Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
}
destroySurfaceImp();
}
@@ -1190,9 +1163,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
}
}
- public void terminate() {
+ public void finish() {
if (LOG_EGL) {
- Log.d(TAG, "terminate() tid=" + Thread.currentThread().getId());
+ Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
}
if (mEglContext != null) {
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
@@ -1214,7 +1187,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
public static void throwEglException(String function, int error) {
String message = formatEglError(function, error);
if (LOG_THREADS) {
- Log.e(TAG, "throwEglException tid=" + Thread.currentThread().getId() + " "
+ Log.e("EglHelper", "throwEglException tid=" + Thread.currentThread().getId() + " "
+ message);
}
throw new RuntimeException(message);
@@ -1234,423 +1207,584 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
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 HandlerThread {
- // only accessed from GLThread
- private GL10 mGLContext;
- private int mWidth;
- private int mHeight;
- private boolean mSizeChanged;
- // current render mode
- private int mRenderMode;
- // the EGLSurface exists but isn't working for some reason
- private boolean mEglSurfaceIsBad;
- // we have an EGLContext
- private boolean mHaveEglContext;
- // we have an EGLSurface
- private boolean mHaveEglSurface;
- // we have a Surface (i.e.: EGLNativeWindowType)
- private boolean mHasSurface;
- // activity is paused
- private boolean mPaused;
-
- // constants
- private EglHelper mEglHelper;
- private Handler mGLHandler;
- private Choreographer mChoreographer;
-
- // this runnable could be called often, so we keep an instance around
- class GetRenderModeRunnable implements Runnable {
- volatile public int renderMode;
- @Override
- public void run() {
- renderMode = doGetRenderMode();
- }
- };
-
- private final GetRenderModeRunnable mGetRenderModeRunnable = new GetRenderModeRunnable();
-
- /*
- * 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 final WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
-
- private final Runnable mExecuteDrawAction = new Runnable() {
- private int mTraceVsyncCounter = 0;
- @Override
- public void run() {
- if (TRACE_ENABLED) {
- Trace.traceCounter(Trace.TRACE_TAG_GRAPHICS,
- "GLSurfaceView VSYNC counter", (mTraceVsyncCounter++) & 0xf);
- }
- executeDraw();
- }
- };
-
- public GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
- super("GLThread", android.os.Process.THREAD_PRIORITY_DISPLAY);
- if (LOG_THREADS) {
- Log.d(TAG, "*** Starting GLThread ***");
- }
+ static class GLThread extends Thread {
+ GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
+ super();
mWidth = 0;
mHeight = 0;
+ mRequestRender = true;
mRenderMode = RENDERMODE_CONTINUOUSLY;
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
}
- private void readyToRun() {
- mChoreographer = Choreographer.getInstance();
- mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
- }
-
- @Override
- public void start() {
- super.start();
- // getLooper() blocks until the thread is running
- Looper looper = getLooper();
- mGLHandler = new Handler(looper);
- // don't return until the GLThread state has been initialized
- mGLHandler.runWithScissors(new Runnable() {
- @Override
- public void run() {
- readyToRun();
- }
- }, 0);
- }
-
@Override
public void run() {
+ setName("GLThread " + getId());
+ if (LOG_THREADS) {
+ Log.i("GLThread", "starting tid=" + getId());
+ }
+
try {
- super.run();
+ guardedRun();
+ } catch (InterruptedException e) {
+ // fall thru and exit normally
} finally {
- // by definition the GLThread is not running anymore here
- stopEglContext();
- stopEglSurface();
+ sGLThreadManager.threadExiting(this);
}
}
- // only call from the GLThread
- private void stopEglSurface() {
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglSurfaceLocked() {
if (mHaveEglSurface) {
- if (LOG_SURFACE) {
- Log.d(TAG, "releasing EGL surface because paused tid=" + getId());
- }
mHaveEglSurface = false;
mEglHelper.destroySurface();
}
}
- // only call from the GLThread
- private void stopEglContext() {
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglContextLocked() {
if (mHaveEglContext) {
- mEglHelper.terminate();
+ mEglHelper.finish();
mHaveEglContext = false;
- if (LOG_SURFACE) {
- Log.d(TAG, "releasing EGL context because paused tid=" + getId());
- }
+ 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;
+ }
- private void updateState() {
- final boolean wasAbleToDraw = isAbleToDraw();
- if (!isReadyToDraw()) {
- return;
- }
+ if (! mEventQueue.isEmpty()) {
+ event = mEventQueue.remove(0);
+ break;
+ }
- if (!mHaveEglSurface || mSizeChanged) {
- // create EGL context if needed
- boolean reportSurfaceCreated = false;
- if (!mHaveEglContext) {
- mEglHelper.initialize();
- mHaveEglContext = true;
- reportSurfaceCreated = true;
- }
+ // 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());
+ }
+ }
- // get the GL interface for the active EGLContext
- mGLContext = (GL10)mEglHelper.createGL();
+ // 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;
+ }
- // create EGL Surface
- mHaveEglSurface = mEglHelper.createSurface();
- mEglSurfaceIsBad = !mHaveEglSurface;
- mSizeChanged = false;
+ // Have we lost the EGL context?
+ if (lostEglContext) {
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ lostEglContext = false;
+ }
- // notify use of surface size change
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- if (reportSurfaceCreated) {
- if (LOG_RENDERER) {
- Log.d(TAG, "onSurfaceCreated");
+ // 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) {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ 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 (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
+ + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface
+ + " mPaused: " + mPaused
+ + " mHasSurface: " + mHasSurface
+ + " mSurfaceIsBad: " + mSurfaceIsBad
+ + " mWaitingForSurface: " + mWaitingForSurface
+ + " mWidth: " + mWidth
+ + " mHeight: " + mHeight
+ + " mRequestRender: " + mRequestRender
+ + " mRenderMode: " + mRenderMode);
+ }
+ sGLThreadManager.wait();
}
- view.mRenderer.onSurfaceCreated(mGLContext, mEglHelper.mEglConfig);
- }
+ } // end of synchronized(sGLThreadManager)
- if (LOG_RENDERER) {
- Log.d(TAG, "onSurfaceChanged(" + mWidth + ", " + mHeight + ")");
+ if (event != null) {
+ event.run();
+ event = null;
+ continue;
}
- view.mRenderer.onSurfaceChanged(mGLContext, mWidth, mHeight);
- }
- }
- // see if we should kick the rendering loop
- if (!wasAbleToDraw && isAbleToDraw()) {
- // we're now able to draw
- if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
- requestRender();
- }
- }
+ if (createEglSurface) {
+ if (LOG_SURFACE) {
+ Log.w("GLThread", "egl createSurface");
+ }
+ if (mEglHelper.createSurface()) {
+ synchronized(sGLThreadManager) {
+ mFinishedCreatingEglSurface = true;
+ sGLThreadManager.notifyAll();
+ }
+ } else {
+ synchronized(sGLThreadManager) {
+ mFinishedCreatingEglSurface = true;
+ mSurfaceIsBad = true;
+ sGLThreadManager.notifyAll();
+ }
+ continue;
+ }
+ createEglSurface = false;
+ }
- // By design, this is the only place in a GLThread thread where we wait().
- if (LOG_THREADS) {
- Log.d(TAG, "waiting tid=" + getId()
- + " mHaveEglContext: " + mHaveEglContext
- + " mHaveEglSurface: " + mHaveEglSurface
- + " mPaused: " + mPaused
- + " mHasSurface: " + mHasSurface
- + " mSurfaceIsBad: " + mEglSurfaceIsBad
- + " mWidth: " + mWidth
- + " mHeight: " + mHeight
- + " mRenderMode: " + mRenderMode);
- }
- }
+ if (createGlInterface) {
+ gl = (GL10) mEglHelper.createGL();
- private void executeDraw() {
- if (TRACE_ENABLED) {
- Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "executeDraw");
- }
+ sGLThreadManager.checkGLDriver(gl);
+ createGlInterface = false;
+ }
- if (isAbleToDraw()) {
- if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
- requestRender();
- }
+ if (createEglContext) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceCreated");
+ }
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+ }
+ createEglContext = false;
+ }
- if (LOG_RENDERER_DRAW_FRAME) {
- Log.d(TAG, "onDrawFrame tid=" + getId());
- }
+ if (sizeChanged) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
+ }
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mRenderer.onSurfaceChanged(gl, w, h);
+ }
+ sizeChanged = false;
+ }
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- if (view != null) {
- view.mRenderer.onDrawFrame(mGLContext);
+ if (LOG_RENDERER_DRAW_FRAME) {
+ Log.w("GLThread", "onDrawFrame tid=" + getId());
+ }
+ {
+ GLSurfaceView view = mGLSurfaceViewWeakRef.get();
+ if (view != null) {
+ view.mRenderer.onDrawFrame(gl);
+ }
+ }
int swapError = mEglHelper.swap();
switch (swapError) {
case EGL10.EGL_SUCCESS:
break;
case EGL11.EGL_CONTEXT_LOST:
if (LOG_SURFACE) {
- Log.d(TAG, "egl context lost tid=" + getId());
+ Log.i("GLThread", "egl context lost tid=" + getId());
}
- stopEglSurface();
- stopEglContext();
+ 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(TAG, "eglSwapBuffers", swapError);
- mEglSurfaceIsBad = true;
+ EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
+
+ synchronized(sGLThreadManager) {
+ mSurfaceIsBad = true;
+ sGLThreadManager.notifyAll();
+ }
break;
}
+
+ if (wantRenderNotification) {
+ doRenderNotification = true;
+ }
}
- }
- if (TRACE_ENABLED) {
- Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
+ } finally {
+ /*
+ * clean-up everything...
+ */
+ synchronized (sGLThreadManager) {
+ stopEglSurfaceLocked();
+ stopEglContextLocked();
+ }
}
}
- private boolean isAbleToDraw() {
- return mHaveEglContext && mHaveEglSurface && isReadyToDraw();
- }
-
- private boolean isReadyToDraw() {
- return (!mPaused) && mHasSurface && (!mEglSurfaceIsBad)
- && (mWidth > 0) && (mHeight > 0);
+ public boolean ableToDraw() {
+ return mHaveEglContext && mHaveEglSurface && readyToDraw();
}
- private boolean isEglContextReleasedWhenPausing() {
- GLSurfaceView view = mGLSurfaceViewWeakRef.get();
- return (view != null) ? !view.mPreserveEGLContextOnPause : false;
+ private boolean readyToDraw() {
+ return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
+ && (mWidth > 0) && (mHeight > 0)
+ && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
}
- public void queueEvent(Runnable r) {
- if (r == null) {
- throw new IllegalArgumentException("Runnable r must not be null");
+ public void setRenderMode(int renderMode) {
+ if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
+ throw new IllegalArgumentException("renderMode");
}
- mGLHandler.post(r);
- }
-
- /*
- * the call-backs below all run on the GLThread and implement state
- * changes of the GLSurfaceView and Activity life cycle.
- */
-
- private void doSurfaceCreated() {
- mHasSurface = true;
- updateState();
- }
-
- private void doSurfaceDestroyed() {
- if (mHasSurface) {
- if (LOG_SURFACE) {
- Log.d(TAG, "noticed surfaceView surface lost tid=" + getId());
- }
- stopEglSurface();
+ synchronized(sGLThreadManager) {
+ mRenderMode = renderMode;
+ sGLThreadManager.notifyAll();
}
- mHasSurface = false;
}
- private void doPause() {
- if (mPaused == false) {
- mPaused = true;
- stopEglSurface();
- // When pausing, optionally release the EGL Context:
- if (mHaveEglContext && isEglContextReleasedWhenPausing()) {
- stopEglContext();
- }
+ public int getRenderMode() {
+ synchronized(sGLThreadManager) {
+ return mRenderMode;
}
}
- private void doResume() {
- mPaused = false;
- updateState();
- if (mRenderMode == RENDERMODE_WHEN_DIRTY) {
- requestRender();
+ public void requestRender() {
+ synchronized(sGLThreadManager) {
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
}
}
- private void doWindowResize(int width, int height) {
- // we were not drawing yet. Update the window size and
- // state and attempt to draw a frame.
- mSizeChanged = (mWidth != width || mHeight != height);
- mWidth = width;
- mHeight = height;
- updateState();
- // we always (attempt to) draw a frame before returning
- executeDraw();
- }
-
- private void doSetRenderMode(int renderMode) {
- mRenderMode = renderMode;
- }
-
-
- private int doGetRenderMode() {
- return mRenderMode;
- }
-
- /*
- * the call-backs below run on the main UI thread, they just
- * wait while executing work on the GLThread.
- */
-
public void surfaceCreated() {
- mGLHandler.runWithScissors(new Runnable() {
- @Override
- public void run() {
- doSurfaceCreated();
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceCreated tid=" + getId());
}
- }, 0);
+ mHasSurface = true;
+ mFinishedCreatingEglSurface = false;
+ sGLThreadManager.notifyAll();
+ while (mWaitingForSurface
+ && !mFinishedCreatingEglSurface
+ && !mExited) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
}
public void surfaceDestroyed() {
- mGLHandler.runWithScissors(new Runnable() {
- @Override
- public void run() {
- doSurfaceDestroyed();
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ Log.i("GLThread", "surfaceDestroyed tid=" + getId());
}
- }, 0);
+ mHasSurface = false;
+ sGLThreadManager.notifyAll();
+ while((!mWaitingForSurface) && (!mExited)) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
}
public void onPause() {
- mGLHandler.runWithScissors(new Runnable() {
- @Override
- public void run() {
- doPause();
+ 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 (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
}
- }, 0);
+ }
}
public void onResume() {
- mGLHandler.runWithScissors(new Runnable() {
- @Override
- public void run() {
- doResume();
+ 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 (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
}
- }, 0);
+ }
}
public void onWindowResize(int w, int h) {
- final int width = w;
- final int height = h;
- mGLHandler.runWithScissors(new Runnable() {
- @Override
- public void run() {
- doWindowResize(width, height);
+ synchronized (sGLThreadManager) {
+ mWidth = w;
+ mHeight = h;
+ 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 (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
}
- }, 0);
+ }
}
- /*
- * the methods below can be called from any thread
- */
-
- public void requestRender() {
- if (mRenderMode == RENDERMODE_CONTINUOUSLY) {
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
- mExecuteDrawAction, null);
- } else {
- /*
- * in RENDERMODE_WHEN_DIRTY we schedule the draw callback
- * immediately because the developer is manager her
- * timing loop manually -- in particular she could be
- * using the Choreographer already.
- */
- mGLHandler.post(mExecuteDrawAction);
+ 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 (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
}
}
- public void setRenderMode(final int renderMode) {
- mGLHandler.runWithScissors(new Runnable() {
- @Override
- public void run() {
- doSetRenderMode(renderMode);
- }
- }, 0);
+ public void requestReleaseEglContextLocked() {
+ mShouldReleaseEglContext = true;
+ sGLThreadManager.notifyAll();
}
- public int getRenderMode() {
- mGLHandler.runWithScissors(mGetRenderModeRunnable, 0);
- return mGetRenderModeRunnable.renderMode;
+ /**
+ * 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(Runnable r) {
+ if (r == null) {
+ throw new IllegalArgumentException("r must not be null");
+ }
+ synchronized(sGLThreadManager) {
+ mEventQueue.add(r);
+ sGLThreadManager.notifyAll();
+ }
}
- } // class GLThread
+
+ // 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 mFinishedCreatingEglSurface;
+ 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<GLSurfaceView> mGLSurfaceViewWeakRef;
+
+ }
static class LogWriter extends Writer {
- @Override
- public void close() {
+
+ @Override public void close() {
flushBuilder();
}
- @Override
- public void flush() {
+ @Override public void flush() {
flushBuilder();
}
- @Override
- public void write(char[] buf, int offset, int count) {
- for (int i = 0; i < count; i++) {
+ @Override public void write(char[] buf, int offset, int count) {
+ for(int i = 0; i < count; i++) {
char c = buf[offset + i];
- if (c == '\n') {
+ if ( c == '\n') {
flushBuilder();
- } else {
+ }
+ else {
mBuilder.append(c);
}
}
@@ -1658,7 +1792,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
private void flushBuilder() {
if (mBuilder.length() > 0) {
- Log.v(TAG, mBuilder.toString());
+ Log.v("GLSurfaceView", mBuilder.toString());
mBuilder.delete(0, mBuilder.length());
}
}
@@ -1666,10 +1800,141 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
private StringBuilder mBuilder = new StringBuilder();
}
+
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(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(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(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(GL10 gl) {
+ if (! mGLESDriverCheckComplete) {
+ checkGLESVersion();
+ String renderer = gl.glGetString(GL10.GL_RENDERER);
+ if (mGLESVersion < kGLES_20) {
+ mMultipleGLESContextsAllowed =
+ ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);
+ notifyAll();
+ }
+ mLimitedGLESContexts = !mMultipleGLESContextsAllowed;
+ if (LOG_SURFACE) {
+ Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
+ + mMultipleGLESContextsAllowed
+ + " mLimitedGLESContexts = " + mLimitedGLESContexts);
+ }
+ mGLESDriverCheckComplete = true;
+ }
+ }
+
+ private void checkGLESVersion() {
+ if (! mGLESVersionCheckComplete) {
+ mGLESVersion = SystemProperties.getInt(
+ "ro.opengles.version",
+ ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
+ if (mGLESVersion >= kGLES_20) {
+ mMultipleGLESContextsAllowed = true;
+ }
+ if (LOG_SURFACE) {
+ Log.w(TAG, "checkGLESVersion mGLESVersion =" +
+ " " + mGLESVersion + " 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 int mGLESVersion;
+ 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 GLThread mEglOwner;
+ }
+
+ private static final GLThreadManager sGLThreadManager = new GLThreadManager();
+
+ private final WeakReference<GLSurfaceView> mThisWeakRef =
+ new WeakReference<GLSurfaceView>(this);
+ private GLThread mGLThread;
+ private Renderer mRenderer;
+ private boolean mDetached;
+ private EGLConfigChooser mEGLConfigChooser;
+ private EGLContextFactory mEGLContextFactory;
+ private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
+ private GLWrapper mGLWrapper;
+ private int mDebugFlags;
+ private int mEGLContextClientVersion;
+ private boolean mPreserveEGLContextOnPause;
}