diff --git a/alienblaster/project/src/GLSurfaceView_SDL.java b/alienblaster/project/src/GLSurfaceView_SDL.java index 19f748108..6567ddeff 100644 --- a/alienblaster/project/src/GLSurfaceView_SDL.java +++ b/alienblaster/project/src/GLSurfaceView_SDL.java @@ -18,10 +18,11 @@ fixed with a hammer and rasp to work with libSDL port */ // This string is autogenerated by ChangeAppSettings.sh, do not change spaces amount -package de.schwardtnet.alienblaster; +package com.demo.glxgears; 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; @@ -148,11 +149,6 @@ import android.view.SurfaceView; * */ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Callback { - private final static boolean LOG_THREADS = false; - private final static boolean LOG_SURFACE = false; - private final static boolean LOG_RENDERER = false; - // Work-around for bug 2263168 - private final static boolean DRAW_TWICE_AFTER_SIZE_CHANGED = true; /** * The renderer only renders * when the surface is created, or when {@link #requestRender} is called. @@ -212,6 +208,7 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call // underlying surface is created and destroyed SurfaceHolder holder = getHolder(); holder.addCallback(this); + holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); } /** @@ -279,49 +276,17 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call * @param renderer the renderer to use to perform OpenGL drawing. */ public void setRenderer(Renderer renderer) { - checkRenderThreadState(); + if (mGLThread != null) { + throw new IllegalStateException( + "setRenderer has already been called for this instance."); + } if (mEGLConfigChooser == null) { mEGLConfigChooser = new SimpleEGLConfigChooser(true); } - if (mEGLContextFactory == null) { - mEGLContextFactory = new DefaultContextFactory(); - } - if (mEGLWindowSurfaceFactory == null) { - mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); - } mGLThread = new GLThread(renderer); mGLThread.start(); } - /** - * Install a custom EGLContextFactory. - *
If this method is - * called, it must be called before {@link #setRenderer(Renderer)} - * is called. - *
- * If this method is not called, then by default - * a context will be created with no shared context and - * with a null attribute list. - */ - public void setEGLContextFactory(EGLContextFactory factory) { - checkRenderThreadState(); - mEGLContextFactory = factory; - } - - /** - * Install a custom EGLWindowSurfaceFactory. - *
If this method is - * called, it must be called before {@link #setRenderer(Renderer)} - * is called. - *
- * If this method is not called, then by default - * a window surface will be created with a null attribute list. - */ - public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { - checkRenderThreadState(); - mEGLWindowSurfaceFactory = factory; - } - /** * Install a custom EGLConfigChooser. *
If this method is @@ -334,7 +299,10 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call * @param configChooser */ public void setEGLConfigChooser(EGLConfigChooser configChooser) { - checkRenderThreadState(); + if (mGLThread != null) { + throw new IllegalStateException( + "setRenderer has already been called for this instance."); + } mEGLConfigChooser = configChooser; } @@ -630,54 +598,6 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call private SwapBuffersCallback mSwapBuffersCallback = null; } - /** - * An interface for customizing the eglCreateContext and eglDestroyContext calls. - *
- * This interface must be implemented by clients wishing to call - * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)} - */ - public interface EGLContextFactory { - EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); - void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); - } - - private static class DefaultContextFactory implements EGLContextFactory { - - public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { - return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, null); - } - - public void destroyContext(EGL10 egl, EGLDisplay display, - EGLContext context) { - egl.eglDestroyContext(display, context); - } - } - - /** - * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls. - *
- * This interface must be implemented by clients wishing to call
- * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
- */
- public interface EGLWindowSurfaceFactory {
- EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
- Object nativeWindow);
- void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
- }
-
- private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
-
- public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
- EGLConfig config, Object nativeWindow) {
- return egl.eglCreateWindowSurface(display, config, nativeWindow, null);
- }
-
- public void destroySurface(EGL10 egl, EGLDisplay display,
- EGLSurface surface) {
- egl.eglDestroySurface(display, surface);
- }
- }
-
/**
* An interface for choosing an EGLConfig configuration from a list of
* potential configurations.
@@ -756,27 +676,25 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call
EGLConfig closestConfig = null;
int closestDistance = 1000;
for(EGLConfig config : configs) {
+ 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 d = findConfigAttrib(egl, display, config,
EGL10.EGL_DEPTH_SIZE, 0);
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 distance = Math.abs(r - mRedSize)
- + Math.abs(g - mGreenSize)
- + Math.abs(b - mBlueSize)
- + Math.abs(a - mAlphaSize);
- if (distance < closestDistance) {
- closestDistance = distance;
- closestConfig = config;
- }
+ int distance = Math.abs(r - mRedSize)
+ + Math.abs(g - mGreenSize)
+ + Math.abs(b - mBlueSize) + Math.abs(a - mAlphaSize)
+ + Math.abs(d - mDepthSize) + Math.abs(s - mStencilSize);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestConfig = config;
}
}
return closestConfig;
@@ -852,10 +770,8 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call
* Create an OpenGL ES context. This must be done only once, an
* OpenGL context is a somewhat heavy object.
*/
- mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
- if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
- throw new RuntimeException("createContext failed");
- }
+ mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig,
+ EGL10.EGL_NO_CONTEXT, null);
mEglSurface = null;
}
@@ -869,7 +785,7 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call
* The window size has changed, so we need to create a new
* surface.
*/
- if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+ if (mEglSurface != null) {
/*
* Unbind and destroy the old EGL surface, if
@@ -877,26 +793,22 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call
*/
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
- mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
}
/*
* Create an EGL surface we can render into.
*/
- mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
- mEglDisplay, mEglConfig, holder);
-
- if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
- throwEglException("createWindowSurface");
- }
+ mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay,
+ mEglConfig, holder, null);
/*
* 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)) {
- throwEglException("eglMakeCurrent");
- }
+ mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+ mEglContext);
+
GL gl = mEglContext.getGL();
if (mGLWrapper != null) {
@@ -922,19 +834,16 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call
return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
}
- public void destroySurface() {
- if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+ public void finish() {
+ if (mEglSurface != null) {
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_CONTEXT);
- mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
mEglSurface = null;
}
- }
-
- public void finish() {
if (mEglContext != null) {
- mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
+ mEgl.eglDestroyContext(mEglDisplay, mEglContext);
mEglContext = null;
}
if (mEglDisplay != null) {
@@ -943,10 +852,6 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call
}
}
- private void throwEglException(String function) {
- throw new RuntimeException(function + " failed: " + mEgl.eglGetError());
- }
-
EGL10 mEgl;
EGLDisplay mEglDisplay;
EGLSurface mEglSurface;
@@ -959,181 +864,134 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call
* 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 implements SwapBuffersCallback {
GLThread(Renderer renderer) {
super();
+ mDone = false;
mWidth = 0;
mHeight = 0;
mRequestRender = true;
mRenderMode = RENDERMODE_CONTINUOUSLY;
mRenderer = renderer;
mRenderer.setSwapBuffersCallback(this);
+ setName("GLThread");
}
@Override
public void run() {
- setName("GLThread " + getId());
- if (LOG_THREADS) {
- 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 {
+ sEglSemaphore.acquire();
+ } catch (InterruptedException e) {
+ return;
+ }
- mEglHelper = new EglHelper();
- SwapBuffers();
- SwapBuffers();
+ mEglHelper = new EglHelper();
+ // mEglHelper.start();
+ mNeedStart = true;
+ SwapBuffers();
+ SwapBuffers();
- mRenderer.onDrawFrame(mGL);
+ mRenderer.onDrawFrame(mGL);
- synchronized (sGLThreadManager) {
- stopEglLocked();
- }
-
- 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.releaseEglSurfaceLocked(this);
- }
+
+ /*
+ synchronized (sGLThreadManager) {
+ stopEglLocked();
+ }
+ sGLThreadManager.threadExiting(this);
+ */
+
+ sEglSemaphore.release();
}
public boolean SwapBuffers() {
+
+ boolean tellRendererSurfaceCreated = false;
+ boolean tellRendererSurfaceChanged = false;
+
+ /*
+ * This is our main activity thread's loop, we go until
+ * asked to quit.
+ */
try {
-
- boolean createEglSurface = false;
- boolean sizeChanged = false;
- int w = 0;
- int h = 0;
- Runnable event = null;
- synchronized (sGLThreadManager) {
- while (true) {
- if (mShouldExit) {
- return false;
- }
-
- if (! mEventQueue.isEmpty()) {
- event = mEventQueue.remove(0);
- break;
- }
-
- // Do we need to release the EGL surface?
- if (mHaveEgl && mPaused) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
- }
- stopEglLocked();
- }
-
- // Have we lost the surface view surface?
- if ((! mHasSurface) && (! mWaitingForSurface)) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
- }
- if (mHaveEgl) {
- stopEglLocked();
- }
- mWaitingForSurface = true;
- 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();
- }
-
- // Ready to draw?
- if ((!mPaused) && mHasSurface
- && (mWidth > 0) && (mHeight > 0)
- && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
-
- // If we don't have an egl surface, try to acquire one.
- if ((! mHaveEgl) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) {
- mHaveEgl = true;
- mEglHelper.start();
- createEglSurface = true;
- sizeChanged = true;
- sGLThreadManager.notifyAll();
- }
-
- if (mHaveEgl) {
- if (mSizeChanged) {
- sizeChanged = true;
- w = mWidth;
- h = mHeight;
- if (DRAW_TWICE_AFTER_SIZE_CHANGED) {
- // We keep mRequestRender true so that we draw twice after the size changes.
- // (Once because of mSizeChanged, the second time because of mRequestRender.)
- // This forces the updated graphics onto the screen.
- } else {
- mRequestRender = false;
- }
- mSizeChanged = false;
- } else {
- 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());
- }
- sGLThreadManager.wait();
- }
- } // end of synchronized(sGLThreadManager)
-
- if (event != null) {
- event.run();
- event = null;
+ /*
+ * Update the asynchronous state (window size)
+ */
+ int w, h;
+ boolean changed;
+ synchronized (this) {
+ Runnable r;
+ while ((r = getEvent()) != null) {
+ r.run();
}
-
- if (createEglSurface) {
- mGL = (GL10) mEglHelper.createSurface(getHolder());
- if (LOG_RENDERER) {
- Log.w("GLThread", "onSurfaceCreated");
- }
- mRenderer.onSurfaceCreated(mGL, mEglHelper.mEglConfig);
- createEglSurface = false;
+ if (mPaused) {
+ mEglHelper.finish();
+ mNeedStart = true;
}
-
- if (sizeChanged) {
- if (LOG_RENDERER) {
- Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
- }
- mRenderer.onSurfaceChanged(mGL, w, h);
- sizeChanged = false;
+ while (needToWait()) {
+ wait();
}
-
- if (LOG_RENDERER) {
- Log.w("GLThread", "onDrawFrame");
- }
- if(!mEglHelper.swap()) {
- if (LOG_SURFACE) {
- Log.i("GLThread", "egl surface lost tid=" + getId());
- return false;
- }
+ if (mDone) {
+ return false;
}
+ changed = mSizeChanged;
+ w = mWidth;
+ h = mHeight;
+ mSizeChanged = false;
+ mRequestRender = false;
+ }
+ if (mNeedStart) {
+ mEglHelper.start();
+ tellRendererSurfaceCreated = true;
+ changed = true;
+ }
+ if (changed) {
+ mGL = (GL10) mEglHelper.createSurface(getHolder());
+ tellRendererSurfaceChanged = true;
+ }
+ if (tellRendererSurfaceCreated) {
+ mRenderer.onSurfaceCreated(mGL, mEglHelper.mEglConfig);
+ tellRendererSurfaceCreated = false;
+ }
+ if (tellRendererSurfaceChanged) {
+ mRenderer.onSurfaceChanged(mGL, w, h);
+ tellRendererSurfaceChanged = false;
+ }
+ /*
+ * Once we're done with GL, we need to call swapBuffers()
+ * to instruct the system to display the rendered frame
+ */
+ return mEglHelper.swap();
- } catch (InterruptedException e) {
+ } catch (java.lang.InterruptedException e) {
+ return false;
+ }
+ }
+
+ private boolean needToWait() {
+ if (mDone) {
return false;
}
+
+ if (mPaused || (! mHasSurface)) {
+ return true;
+ }
+
+ if ((mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
+ return false;
+ }
+
return true;
}
@@ -1141,90 +999,74 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call
if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
throw new IllegalArgumentException("renderMode");
}
- synchronized(sGLThreadManager) {
+ synchronized(this) {
mRenderMode = renderMode;
- sGLThreadManager.notifyAll();
+ if (renderMode == RENDERMODE_CONTINUOUSLY) {
+ notify();
+ }
}
}
public int getRenderMode() {
- synchronized(sGLThreadManager) {
+ synchronized(this) {
return mRenderMode;
}
}
public void requestRender() {
- synchronized(sGLThreadManager) {
+ synchronized(this) {
mRequestRender = true;
- sGLThreadManager.notifyAll();
+ notify();
}
}
public void surfaceCreated() {
- synchronized(sGLThreadManager) {
- if (LOG_THREADS) {
- Log.i("GLThread", "surfaceCreated tid=" + getId());
- }
+ synchronized(this) {
mHasSurface = true;
- sGLThreadManager.notifyAll();
+ notify();
}
}
public void surfaceDestroyed() {
- synchronized(sGLThreadManager) {
- if (LOG_THREADS) {
- Log.i("GLThread", "surfaceDestroyed tid=" + getId());
- }
+ synchronized(this) {
mHasSurface = false;
- sGLThreadManager.notifyAll();
- while((!mWaitingForSurface) && (!mExited)) {
- try {
- sGLThreadManager.wait();
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
+ notify();
}
}
public void onPause() {
- synchronized (sGLThreadManager) {
+ synchronized (this) {
mPaused = true;
- sGLThreadManager.notifyAll();
}
}
public void onResume() {
- synchronized (sGLThreadManager) {
+ synchronized (this) {
mPaused = false;
- mRequestRender = true;
- sGLThreadManager.notifyAll();
+ notify();
}
}
public void onWindowResize(int w, int h) {
- synchronized (sGLThreadManager) {
+ synchronized (this) {
mWidth = w;
mHeight = h;
mSizeChanged = true;
- mRequestRender = true;
- sGLThreadManager.notifyAll();
+ notify();
}
}
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();
- }
- }
+ synchronized(this) {
+ mDone = true;
+ notify();
+ }
+ try {
+ join();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
}
}
@@ -1233,33 +1075,33 @@ public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Call
* @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) {
+ synchronized(this) {
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 Runnable getEvent() {
+ synchronized(this) {
+ if (mEventQueue.size() > 0) {
+ return mEventQueue.remove(0);
+ }
+
+ }
+ return null;
+ }
+
+ 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 ArrayList
+ * A GLSurfaceView provides the following features:
+ *
+ *
+ * Typically you use GLSurfaceView by subclassing it and overriding one or more of the
+ * View system input event methods. If your application does not need to override event
+ * methods then GLSurfaceView can be used as-is. For the most part
+ * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
+ * For example, unlike a regular View, drawing is delegated to a separate Renderer object which
+ * is registered with the GLSurfaceView
+ * using the {@link #setRenderer(Renderer)} call.
+ *
+ *
+ *
+ * By default GLSurfaceView chooses an available surface that's closest to a 16-bit R5G6B5 surface
+ * with a 16-bit depth buffer and no stencil. If you would prefer a different surface (for example,
+ * if you do not need a depth buffer) you can override the default behavior by calling one of the
+ * setEGLConfigChooser methods.
+ *
+ *
+ *
+ *
+ *
+ *
+ * To handle an event you will typically subclass GLSurfaceView and override the
+ * appropriate method, just as you would with any other View. However, when handling
+ * the event, you may need to communicate with the Renderer object
+ * that's running in the rendering thread. You can do this using any
+ * standard Java cross-thread communication mechanism. In addition,
+ * one relatively easy way to communicate with your renderer is
+ * to call
+ * {@link #queueEvent(Runnable)}. For example:
+ *
+ * Wrapping is typically used for debugging purposes.
+ *
+ * The default value is null.
+ * @param glWrapper the new GLWrapper
+ */
+ public void setGLWrapper(GLWrapper glWrapper) {
+ mGLWrapper = glWrapper;
+ }
+
+ /**
+ * Set the debug flags to a new value. The value is
+ * constructed by OR-together zero or more
+ * of the DEBUG_CHECK_* constants. The debug flags take effect
+ * whenever a surface is created. The default value is zero.
+ * @param debugFlags the new debug flags
+ * @see #DEBUG_CHECK_GL_ERROR
+ * @see #DEBUG_LOG_GL_CALLS
+ */
+ public void setDebugFlags(int debugFlags) {
+ mDebugFlags = debugFlags;
+ }
+
+ /**
+ * Get the current value of the debug flags.
+ * @return the current value of the debug flags.
+ */
+ public int getDebugFlags() {
+ return mDebugFlags;
+ }
+
+ /**
+ * Set the renderer associated with this view. Also starts the thread that
+ * will call the renderer, which in turn causes the rendering to start.
+ * This method should be called once and only once in the life-cycle of
+ * a GLSurfaceView.
+ * The following GLSurfaceView methods can only be called before
+ * setRenderer is called:
+ *
+ * The following GLSurfaceView methods can only be called after
+ * setRenderer is called:
+ * If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ *
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose a config as close to 16-bit RGB as possible, with
+ * a depth buffer as close to 16 bits as possible.
+ * @param configChooser
+ */
+ public void setEGLConfigChooser(EGLConfigChooser configChooser) {
+ if (mGLThread != null) {
+ throw new IllegalStateException(
+ "setRenderer has already been called for this instance.");
+ }
+ mEGLConfigChooser = configChooser;
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * as close to 16-bit RGB as possible, with or without an optional depth
+ * buffer as close to 16-bits as possible.
+ * If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ *
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose a config as close to 16-bit RGB as possible, with
+ * a depth buffer as close to 16 bits as possible.
+ *
+ * @param needDepth
+ */
+ public void setEGLConfigChooser(boolean needDepth) {
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * with at least the specified component sizes, and as close
+ * to the specified component sizes as possible.
+ * If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ *
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose a config as close to 16-bit RGB as possible, with
+ * a depth buffer as close to 16 bits as possible.
+ *
+ */
+ public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
+ blueSize, alphaSize, depthSize, stencilSize));
+ }
+ /**
+ * Set the rendering mode. When renderMode is
+ * RENDERMODE_CONTINUOUSLY, the renderer is called
+ * repeatedly to re-render the scene. When renderMode
+ * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
+ * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
+ *
+ * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
+ * by allowing the GPU and CPU to idle when the view does not need to be updated.
+ *
+ * This method can only be called after {@link #setRenderer(Renderer)}
+ *
+ * @param renderMode one of the RENDERMODE_X constants
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public void setRenderMode(int renderMode) {
+ mGLThread.setRenderMode(renderMode);
+ }
+
+ /**
+ * Get the current rendering mode. May be called
+ * from any thread. Must not be called before a renderer has been set.
+ * @return the current rendering mode.
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public int getRenderMode() {
+ return mGLThread.getRenderMode();
+ }
+
+ /**
+ * Request that the renderer render a frame.
+ * This method is typically used when the render mode has been set to
+ * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
+ * May be called
+ * from any thread. Must not be called before a renderer has been set.
+ */
+ public void requestRender() {
+ mGLThread.requestRender();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceCreated(SurfaceHolder holder) {
+ mGLThread.surfaceCreated();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return
+ mGLThread.surfaceDestroyed();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ mGLThread.onWindowResize(w, h);
+ }
+
+ /**
+ * Inform the view that the activity is paused. The owner of this view must
+ * call this method when the activity is paused. Calling this method will
+ * pause the rendering thread.
+ * Must not be called before a renderer has been set.
+ */
+ public void onPause() {
+ mGLThread.onPause();
+ }
+
+ /**
+ * Inform the view that the activity is resumed. The owner of this view must
+ * call this method when the activity is resumed. Calling this method will
+ * recreate the OpenGL display and resume the rendering
+ * thread.
+ * Must not be called before a renderer has been set.
+ */
+ public void onResume() {
+ mGLThread.onResume();
+ }
+
+ /**
+ * Queue a runnable to be run on the GL rendering thread. This can be used
+ * to communicate with the Renderer on the rendering thread.
+ * Must not be called before a renderer has been set.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ mGLThread.queueEvent(r);
+ }
+
+ /**
+ * This method is used as part of the View class and is not normally
+ * called or subclassed by clients of GLSurfaceView.
+ * Must not be called before a renderer has been set.
+ */
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mGLThread.requestExitAndWait();
+ }
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * An interface used to wrap a GL interface.
+ * Typically
+ * used for implementing debugging and tracing on top of the default
+ * GL interface. You would typically use this by creating your own class
+ * that implemented all the GL methods by delegating to another GL instance.
+ * Then you could add your own behavior before or after calling the
+ * delegate. All the GLWrapper would do was instantiate and return the
+ * wrapper GL instance:
+ *
+ * The renderer is responsible for making OpenGL calls to render a frame.
+ *
+ * GLSurfaceView clients typically create their own classes that implement
+ * this interface, and then call {@link GLSurfaceView#setRenderer} to
+ * register the renderer with the GLSurfaceView.
+ *
+ *
+ *
+ * Called when the rendering thread
+ * starts and whenever the EGL context is lost. The context will typically
+ * be lost when the Android device awakes after going to sleep.
+ *
+ * Since this method is called at the beginning of rendering, as well as
+ * every time the EGL context is lost, this method is a convenient place to put
+ * code to create resources that need to be created when the rendering
+ * starts, and that need to be recreated when the EGL context is lost.
+ * Textures are an example of a resource that you might want to create
+ * here.
+ *
+ * Note that when the EGL context is lost, all OpenGL resources associated
+ * with that context will be automatically deleted. You do not need to call
+ * the corresponding "glDelete" methods such as glDeleteTextures to
+ * manually delete these lost resources.
+ *
+ * @param gl the GL interface. Use
+ * Called after the surface is created and whenever
+ * the OpenGL ES surface size changes.
+ *
+ * Typically you will set your viewport here. If your camera
+ * is fixed then you could also set your projection matrix here:
+ *
+ * This method is responsible for drawing the current frame.
+ *
+ * The implementation of this method typically looks like this:
+ *
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
+ */
+ public interface EGLConfigChooser {
+ /**
+ * Choose a configuration from the list. Implementors typically
+ * implement this method by calling
+ * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
+ * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
+ * @param egl the EGL10 for the current display.
+ * @param display the current display.
+ * @return the chosen configuration.
+ */
+ EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
+ }
+
+ private static abstract class BaseConfigChooser
+ implements EGLConfigChooser {
+ public BaseConfigChooser(int[] configSpec) {
+ mConfigSpec = configSpec;
+ }
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] num_config = new int[1];
+ egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config);
+
+ int numConfigs = num_config[0];
+
+ if (numConfigs <= 0) {
+ throw new IllegalArgumentException(
+ "No configs match configSpec");
+ }
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+ num_config);
+ EGLConfig config = chooseConfig(egl, display, configs);
+ if (config == null) {
+ throw new IllegalArgumentException("No config chosen");
+ }
+ return config;
+ }
+
+ abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs);
+
+ protected int[] mConfigSpec;
+ }
+
+ private static class ComponentSizeChooser extends BaseConfigChooser {
+ public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ super(new int[] {
+ EGL10.EGL_RED_SIZE, redSize,
+ EGL10.EGL_GREEN_SIZE, greenSize,
+ EGL10.EGL_BLUE_SIZE, blueSize,
+ EGL10.EGL_ALPHA_SIZE, alphaSize,
+ EGL10.EGL_DEPTH_SIZE, depthSize,
+ EGL10.EGL_STENCIL_SIZE, stencilSize,
+ EGL10.EGL_NONE});
+ mValue = new int[1];
+ mRedSize = redSize;
+ mGreenSize = greenSize;
+ mBlueSize = blueSize;
+ mAlphaSize = alphaSize;
+ mDepthSize = depthSize;
+ mStencilSize = stencilSize;
+ }
+
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ EGLConfig closestConfig = null;
+ int closestDistance = 1000;
+ for(EGLConfig config : configs) {
+ 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 d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ int distance = Math.abs(r - mRedSize)
+ + Math.abs(g - mGreenSize)
+ + Math.abs(b - mBlueSize) + Math.abs(a - mAlphaSize)
+ + Math.abs(d - mDepthSize) + Math.abs(s - mStencilSize);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestConfig = config;
+ }
+ }
+ return closestConfig;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private int[] mValue;
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ }
+
+ /**
+ * This class will choose a supported surface as close to
+ * RGB565 as possible, with or without a depth buffer.
+ *
+ */
+ private static class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer) {
+ super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0);
+ // Adjust target values. This way we'll accept a 4444 or
+ // 555 buffer if there's no 565 buffer available.
+ mRedSize = 5;
+ mGreenSize = 6;
+ mBlueSize = 5;
+ }
+ }
+
+ /**
+ * An EGL helper class.
+ */
+
+ private class EglHelper {
+ public EglHelper() {
+
+ }
+
+ /**
+ * Initialize EGL for a given configuration spec.
+ * @param configSpec
+ */
+ public void start(){
+ /*
+ * Get an EGL instance
+ */
+ mEgl = (EGL10) EGLContext.getEGL();
+
+ /*
+ * Get to the default display.
+ */
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ /*
+ * We can now initialize EGL for that display
+ */
+ int[] version = new int[2];
+ mEgl.eglInitialize(mEglDisplay, version);
+ mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
+
+ /*
+ * Create an OpenGL ES context. This must be done only once, an
+ * OpenGL context is a somewhat heavy object.
+ */
+ mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig,
+ EGL10.EGL_NO_CONTEXT, null);
+
+ mEglSurface = null;
+ }
+
+ /*
+ * React to the creation of a new surface by creating and returning an
+ * OpenGL interface that renders to that surface.
+ */
+ public GL createSurface(SurfaceHolder holder) {
+ /*
+ * The window size has changed, so we need to create a new
+ * surface.
+ */
+ if (mEglSurface != null) {
+
+ /*
+ * Unbind and destroy the old EGL surface, if
+ * there is one.
+ */
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+ }
+
+ /*
+ * Create an EGL surface we can render into.
+ */
+ mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay,
+ mEglConfig, holder, null);
+
+ /*
+ * Before we can issue GL commands, we need to make sure
+ * the context is current and bound to a surface.
+ */
+ mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+ mEglContext);
+
+
+ GL gl = mEglContext.getGL();
+ if (mGLWrapper != null) {
+ gl = mGLWrapper.wrap(gl);
+ }
+
+ return gl;
+ }
+
+ /**
+ * Display the current render surface.
+ * @return false if the context has been lost.
+ */
+ public boolean swap() {
+ mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+
+ /*
+ * Always check for EGL_CONTEXT_LOST, which means the context
+ * and all associated data were lost (For instance because
+ * the device went to sleep). We need to sleep until we
+ * get a new surface.
+ */
+ return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
+ }
+
+ public void finish() {
+ if (mEglSurface != null) {
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_CONTEXT);
+ mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+ mEglSurface = null;
+ }
+ if (mEglContext != null) {
+ mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+ mEglContext = null;
+ }
+ if (mEglDisplay != null) {
+ mEgl.eglTerminate(mEglDisplay);
+ mEglDisplay = null;
+ }
+ }
+
+ 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.
+ *
+ */
+ class GLThread extends Thread implements SwapBuffersCallback {
+ GLThread(Renderer renderer) {
+ super();
+ mDone = false;
+ mWidth = 0;
+ mHeight = 0;
+ mRequestRender = true;
+ mRenderMode = RENDERMODE_CONTINUOUSLY;
+ mRenderer = renderer;
+ mRenderer.setSwapBuffersCallback(this);
+ 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.
+ */
+ try {
+ sEglSemaphore.acquire();
+ } catch (InterruptedException e) {
+ return;
+ }
+
+ mEglHelper = new EglHelper();
+ // mEglHelper.start();
+ mNeedStart = true;
+ SwapBuffers();
+ SwapBuffers();
+
+ mRenderer.onDrawFrame(mGL);
+
+ mEglHelper.finish();
+
+ /*
+ synchronized (sGLThreadManager) {
+ stopEglLocked();
+ }
+ sGLThreadManager.threadExiting(this);
+ */
+
+ sEglSemaphore.release();
+ }
+
+ public boolean SwapBuffers() {
+
+ boolean tellRendererSurfaceCreated = false;
+ boolean tellRendererSurfaceChanged = false;
+
+ /*
+ * This is our main activity thread's loop, we go until
+ * asked to quit.
+ */
+ try {
+
+ /*
+ * Update the asynchronous state (window size)
+ */
+ int w, h;
+ boolean changed;
+ synchronized (this) {
+ Runnable r;
+ while ((r = getEvent()) != null) {
+ r.run();
+ }
+ if (mPaused) {
+ mEglHelper.finish();
+ mNeedStart = true;
+ }
+ while (needToWait()) {
+ wait();
+ }
+ if (mDone) {
+ return false;
+ }
+ changed = mSizeChanged;
+ w = mWidth;
+ h = mHeight;
+ mSizeChanged = false;
+ mRequestRender = false;
+ }
+ if (mNeedStart) {
+ mEglHelper.start();
+ tellRendererSurfaceCreated = true;
+ changed = true;
+ }
+ if (changed) {
+ mGL = (GL10) mEglHelper.createSurface(getHolder());
+ tellRendererSurfaceChanged = true;
+ }
+ if (tellRendererSurfaceCreated) {
+ mRenderer.onSurfaceCreated(mGL, mEglHelper.mEglConfig);
+ tellRendererSurfaceCreated = false;
+ }
+ if (tellRendererSurfaceChanged) {
+ mRenderer.onSurfaceChanged(mGL, w, h);
+ tellRendererSurfaceChanged = false;
+ }
+ /*
+ * Once we're done with GL, we need to call swapBuffers()
+ * to instruct the system to display the rendered frame
+ */
+ return mEglHelper.swap();
+
+ } catch (java.lang.InterruptedException e) {
+ return false;
+ }
+ }
+
+ private boolean needToWait() {
+ if (mDone) {
+ return false;
+ }
+
+ if (mPaused || (! mHasSurface)) {
+ 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) {
+ mRenderMode = renderMode;
+ if (renderMode == RENDERMODE_CONTINUOUSLY) {
+ notify();
+ }
+ }
+ }
+
+ public int getRenderMode() {
+ synchronized(this) {
+ return mRenderMode;
+ }
+ }
+
+ public void requestRender() {
+ synchronized(this) {
+ mRequestRender = true;
+ notify();
+ }
+ }
+
+ public void surfaceCreated() {
+ synchronized(this) {
+ mHasSurface = true;
+ notify();
+ }
+ }
+
+ public void surfaceDestroyed() {
+ synchronized(this) {
+ mHasSurface = false;
+ notify();
+ }
+ }
+
+ public void onPause() {
+ synchronized (this) {
+ mPaused = true;
+ }
+ }
+
+ public void onResume() {
+ synchronized (this) {
+ mPaused = false;
+ notify();
+ }
+ }
+
+ public void onWindowResize(int w, int h) {
+ synchronized (this) {
+ mWidth = w;
+ mHeight = h;
+ mSizeChanged = true;
+ notify();
+ }
+ }
+
+ public void requestExitAndWait() {
+ // don't call this from GLThread thread or it is a guaranteed
+ // deadlock!
+ synchronized(this) {
+ mDone = true;
+ notify();
+ }
+ try {
+ join();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ /**
+ * 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) {
+ synchronized(this) {
+ mEventQueue.add(r);
+ }
+ }
+
+ private Runnable getEvent() {
+ synchronized(this) {
+ if (mEventQueue.size() > 0) {
+ return mEventQueue.remove(0);
+ }
+
+ }
+ return null;
+ }
+
+ private boolean mDone;
+ private boolean mPaused;
+ private boolean mHasSurface;
+ private int mWidth;
+ private int mHeight;
+ private int mRenderMode;
+ private boolean mRequestRender;
+ private Renderer mRenderer;
+ private ArrayList
+ * A GLSurfaceView provides the following features:
+ *
+ *
+ * Typically you use GLSurfaceView by subclassing it and overriding one or more of the
+ * View system input event methods. If your application does not need to override event
+ * methods then GLSurfaceView can be used as-is. For the most part
+ * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
+ * For example, unlike a regular View, drawing is delegated to a separate Renderer object which
+ * is registered with the GLSurfaceView
+ * using the {@link #setRenderer(Renderer)} call.
+ *
+ *
+ *
+ * By default GLSurfaceView chooses an available surface that's closest to a 16-bit R5G6B5 surface
+ * with a 16-bit depth buffer and no stencil. If you would prefer a different surface (for example,
+ * if you do not need a depth buffer) you can override the default behavior by calling one of the
+ * setEGLConfigChooser methods.
+ *
+ *
+ *
+ *
+ *
+ *
+ * To handle an event you will typically subclass GLSurfaceView and override the
+ * appropriate method, just as you would with any other View. However, when handling
+ * the event, you may need to communicate with the Renderer object
+ * that's running in the rendering thread. You can do this using any
+ * standard Java cross-thread communication mechanism. In addition,
+ * one relatively easy way to communicate with your renderer is
+ * to call
+ * {@link #queueEvent(Runnable)}. For example:
+ *
+ * Wrapping is typically used for debugging purposes.
+ *
+ * The default value is null.
+ * @param glWrapper the new GLWrapper
+ */
+ public void setGLWrapper(GLWrapper glWrapper) {
+ mGLWrapper = glWrapper;
+ }
+
+ /**
+ * Set the debug flags to a new value. The value is
+ * constructed by OR-together zero or more
+ * of the DEBUG_CHECK_* constants. The debug flags take effect
+ * whenever a surface is created. The default value is zero.
+ * @param debugFlags the new debug flags
+ * @see #DEBUG_CHECK_GL_ERROR
+ * @see #DEBUG_LOG_GL_CALLS
+ */
+ public void setDebugFlags(int debugFlags) {
+ mDebugFlags = debugFlags;
+ }
+
+ /**
+ * Get the current value of the debug flags.
+ * @return the current value of the debug flags.
+ */
+ public int getDebugFlags() {
+ return mDebugFlags;
+ }
+
+ /**
+ * Set the renderer associated with this view. Also starts the thread that
+ * will call the renderer, which in turn causes the rendering to start.
+ * This method should be called once and only once in the life-cycle of
+ * a GLSurfaceView.
+ * The following GLSurfaceView methods can only be called before
+ * setRenderer is called:
+ *
+ * The following GLSurfaceView methods can only be called after
+ * setRenderer is called:
+ * If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ *
+ * If this method is not called, then by default
+ * a context will be created with no shared context and
+ * with a null attribute list.
+ */
+ public void setEGLContextFactory(EGLContextFactory factory) {
+ checkRenderThreadState();
+ mEGLContextFactory = factory;
+ }
+
+ /**
+ * Install a custom EGLWindowSurfaceFactory.
+ * If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ *
+ * If this method is not called, then by default
+ * a window surface will be created with a null attribute list.
+ */
+ public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
+ checkRenderThreadState();
+ mEGLWindowSurfaceFactory = factory;
+ }
+
+ /**
+ * Install a custom EGLConfigChooser.
+ * If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ *
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose a config as close to 16-bit RGB as possible, with
+ * a depth buffer as close to 16 bits as possible.
+ * @param configChooser
+ */
+ public void setEGLConfigChooser(EGLConfigChooser configChooser) {
+ checkRenderThreadState();
+ mEGLConfigChooser = configChooser;
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * as close to 16-bit RGB as possible, with or without an optional depth
+ * buffer as close to 16-bits as possible.
+ * If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ *
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose a config as close to 16-bit RGB as possible, with
+ * a depth buffer as close to 16 bits as possible.
+ *
+ * @param needDepth
+ */
+ public void setEGLConfigChooser(boolean needDepth) {
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * with at least the specified component sizes, and as close
+ * to the specified component sizes as possible.
+ * If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ *
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose a config as close to 16-bit RGB as possible, with
+ * a depth buffer as close to 16 bits as possible.
+ *
+ */
+ public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
+ blueSize, alphaSize, depthSize, stencilSize));
+ }
+ /**
+ * Set the rendering mode. When renderMode is
+ * RENDERMODE_CONTINUOUSLY, the renderer is called
+ * repeatedly to re-render the scene. When renderMode
+ * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
+ * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
+ *
+ * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
+ * by allowing the GPU and CPU to idle when the view does not need to be updated.
+ *
+ * This method can only be called after {@link #setRenderer(Renderer)}
+ *
+ * @param renderMode one of the RENDERMODE_X constants
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public void setRenderMode(int renderMode) {
+ mGLThread.setRenderMode(renderMode);
+ }
+
+ /**
+ * Get the current rendering mode. May be called
+ * from any thread. Must not be called before a renderer has been set.
+ * @return the current rendering mode.
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public int getRenderMode() {
+ return mGLThread.getRenderMode();
+ }
+
+ /**
+ * Request that the renderer render a frame.
+ * This method is typically used when the render mode has been set to
+ * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
+ * May be called
+ * from any thread. Must not be called before a renderer has been set.
+ */
+ public void requestRender() {
+ mGLThread.requestRender();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceCreated(SurfaceHolder holder) {
+ mGLThread.surfaceCreated();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return
+ mGLThread.surfaceDestroyed();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ mGLThread.onWindowResize(w, h);
+ }
+
+ /**
+ * Inform the view that the activity is paused. The owner of this view must
+ * call this method when the activity is paused. Calling this method will
+ * pause the rendering thread.
+ * Must not be called before a renderer has been set.
+ */
+ public void onPause() {
+ mGLThread.onPause();
+ }
+
+ /**
+ * Inform the view that the activity is resumed. The owner of this view must
+ * call this method when the activity is resumed. Calling this method will
+ * recreate the OpenGL display and resume the rendering
+ * thread.
+ * Must not be called before a renderer has been set.
+ */
+ public void onResume() {
+ mGLThread.onResume();
+ }
+
+ /**
+ * Queue a runnable to be run on the GL rendering thread. This can be used
+ * to communicate with the Renderer on the rendering thread.
+ * Must not be called before a renderer has been set.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ mGLThread.queueEvent(r);
+ }
+
+ /**
+ * This method is used as part of the View class and is not normally
+ * called or subclassed by clients of GLSurfaceView.
+ * Must not be called before a renderer has been set.
+ */
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mGLThread.requestExitAndWait();
+ }
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * An interface used to wrap a GL interface.
+ * Typically
+ * used for implementing debugging and tracing on top of the default
+ * GL interface. You would typically use this by creating your own class
+ * that implemented all the GL methods by delegating to another GL instance.
+ * Then you could add your own behavior before or after calling the
+ * delegate. All the GLWrapper would do was instantiate and return the
+ * wrapper GL instance:
+ *
+ * The renderer is responsible for making OpenGL calls to render a frame.
+ *
+ * GLSurfaceView clients typically create their own classes that implement
+ * this interface, and then call {@link GLSurfaceView#setRenderer} to
+ * register the renderer with the GLSurfaceView.
+ *
+ *
+ *
+ * Called when the rendering thread
+ * starts and whenever the EGL context is lost. The context will typically
+ * be lost when the Android device awakes after going to sleep.
+ *
+ * Since this method is called at the beginning of rendering, as well as
+ * every time the EGL context is lost, this method is a convenient place to put
+ * code to create resources that need to be created when the rendering
+ * starts, and that need to be recreated when the EGL context is lost.
+ * Textures are an example of a resource that you might want to create
+ * here.
+ *
+ * Note that when the EGL context is lost, all OpenGL resources associated
+ * with that context will be automatically deleted. You do not need to call
+ * the corresponding "glDelete" methods such as glDeleteTextures to
+ * manually delete these lost resources.
+ *
+ * @param gl the GL interface. Use
+ * Called after the surface is created and whenever
+ * the OpenGL ES surface size changes.
+ *
+ * Typically you will set your viewport here. If your camera
+ * is fixed then you could also set your projection matrix here:
+ *
+ * This method is responsible for drawing the current frame.
+ *
+ * The implementation of this method typically looks like this:
+ *
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)}
+ */
+ public interface EGLContextFactory {
+ EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
+ void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
+ }
+
+ private static class DefaultContextFactory implements EGLContextFactory {
+
+ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
+ return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, null);
+ }
+
+ public void destroyContext(EGL10 egl, EGLDisplay display,
+ EGLContext context) {
+ egl.eglDestroyContext(display, context);
+ }
+ }
+
+ /**
+ * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
+ *
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
+ */
+ public interface EGLWindowSurfaceFactory {
+ EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
+ Object nativeWindow);
+ void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
+ }
+
+ private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
+
+ public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
+ EGLConfig config, Object nativeWindow) {
+ return egl.eglCreateWindowSurface(display, config, nativeWindow, null);
+ }
+
+ public void destroySurface(EGL10 egl, EGLDisplay display,
+ EGLSurface surface) {
+ egl.eglDestroySurface(display, surface);
+ }
+ }
+
+ /**
+ * An interface for choosing an EGLConfig configuration from a list of
+ * potential configurations.
+ *
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
+ */
+ public interface EGLConfigChooser {
+ /**
+ * Choose a configuration from the list. Implementors typically
+ * implement this method by calling
+ * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
+ * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
+ * @param egl the EGL10 for the current display.
+ * @param display the current display.
+ * @return the chosen configuration.
+ */
+ EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
+ }
+
+ private static abstract class BaseConfigChooser
+ implements EGLConfigChooser {
+ public BaseConfigChooser(int[] configSpec) {
+ mConfigSpec = configSpec;
+ }
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] num_config = new int[1];
+ egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config);
+
+ int numConfigs = num_config[0];
+
+ if (numConfigs <= 0) {
+ throw new IllegalArgumentException(
+ "No configs match configSpec");
+ }
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+ num_config);
+ EGLConfig config = chooseConfig(egl, display, configs);
+ if (config == null) {
+ throw new IllegalArgumentException("No config chosen");
+ }
+ return config;
+ }
+
+ abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs);
+
+ protected int[] mConfigSpec;
+ }
+
+ private static class ComponentSizeChooser extends BaseConfigChooser {
+ public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ super(new int[] {
+ EGL10.EGL_RED_SIZE, redSize,
+ EGL10.EGL_GREEN_SIZE, greenSize,
+ EGL10.EGL_BLUE_SIZE, blueSize,
+ EGL10.EGL_ALPHA_SIZE, alphaSize,
+ EGL10.EGL_DEPTH_SIZE, depthSize,
+ EGL10.EGL_STENCIL_SIZE, stencilSize,
+ EGL10.EGL_NONE});
+ mValue = new int[1];
+ mRedSize = redSize;
+ mGreenSize = greenSize;
+ mBlueSize = blueSize;
+ mAlphaSize = alphaSize;
+ mDepthSize = depthSize;
+ mStencilSize = stencilSize;
+ }
+
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ EGLConfig closestConfig = null;
+ int closestDistance = 1000;
+ for(EGLConfig config : configs) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ 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 distance = Math.abs(r - mRedSize)
+ + Math.abs(g - mGreenSize)
+ + Math.abs(b - mBlueSize)
+ + Math.abs(a - mAlphaSize);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestConfig = config;
+ }
+ }
+ }
+ return closestConfig;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private int[] mValue;
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ }
+
+ /**
+ * This class will choose a supported surface as close to
+ * RGB565 as possible, with or without a depth buffer.
+ *
+ */
+ private static class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer) {
+ super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0);
+ // Adjust target values. This way we'll accept a 4444 or
+ // 555 buffer if there's no 565 buffer available.
+ mRedSize = 5;
+ mGreenSize = 6;
+ mBlueSize = 5;
+ }
+ }
+
+ /**
+ * An EGL helper class.
+ */
+
+ private class EglHelper {
+ public EglHelper() {
+
+ }
+
+ /**
+ * Initialize EGL for a given configuration spec.
+ * @param configSpec
+ */
+ public void start(){
+ /*
+ * Get an EGL instance
+ */
+ mEgl = (EGL10) EGLContext.getEGL();
+
+ /*
+ * Get to the default display.
+ */
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ /*
+ * We can now initialize EGL for that display
+ */
+ int[] version = new int[2];
+ mEgl.eglInitialize(mEglDisplay, version);
+ mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
+
+ /*
+ * Create an OpenGL ES context. This must be done only once, an
+ * OpenGL context is a somewhat heavy object.
+ */
+ mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
+ if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
+ throw new RuntimeException("createContext failed");
+ }
+
+ mEglSurface = null;
+ }
+
+ /*
+ * React to the creation of a new surface by creating and returning an
+ * OpenGL interface that renders to that surface.
+ */
+ public GL createSurface(SurfaceHolder holder) {
+ /*
+ * The window size has changed, so we need to create a new
+ * surface.
+ */
+ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+
+ /*
+ * Unbind and destroy the old EGL surface, if
+ * there is one.
+ */
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ }
+
+ /*
+ * Create an EGL surface we can render into.
+ */
+ mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
+ mEglDisplay, mEglConfig, holder);
+
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ throwEglException("createWindowSurface");
+ }
+
+ /*
+ * 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)) {
+ throwEglException("eglMakeCurrent");
+ }
+
+ GL gl = mEglContext.getGL();
+ if (mGLWrapper != null) {
+ gl = mGLWrapper.wrap(gl);
+ }
+
+ return gl;
+ }
+
+ /**
+ * Display the current render surface.
+ * @return false if the context has been lost.
+ */
+ public boolean swap() {
+ mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+
+ /*
+ * Always check for EGL_CONTEXT_LOST, which means the context
+ * and all associated data were lost (For instance because
+ * the device went to sleep). We need to sleep until we
+ * get a new surface.
+ */
+ return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
+ }
+
+ public void destroySurface() {
+ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_CONTEXT);
+ mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ mEglSurface = null;
+ }
+ }
+
+ public void finish() {
+ if (mEglContext != null) {
+ mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
+ mEglContext = null;
+ }
+ if (mEglDisplay != null) {
+ mEgl.eglTerminate(mEglDisplay);
+ mEglDisplay = null;
+ }
+ }
+
+ private void throwEglException(String function) {
+ throw new RuntimeException(function + " failed: " + mEgl.eglGetError());
+ }
+
+ 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.
+ *
+ */
+ class GLThread extends Thread implements SwapBuffersCallback {
+ GLThread(Renderer renderer) {
+ super();
+ mWidth = 0;
+ mHeight = 0;
+ mRequestRender = true;
+ mRenderMode = RENDERMODE_CONTINUOUSLY;
+ mRenderer = renderer;
+ mRenderer.setSwapBuffersCallback(this);
+ }
+
+ @Override
+ public void run() {
+ setName("GLThread " + getId());
+ if (LOG_THREADS) {
+ Log.i("GLThread", "starting tid=" + getId());
+ }
+
+ mEglHelper = new EglHelper();
+ SwapBuffers();
+ SwapBuffers();
+
+ mRenderer.onDrawFrame(mGL);
+
+ synchronized (sGLThreadManager) {
+ stopEglLocked();
+ }
+
+ 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.releaseEglSurfaceLocked(this);
+ }
+ }
+
+ public boolean SwapBuffers() {
+ try {
+
+ boolean createEglSurface = false;
+ boolean sizeChanged = false;
+ int w = 0;
+ int h = 0;
+ Runnable event = null;
+
+ synchronized (sGLThreadManager) {
+ while (true) {
+ if (mShouldExit) {
+ return false;
+ }
+
+ if (! mEventQueue.isEmpty()) {
+ event = mEventQueue.remove(0);
+ break;
+ }
+
+ // Do we need to release the EGL surface?
+ if (mHaveEgl && mPaused) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
+ }
+ stopEglLocked();
+ }
+
+ // Have we lost the surface view surface?
+ if ((! mHasSurface) && (! mWaitingForSurface)) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
+ }
+ if (mHaveEgl) {
+ stopEglLocked();
+ }
+ mWaitingForSurface = true;
+ 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();
+ }
+
+ // Ready to draw?
+ if ((!mPaused) && mHasSurface
+ && (mWidth > 0) && (mHeight > 0)
+ && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
+
+ // If we don't have an egl surface, try to acquire one.
+ if ((! mHaveEgl) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) {
+ mHaveEgl = true;
+ mEglHelper.start();
+ createEglSurface = true;
+ sizeChanged = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ if (mHaveEgl) {
+ if (mSizeChanged) {
+ sizeChanged = true;
+ w = mWidth;
+ h = mHeight;
+ if (DRAW_TWICE_AFTER_SIZE_CHANGED) {
+ // We keep mRequestRender true so that we draw twice after the size changes.
+ // (Once because of mSizeChanged, the second time because of mRequestRender.)
+ // This forces the updated graphics onto the screen.
+ } else {
+ mRequestRender = false;
+ }
+ mSizeChanged = false;
+ } else {
+ 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());
+ }
+ sGLThreadManager.wait();
+ }
+ } // end of synchronized(sGLThreadManager)
+
+ if (event != null) {
+ event.run();
+ event = null;
+ }
+
+ if (createEglSurface) {
+ mGL = (GL10) mEglHelper.createSurface(getHolder());
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceCreated");
+ }
+ mRenderer.onSurfaceCreated(mGL, mEglHelper.mEglConfig);
+ createEglSurface = false;
+ }
+
+ if (sizeChanged) {
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
+ }
+ mRenderer.onSurfaceChanged(mGL, w, h);
+ sizeChanged = false;
+ }
+
+ if (LOG_RENDERER) {
+ Log.w("GLThread", "onDrawFrame");
+ }
+ if(!mEglHelper.swap()) {
+ if (LOG_SURFACE) {
+ Log.i("GLThread", "egl surface lost tid=" + getId());
+ return false;
+ }
+ }
+
+ } catch (InterruptedException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public void setRenderMode(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();
+ }
+ }
+
+ 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 (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onPause() {
+ synchronized (sGLThreadManager) {
+ mPaused = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void onResume() {
+ synchronized (sGLThreadManager) {
+ mPaused = false;
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void onWindowResize(int w, int h) {
+ synchronized (sGLThreadManager) {
+ mWidth = w;
+ mHeight = h;
+ mSizeChanged = true;
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ 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();
+ }
+ }
+ }
+ }
+
+ /**
+ * 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();
+ }
+ }
+
+ // 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 mPaused;
+ private boolean mHasSurface;
+ private boolean mWaitingForSurface;
+ private boolean mHaveEgl;
+ private int mWidth;
+ private int mHeight;
+ private int mRenderMode;
+ private boolean mRequestRender;
+ private ArrayList
+ *
+ *
+ * Using GLSurfaceView
+ * Initializing GLSurfaceView
+ * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
+ * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
+ * more of these methods before calling setRenderer:
+ *
+ *
+ * Choosing an EGL Configuration
+ * A given Android device may support multiple possible types of drawing surfaces.
+ * The available surfaces may differ in how may channels of data are present, as
+ * well as how many bits are allocated to each channel. Therefore, the first thing
+ * GLSurfaceView has to do when starting to render is choose what type of surface to use.
+ * Debug Behavior
+ * You can optionally modify the behavior of GLSurfaceView by calling
+ * one or more of the debugging methods {@link #setDebugFlags(int)},
+ * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
+ * typically they are called before setRenderer so that they take effect immediately.
+ * Setting a Renderer
+ * Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
+ * The renderer is
+ * responsible for doing the actual OpenGL rendering.
+ * Rendering Mode
+ * Once the renderer is set, you can control whether the renderer draws
+ * continuously or on-demand by calling
+ * {@link #setRenderMode}. The default is continuous rendering.
+ * Activity Life-cycle
+ * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients
+ * are required to call {@link #onPause()} when the activity pauses and
+ * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to
+ * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
+ * the OpenGL display.
+ * Handling events
+ *
+ * class MyGLSurfaceView extends GLSurfaceView {
+ *
+ * private MyRenderer mMyRenderer;
+ *
+ * public void start() {
+ * mMyRenderer = ...;
+ * setRenderer(mMyRenderer);
+ * }
+ *
+ * public boolean onKeyDown(int keyCode, KeyEvent event) {
+ * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ * queueEvent(new Runnable() {
+ * // This method will be called on the rendering
+ * // thread:
+ * public void run() {
+ * mMyRenderer.handleDpadCenter();
+ * }});
+ * return true;
+ * }
+ * return super.onKeyDown(keyCode, event);
+ * }
+ * }
+ *
+ *
+ */
+public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Callback {
+ /**
+ * The renderer only renders
+ * when the surface is created, or when {@link #requestRender} is called.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ */
+ public final static int RENDERMODE_WHEN_DIRTY = 0;
+ /**
+ * The renderer is called
+ * continuously to re-render the scene.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ * @see #requestRender()
+ */
+ public final static int RENDERMODE_CONTINUOUSLY = 1;
+
+ /**
+ * Check glError() after every GL call and throw an exception if glError indicates
+ * that an error has occurred. This can be used to help track down which OpenGL ES call
+ * is causing an error.
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_CHECK_GL_ERROR = 1;
+
+ /**
+ * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_LOG_GL_CALLS = 2;
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView_SDL(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView_SDL(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ }
+
+ /**
+ * Set the glWrapper. If the glWrapper is not null, its
+ * {@link GLWrapper#wrap(GL)} method is called
+ * whenever a surface is created. A GLWrapper can be used to wrap
+ * the GL object that's passed to the renderer. Wrapping a GL
+ * object enables examining and modifying the behavior of the
+ * GL calls made by the renderer.
+ *
+ *
+ *
+ *
+ *
+ * @param renderer the renderer to use to perform OpenGL drawing.
+ */
+ public void setRenderer(Renderer renderer) {
+ if (mGLThread != null) {
+ throw new IllegalStateException(
+ "setRenderer has already been called for this instance.");
+ }
+ if (mEGLConfigChooser == null) {
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true);
+ }
+ mGLThread = new GLThread(renderer);
+ mGLThread.start();
+ }
+
+ /**
+ * Install a custom EGLConfigChooser.
+ *
+ * class MyGLWrapper implements GLWrapper {
+ * GL wrap(GL gl) {
+ * return new MyGLImplementation(gl);
+ * }
+ * static class MyGLImplementation implements GL,GL10,GL11,... {
+ * ...
+ * }
+ * }
+ *
+ * @see #setGLWrapper(GLWrapper)
+ */
+ public interface GLWrapper {
+ /**
+ * Wraps a gl interface in another gl interface.
+ * @param gl a GL interface that is to be wrapped.
+ * @return either the input argument or another GL object that wraps the input argument.
+ */
+ GL wrap(GL gl);
+ }
+
+ /**
+ * A generic renderer interface.
+ * Threading
+ * The renderer will be called on a separate thread, so that rendering
+ * performance is decoupled from the UI thread. Clients typically need to
+ * communicate with the renderer from the UI thread, because that's where
+ * input events are received. Clients can communicate using any of the
+ * standard Java techniques for cross-thread communication, or they can
+ * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method.
+ * EGL Context Lost
+ * There are situations where the EGL rendering context will be lost. This
+ * typically happens when device wakes up after going to sleep. When
+ * the EGL context is lost, all OpenGL resources (such as textures) that are
+ * associated with that context will be automatically deleted. In order to
+ * keep rendering correctly, a renderer must recreate any lost resources
+ * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method
+ * is a convenient place to do this.
+ *
+ *
+ * @see #setRenderer(Renderer)
+ */
+ public static interface SwapBuffersCallback {
+ public boolean SwapBuffers();
+ }
+
+ public static abstract class Renderer {
+ /**
+ * Called when the surface is created or recreated.
+ * instanceof to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param config the EGLConfig of the created surface. Can be used
+ * to create matching pbuffers.
+ */
+ public abstract void onSurfaceCreated(GL10 gl, EGLConfig config);
+
+ /**
+ * Called when the surface changed size.
+ *
+ * void onSurfaceChanged(GL10 gl, int width, int height) {
+ * gl.glViewport(0, 0, width, height);
+ * // for a fixed camera, set the projection too
+ * float ratio = (float) width / height;
+ * gl.glMatrixMode(GL10.GL_PROJECTION);
+ * gl.glLoadIdentity();
+ * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+ * }
+ *
+ * @param gl the GL interface. Use instanceof to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param width
+ * @param height
+ */
+ public abstract void onSurfaceChanged(GL10 gl, int width, int height);
+
+ /**
+ * Called to draw the current frame.
+ *
+ * void onDrawFrame(GL10 gl) {
+ * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ * //... other gl calls to render the scene ...
+ * }
+ *
+ * @param gl the GL interface. Use instanceof to
+ * test if the interface supports GL11 or higher interfaces.
+ */
+ public abstract void onDrawFrame(GL10 gl);
+
+ public boolean SwapBuffers() {
+ if( mSwapBuffersCallback != null )
+ return mSwapBuffersCallback.SwapBuffers();
+ return false;
+ }
+
+ public void setSwapBuffersCallback( SwapBuffersCallback c ) {
+ mSwapBuffersCallback = c;
+ }
+
+ private SwapBuffersCallback mSwapBuffersCallback = null;
+ }
+
+ /**
+ * An interface for choosing an EGLConfig configuration from a list of
+ * potential configurations.
+ *
+ *
+ *
+ * Using GLSurfaceView
+ * Initializing GLSurfaceView
+ * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
+ * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
+ * more of these methods before calling setRenderer:
+ *
+ *
+ * Choosing an EGL Configuration
+ * A given Android device may support multiple possible types of drawing surfaces.
+ * The available surfaces may differ in how may channels of data are present, as
+ * well as how many bits are allocated to each channel. Therefore, the first thing
+ * GLSurfaceView has to do when starting to render is choose what type of surface to use.
+ * Debug Behavior
+ * You can optionally modify the behavior of GLSurfaceView by calling
+ * one or more of the debugging methods {@link #setDebugFlags(int)},
+ * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
+ * typically they are called before setRenderer so that they take effect immediately.
+ * Setting a Renderer
+ * Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
+ * The renderer is
+ * responsible for doing the actual OpenGL rendering.
+ * Rendering Mode
+ * Once the renderer is set, you can control whether the renderer draws
+ * continuously or on-demand by calling
+ * {@link #setRenderMode}. The default is continuous rendering.
+ * Activity Life-cycle
+ * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients
+ * are required to call {@link #onPause()} when the activity pauses and
+ * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to
+ * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
+ * the OpenGL display.
+ * Handling events
+ *
+ * class MyGLSurfaceView extends GLSurfaceView {
+ *
+ * private MyRenderer mMyRenderer;
+ *
+ * public void start() {
+ * mMyRenderer = ...;
+ * setRenderer(mMyRenderer);
+ * }
+ *
+ * public boolean onKeyDown(int keyCode, KeyEvent event) {
+ * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ * queueEvent(new Runnable() {
+ * // This method will be called on the rendering
+ * // thread:
+ * public void run() {
+ * mMyRenderer.handleDpadCenter();
+ * }});
+ * return true;
+ * }
+ * return super.onKeyDown(keyCode, event);
+ * }
+ * }
+ *
+ *
+ */
+public class GLSurfaceView_SDL extends SurfaceView implements SurfaceHolder.Callback {
+ private final static boolean LOG_THREADS = false;
+ private final static boolean LOG_SURFACE = false;
+ private final static boolean LOG_RENDERER = false;
+ // Work-around for bug 2263168
+ private final static boolean DRAW_TWICE_AFTER_SIZE_CHANGED = true;
+ /**
+ * The renderer only renders
+ * when the surface is created, or when {@link #requestRender} is called.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ */
+ public final static int RENDERMODE_WHEN_DIRTY = 0;
+ /**
+ * The renderer is called
+ * continuously to re-render the scene.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ * @see #requestRender()
+ */
+ public final static int RENDERMODE_CONTINUOUSLY = 1;
+
+ /**
+ * Check glError() after every GL call and throw an exception if glError indicates
+ * that an error has occurred. This can be used to help track down which OpenGL ES call
+ * is causing an error.
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_CHECK_GL_ERROR = 1;
+
+ /**
+ * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_LOG_GL_CALLS = 2;
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView_SDL(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView_SDL(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ }
+
+ /**
+ * Set the glWrapper. If the glWrapper is not null, its
+ * {@link GLWrapper#wrap(GL)} method is called
+ * whenever a surface is created. A GLWrapper can be used to wrap
+ * the GL object that's passed to the renderer. Wrapping a GL
+ * object enables examining and modifying the behavior of the
+ * GL calls made by the renderer.
+ *
+ *
+ *
+ *
+ *
+ * @param renderer the renderer to use to perform OpenGL drawing.
+ */
+ public void setRenderer(Renderer renderer) {
+ checkRenderThreadState();
+ if (mEGLConfigChooser == null) {
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true);
+ }
+ if (mEGLContextFactory == null) {
+ mEGLContextFactory = new DefaultContextFactory();
+ }
+ if (mEGLWindowSurfaceFactory == null) {
+ mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
+ }
+ mGLThread = new GLThread(renderer);
+ mGLThread.start();
+ }
+
+ /**
+ * Install a custom EGLContextFactory.
+ *
+ * class MyGLWrapper implements GLWrapper {
+ * GL wrap(GL gl) {
+ * return new MyGLImplementation(gl);
+ * }
+ * static class MyGLImplementation implements GL,GL10,GL11,... {
+ * ...
+ * }
+ * }
+ *
+ * @see #setGLWrapper(GLWrapper)
+ */
+ public interface GLWrapper {
+ /**
+ * Wraps a gl interface in another gl interface.
+ * @param gl a GL interface that is to be wrapped.
+ * @return either the input argument or another GL object that wraps the input argument.
+ */
+ GL wrap(GL gl);
+ }
+
+ /**
+ * A generic renderer interface.
+ * Threading
+ * The renderer will be called on a separate thread, so that rendering
+ * performance is decoupled from the UI thread. Clients typically need to
+ * communicate with the renderer from the UI thread, because that's where
+ * input events are received. Clients can communicate using any of the
+ * standard Java techniques for cross-thread communication, or they can
+ * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method.
+ * EGL Context Lost
+ * There are situations where the EGL rendering context will be lost. This
+ * typically happens when device wakes up after going to sleep. When
+ * the EGL context is lost, all OpenGL resources (such as textures) that are
+ * associated with that context will be automatically deleted. In order to
+ * keep rendering correctly, a renderer must recreate any lost resources
+ * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method
+ * is a convenient place to do this.
+ *
+ *
+ * @see #setRenderer(Renderer)
+ */
+ public static interface SwapBuffersCallback {
+ public boolean SwapBuffers();
+ }
+
+ public static abstract class Renderer {
+ /**
+ * Called when the surface is created or recreated.
+ * instanceof to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param config the EGLConfig of the created surface. Can be used
+ * to create matching pbuffers.
+ */
+ public abstract void onSurfaceCreated(GL10 gl, EGLConfig config);
+
+ /**
+ * Called when the surface changed size.
+ *
+ * void onSurfaceChanged(GL10 gl, int width, int height) {
+ * gl.glViewport(0, 0, width, height);
+ * // for a fixed camera, set the projection too
+ * float ratio = (float) width / height;
+ * gl.glMatrixMode(GL10.GL_PROJECTION);
+ * gl.glLoadIdentity();
+ * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+ * }
+ *
+ * @param gl the GL interface. Use instanceof to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param width
+ * @param height
+ */
+ public abstract void onSurfaceChanged(GL10 gl, int width, int height);
+
+ /**
+ * Called to draw the current frame.
+ *
+ * void onDrawFrame(GL10 gl) {
+ * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ * //... other gl calls to render the scene ...
+ * }
+ *
+ * @param gl the GL interface. Use instanceof to
+ * test if the interface supports GL11 or higher interfaces.
+ */
+ public abstract void onDrawFrame(GL10 gl);
+
+ public boolean SwapBuffers() {
+ if( mSwapBuffersCallback != null )
+ return mSwapBuffersCallback.SwapBuffers();
+ return false;
+ }
+
+ public void setSwapBuffersCallback( SwapBuffersCallback c ) {
+ mSwapBuffersCallback = c;
+ }
+
+ private SwapBuffersCallback mSwapBuffersCallback = null;
+ }
+
+ /**
+ * An interface for customizing the eglCreateContext and eglDestroyContext calls.
+ *