summaryrefslogtreecommitdiffstats
path: root/opengl/java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:43 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:43 -0800
commitf013e1afd1e68af5e3b868c26a653bbfb39538f8 (patch)
tree7ad6c8fd9c7b55f4b4017171dec1cb760bbd26bf /opengl/java
parente70cfafe580c6f2994c4827cd8a534aabf3eb05c (diff)
downloadframeworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.zip
frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.gz
frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.bz2
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'opengl/java')
-rw-r--r--opengl/java/android/opengl/GLDebugHelper.java2
-rw-r--r--opengl/java/android/opengl/GLSurfaceView.java661
-rw-r--r--opengl/java/android/opengl/GLU.java15
3 files changed, 669 insertions, 9 deletions
diff --git a/opengl/java/android/opengl/GLDebugHelper.java b/opengl/java/android/opengl/GLDebugHelper.java
index 09b5878..18a1e73 100644
--- a/opengl/java/android/opengl/GLDebugHelper.java
+++ b/opengl/java/android/opengl/GLDebugHelper.java
@@ -65,7 +65,7 @@ public class GLDebugHelper {
public static final int CONFIG_CHECK_THREAD = (1 << 1);
/**
- * Check if all calls are on the same thread.
+ * Print argument names when logging GL Calls.
*/
public static final int CONFIG_LOG_ARGUMENT_NAMES = (1 << 2);
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
new file mode 100644
index 0000000..42c53f9
--- /dev/null
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.opengl;
+
+import java.io.IOException;
+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;
+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.GL;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation. This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+ public final static int RENDERMODE_WHEN_DIRTY = 0;
+ public final static int RENDERMODE_CONTUOUSLY = 1;
+
+ /**
+ * Check glError() after every GL call.
+ */
+ public final static int DEBUG_CHECK_GL_ERROR = 1;
+
+ /**
+ * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
+ */
+ public final static int DEBUG_LOG_GL_CALLS = 2;
+
+ public GLSurfaceView(Context context) {
+ super(context);
+ init();
+ }
+
+ public GLSurfaceView(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 to a new value. The current glWrapper is used
+ * whenever a surface is created. 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 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;
+ }
+
+ public int getDebugFlags() {
+ return mDebugFlags;
+ }
+
+ /**
+ * Set the renderer associated with this view. Can only be called once.
+ * @param renderer
+ */
+ public void setRenderer(Renderer renderer) {
+ if (mGLThread != null) {
+ throw new IllegalStateException("setRenderer has already been called for this instance.");
+ }
+ mGLThread = new GLThread(renderer);
+ mGLThread.start();
+ }
+
+ /**
+ * Set the rendering mode. When the renderMode is
+ * RENDERMODE_CONTINUOUSLY, the renderer is called
+ * repeatedly to re-render the scene. When the rendermode
+ * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
+ * is created, or when requestRender is called. Defaults to RENDERMODE_CONTINUOUSLY.
+ * @param renderMode one of the RENDERMODE_X constants
+ */
+ 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 true if the renderer will render continuously.
+ */
+ public int getRenderMode() {
+ return mGLThread.getRenderMode();
+ }
+
+ /**
+ * Request that the renderer render a frame. May be called
+ * from any thread. Must not be called before a renderer has been set.
+ * This method is typically used when the render mode has been set to
+ * false, so that frames are only rendered on demand.
+ */
+ public void requestRender() {
+ mGLThread.requestRender();
+ }
+
+ public void surfaceCreated(SurfaceHolder holder) {
+ mGLThread.surfaceCreated();
+ }
+
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return
+ mGLThread.surfaceDestroyed();
+ }
+
+ 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.
+ */
+ 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.
+ */
+ public void onResume() {
+ mGLThread.onResume();
+ }
+
+ /**
+ * 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) {
+ mGLThread.queueEvent(r);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mGLThread.requestExitAndWait();
+ }
+
+ // ----------------------------------------------------------------------
+
+ public interface GLWrapper {
+ GL wrap(GL gl);
+ }
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * A generic renderer interface.
+ */
+ public interface Renderer {
+ /**
+ * @return the EGL configuration specification desired by the renderer.
+ */
+ int[] getConfigSpec();
+
+ /**
+ * Surface created.
+ * Called when the surface is created. Called when the application
+ * starts, and whenever the GPU is reinitialized. This will
+ * typically happen when the device awakes after going to sleep.
+ * Set your textures here.
+ * @param gl the GL interface. Use <code>instanceof</code> 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.
+ */
+ void onSurfaceCreated(GL10 gl, EGLConfig config);
+ /**
+ * Surface changed size.
+ * Called after the surface is created and whenever
+ * the OpenGL ES surface size changes. Set your viewport here.
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param width
+ * @param height
+ */
+ void onSurfaceChanged(GL10 gl, int width, int height);
+ /**
+ * Draw the current frame.
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ */
+ void onDrawFrame(GL10 gl);
+ }
+
+ /**
+ * An EGL helper class.
+ */
+
+ private class EglHelper {
+ public EglHelper() {
+
+ }
+
+ /**
+ * Initialize EGL for a given configuration spec.
+ * @param configSpec
+ */
+ public void start(int[] configSpec){
+ /*
+ * 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);
+
+ EGLConfig[] configs = new EGLConfig[1];
+ int[] num_config = new int[1];
+ mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1,
+ num_config);
+ mEglConfig = configs[0];
+
+ /*
+ * 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);
+ }
+
+ if ((mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS))!= 0) {
+ int configFlags = 0;
+ Writer log = null;
+ if ((mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
+ configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
+ }
+ if ((mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {
+ log = new LogWriter();
+ }
+ gl = GLDebugHelper.wrap(gl, configFlags, log);
+ }
+ 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 {
+ GLThread(Renderer renderer) {
+ super();
+ mDone = false;
+ mWidth = 0;
+ mHeight = 0;
+ mRequestRender = true;
+ mRenderMode = RENDERMODE_CONTUOUSLY;
+ mRenderer = renderer;
+ setName("GLThread");
+ }
+
+ @Override
+ public void run() {
+ /*
+ * When the android framework launches a second instance of
+ * an activity, the new instance's onCreate() method may be
+ * called before the first instance returns from onDestroy().
+ *
+ * This semaphore ensures that only one instance at a time
+ * accesses EGL.
+ */
+ try {
+ try {
+ sEglSemaphore.acquire();
+ } catch (InterruptedException e) {
+ return;
+ }
+ guardedRun();
+ } catch (InterruptedException e) {
+ // fall thru and exit normally
+ } finally {
+ sEglSemaphore.release();
+ }
+ }
+
+ private void guardedRun() throws InterruptedException {
+ mEglHelper = new EglHelper();
+ /*
+ * Specify a configuration for our opengl session
+ * and grab the first configuration that matches is
+ */
+ int[] configSpec = mRenderer.getConfigSpec();
+ mEglHelper.start(configSpec);
+
+ GL10 gl = null;
+ boolean tellRendererSurfaceCreated = true;
+ boolean tellRendererSurfaceChanged = true;
+
+ /*
+ * This is our main activity thread's loop, we go until
+ * asked to quit.
+ */
+ while (!mDone) {
+
+ /*
+ * Update the asynchronous state (window size)
+ */
+ int w, h;
+ boolean changed;
+ boolean needStart = false;
+ synchronized (this) {
+ Runnable r;
+ while ((r = getEvent()) != null) {
+ r.run();
+ }
+ if (mPaused) {
+ mEglHelper.finish();
+ needStart = true;
+ }
+ while (needToWait()) {
+ wait();
+ }
+ if (mDone) {
+ break;
+ }
+ changed = mSizeChanged;
+ w = mWidth;
+ h = mHeight;
+ mSizeChanged = false;
+ mRequestRender = false;
+ }
+ if (needStart) {
+ mEglHelper.start(configSpec);
+ tellRendererSurfaceCreated = true;
+ changed = true;
+ }
+ if (changed) {
+ gl = (GL10) mEglHelper.createSurface(getHolder());
+ tellRendererSurfaceChanged = true;
+ }
+ if (tellRendererSurfaceCreated) {
+ mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+ tellRendererSurfaceCreated = false;
+ }
+ if (tellRendererSurfaceChanged) {
+ mRenderer.onSurfaceChanged(gl, w, h);
+ tellRendererSurfaceChanged = false;
+ }
+ if ((w > 0) && (h > 0)) {
+ /* draw a frame here */
+ mRenderer.onDrawFrame(gl);
+
+ /*
+ * Once we're done with GL, we need to call swapBuffers()
+ * to instruct the system to display the rendered frame
+ */
+ mEglHelper.swap();
+ }
+ }
+
+ /*
+ * clean-up everything...
+ */
+ mEglHelper.finish();
+ }
+
+ private boolean needToWait() {
+ if (mDone) {
+ return false;
+ }
+
+ if (mPaused || (! mHasSurface)) {
+ return true;
+ }
+
+ if ((mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTUOUSLY))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void setRenderMode(int renderMode) {
+ if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTUOUSLY)) ) {
+ throw new IllegalArgumentException("renderMode");
+ }
+ synchronized(this) {
+ mRenderMode = renderMode;
+ if (renderMode == RENDERMODE_CONTUOUSLY) {
+ 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<Runnable> mEventQueue = new ArrayList<Runnable>();
+ private EglHelper mEglHelper;
+ }
+
+ static class LogWriter extends Writer {
+
+ @Override public void close() {
+ flushBuilder();
+ }
+
+ @Override public void flush() {
+ flushBuilder();
+ }
+
+ @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') {
+ flushBuilder();
+ }
+ else {
+ mBuilder.append(c);
+ }
+ }
+ }
+
+ private void flushBuilder() {
+ if (mBuilder.length() > 0) {
+ Log.v("GLSurfaceView", mBuilder.toString());
+ mBuilder.delete(0, mBuilder.length());
+ }
+ }
+
+ private StringBuilder mBuilder = new StringBuilder();
+ }
+
+ private static final Semaphore sEglSemaphore = new Semaphore(1);
+ private boolean mSizeChanged = true;
+
+ private GLThread mGLThread;
+ private GLWrapper mGLWrapper;
+ private int mDebugFlags;
+}
diff --git a/opengl/java/android/opengl/GLU.java b/opengl/java/android/opengl/GLU.java
index ff2741a..0152f42 100644
--- a/opengl/java/android/opengl/GLU.java
+++ b/opengl/java/android/opengl/GLU.java
@@ -85,18 +85,17 @@ public class GLU {
fy *= rlf;
fz *= rlf;
- // Normalize up
- float rlup = 1.0f / Matrix.length(upX, upY, upZ);
- upX *= rlup;
- upY *= rlup;
- upZ *= rlup;
-
// compute s = f x up (x means "cross product")
-
float sx = fy * upZ - fz * upY;
float sy = fz * upX - fx * upZ;
float sz = fx * upY - fy * upX;
-
+
+ // and normalize s
+ float rls = 1.0f / Matrix.length(sx, sy, sz);
+ sx *= rls;
+ sy *= rls;
+ sz *= rls;
+
// compute u = s x f
float ux = sy * fz - sz * fy;
float uy = sz * fx - sx * fz;