package com.google.android.diskusage.opengl; import java.util.ArrayList; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import javax.microedition.khronos.opengles.GL10; import android.util.Log; import android.view.SurfaceHolder; public abstract class AbstractRenderingThread extends Thread { public abstract boolean renderFrame(GL10 gl); public abstract void sizeChanged(GL10 gl, int w, int h); public abstract void createResources(GL10 gl); public abstract void releaseResources(GL10 gl); private class ExitException extends RuntimeException { private static final long serialVersionUID = 1L; }; private ArrayList<Runnable> events = new ArrayList<Runnable>(); /** * True when surfaceAvailable callback was received from surfaceHolder and * surfaceDestroyed wasn't yet received. * egl is initialized able to render. */ private boolean surfaceAvailable = false; /** * Window geometry received by SurfaceChangedEvent(). */ private boolean sizeInitialized = false; /** * Repaint was requested due to unfinished animation on drawFrame(). */ private boolean renderLoop = true; /** * Repaint was requested using requestRepaintGPU() call. */ private boolean repaintEvent = false; /** * Stop rendering thread request received. */ private boolean stopRenderingThread = false; private EglTools eglTools; GL10 gl; @Override public void run() { eglTools = new EglTools(); gl = eglTools.getGL(); try { while (true) { runEvents(); renderLoop = renderFrame(gl); eglTools.swapBuffers(); } } catch (ExitException e) { Log.d("diskusage", "rendering thread exited cleanly"); } catch (InterruptedException e) { Log.d("diskusage", "rendering thread was interrupted"); } } public void runEvents() throws InterruptedException { while (true) { Runnable e = null; synchronized (events) { if (events.isEmpty()) { if (stopRenderingThread && !surfaceAvailable) { Log.d("diskusage", "*** Rendering thread is about to finish. ***"); throw new ExitException(); } if (surfaceAvailable && sizeInitialized && !stopRenderingThread && (renderLoop || repaintEvent)) { repaintEvent = false; return; } events.wait(); continue; } e = events.remove(0); } if (e instanceof ControlEvent || !stopRenderingThread) { e.run(); } } } public void addEvent(Runnable event) { synchronized (events) { events.add(event); events.notify(); } } private abstract class ControlEvent implements Runnable { public abstract void run(); } public class SurfaceAvailableEvent extends ControlEvent { private boolean a; private SurfaceHolder holder; public SurfaceAvailableEvent(SurfaceHolder holder, boolean available) { this.holder = holder; a = available; } public void run() { surfaceAvailable = a; if (a) { eglTools.initSurface(holder); createResources(gl); } else { eglTools.destroySurface(holder); releaseResources(gl); } } } public class ExitEvent extends ControlEvent { public void run() { stopRenderingThread = true; releaseResources(gl); } } public class SurfaceChangedEvent extends ControlEvent { SurfaceHolder holder; int w, h; public SurfaceChangedEvent(SurfaceHolder holder, int w, int h) { this.holder = holder; this.w = w; this.h = h; } public void run() { sizeChanged(gl, w, h); sizeInitialized = (w > 0 && h > 0); } } private class EglTools { private final EGL10 egl; private final EGLDisplay eglDisplay; private final EGLContext eglContext; private final EGLConfig eglConfig; private EGLSurface surface; public EglTools() { egl = (EGL10) EGLContext.getEGL(); eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); int[] version = new int[2]; egl.eglInitialize(eglDisplay, version); int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 6, EGL10.EGL_NONE }; final EGLConfig[] matched_configs = new EGLConfig[1]; int num_configs[] = new int[1]; egl.eglChooseConfig(eglDisplay, configSpec, matched_configs, 1, num_configs); eglConfig = matched_configs[0]; eglContext = egl.eglCreateContext( eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, null); } public GL10 getGL() { return (GL10) eglContext.getGL(); } public void initSurface(SurfaceHolder holder) { Log.d("diskusage", "*** init surface ****"); // Note: I haven't found how to avoid race condition with surfaceCreated // and surfaceDestroyed in SurfaceHolder.Callback and the renderer thread. try { surface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, holder, null); egl.eglMakeCurrent(eglDisplay, surface, surface, eglContext); } catch (Exception e) { Log.e("diskusage", "initSurface", e); } } public void destroySurface(SurfaceHolder holder) { Log.d("diskusage", "*** destroy surface ***"); try { egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); egl.eglDestroySurface(eglDisplay, surface); egl.eglDestroyContext(eglDisplay, eglContext); egl.eglTerminate(eglDisplay); } catch (Exception e) { Log.e("diskusage", "destroySurface", e); } } public void swapBuffers() { egl.eglSwapBuffers(eglDisplay, surface); } } public void addEmptyEvent() { synchronized (events) { repaintEvent = true; events.notify(); } }; }