donut snapshot
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 8fa7566..3e9c6a5 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -951,6 +951,8 @@
     v->index = first;
     first &= vertex_cache_t::INDEX_MASK;
     const GLubyte* vp = c->arrays.vertex.element(first);
+    v->obj.z = 0;
+    v->obj.w = 0x10000;
     c->arrays.vertex.fetch(c, v->obj.v, vp);
     c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj);
     c->arrays.perspective(c, v);
@@ -966,6 +968,8 @@
     do {
         v->flags = 0;
         v->index = first++;
+        v->obj.z = 0;
+        v->obj.w = 0x10000;
         c->arrays.vertex.fetch(c, v->obj.v, vp);
         c->arrays.mvp_transform(mvp, &v->clip, &v->obj);
         c->arrays.perspective(c, v);
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index 25c41d0..8ae32cc 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -38,13 +38,14 @@
 static void lightVertexMaterial(ogles_context_t* c, vertex_t* v);
 
 static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s);
-static inline void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b);
 
 static __attribute__((noinline))
 void vnorm3(GLfixed* d, const GLfixed* a);
 
 static inline void vsa3(GLfixed* d,
     const GLfixed* m, GLfixed s, const GLfixed* a);
+static inline void vss3(GLfixed* d,
+    const GLfixed* m, GLfixed s, const GLfixed* a);
 static inline void vmla3(GLfixed* d,
     const GLfixed* m0, const GLfixed* m1, const GLfixed* a);
 static inline void vmul3(GLfixed* d,
@@ -151,18 +152,10 @@
 }
 
 static inline
-void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b) {
-    const GLfixed wa = a[3];
-    const GLfixed wb = b[3];
-    if (ggl_likely(wa == wb)) {
-        d[0] = a[0] - b[0];
-        d[1] = a[1] - b[1];
-        d[2] = a[2] - b[2];
-    } else {
-        d[0] = gglMulSubx(a[0], wb, gglMulx(b[0], wa));
-        d[1] = gglMulSubx(a[1], wb, gglMulx(b[1], wa));
-        d[2] = gglMulSubx(a[2], wb, gglMulx(b[2], wa));
-    }
+void vss3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
+    d[0] = gglMulSubx(m[0], s, a[0]);
+    d[1] = gglMulSubx(m[1], s, a[1]);
+    d[2] = gglMulSubx(m[2], s, a[2]);
 }
 
 static inline
@@ -227,7 +220,7 @@
         const int i = 31 - gglClz(en);
         en &= ~(1<<i);
         light_t& l = c->lighting.lights[i];
-        c->transforms.mvui.point3(&c->transforms.mvui,
+        c->transforms.mvui.point4(&c->transforms.mvui,
                 &l.objPosition, &l.position);
         vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
     }
@@ -318,6 +311,11 @@
         vmul3(l.implicitAmbient.v,  material.ambient.v,  l.ambient.v);
         vmul3(l.implicitDiffuse.v,  material.diffuse.v,  l.diffuse.v);
         vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v);
+        // this is just a flag to tell if we have a specular component
+        l.implicitSpecular.v[3] =
+                l.implicitSpecular.r |
+                l.implicitSpecular.g |
+                l.implicitSpecular.b;
     }
     // emission and ambient for the whole scene
     vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
@@ -343,7 +341,11 @@
         vec4_t n;
         c->arrays.normal.fetch(c, n.v,
             c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK));
-        if (c->transforms.rescaleNormals == GL_NORMALIZE)
+
+        // TODO: right now we handle GL_RESCALE_NORMALS as if ti were
+        // GL_NORMALIZE. We could optimize this by  scaling mvui 
+        // appropriately instead.
+        if (c->transforms.rescaleNormals)
             vnorm3(n.v, n.v);
 
         const material_t& material = c->lighting.front;
@@ -360,7 +362,8 @@
 
             // compute vertex-to-light vector
             if (ggl_unlikely(l.position.w)) {
-                vsub3w(d.v, l.objPosition.v, v->obj.v);
+                // lightPos/1.0 - vertex/vertex.w == lightPos*vertex.w - vertex
+                vss3(d.v, l.objPosition.v, v->obj.w, v->obj.v);
                 sqDist = dot3(d.v, d.v);
                 vscale3(d.v, d.v, gglSqrtRecipx(sqDist));
             } else {
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index f175cda..0b68dc0 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -55,7 +55,7 @@
 static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o);
-static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o);
+static void point4__mvui(transform_t const*, vec4_t* c, vec4_t const* o);
 
 // ----------------------------------------------------------------------------
 #if 0
@@ -209,7 +209,8 @@
 {
     flags = 0;
     ops = OP_ALL;
-    point3 = normal__generic;
+    point3 = point4__mvui;
+    point4 = point4__mvui;
 }
 
 void transform_t::dump(const char* what)
@@ -596,66 +597,19 @@
 
 void transform_state_t::update_mvui()
 {
+    GLfloat r[16];
     const GLfloat* const mv = modelview.top().elements();
-
-    /*
-    When transforming normals, we can use the upper 3x3 matrix, see:
-    http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html
-    */
     
-    // Also note that:
-    //      l(obj) =  tr(M).l(eye) for infinite light
-    //      l(obj) = inv(M).l(eye) for local light
-
-    const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE;
-    if (ggl_likely((!(ops & ~OP_ROTATE)) ||
-        (rescaleNormals && modelview.isRigidBody()))) {
-        // if the modelview matrix is a rigid body transformation
-        // (translation, rotation, uniform scaling), then we can bypass
-        // the inverse by transposing the matrix.
-        GLfloat rescale = 1.0f;
-        if (rescaleNormals == GL_RESCALE_NORMAL) {
-            if (!(ops & ~OP_UNIFORM_SCALE)) {
-                rescale = reciprocalf(mv[I(0,0)]);
-            } else {
-                rescale = rsqrtf(
-                        sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)]));
-            }
-        }
-        GLfixed* const x = mvui.matrix.m;
-        for (int i=0 ; i<3 ; i++) {
-            x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale);
-            x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale);
-            x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale);
-        }
-        mvui.picker();
-        return;
-    }
-
-    GLfloat r[3][3];
-    r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]);
-    r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]);
-    r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]);
-    r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]);
-    r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]);
-    r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]);
-    r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]);
-    r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]);
-    r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]);        
-
-    GLfloat rdet;
-    if (rescaleNormals == GL_RESCALE_NORMAL) {
-        rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2]));
-    } else {
-        rdet = reciprocalf( 
-            r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]);
-    }
+    // TODO: we need a faster invert, especially for when the modelview
+    // is a rigid-body matrix
+    invert(r, mv);
 
     GLfixed* const x = mvui.matrix.m;
-    for (int i=0 ; i<3 ; i++) {
-        x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet);
-        x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet);
-        x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet);
+    for (int i=0 ; i<4 ; i++) {
+        x[I(i,0)] = gglFloatToFixed(r[I(i,0)]);
+        x[I(i,1)] = gglFloatToFixed(r[I(i,1)]);
+        x[I(i,2)] = gglFloatToFixed(r[I(i,2)]);
+        x[I(i,4)] = gglFloatToFixed(r[I(i,3)]);
     }
     mvui.picker();
 }
@@ -783,14 +737,19 @@
     lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]);
 }
 
-void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+void point4__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+    // this used for transforming light positions back to object space.
+    // Lights have 3 components positions, so w is always 1.
+    // however, it is used as a switch for directional lights, so we need
+    // to preserve it.
     const GLfixed* const m = mx->matrix.m;
     const GLfixed rx = rhs->x;
     const GLfixed ry = rhs->y;
     const GLfixed rz = rhs->z;
-    lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]); 
-    lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]);
-    lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]);
+    lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); 
+    lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
+    lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
+    lhs->w = rhs->w;
 }
 
 
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index b6f534b..14a910c 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -900,7 +900,7 @@
         memcpy(textureObject->crop_rect, params, 4*sizeof(GLint));
         break;
     default:
-        ogles_error(c, GL_INVALID_ENUM);
+        texParameterx(target, pname, GLfixed(params[0]), c);
         return;
     }
 }
@@ -919,6 +919,13 @@
     texParameterx(target, pname, param, c);
 }
 
+void glTexParameteri(
+        GLenum target, GLenum pname, GLint param)
+{
+    ogles_context_t* c = ogles_context_t::get();
+    texParameterx(target, pname, GLfixed(param), c);
+}
+
 // ----------------------------------------------------------------------------
 #if 0
 #pragma mark -
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 5ba6b76..23304d5 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -39,7 +39,7 @@
 	GLES_CM/gl.cpp.arm 		\
 #
 
-LOCAL_SHARED_LIBRARIES += libcutils libutils libui libEGL
+LOCAL_SHARED_LIBRARIES += libcutils libEGL
 LOCAL_LDLIBS := -lpthread -ldl
 LOCAL_MODULE:= libGLESv1_CM
 
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 25e31ee..de323b3 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -1052,23 +1052,25 @@
     if (!validate_display_context(dpy, ctx))
         return EGL_FALSE;    
     
+    EGLSurface impl_draw = EGL_NO_SURFACE;
+    EGLSurface impl_read = EGL_NO_SURFACE;
     egl_context_t * const c = get_context(ctx);
     if (draw != EGL_NO_SURFACE) {
         egl_surface_t const * d = get_surface(draw);
         if (!d) return setError(EGL_BAD_SURFACE, EGL_FALSE);
         if (d->impl != c->impl)
             return setError(EGL_BAD_MATCH, EGL_FALSE);
-        draw = d->surface;
+        impl_draw = d->surface;
     }
     if (read != EGL_NO_SURFACE) {
         egl_surface_t const * r = get_surface(read);
         if (!r) return setError(EGL_BAD_SURFACE, EGL_FALSE);
         if (r->impl != c->impl)
             return setError(EGL_BAD_MATCH, EGL_FALSE);
-        read = r->surface;
+        impl_read = r->surface;
     }
     EGLBoolean result = c->cnx->hooks->egl.eglMakeCurrent(
-            dp->dpys[c->impl], draw, read, c->context);
+            dp->dpys[c->impl], impl_draw, impl_read, c->context);
 
     if (result == EGL_TRUE) {
         setGlThreadSpecific(c->cnx->hooks);
diff --git a/opengl/libs/EGL/gpu.cpp b/opengl/libs/EGL/gpu.cpp
index f9dc5f1..4c902c8 100644
--- a/opengl/libs/EGL/gpu.cpp
+++ b/opengl/libs/EGL/gpu.cpp
@@ -118,6 +118,11 @@
         return 0;
     }
 
+    if (info.regs == 0) {
+        LOGD("requestGPU() failed");
+        return 0;
+    }
+
     bool failed = false;
     request_gpu_t* gpu = &gRegions;
     memset(gpu, 0, sizeof(*gpu));
diff --git a/opengl/tests/angeles/Android.mk b/opengl/tests/angeles/Android.mk
index 46958d3..e193483 100644
--- a/opengl/tests/angeles/Android.mk
+++ b/opengl/tests/angeles/Android.mk
@@ -5,7 +5,7 @@
 LOCAL_SRC_FILES:= app-linux.c demo.c.arm
 LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM libui
 LOCAL_MODULE:= angeles
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
 
 
@@ -13,5 +13,5 @@
 LOCAL_SRC_FILES:= gpustate.c
 LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM
 LOCAL_MODULE:= gpustate
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/filter/Android.mk b/opengl/tests/filter/Android.mk
index a448f0d..31b7d9a 100644
--- a/opengl/tests/filter/Android.mk
+++ b/opengl/tests/filter/Android.mk
@@ -12,6 +12,6 @@
 
 LOCAL_MODULE:= test-opengl-filter
 
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/finish/Android.mk b/opengl/tests/finish/Android.mk
index 26836c1..8b46cd7 100644
--- a/opengl/tests/finish/Android.mk
+++ b/opengl/tests/finish/Android.mk
@@ -12,6 +12,6 @@
 
 LOCAL_MODULE:= test-opengl-finish
 
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/lighting1709/Android.mk b/opengl/tests/lighting1709/Android.mk
new file mode 100644
index 0000000..9563e61
--- /dev/null
+++ b/opengl/tests/lighting1709/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LightingTest
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/lighting1709/AndroidManifest.xml b/opengl/tests/lighting1709/AndroidManifest.xml
new file mode 100644
index 0000000..6c23d42
--- /dev/null
+++ b/opengl/tests/lighting1709/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.lightingtest">
+
+    <application>
+        <activity android:name="ClearActivity" android:label="LightingTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
new file mode 100644
index 0000000..3ae8c5c
--- /dev/null
+++ b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2007 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 com.android.lightingtest;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.app.Activity;
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.MotionEvent;
+
+public class ClearActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mGLView = new ClearGLSurfaceView(this);
+        setContentView(mGLView);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mGLView.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mGLView.onResume();
+    }
+    private GLSurfaceView mGLView;
+}
+
+class ClearGLSurfaceView extends GLSurfaceView {
+    public ClearGLSurfaceView(Context context) {
+        super(context);
+        mRenderer = new ClearRenderer();
+        setRenderer(mRenderer);
+    }
+
+    ClearRenderer mRenderer;
+}
+
+class ClearRenderer implements GLSurfaceView.Renderer {
+    public ClearRenderer() {
+    }
+
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        // Do nothing special.
+    }
+
+    public void onSurfaceChanged(GL10 gl, int w, int h) {
+        // Compute the projection matrix
+        gl.glMatrixMode(GL10.GL_PROJECTION);
+        gl.glLoadIdentity();
+
+        // Compute the boundaries of the frustum
+        float fl = (float) (-(w / 2)) / 288;
+        float fr = (float) (w / 2) / 288;
+        float ft = (float) (h / 2) / 288;
+        float fb = (float) (-(h / 2)) / 288;
+
+        // Set the view frustum
+        gl.glFrustumf(fl, fr, fb, ft, 1.0f, 2000.0f);
+
+        // Set the viewport dimensions
+        gl.glMatrixMode(GL10.GL_MODELVIEW);
+        gl.glLoadIdentity();
+        gl.glViewport(0, 0, w, h);
+    }
+
+    public void onDrawFrame(GL10 gl) {
+        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+        final float lightOff[]        = {0.0f, 0.0f,  0.0f, 1.0f};
+        final float lightAmbient[]    = {5.0f, 0.0f,  0.0f, 1.0f};
+        final float lightDiffuse[]    = {0.0f, 2.0f,  0.0f, 0.0f};
+        final float lightPosSpot[]    = {0.0f, 0.0f, -8.0f, 1.0f};
+
+        final float pos[] = {
+                    -5.0f, -1.5f, 0.0f,
+                     0.0f, -1.5f, 0.0f,
+                     5.0f, -1.5f, 0.0f,
+                };
+        
+        final float v[] = new float[9];
+        ByteBuffer vbb = ByteBuffer.allocateDirect(v.length*4);
+        vbb.order(ByteOrder.nativeOrder());
+        FloatBuffer vb = vbb.asFloatBuffer();
+
+        gl.glDisable(GL10.GL_DITHER);
+
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, lightOff, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPosSpot, 0);
+        gl.glEnable(GL10.GL_LIGHT0);
+        
+        gl.glEnable(GL10.GL_LIGHTING);
+
+
+        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+        gl.glNormal3f(0, 0, 1);
+        
+
+        // draw first 3 triangles, without using transforms
+        for (int i=0 ; i<3 ; i++) {
+            v[0] = -1; v[1] =-1; v[2] = -10;
+            v[3] =  0; v[4] = 1; v[5] = -10;
+            v[6] =  1; v[7] =-1; v[8] = -10;
+            for (int j=0 ; j<3 ; j++) {
+                v[j*3+0] -= pos[i*3+0];
+                v[j*3+1] -= pos[i*3+1];
+                v[j*3+2] -= pos[i*3+2];
+            }
+            vb.put(v).position(0);
+            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        }
+        
+        // draw the 2nd batch this time with transforms
+        v[0] = -1; v[1] =-1; v[2] = -10;
+        v[3] =  0; v[4] = 1; v[5] = -10;
+        v[6] =  1; v[7] =-1; v[8] = -10;
+        vb.put(v).position(0);
+        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+
+        // draw lower left triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[0], pos[1], pos[2]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();
+
+        // draw lower middle triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[3], pos[4], pos[5]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();
+
+        // draw lower right triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[6], pos[7], pos[8]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();      
+    }
+
+    public int[] getConfigSpec() {
+        int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
+        return configSpec;      
+    }
+}
+
diff --git a/opengl/tests/textures/Android.mk b/opengl/tests/textures/Android.mk
index a8c6220..8d5f56d 100644
--- a/opengl/tests/textures/Android.mk
+++ b/opengl/tests/textures/Android.mk
@@ -12,6 +12,6 @@
 
 LOCAL_MODULE:= test-opengl-textures
 
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/tritex/Android.mk b/opengl/tests/tritex/Android.mk
index 5cd1f04..76fd8dd 100644
--- a/opengl/tests/tritex/Android.mk
+++ b/opengl/tests/tritex/Android.mk
@@ -12,6 +12,6 @@
 
 LOCAL_MODULE:= test-opengl-tritex
 
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_EXECUTABLE)
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index 011a6ed..9bff0b2 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -4,14 +4,31 @@
 rm -rf out generated
 
 mkdir out
+
+# Create dummy Java files for Android APIs that are used by the code we generate.
+# This allows us to test the generated code without building the rest of Android.
+
 mkdir -p out/javax/microedition/khronos/opengles
 mkdir -p out/com/google/android/gles_jni
+mkdir -p out/android/app
 mkdir -p out/android/graphics
 mkdir -p out/android/opengl
+mkdir -p out/android/content
+mkdir -p out/android/content/pm
+mkdir -p out/android/os
+mkdir -p out/android/util
 
 echo "package android.graphics;" > out/android/graphics/Canvas.java
 echo "public interface Canvas {}" >> out/android/graphics/Canvas.java
 
+echo "package android.app; import android.content.pm.IPackageManager; public class ActivityThread { public static final ActivityThread currentActivityThread() { return null; } public static final String currentPackageName(){ return null; } public static IPackageManager getPackageManager() { return null;} }" > out/android/app/ActivityThread.java
+# echo "package android.content; import android.content.pm.PackageManager; public interface Context { public PackageManager getPackageManager(); }" > out/android/content/Context.java
+echo "package android.content.pm; public class ApplicationInfo {public int targetSdkVersion;}" > out/android/content/pm/ApplicationInfo.java
+echo "package android.content.pm; public interface IPackageManager {ApplicationInfo getApplicationInfo(java.lang.String packageName, int flags) throws android.os.RemoteException;}" > out/android/content/pm/IPackageManager.java
+echo "package android.os; public class Build {public static class VERSION_CODES { public static final int CUPCAKE = 3;};	}" > out/android/os/Build.java
+echo "package android.os; public class RemoteException extends Exception {}" > out/android/os/RemoteException.java
+echo "package android.util; public class Log {public static void w(String a, String b) {} public static void e(String a, String b) {}}" > out/android/util/Log.java
+
 GLFILE=out/javax/microedition/khronos/opengles/GL.java
 cp stubs/jsr239/GLHeader.java-if $GLFILE
 
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index b0997b1..7340357 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -890,16 +890,10 @@
                                 cname +
                                 " = (" +
                                 cfunc.getArgType(cIndex).getDeclaration() +
-                                ") _env->GetDirectBufferAddress(" +
-                                (mUseCPlusPlus ? "" : "_env, ") +
+                                ") getDirectBufferPointer(_env, " +
                                 cname + "_buf);");
                         String iii = "    ";
-                        out.println(iii + indent + "if ( ! " + cname + " ) {");
-                        out.println(iii + iii + indent +
-                                (mUseCPlusPlus ? "_env" : "(*_env)") +
-                                "->ThrowNew(" +
-                                (mUseCPlusPlus ? "" : "_env, ") +
-                                "IAEClass, \"Must use a native order direct Buffer\");");
+                        out.println(iii + indent + "if ( ! " + cname + " ) {");	
                         out.println(iii + iii + indent + "return;");
                         out.println(iii + indent + "}");
                     } else {
diff --git a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
index 3948fd3..e1c09f4 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
@@ -132,6 +132,19 @@
 					   commit ? 0 : JNI_ABORT);
 }
 
+static void *
+getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
+    char* buf = (char*) _env->GetDirectBufferAddress(buffer);
+    if (buf) {
+        jint position = _env->GetIntField(buffer, positionID);
+        jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+        buf += position << elementSizeShift;
+    } else {
+        _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+    }
+    return (void*) buf;
+}
+
 static int
 getNumCompressedTextureFormats() {
     int numCompressedTextureFormats = 0;
diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
index 11c6087..4494643 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
@@ -44,9 +44,11 @@
 static jclass UOEClass;
 static jclass IAEClass;
 static jclass AIOOBEClass;
+static jclass G11ImplClass;
 static jmethodID getBasePointerID;
 static jmethodID getBaseArrayID;
 static jmethodID getBaseArrayOffsetID;
+static jmethodID allowIndirectBuffersID;
 static jfieldID positionID;
 static jfieldID limitID;
 static jfieldID elementSizeShiftID;
@@ -62,13 +64,17 @@
     jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
     bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
 
+    jclass g11impClassLocal = _env->FindClass("com/google/android/gles_jni/GLImpl");
+    G11ImplClass = (jclass) _env->NewGlobalRef(g11impClassLocal);
+
     getBasePointerID = _env->GetStaticMethodID(nioAccessClass,
             "getBasePointer", "(Ljava/nio/Buffer;)J");
     getBaseArrayID = _env->GetStaticMethodID(nioAccessClass,
             "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
     getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass,
             "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
-
+    allowIndirectBuffersID = _env->GetStaticMethodID(g11impClassLocal,
+            "allowIndirectBuffers", "(Ljava/lang/String;)Z");
     positionID = _env->GetFieldID(bufferClass, "position", "I");
     limitID = _env->GetFieldID(bufferClass, "limit", "I");
     elementSizeShiftID =
@@ -118,6 +124,9 @@
     
     *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
             getBaseArrayID, buffer);
+    if (*array == NULL) {
+        return (void*) NULL;
+    }
     offset = _env->CallStaticIntMethod(nioAccessClass,
             getBaseArrayOffsetID, buffer);
     data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0);
@@ -132,6 +141,45 @@
 					   commit ? 0 : JNI_ABORT);
 }
 
+extern "C" {
+extern char*  __progname;
+}
+
+static bool
+allowIndirectBuffers(JNIEnv *_env) {
+    static jint sIndirectBufferCompatability;
+    if (sIndirectBufferCompatability == 0) {
+        jobject appName = _env->NewStringUTF(::__progname);
+        sIndirectBufferCompatability = _env->CallStaticBooleanMethod(G11ImplClass, allowIndirectBuffersID, appName) ? 2 : 1;
+    }
+    return sIndirectBufferCompatability == 2;
+}
+
+static void *
+getDirectBufferPointer(JNIEnv *_env, jobject buffer) {
+    if (!buffer) {
+        return NULL;
+    }
+    void* buf = _env->GetDirectBufferAddress(buffer);
+    if (buf) {
+        jint position = _env->GetIntField(buffer, positionID);
+        jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+        buf = ((char*) buf) + (position << elementSizeShift);
+    } else {
+        if (allowIndirectBuffers(_env)) {
+            jarray array = 0;
+            jint remaining;
+            buf = getPointer(_env, buffer, &array, &remaining);
+            if (array) {
+                releasePointer(_env, array, buf, 0);
+            }
+        } else {
+            _env->ThrowNew(IAEClass, "Must use a native order direct Buffer");
+        }
+    }
+    return buf;
+}
+
 static int
 getNumCompressedTextureFormats() {
     int numCompressedTextureFormats = 0;
diff --git a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
index db3a41c..fe60c5d 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
+++ b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl
@@ -18,6 +18,12 @@
 
 package com.google.android.gles_jni;
 
+import android.app.ActivityThread;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.os.Build;
+import android.util.Log;
+
 import java.nio.Buffer;
 import javax.microedition.khronos.opengles.GL10;
 import javax.microedition.khronos.opengles.GL10Ext;
@@ -42,7 +48,28 @@
     public GLImpl() {
     }
 
-     public void glGetPointerv(int pname, java.nio.Buffer[] params) {
-         throw new UnsupportedOperationException("glGetPointerv");
-     }
+    public void glGetPointerv(int pname, java.nio.Buffer[] params) {
+        throw new UnsupportedOperationException("glGetPointerv");
+    }
+
+    private static boolean allowIndirectBuffers(String appName) {
+        boolean result = false;
+        int version = 0;
+        IPackageManager pm = ActivityThread.getPackageManager();
+        try {
+            ApplicationInfo applicationInfo = pm.getApplicationInfo(appName, 0);
+            if (applicationInfo != null) {
+                version = applicationInfo.targetSdkVersion;
+            }
+        } catch (android.os.RemoteException e) {
+            // ignore
+        }
+        Log.e("OpenGLES", String.format(
+            "Application %s (SDK target %d) called a GL11 Pointer method with an indirect Buffer.",
+            appName, version));
+        if (version <= Build.VERSION_CODES.CUPCAKE) {
+            result = true;
+        }
+        return result;
+    }